diff --git a/src/include/wren.h b/src/include/wren.h index a7db435c9..0dddc4303 100644 --- a/src/include/wren.h +++ b/src/include/wren.h @@ -520,6 +520,14 @@ void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot, void wrenGetVariable(WrenVM* vm, const char* module, const char* name, int slot); +// Looks up the top level variable with [name] in resolved [module], +// returns false if not found. The module must be imported at the time, +// use wrenHasModule to ensure that before calling. +bool wrenHasVariable(WrenVM* vm, const char* module, const char* name); + +// Returns true if [module] has been imported/resolved before, false if not. +bool wrenHasModule(WrenVM* vm, const char* module); + // Sets the current fiber to be aborted, and uses the value in [slot] as the // runtime error object. void wrenAbortFiber(WrenVM* vm, int slot); diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 3f19d45da..b77a12161 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -1898,6 +1898,40 @@ void wrenGetVariable(WrenVM* vm, const char* module, const char* name, setSlot(vm, slot, moduleObj->variables.data[variableSlot]); } +bool wrenHasVariable(WrenVM* vm, const char* module, const char* name) +{ + ASSERT(module != NULL, "Module cannot be NULL."); + ASSERT(name != NULL, "Variable name cannot be NULL."); + + Value moduleName = wrenStringFormat(vm, "$", module); + wrenPushRoot(vm, AS_OBJ(moduleName)); + + //We don't use wrenHasModule since we want to use the module object. + ObjModule* moduleObj = getModule(vm, moduleName); + ASSERT(moduleObj != NULL, "Could not find module."); + + wrenPopRoot(vm); // moduleName. + + int variableSlot = wrenSymbolTableFind(&moduleObj->variableNames, + name, strlen(name)); + + return variableSlot != -1; +} + +bool wrenHasModule(WrenVM* vm, const char* module) +{ + ASSERT(module != NULL, "Module cannot be NULL."); + + Value moduleName = wrenStringFormat(vm, "$", module); + wrenPushRoot(vm, AS_OBJ(moduleName)); + + ObjModule* moduleObj = getModule(vm, moduleName); + + wrenPopRoot(vm); // moduleName. + + return moduleObj != NULL; +} + void wrenAbortFiber(WrenVM* vm, int slot) { validateApiSlot(vm, slot); diff --git a/test/api/get_variable.c b/test/api/get_variable.c index 41cfb0bc4..94dd05a5d 100644 --- a/test/api/get_variable.c +++ b/test/api/get_variable.c @@ -32,6 +32,25 @@ static void otherModule(WrenVM* vm) wrenGetVariable(vm, "./test/api/get_variable_module", "Variable", 0); } +static void hasVariable(WrenVM* vm) +{ + const char* module = wrenGetSlotString(vm, 1); + const char* variable = wrenGetSlotString(vm, 2); + + bool result = wrenHasVariable(vm, module, variable); + wrenEnsureSlots(vm, 1); + wrenSetSlotBool(vm, 0, result); +} + +static void hasModule(WrenVM* vm) +{ + const char* module = wrenGetSlotString(vm, 1); + + bool result = wrenHasModule(vm, module); + wrenEnsureSlots(vm, 1); + wrenSetSlotBool(vm, 0, result); +} + WrenForeignMethodFn getVariableBindMethod(const char* signature) { if (strcmp(signature, "static GetVariable.beforeDefined()") == 0) return beforeDefined; @@ -39,6 +58,9 @@ WrenForeignMethodFn getVariableBindMethod(const char* signature) if (strcmp(signature, "static GetVariable.afterAssigned()") == 0) return afterAssigned; if (strcmp(signature, "static GetVariable.otherSlot()") == 0) return otherSlot; if (strcmp(signature, "static GetVariable.otherModule()") == 0) return otherModule; + + if (strcmp(signature, "static Has.variable(_,_)") == 0) return hasVariable; + if (strcmp(signature, "static Has.module(_)") == 0) return hasModule; return NULL; } diff --git a/test/api/get_variable.wren b/test/api/get_variable.wren index 48cdf84c7..8d884264d 100644 --- a/test/api/get_variable.wren +++ b/test/api/get_variable.wren @@ -8,6 +8,11 @@ class GetVariable { foreign static otherModule() } +class Has { + foreign static variable(module, variable) + foreign static module(module) +} + System.print(GetVariable.beforeDefined()) // expect: null var A = "a" @@ -22,3 +27,13 @@ var B = "b" System.print(GetVariable.otherSlot()) // expect: b System.print(GetVariable.otherModule()) // expect: value + + +System.print(Has.variable("./test/api/get_variable_module", "Variable")) // expect: true +System.print(Has.variable("./test/api/get_variable_module", "NotAVariable")) // expect: false +System.print(Has.variable("./test/api/get_variable", "Has")) // expect: true +System.print(Has.variable("./test/api/get_variable", "Fake")) // expect: false + +System.print(Has.module("./test/api/get_variable_module")) // expect: true +System.print(Has.module("./test/api/get_variable")) // expect: true +System.print(Has.module("not a module")) // expect: false \ No newline at end of file