Host object APIs
This page describes the current native model in tamarin tracing.
Everything works by running the GlobalOptimizer on abc's to be compiled into the host application. Typically there are two abc's: the application specific host objects and the avmplus builtin objects. GlobalOptimizer is like a linker and merges the abcs and performs various optimizations. It also produces a .h and .cpp file that provide all the C++ native linkage.
The philosophy behind tamarin tracing is to expose as much to the trace JIT optimizer as possible, as such the native API is very limited. For instance you can't do these things from C++:
- try/catch exceptions
- get/set properties
- invoke arbritrary methods/properties
- construct objects
Ideally as much as possible is written in JS and native code is only invoked when absolutely needed.
Contents
Startup
To bootstrap the VM GlobalOptimizer generates an entry point. Creating a VM looks like this:
using namespace avmplus; Configuration vm_config; //vm_config.optimizer = 0; //vm_config.verbose = 1; avm vm = host_init(gc, vm_config, host_abc_data, sizeof(host_abc_data));
The "host" string is provided to the GlobalOptimizer invocation. The generated header introduces all the "host_" symbols. The data can be provided as is or the host can provide alternate data, for instance maybe the data isn't compiled into the application but loaded from the disk. "avm" is an opaque pointer that's really an AbcEnv under the covers but that shouldn't be exposed.
The ultimate goal is to the have the C++ hosting API entirely based on the generated headers and have none of the VM's internals exposed to the host application.
Storing C++ pointers in JS
// declare a native type that can be used in JS native class ScriptPlayer;
class Foo { // this reserves space for my native pointer var foo:ScriptPlayer; }
Rules:
- value is always undefined, ie the pointer value is opaque to JS
- values can be stored/retrieved from Array, Object and Dictionary (including weak keys)
JS to C++
JS:
class Foo { private native function callout():void; }
C++:
#include "GENERATED_HEADER.h" namespace avmplus { AVMPLUS_NATIVE_METHOD(void, Foo_private_callout) { ... } }
Rules:
- No native constructors
- No rest arguments
- No varargs
- C++ must be in avmplus namespace, the builtin abcs can be merged so the generated header contains the builtins from avmplus core.
C++ to JS
JS:
[native] function do_something(foo:ScriptPlayer):void { try { ... } catch(e) {} }
C++:
ScriptPlayer *sp; avmplus::host_do_something(vm, sp);
Rules:
- Must catch exceptions (what if they want to throw? TBD)
- toplevel functions only (no class functions static or instance) this keeps the implementation simple
- the vm pointer is what the _init routine returns
Types
- JS == C++
- void == void
- * == BoxReturnType
- int == int32_t
- uint == uint32_t
- Boolean == bool
- String == Stringp
- Namespace == Namespacep
- Number == double
- UDT == ScriptObjectp
Having every host class exposed as a ScriptObjectp feels bad. Currently we're just doing things like:
typedef FooBar ScriptObject;
So that C++ code can use FooBar* instead of ScriptObjectp. We'd like to make this even more type-safe. Perhaps something like:
template<const char *TNAME> class NativeObject { ScriptObjectp obj; NativeObject(ScriptObjectp obj) :obj(obj) { ASSERT_TYPE(obj, TNAME); } }
typedef NativeObject<"FooBar"> FooBar;
So everytime you assign a ScriptObjectp into a FooBar the type will be validated, ASSERT_TYPE can be made to be a DEBUG only thing.
Misc
String
Currently we don't want to expose AvmCore to the host, but right now you need to do this to create a String:
Stringp str = ((AvmCore*)((AbcEnv*)vm)->core())->constantString("c string")
We need to come up with something more transparent, maybe
Stringp str = vm->createString("c string")
Domain
But wait how do I dynamically load code into the VM? Like abc compiled dynamically or loaded from a swf or something. Construct a ByteArray from native code using and pass it into a callin that uses Domain.loadBytes.
Want to get a Class object from a String to construct a class from a dynamically loaded library, use Domain.getClass.