mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 06:08:41 +01:00
Add a limited form of re-entrant calls.
This doesn't let you arbitrarily call back into the VM from within foreign methods. I'm still not sure if that's even a good idea since God knows what that would mean if you switch fibers while doing that. But this does allow the very important use case of being able to call a foreign method from within a call to wrenCall(). In other words, foreign methods need to always be leaf calls on the call stack, but the root of that stack can now come from runInterpreter() or wrenCall(). Fix #510.
This commit is contained in:
@ -66,6 +66,7 @@ void callRunTests(WrenVM* vm)
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, getValue);
|
||||
printf("slots after call: %d\n", wrenGetSlotCount(vm));
|
||||
WrenHandle* value = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
// Different argument types.
|
||||
|
||||
@ -48,6 +48,7 @@ class Call {
|
||||
// expect: subscript 1 2
|
||||
// expect: subscript set 1 2 3
|
||||
|
||||
// expect: slots after call: 1
|
||||
// expect: two true false
|
||||
// expect: two 1.2 3.4
|
||||
// expect: two string another
|
||||
|
||||
44
test/api/call_calls_foreign.c
Normal file
44
test/api/call_calls_foreign.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wren.h"
|
||||
|
||||
static void api(WrenVM *vm) {
|
||||
// Grow the slot array. This should trigger the stack to be moved.
|
||||
wrenEnsureSlots(vm, 10);
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
wrenSetSlotDouble(vm, i, i);
|
||||
wrenInsertInList(vm, 0, -1, i);
|
||||
}
|
||||
}
|
||||
|
||||
WrenForeignMethodFn callCallsForeignBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static CallCallsForeign.api()") == 0) return api;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void callCallsForeignRunTests(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "./test/api/call_calls_foreign", "CallCallsForeign", 0);
|
||||
WrenHandle* apiClass = wrenGetSlotHandle(vm, 0);
|
||||
WrenHandle *call = wrenMakeCallHandle(vm, "call(_)");
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotHandle(vm, 0, apiClass);
|
||||
wrenSetSlotString(vm, 1, "parameter");
|
||||
|
||||
printf("slots before %d\n", wrenGetSlotCount(vm));
|
||||
wrenCall(vm, call);
|
||||
|
||||
// We should have a single slot count for the return.
|
||||
printf("slots after %d\n", wrenGetSlotCount(vm));
|
||||
|
||||
wrenReleaseHandle(vm, call);
|
||||
wrenReleaseHandle(vm, apiClass);
|
||||
}
|
||||
4
test/api/call_calls_foreign.h
Normal file
4
test/api/call_calls_foreign.h
Normal file
@ -0,0 +1,4 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn callCallsForeignBindMethod(const char* signature);
|
||||
void callCallsForeignRunTests(WrenVM* vm);
|
||||
17
test/api/call_calls_foreign.wren
Normal file
17
test/api/call_calls_foreign.wren
Normal file
@ -0,0 +1,17 @@
|
||||
// Regression test for https://github.com/munificent/wren/issues/510.
|
||||
//
|
||||
// Tests that re-entrant API calls are handled correctly. The host uses
|
||||
// `wrenCall()` to invoke `CallCallsForeign.call()`. That in turn calls
|
||||
// `CallCallsForeign.api()`, which goes back through the API.
|
||||
class CallCallsForeign {
|
||||
foreign static api()
|
||||
|
||||
static call(param) {
|
||||
System.print(api())
|
||||
// expect: slots before 2
|
||||
// expect: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
System.print(param) // expect: parameter
|
||||
// expect: slots after 1
|
||||
return "result"
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "call.h"
|
||||
#include "call_calls_foreign.h"
|
||||
#include "call_wren_call_root.h"
|
||||
#include "error.h"
|
||||
#include "get_variable.h"
|
||||
@ -42,6 +43,9 @@ static WrenForeignMethodFn bindForeignMethod(
|
||||
method = benchmarkBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = callCallsForeignBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = errorBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
@ -102,6 +106,10 @@ static void afterLoad(WrenVM* vm)
|
||||
{
|
||||
callRunTests(vm);
|
||||
}
|
||||
else if (strstr(testName, "/call_calls_foreign.wren") != NULL)
|
||||
{
|
||||
callCallsForeignRunTests(vm);
|
||||
}
|
||||
else if (strstr(testName, "/call_wren_call_root.wren") != NULL)
|
||||
{
|
||||
callWrenCallRootRunTests(vm);
|
||||
|
||||
@ -10,7 +10,7 @@ void resetStackAfterCallAbortRunTests(WrenVM* vm)
|
||||
WrenHandle* testClass = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
WrenHandle* abortFiber = wrenMakeCallHandle(vm, "abortFiber()");
|
||||
WrenHandle* afterConstruct = wrenMakeCallHandle(vm, "afterAbort(_,_)");
|
||||
WrenHandle* afterAbort = wrenMakeCallHandle(vm, "afterAbort(_,_)");
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, testClass);
|
||||
@ -20,9 +20,9 @@ void resetStackAfterCallAbortRunTests(WrenVM* vm)
|
||||
wrenSetSlotHandle(vm, 0, testClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenCall(vm, afterConstruct);
|
||||
wrenCall(vm, afterAbort);
|
||||
|
||||
wrenReleaseHandle(vm, testClass);
|
||||
wrenReleaseHandle(vm, abortFiber);
|
||||
wrenReleaseHandle(vm, afterConstruct);
|
||||
wrenReleaseHandle(vm, afterAbort);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user