diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 5f69ccb3..bac89fe0 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -659,6 +659,7 @@ static void createForeign(WrenVM* vm, ObjFiber* fiber, Value* stack) method->fn.foreign(vm); + vm->apiStack = NULL; // TODO: Check that allocateForeign was called. } diff --git a/test/api/main.c b/test/api/main.c index b28d9207..6306418d 100644 --- a/test/api/main.c +++ b/test/api/main.c @@ -12,6 +12,7 @@ #include "handle.h" #include "lists.h" #include "new_vm.h" +#include "reset_stack_after_foreign_construct.h" #include "slots.h" // The name of the currently executing API test. @@ -73,9 +74,11 @@ static WrenForeignClassMethods bindForeignClass( foreignClassBindClass(className, &methods); if (methods.allocate != NULL) return methods; - slotsBindClass(className, &methods); + resetStackAfterForeignConstructBindClass(className, &methods); if (methods.allocate != NULL) return methods; + slotsBindClass(className, &methods); + if (methods.allocate != NULL) return methods; fprintf(stderr, "Unknown foreign class '%s' for test '%s'\n", className, testName); @@ -85,6 +88,10 @@ static WrenForeignClassMethods bindForeignClass( static void afterLoad(WrenVM* vm) { if (strstr(testName, "/call.wren") != NULL) callRunTests(vm); + if (strstr(testName, "/reset_stack_after_foreign_construct.wren") != NULL) + { + resetStackAfterForeignConstructRunTests(vm); + } } int main(int argc, const char* argv[]) diff --git a/test/api/reset_stack_after_foreign_construct.c b/test/api/reset_stack_after_foreign_construct.c new file mode 100644 index 00000000..78075293 --- /dev/null +++ b/test/api/reset_stack_after_foreign_construct.c @@ -0,0 +1,44 @@ +#include +#include + +#include "foreign_class.h" + +static void counterAllocate(WrenVM* vm) +{ + double* counter = (double*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(double)); + *counter = wrenGetSlotDouble(vm, 1); +} + +void resetStackAfterForeignConstructBindClass( + const char* className, WrenForeignClassMethods* methods) +{ + if (strcmp(className, "ResetStackForeign") == 0) + { + methods->allocate = counterAllocate; + return; + } +} + +void resetStackAfterForeignConstructRunTests(WrenVM* vm) +{ + wrenEnsureSlots(vm, 1); + wrenGetVariable(vm, "main", "Test", 0); + WrenHandle* testClass = wrenGetSlotHandle(vm, 0); + + WrenHandle* callConstruct = wrenMakeCallHandle(vm, "callConstruct()"); + WrenHandle* afterConstruct = wrenMakeCallHandle(vm, "afterConstruct(_,_)"); + + wrenEnsureSlots(vm, 1); + wrenSetSlotHandle(vm, 0, testClass); + wrenCall(vm, callConstruct); + + wrenEnsureSlots(vm, 3); + wrenSetSlotHandle(vm, 0, testClass); + wrenSetSlotDouble(vm, 1, 1.0); + wrenSetSlotDouble(vm, 2, 2.0); + wrenCall(vm, afterConstruct); + + wrenReleaseHandle(vm, testClass); + wrenReleaseHandle(vm, callConstruct); + wrenReleaseHandle(vm, afterConstruct); +} \ No newline at end of file diff --git a/test/api/reset_stack_after_foreign_construct.h b/test/api/reset_stack_after_foreign_construct.h new file mode 100644 index 00000000..219317a1 --- /dev/null +++ b/test/api/reset_stack_after_foreign_construct.h @@ -0,0 +1,5 @@ +#include "wren.h" + +void resetStackAfterForeignConstructBindClass( + const char* className, WrenForeignClassMethods* methods); +void resetStackAfterForeignConstructRunTests(WrenVM* vm); \ No newline at end of file diff --git a/test/api/reset_stack_after_foreign_construct.wren b/test/api/reset_stack_after_foreign_construct.wren new file mode 100644 index 00000000..fc0e2599 --- /dev/null +++ b/test/api/reset_stack_after_foreign_construct.wren @@ -0,0 +1,18 @@ +// Regression test. +// +// After a foreign constructor was called, it did not reset the API stack. If +// you tried to immediately reuse the API stack by calling wrenCall(), it +// would be in a broken state. +foreign class ResetStackForeign { + construct new(a) {} +} + +class Test { + static callConstruct() { + ResetStackForeign.new(1) + } + + static afterConstruct(a, b) { + System.print(a + b) // expect: 3 + } +} diff --git a/util/xcode/wren.xcodeproj/project.pbxproj b/util/xcode/wren.xcodeproj/project.pbxproj index 7d5529b8..95dec647 100644 --- a/util/xcode/wren.xcodeproj/project.pbxproj +++ b/util/xcode/wren.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 29A4273A1BDBE435001E6E22 /* wren_opt_random.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 29A427331BDBE435001E6E22 /* wren_opt_random.wren.inc */; }; 29A4273B1BDBE435001E6E22 /* wren_opt_random.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 29A427331BDBE435001E6E22 /* wren_opt_random.wren.inc */; }; 29AD96611D0A57F800C4DFE7 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 29AD965F1D0A57F800C4DFE7 /* error.c */; }; + 29C80D5A1D73332A00493837 /* reset_stack_after_foreign_construct.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C80D581D73332A00493837 /* reset_stack_after_foreign_construct.c */; }; 29C8A9331AB71FFF00DEC81D /* vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C8A9311AB71FFF00DEC81D /* vm.c */; }; 29C946981C88F14F00B4A4F3 /* new_vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C946961C88F14F00B4A4F3 /* new_vm.c */; }; 29D025E31C19CD1000A3BB28 /* os.c in Sources */ = {isa = PBXBuildFile; fileRef = 29D025E01C19CD1000A3BB28 /* os.c */; }; @@ -134,6 +135,8 @@ 29AB1F061816E3AD004B501E /* wren */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wren; sourceTree = BUILT_PRODUCTS_DIR; }; 29AD965F1D0A57F800C4DFE7 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = error.c; path = ../../test/api/error.c; sourceTree = ""; }; 29AD96601D0A57F800C4DFE7 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = error.h; path = ../../test/api/error.h; sourceTree = ""; }; + 29C80D581D73332A00493837 /* reset_stack_after_foreign_construct.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = reset_stack_after_foreign_construct.c; path = ../../test/api/reset_stack_after_foreign_construct.c; sourceTree = ""; }; + 29C80D591D73332A00493837 /* reset_stack_after_foreign_construct.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reset_stack_after_foreign_construct.h; path = ../../test/api/reset_stack_after_foreign_construct.h; sourceTree = ""; }; 29C8A9311AB71FFF00DEC81D /* vm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vm.c; path = ../../src/cli/vm.c; sourceTree = ""; }; 29C8A9321AB71FFF00DEC81D /* vm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vm.h; path = ../../src/cli/vm.h; sourceTree = ""; }; 29C946961C88F14F00B4A4F3 /* new_vm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = new_vm.c; path = ../../test/api/new_vm.c; sourceTree = ""; }; @@ -291,6 +294,8 @@ 29932D531C210F8D00099DEE /* lists.h */, 29C946961C88F14F00B4A4F3 /* new_vm.c */, 29C946971C88F14F00B4A4F3 /* new_vm.h */, + 29C80D581D73332A00493837 /* reset_stack_after_foreign_construct.c */, + 29C80D591D73332A00493837 /* reset_stack_after_foreign_construct.h */, 29D009AA1B7E39A8000CE58C /* slots.c */, 29D009AB1B7E39A8000CE58C /* slots.h */, ); @@ -424,6 +429,7 @@ 29DC14A91BBA302F008A8274 /* wren_vm.c in Sources */, 29DC14AA1BBA3032008A8274 /* main.c in Sources */, 293B25581CEFD8C7005D9537 /* repl.c in Sources */, + 29C80D5A1D73332A00493837 /* reset_stack_after_foreign_construct.c in Sources */, 29AD96611D0A57F800C4DFE7 /* error.c in Sources */, 293D46961BB43F9900200083 /* call.c in Sources */, 29A427351BDBE435001E6E22 /* wren_opt_meta.c in Sources */,