Executing your custom .ABC

Here's where the fun stuff comes in. As we saw in a Getting to the Forth Interpreter, there is quite a bit of code that is executed prior to ever touching your custom JavaScript. This post is concerned with going from your.abc to the Forth interpreter. It's pretty convoluted, so I hope you have your coffee ready. Starting from runShell:

avmshell.cpp
PoolObject* shellPool;

void Shell::runShell(Thrower& p_thrower) {
// stuff
initBuiltinPool(a_thrower);
initShellPool(a_thrower);

handleActionPool(shellPool, domain, toplevel, codeContext, p_thrower);
}


Looking at what shellPool is:
avmshell.cpp
void Shell::initShellPool(Thrower& p_thrower)
{
avmplus::ScriptBuffer code = newReadOnlyScriptBuffer(shell_abc_data, sizeof(shell_abc_data));
shellPool = parseActionBlock(code, NULL, shell_natives, shell_offset, p_thrower);
}

The shell_abc_data is located in shell_min.cpp which is defined as:
shell_min.cpp
const unsigned char shell_abc_data[3248] = {
16, 0, 46, 0, 2, 0, 1, 1, 102, 6, 79, 98, 106, 101, 99, 116,
.......
These numbers are JavaScript ABC opcodes which represent the ActionScript in /shell. Specifically, by the Python Script shell/shell.py. Now looking into Main.as:

main.as
var d:Domain = Domain.currentDomain
var files:Array = getfiles();
for (var i:int = 0; i < files.length; ++i)
{
d.load(files[i])
}

With EcmaScript 4, you can optionally statically type variables, which is what the : symbol means. Now looking at domain.as:

domain.as
public function load(filename:String)
{
return _init(_readAndLoad(filename));
}

private function _readAndLoad(filename:String)
{
var ba:ByteArray = ByteArray.readFile(filename);
return _load(ba, 0, ba.length);
}

[forth(word="w_init")]
private native function _init(g:Object)

Remeber all these .as files are already represented in the shell_min.cpp as bytecode. Therefore, they are being executed by the Forth interpreter. We'll look into native functions in a minute. Looking at ByteArray.readFile now:

ByteArray.as
public static function readFile(filename:String):ByteArray
{
_checkNull(filename,"filename")
var ba:ByteArray = new ByteArray()
if (!ba._readFile(filename))
throw makeError(Error, 1500 /* kFileOpenError */, filename)
return ba;
}


Which again goes to a native function:
private native function _readFile(filename:String):Boolean;
Let's see how a native function works, specifically the ByteArray::_readFile. Looking at the conveniently named ByteArrayGlue.cpp:

AVMPLUS_NATIVE_METHOD(bool, flash_utils_ByteArray_private__readFile)
{
ScriptObject* self = args->self;
AvmAssert(args->filename != NULL);
FILE *fp = fopen(StringNullTerminatedUTF8(args->self->gc(), args->filename).c_str(), "rb");
}

I'll have an in depth look at how "native" methods are called in a little bit.