Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling wren method handle from inside a bound foreign method body #1169

Open
ConorDamery opened this issue Jun 2, 2023 · 5 comments
Open

Comments

@ConorDamery
Copy link

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.

@ruby0x1
Copy link
Member

ruby0x1 commented Jun 2, 2023

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)

@ConorDamery
Copy link
Author

@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.

@mhermier
Copy link
Contributor

mhermier commented Jun 2, 2023 via email

@ConorDamery
Copy link
Author

@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.

@joshgoebel
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants