wren
wren copied to clipboard
Calling wren method handle from inside a bound foreign method body
Let say that I have these two wren classes:
foreign class Item {
construct new() { }
}
class Builder {
foreign static create(type)
}
Somewhere in the main module I'm calling Builder.create(Item).
And let say that Builder.create is bound to this method:
static void Create(WrenVM* vm)
{
wrenEnsureSlots(vm, 2); // There should be 2 slots (0 for caller and 1 for the argument)
WrenHandle* classHandle = wrenGetSlotHandle(vm, 1); // Get the class type from arg 1 (Item)
WrenHandle* constructorHandle = wrenMakeCallHandle(vm, "new()"); // Make constructor method handle
wrenSetSlotHandle(vm, 0, classHandle); // Set class handle into slot 0 to call constructor on it
WrenInterpretResult res = wrenCall(vm, constructorHandle); // Assume checking result
void* data = wrenGetSlotForeign(vm, 0); // Get return in slot 0, proceed with casting/use
// Clean up ...
}
The idea behind this is that the Builder.create function will construct the class that is passed in it's argument with a new() call, and return the new variable with some processing on the host side.
The problem is that even tho I've verified the class handle to be of type 'Item metaclass', when calling wrenCall the following line of code vm->apiStack = NULL; will remove the handle from the stack. Since wrenSetSlotHandle internally calls vm->apiStack[slot] = value; And will cause the app to crash as it's trying to call a function from a different class, in this case, the class that originally called Builder.create which happens to be called 'Game'.
Is this intended behaviour? And if so, then how can I achieve the functionality I need? Note that if I change the create function to simple do this on wren side it works as expected (but without the host processing):
class Builder {
static create(type) {
return type.new()
}
}
So it should in theory be possible to do this on the host side.
The VM is not currently reentrant, meaning a wrenCall can't happen inside a foreign binding. If you run a debug build you'll likely get an assertion about that? (always test in debug a bit for the assertions to be present atm)
@ruby0x1 well that's rather disappointing, I was hoping there was a way to do what I'm trying.
And yes of course, I'm running and testing on a debug build. But there are no asserts happening until it tries to run a method on the wrongly associated class (Game).
The weird thing is that the Game handle that it tries to run it from is grabbed from the stackTop, and I inspected it's values a bit and it turns out the Item class value is somewhere in that stack, just not where I expect it to be. Whether or not there's a reason behind it I thought it was interesting to note.
You can also split the static in 2 parts. A wren one that handle part of the logic and one ore more private static foreign that handles the part that needs to be done in C land.
@mhermier Somehow I missed your message, but indeed that is a solution. I will try that for now, thank you! Though I would say this is a nice feature to have, later on I might gander at modifying the source to see what can be achieved.
Depending on your needs it's easy enough to fire up TWO entirely different Wren VMs... and one can call into the other - since it's not reentrancy at that point. I've used this strategy before.