Native Methods in Tamarin

Many of the native support libraries in Tamarin are written in ActionScript. Classes that come with the language such as Math, Object, String are partially written in ActionScript and are collectively known as the Builtin types. You can see all the definitions in core/*.as. (e.g. core/ However, the rest of the VM is written in C. The next question is, how are those methods declared/implemented in AS3 actually integrated with the rest of the VM? To explain this, lets use the example of the AS3 function Math::abs().
[native(cls="MathClass", instance="double", methods="auto")]
public final class Math
public native static function abs (x:Number) :Number

The "final" operator means it is illegal to subclass Math. This also means that Math.abs() can be "early bound", or resolved immediately without a runtime lookup. This early binding is done by the ASC compiler. I'm not sure what ASC does, but the already has which method on what object to call in the bytecode. However, this information has to somehow be implemented in Tamarin. That's where comes in! is the glue between the ABC and the C files required by Tamarin itself. reads the file and outputs C files used within Tamarin. All the files that have the comment "machine generated" such as builtin.cpp are emitted by NativeGen. Builtin.cpp contains a bunch of "thunks", or trampoline functions used during method invocation. When a native method is called, the AS3 method actually calls a C thunk, which then finally calls the requested method. Therefore, Tamarin has a mapping from ActionScript 3 method->C thunk. This map is created in builtin.cpp via Macros:

AVMTHUNK_NATIVE_FUNCTION(native_script_function_isXMLName, Toplevel::isXMLName)
AVMTHUNK_NATIVE_METHOD(Math_abs, MathClass::abs)

Now let's reconstruct the macros and see what's being built for our example of the builtin map and the MathClass::abs method:

static const NativeMethodInfo NAME##_methodEntries[] = {
//static const NativeMethodInfo builtin__methodEntries[] = {

Everything below is going to be wrapped in this array of method entries. Lets see how the method definition expands:

Step 1:
AVMTHUNK_NATIVE_METHOD(Math_abs, MathClass::abs)


_AVMTHUNK_NATIVE_METHOD(ScriptObject, Math_abs, MathClass::abs)

Step 2:
{ { _NATIVE_METHOD_CAST_PTR(CLS, &IMPL) }, (AvmThunkNativeThunker)avmplus::NativeID::METHID##_thunk, avmplus::NativeID::METHID },

{ { _NATIVE_METHOD_CAST_PTR(ScriptObject, &MathClass::abs) }, (AvmThunkNativeThunker)avmplus::NativeID::Math_abs_thunk, avmplus::Math_abs) }

Step 3:

// reinterpret_cast allows anything to be cast to anything!

{ { reinterpret_cast<AvmThunkNativeMethodHandler>((void(ScriptObject::*)())(&MathClass::abs)) }, (AvmThunkNativeThunker)avmplus::NativeID::Math_abs_thunk, avmplus::Math_abs) }

Finally we close off the array:

{ { NULL }, NULL, -1 } };

When all the macros are expanded, we get the following:

static const NativeMethodInfo NAMEbuiltin_methodEntries[] { // BEGIN_NATIVE_METHODS
{ { reinterpret_cast<AvmThunkNativeMethodHandler>((void(ScriptObject::*)())(&MathClass::abs)) }, (AvmThunkNativeThunker)avmplus::NativeID::Math_abs_thunk, avmplus::Math_abs) }
{ { NULL }, NULL, -1 } // END_NATIVE_METHOds

Why is MathClass cast to a ScriptObject? Let's look at the inheritance chain of MathClass:

class MathClass : public ClassClosure
class ClassClosure : public ScriptObject

Finally the mapping of avmplus::NativeID's to their thunks is created during the startup phase of Tamarin:

void AvmCore::initBuiltinPool()
builtinPool = AVM_INIT_BUILTIN_ABC(builtin, this);

avmplus::NativeID::initBuiltinABC_##MAPNAME((CORE), (CORE)->builtinDomain, NULL)

PoolObject* initBuiltinABC_##NAME(AvmCore* core, Domain* domain, const List<Stringp, LIST_RCObjects>* includes) { \
NativeInitializer ninit(core, \
avmplus::NativeID::NAME##_abc_data, \
avmplus::NativeID::NAME##_abc_length, \
avmplus::NativeID::NAME##_abc_method_count, \
avmplus::NativeID::NAME##_abc_class_count); \
ninit.fillInClasses(NAME##_classEntries); \
ninit.fillInMethods(NAME##_methodEntries); \ fill in the builtin methodEntries
return ninit.parseBuiltinABC(domain, includes); \

Now we have one big mapping of ActionScript methods to their native thunks. We also have a mapping of the method MathClass::abs() to this AvmThunkNativeThunker thing. If we look at Math_abs_thunk we see:

#define Math_abs_thunk builtin_d2d_od_thunk

Finally, this means anytime we call Math.abs() in ActionScript, the builin_d2d_od_thunk() is executed. How does the know which thunker to call? I think it is based on the method signature. It groups similar method signatures together into the same thunker. When the Math.abs() is finally executed, the thunker is called first. The thunker then finally resolves the actual Math.abs() method implemented and executes it. If we actually look at the generated thunks, we get something like this:

// Date_private__setTime
// Math_cos
// Math_ceil
// Math_acos
// Math_abs
// Math_atan
// Math_asin
// Math_exp
// Math_log
// Math_round
// Math_tan
// Math_sin
// Math_sqrt
// Math_floor
double builtin_d2d_od_thunk(AvmMethodEnv env, uint32_t argc, AvmBox* argv)
enum {
argoff0 = 0
, argoff1 = argoff0 + AvmThunkArgSize_AvmObject
typedef AvmRetType_double (AvmObjectT::*FuncType)(double);
const FuncType func = reinterpret_cast<FuncType>(AVMTHUNK_GET_METHOD_HANDLER(env));

// func will point to Math_abs
return (*(AvmThunkUnbox_AvmReceiver(AvmObject, argv[argoff0])).*(func))( // Call Math::abs()

Finally we can get the absolute number:

double MathClass::abs(double x)
return MathUtils::abs(x);