Skip to content

Commit

Permalink
avm2: Avoid marking interface method definitions with VM_INTERNAL
Browse files Browse the repository at this point in the history
Our asc.jar doesn't seem to apply a version suffix to namespaces for
interface method definitions. This was causing these methods to
get marked as VM_INTERNAL when we loaded playerglobals, preventing SWF
from invoking these methods through the interface (e.g. having a
variable of type `IEventDispatcher`, and calling `dispatchEvent` on it)
  • Loading branch information
Aaron1011 authored and Dinnerbone committed Dec 18, 2023
1 parent 98ebc33 commit 5c37533
Show file tree
Hide file tree
Showing 16 changed files with 74 additions and 2 deletions.
14 changes: 14 additions & 0 deletions core/src/avm2/globals/flash/desktop/IFilePromise.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package flash.desktop {
import flash.utils.IDataInput;
import flash.events.ErrorEvent;

[API("668")] // AIR 2.0
public interface IFilePromise {
function get isAsync():Boolean;
function get relativePath():String;
function close():void;
function open():IDataInput;
function reportError(e:ErrorEvent):void

}
}
1 change: 1 addition & 0 deletions core/src/avm2/globals/globals.as
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ include "flash/utils/ByteArray.as"
include "flash/utils/Dictionary.as"
include "flash/desktop/ClipboardFormats.as"
include "flash/desktop/ClipboardTransferMode.as"
include "flash/desktop/IFilePromise.as"

include "flash/events/IEventDispatcher.as"
include "flash/events/EventDispatcher.as"
Expand Down
15 changes: 14 additions & 1 deletion core/src/avm2/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,20 @@ impl<'gc> Namespace<'gc> {
}

if is_playerglobals {
if !has_version_mark && is_public && is_versioned_url(namespace_name) {
if !has_version_mark
// NOTE - we deviate from avmplus by only appling VM_INTERNAL to unmarked playerglobal namespaces
// that use 'Package', instead of both 'Namespace' and 'Package'. This is because our version
// of asc.jar does *not* apply version markers to method definitions in interfaces (unlike
// method definitions in normal classes). Interface method definitions in playerglobals always
// seem to be emitted with a 'Namespace' namespace, so we can avoid marking them as 'VM_INTERNAL'
// by only applying this check to 'Package' namespaces.
// If playerglobals ever ends up with initialization code that uses a 'Namespace' namespace,
// (e.g. `initproperty QName(Namespace("flash.net"),"DatagramSocket")`), then this would break.
// However, it would do immediately during playerglobals loading, so it would be guaranteed
// to be caught by our test suite.
&& matches!(abc_namespace, AbcNamespace::Package(_))
&& is_versioned_url(namespace_name)
{
api_version = ApiVersion::VM_INTERNAL;
}
// In avmplus, this conversion is done later in in 'getValidApiVersion'
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/swfs/avm2/air_hidden_lookup/Test.as
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import flash.utils.getDefinitionByName;

const HIDDEN_CLASSES: Array = ["flash.net.DatagramSocket"];
const HIDDEN_CLASSES: Array = ["flash.net.DatagramSocket", "flash.desktop.IFilePromise"];

for each (var klass in HIDDEN_CLASSES) {
try {
Expand Down
1 change: 1 addition & 0 deletions tests/tests/swfs/avm2/air_hidden_lookup/output.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
flash.net.DatagramSocket is inaccessible from Flash Player
flash.desktop.IFilePromise is inaccessible from Flash Player
Binary file modified tests/tests/swfs/avm2/air_hidden_lookup/test.swf
Binary file not shown.
14 changes: 14 additions & 0 deletions tests/tests/swfs/avm2/air_ifilepromise/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package {

import flash.display.MovieClip;
import flash.utils.getDefinitionByName;

public class Test extends MovieClip {


public function Test() {
trace("IFilePromise: " + getDefinitionByName("flash.desktop.IFilePromise"));
}
}

}
1 change: 1 addition & 0 deletions tests/tests/swfs/avm2/air_ifilepromise/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IFilePromise: [class IFilePromise]
Binary file added tests/tests/swfs/avm2/air_ifilepromise/test.fla
Binary file not shown.
Binary file added tests/tests/swfs/avm2/air_ifilepromise/test.swf
Binary file not shown.
4 changes: 4 additions & 0 deletions tests/tests/swfs/avm2/air_ifilepromise/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
num_ticks = 1

[player_options]
runtime = "AIR"
22 changes: 22 additions & 0 deletions tests/tests/swfs/avm2/eventdispatcher_interface_invoke/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package {

import flash.display.MovieClip;
import flash.events.IEventDispatcher;
import flash.events.Event;


public class Test extends MovieClip {


public function Test() {
this.invokeDispatcher(new MovieClip());
}

public function invokeDispatcher(dispatch: IEventDispatcher) {
// This method is invoked on the interface, not a concrete class
dispatch.dispatchEvent(new Event("myEvent"));
trace("Called dispatchEvent!");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Called dispatchEvent!
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_ticks = 1

0 comments on commit 5c37533

Please sign in to comment.