diff --git a/doc_classes/LuaAPI.xml b/doc_classes/LuaAPI.xml index be593bdb..892693a0 100644 --- a/doc_classes/LuaAPI.xml +++ b/doc_classes/LuaAPI.xml @@ -89,6 +89,11 @@ + + + Set weather an objects lua_fields method should be treated as a blacklist instead of a whitelist. + + Specifies on which events the hook will be called. diff --git a/project/demo/HelloLua.gd b/project/demo/HelloLua.gd index 668e9267..06deede2 100644 --- a/project/demo/HelloLua.gd +++ b/project/demo/HelloLua.gd @@ -7,12 +7,12 @@ func _lua_print(message: String): return LuaError.new_error("test", LuaError.ERR_RUNTIME) func _ready(): + # All builtin libraries are available to bind with. Use Debug, OS and IO at your own risk. + lua.bind_libraries(["base", "table", "string"]) + lua.push_variant("print", _lua_print) lua.push_variant("message", "Hello lua!") - # All builtin libraries are available to bind with. Use OS and IO at your own risk. - lua.bind_libraries(["base", "table", "string"]) - # Most methods return a LuaError in case of an error var err: LuaError = lua.do_string(""" for i=1,10,1 do diff --git a/project/testing/tests/LuaAPI.call_function.gd b/project/testing/tests/LuaAPI.call_function.gd index d5985e48..a63d9fb2 100644 --- a/project/testing/tests/LuaAPI.call_function.gd +++ b/project/testing/tests/LuaAPI.call_function.gd @@ -8,6 +8,7 @@ func _ready(): id = 9970 lua = LuaAPI.new() + lua.permissive = true # testName and testDescription are for any needed context about the test. testName = "LuaAPI.call_function" diff --git a/project/testing/tests/LuaAPI.expose_constructor.gd b/project/testing/tests/LuaAPI.expose_constructor.gd index 6be7cafd..20f28070 100644 --- a/project/testing/tests/LuaAPI.expose_constructor.gd +++ b/project/testing/tests/LuaAPI.expose_constructor.gd @@ -12,6 +12,7 @@ func _ready(): id = 9950 lua = LuaAPI.new() + lua.permissive = true var err = lua.expose_constructor("TestObj", TestObject) if err is LuaError: errors.append(err) diff --git a/project/testing/tests/LuaAPI.expose_function.gd b/project/testing/tests/LuaAPI.expose_function.gd index ab2be7e1..4156fe33 100644 --- a/project/testing/tests/LuaAPI.expose_function.gd +++ b/project/testing/tests/LuaAPI.expose_function.gd @@ -40,6 +40,7 @@ func _ready(): id = 9940 lua = LuaAPI.new() + lua.permissive = true lua.set_meta("isValid", true) var err = lua.push_variant("test1", LuaCallableExtra.with_tuple(testFuncTuple, 2)) if err is LuaError: diff --git a/project/testing/tests/LuaCoroutine.resume.gd b/project/testing/tests/LuaCoroutine.resume.gd index de481437..c7b5b50f 100644 --- a/project/testing/tests/LuaCoroutine.resume.gd +++ b/project/testing/tests/LuaCoroutine.resume.gd @@ -9,6 +9,7 @@ func _ready(): id = 9500 lua = LuaAPI.new() + lua.permissive = true co = lua.new_coroutine() co.load_string(" diff --git a/project/testing/tests/LuaCoroutine.yield_await.gd b/project/testing/tests/LuaCoroutine.yield_await.gd index d483d8d1..5484c239 100644 --- a/project/testing/tests/LuaCoroutine.yield_await.gd +++ b/project/testing/tests/LuaCoroutine.yield_await.gd @@ -13,6 +13,7 @@ func _ready(): id = 9490 lua = LuaAPI.new() + lua.permissive = true co = lua.new_coroutine() co.push_variant("test_yield_await", _test_yield_await) diff --git a/project/testing/tests/general.base_types.gd b/project/testing/tests/general.base_types.gd index 0433cda7..7bb82f21 100644 --- a/project/testing/tests/general.base_types.gd +++ b/project/testing/tests/general.base_types.gd @@ -8,6 +8,7 @@ func _ready(): id = 9825 lua = LuaAPI.new() + lua.permissive = true # testName and testDescription are for any needed context about the test. testName = "General.base_types" diff --git a/project/testing/tests/general.object_metamethods.gd b/project/testing/tests/general.object_metamethods.gd index ab7015b1..95c8567a 100644 --- a/project/testing/tests/general.object_metamethods.gd +++ b/project/testing/tests/general.object_metamethods.gd @@ -16,6 +16,7 @@ func _ready(): id = 9850 lua = LuaAPI.new() + lua.permissive = true lua.set_meta("isValid", true) testObj = TestObject.new() lua.push_variant("testObj", testObj) diff --git a/project/testing/tests/general.object_push.gd b/project/testing/tests/general.object_push.gd index b5ee1a74..8bc3af16 100644 --- a/project/testing/tests/general.object_push.gd +++ b/project/testing/tests/general.object_push.gd @@ -14,6 +14,7 @@ func _ready(): testObj = TestObject.new() lua = LuaAPI.new() + lua.permissive = true var err = lua.push_variant("testObj", testObj) if err is LuaError: errors.append(err) diff --git a/project/testing/tests/luaAPI.do_file.gd b/project/testing/tests/luaAPI.do_file.gd index 5a65639e..ac3d5f47 100644 --- a/project/testing/tests/luaAPI.do_file.gd +++ b/project/testing/tests/luaAPI.do_file.gd @@ -8,6 +8,7 @@ func _ready(): id = 9990 lua = LuaAPI.new() + lua.permissive = true # testName and testDescription are for any needed context about the test. testName = "LuaAPI.do_file()" diff --git a/project/testing/tests/luaAPI.do_string.gd b/project/testing/tests/luaAPI.do_string.gd index 145ee018..a7581bd2 100644 --- a/project/testing/tests/luaAPI.do_string.gd +++ b/project/testing/tests/luaAPI.do_string.gd @@ -8,6 +8,7 @@ func _ready(): id = 10000 lua = LuaAPI.new() + lua.permissive = true # testName and testDescription are for any needed context about the test. testName = "LuaAPI.do_string()" diff --git a/project/testing/tests/luaAPI.pull_variant.gd b/project/testing/tests/luaAPI.pull_variant.gd index b9d8f383..288c4ad0 100644 --- a/project/testing/tests/luaAPI.pull_variant.gd +++ b/project/testing/tests/luaAPI.pull_variant.gd @@ -8,6 +8,7 @@ func _ready(): id = 9980 lua = LuaAPI.new() + lua.permissive = true # testName and testDescription are for any needed context about the test. testName = "LuaAPI.pull_variant()" diff --git a/project/testing/tests/luaAPI.push_variant.gd b/project/testing/tests/luaAPI.push_variant.gd index 0a30021e..92027840 100644 --- a/project/testing/tests/luaAPI.push_variant.gd +++ b/project/testing/tests/luaAPI.push_variant.gd @@ -8,6 +8,7 @@ func _ready(): id = 9960 lua = LuaAPI.new() + lua.permissive = true # testName and testDescription are for any needed context about the test. testName = "LuaAPI.push_variant()" diff --git a/src/classes/luaAPI.cpp b/src/classes/luaAPI.cpp index 7b01bb76..0790832f 100644 --- a/src/classes/luaAPI.cpp +++ b/src/classes/luaAPI.cpp @@ -37,6 +37,11 @@ void LuaAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("new_coroutine"), &LuaAPI::newCoroutine); ClassDB::bind_method(D_METHOD("get_running_coroutine"), &LuaAPI::getRunningCoroutine); + ClassDB::bind_method(D_METHOD("set_permissive", "value"), &LuaAPI::setPermissive); + ClassDB::bind_method(D_METHOD("get_permissive"), &LuaAPI::getPermissive); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "permissive"), "set_permissive", "get_permissive"); + BIND_ENUM_CONSTANT(HOOK_MASK_CALL); BIND_ENUM_CONSTANT(HOOK_MASK_RETURN); BIND_ENUM_CONSTANT(HOOK_MASK_LINE); diff --git a/src/classes/luaAPI.h b/src/classes/luaAPI.h index 92cd7479..d8ef5ced 100644 --- a/src/classes/luaAPI.h +++ b/src/classes/luaAPI.h @@ -35,6 +35,14 @@ class LuaAPI : public RefCounted { void bindLibraries(Array libs); void setHook(Callable hook, int mask, int count); + inline void setPermissive(bool permissive) { + this->permissive = permissive; + } + + inline bool getPermissive() const { + return permissive; + } + bool luaFunctionExists(String functionName); Variant pullVariant(String name); @@ -68,7 +76,9 @@ class LuaAPI : public RefCounted { private: LuaState state; - lua_State *lState; + lua_State *lState = nullptr; + + bool permissive = false; // Temp. Looking for better method. Maybe? Array refs; diff --git a/src/luaState.h b/src/luaState.h index e188e47d..41c299a9 100644 --- a/src/luaState.h +++ b/src/luaState.h @@ -18,7 +18,6 @@ class LuaState { public: void setState(lua_State *state, RefCounted *obj, bool bindAPI); void bindLibraries(Array libs); - void setHook(Callable hook, int mask, int count); bool luaFunctionExists(String functionName); diff --git a/src/metatables.cpp b/src/metatables.cpp index 22bc5c3d..2eacf373 100644 --- a/src/metatables.cpp +++ b/src/metatables.cpp @@ -417,19 +417,44 @@ void LuaState::createObjectMetatable() { luaL_newmetatable(L, "mt_Object"); LUA_METAMETHOD_TEMPLATE(L, -1, "__index", { + Ref lua_api = dynamic_cast(OBJ); + if (lua_api == nullptr) { + LuaError *err = LuaError::newError("Object not found!", LuaError::ERR_RUNTIME); + LuaState::pushVariant(inner_state, err); + return 0; + } + // If object overrides if (arg1.has_method("__index")) { - LuaState::pushVariant(inner_state, arg1.call("__index", Ref(OBJ), arg2)); + LuaState::pushVariant(inner_state, arg1.call("__index", lua_api, arg2)); return 1; } + bool permissive = lua_api->getPermissive(); Array allowedFields = Array(); if (arg1.has_method("lua_fields")) { allowedFields = arg1.call("lua_fields"); } + // In permissive mode, allowedFields beomces a blacklist. + if (permissive) { + if (!allowedFields.has(arg2) && arg1.has_method(arg2.operator String())) { + lua_pushlightuserdata(inner_state, lua_touserdata(inner_state, 1)); + LuaState::pushVariant(inner_state, arg2); + lua_pushcclosure(inner_state, luaUserdataFuncCall, 2); + return 1; + } + + if (!allowedFields.has(arg2)) { + Variant var = arg1.get(arg2); + LuaState::pushVariant(inner_state, var); + return 1; + } + return 0; + } + // If the functions is allowed and exists - if ((allowedFields.is_empty() || allowedFields.has(arg2)) && arg1.has_method(arg2.operator String())) { + if (allowedFields.has(arg2) && arg1.has_method(arg2.operator String())) { lua_pushlightuserdata(inner_state, lua_touserdata(inner_state, 1)); LuaState::pushVariant(inner_state, arg2); lua_pushcclosure(inner_state, luaUserdataFuncCall, 2); @@ -437,7 +462,7 @@ void LuaState::createObjectMetatable() { } // If the field is allowed - if (allowedFields.is_empty() || allowedFields.has(arg2)) { + if (allowedFields.has(arg2)) { Variant var = arg1.get(arg2); LuaState::pushVariant(inner_state, var); return 1; @@ -447,20 +472,30 @@ void LuaState::createObjectMetatable() { }); LUA_METAMETHOD_TEMPLATE(L, -1, "__newindex", { + Ref lua_api = dynamic_cast(OBJ); + if (lua_api == nullptr) { + LuaError *err = LuaError::newError("Object not found!", LuaError::ERR_RUNTIME); + LuaState::pushVariant(inner_state, err); + return 0; + } + // If object overrides if (arg1.has_method("__newindex")) { - LuaState::pushVariant(inner_state, arg1.call("__newindex", Ref(OBJ), arg2, arg3)); + LuaState::pushVariant(inner_state, arg1.call("__newindex", lua_api, arg2, arg3)); return 1; } + bool permissive = lua_api->getPermissive(); Array allowedFields = Array(); if (arg1.has_method("lua_fields")) { allowedFields = arg1.call("lua_fields"); } - if (allowedFields.is_empty() || allowedFields.has(arg2)) { + if (!permissive && allowedFields.has(arg2)) { // We can't use arg1 here because we need to reference the userdata ((Variant *)lua_touserdata(inner_state, 1))->set(arg2, arg3); + } else if (permissive && !allowedFields.has(arg2)) { // In permissive mode, allowedFields beomces a blacklist. + ((Variant *)lua_touserdata(inner_state, 1))->set(arg2, arg3); } return 0; });