Some Background
I’m working on an integration of the straight-C Duktape embedded JavaScript engine into a hxcpp project. I want to use Haxe reflection and/or RTTI to do run-time binding of Haxe objects into JavaScript.
To bind a Haxe object into a JS object, first you have to create a JS object, and then populate it with property getters and functions that will call into the backing Haxe object. To correlate the JS object to the Haxe object, I need to store some way of getting back to the Haxe object. If I were in C/C++, the most obvious way to do this would be to just stick a pointer to the C++ object as a hidden property of the JS object. Duktape provides such a facility (and even a primitive “pointer” JS Value type for the purpose).
The Problem
The problem is when I pass the pointer into Duktape, and pull it out later in the getter, it crashes when I try to reference it.
I am extracting the pointer with code like this
function pushNativeObject(value:Dynamic):Void {
var raw = cpp.RawPointer.addressOf(value);
untyped __cpp__("duk_push_pointer(context, static_cast<void*>(raw))");
referencedHaxe.set(value, true);
}
In this case I’ve already determined through Type
that value is in fact an instance of a Haxe class.
Of course, the static_cast
looks scary, but I should be able to go to and from void*
without any damage.
I add the object to a referencedHaxe:ObjectMap<Dynamic, Bool>
, because otherwise there may be nothing to prevent Haxe’s GC from reaping it. That said, I know in this case it’s still referenced on the stack.
I’m reconstituting the pointer with code like this
// ... code to get the property onto the DukTape stack ..
var p:cpp.RawPointer<Void> = untyped __cpp__("duk_require_pointer(context, -1)");
var raw:RawPointer<Dynamic> = untyped __cpp__("static_cast<Dynamic*>(p)");
var ptr = cpp.Pointer.fromRaw(raw);
var ref:Dynamic = ptr.ref;
ref.toString(); // This crashes.
Definitely pulling more funny stuff here. I couldn’t figure out how to cast the cpp.RawPointer<Void>
to cpp.RawPointer<Dynamic>
in Haxe, so I do it not-so-slyly in C++. Then I work my way back to a Dynamic from there using the given APIs.
The traces indicate that the pointer is the same numeric value at both ends.
The generated code looks reasonable to me
(massaged for brevity)
::Dynamic* raw = &(value);
duk_push_pointer(context, raw);
and
void* p = duk_to_pointer(context, index);
::Dynamic* raw = static_cast<Dynamic*>(p);
::cpp::Pointer< ::Dynamic > ptr = ::cpp::Pointer_obj::fromRaw(raw);
::Dynamic ref = ptr->get_ref();
ref->toString();
I’ve been muddling through to get this far, but I don’t have a great concept of how Dynamic works in this case. It seems possible Dynamic is a container that contains the type metadata, and getting a pointer to it is really just referring to the Dynamic container on the stack. But, if so, I don’t know how to accomplish getting the pointer to the underlying object instance, whatever it is (or how to reconstitute it on the other side). What am I doing wrong?
Note: I could just generate a random number and use it as a Map key to get the object back on the Haxe side, since I have to retain the objects in Haxe anyway. Given the overhead of having any kind of scripting engine, it’s probably not too terrible. But it seems like I should be able to do it this more direct way.