HaxeFoundation / HaxeFoundation/haxe

[flash] Indirect calling of dynamic function containing anonymous function with local field access

Open
#10,299 1 comment 0 reactions 0 assignees View on GitHub
bug platform-flash
Dominant language
Haxe
Stars
6.9k
Forks
715
Avg merge
2d 2h
Merged PRs (30d)
11

Description

The following code will throw a runtime error at the invocation of `indirectCallToDynamicFunction`.

```haxe
package;

class DynamicFunctions
{
public static function main()
new DynamicFunctions();

var localVar = 0;

public function new() {
var indirectCallToDynamicFunction = dynamicFunction;
indirectCallToDynamicFunction(); //error here
}

public function runAnonymousFunction(func:()->Void)
func();

public dynamic function dynamicFunction() {
runAnonymousFunction(function() {
trace("localVar=" + localVar + ", called from dynamicFunction");
});
}
}
```

HXML:
```
-main DynamicFunctions
-debug
-swf dynamic.swf
```

The error:
```
TypeError: Error #1034: Type Coercion failed: cannot convert global@433f3e1 to DynamicFunctions.
at MethodInfo-1()[DynamicFunctions.hx:18]
at DynamicFunctions()[DynamicFunctions.hx:12]
at DynamicFunctions$/main()[DynamicFunctions.hx:6]
at boot_e198/init()[?:1]
at flash::Boot/start()[...\haxe\4.2.1-win64\std/flash/Boot.hx:70]
at boot_e198()[...\haxe\4.2.1-win64\std/flash/Boot.hx:40]
```

The decompiled swf looks like this
```as3
package
{
import haxe.Log;

public class DynamicFunctions
{
public var localVar:int;
public var dynamicFunction:Function;

public function DynamicFunctions()
{
if(!dynamicFunction)
{
dynamicFunction = function():void
{
var _gthis:DynamicFunctions = this; //this is the line that throws the error
runAnonymousFunction(function():void
{
Log.trace("localVar=" + _gthis.localVar + ", called from dynamicFunction",{
"fileName":"DynamicFunctions.hx",
"lineNumber":20,
"className":"DynamicFunctions",
"methodName":"dynamicFunction"
});
});
};
}
localVar = 0;
var indirectCallToDynamicFunction:Function = dynamicFunction;
indirectCallToDynamicFunction();
}

public static function main() : void
{
new DynamicFunctions();
}

public function runAnonymousFunction(func:Function) : void
{
func();
}
}
}
```

Here are the relevant opcodes generated at `indirectCallToDynamicFunction();`
```
getlocal1 //puts indirectCallToDynamicFunction onto the stack
getglobalscope //puts the global object onto the stack
call
```

According to the AVM2 Overview, the `call` instruction takes the first item on the stack to be the function, the second item to be the "this" value, and optionally a specified number of additional items to be used as parameters for the function call.

The stack has `indirectCallToDynamicFunction, global object` in it, so the global object will be taken as the "this" value, resulting in the type coercion error when this line of code is run in the called function:
```as3
var _gthis:DynamicFunctions = this;
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.