diff --git a/config.py b/config.py index 8fc11953..6b1396d4 100644 --- a/config.py +++ b/config.py @@ -26,6 +26,8 @@ def get_doc_classes(): "LuaError", "LuaTuple", "LuaCallableExtra", + "LuaObjectMetatable", + "LuaDefaultObjectMetatable", ] def get_doc_path(): diff --git a/doc_classes/LuaAPI.xml b/doc_classes/LuaAPI.xml index 08856b29..c5813a62 100644 --- a/doc_classes/LuaAPI.xml +++ b/doc_classes/LuaAPI.xml @@ -1,5 +1,5 @@ - + Execute Lua code at runtime and make your own API. @@ -105,8 +105,8 @@ - - When set to true all methods will be allowed on Objects be default and lua_fields is treated as a blacklist. When set to false, lua_fields is treated as a whitelist. + + This is the default LuaMetatable to use for object which do not define a lua_metatable field. By default it is a LuaDefaultObjectMetatable. You can change this to a custom metatable to change the behavior of all objects. Sets the memory limit for the state in bytes. If the limit is 0, there is no limit. diff --git a/doc_classes/LuaCallableExtra.xml b/doc_classes/LuaCallableExtra.xml index 839be490..8fa3be9f 100644 --- a/doc_classes/LuaCallableExtra.xml +++ b/doc_classes/LuaCallableExtra.xml @@ -1,5 +1,5 @@ - + A tuple. diff --git a/doc_classes/LuaCoroutine.xml b/doc_classes/LuaCoroutine.xml index 6cd221b8..7d27b899 100644 --- a/doc_classes/LuaCoroutine.xml +++ b/doc_classes/LuaCoroutine.xml @@ -1,5 +1,5 @@ - + A coroutine. diff --git a/doc_classes/LuaDefaultObjectMetatable.xml b/doc_classes/LuaDefaultObjectMetatable.xml new file mode 100644 index 00000000..277bb27d --- /dev/null +++ b/doc_classes/LuaDefaultObjectMetatable.xml @@ -0,0 +1,17 @@ + + + + The default LuaObjectMetatable used by the LuaAPI class as the object_metatable value. + + + This metatable by default checks if the object has a lua_fields method. If it does depending on how permissice is set. The listed fields will be allowed or disallowed. + This metatable also checks if the object overrides any of the metamethods, if it does it will call the overridden method. + + + + + + Sets weather the Objects lua_fields method is treated as a whitelist or a blacklist. If true, the fields method is treated as a blacklist and returned fields will be disallowed. If false, the fields method is treated as a whitelist and returned fields will be allowed. + + + diff --git a/doc_classes/LuaError.xml b/doc_classes/LuaError.xml index d9839c93..0f0e84bf 100644 --- a/doc_classes/LuaError.xml +++ b/doc_classes/LuaError.xml @@ -1,5 +1,5 @@ - + LuaAPI Error. diff --git a/doc_classes/LuaObjectMetatable.xml b/doc_classes/LuaObjectMetatable.xml new file mode 100644 index 00000000..eb6a06a0 --- /dev/null +++ b/doc_classes/LuaObjectMetatable.xml @@ -0,0 +1,244 @@ + + + + Represents a Lua Object Metatable. + + + This interface class allows for the definition of lua metamethods to be used for an objects metatable. Objects can define a [code]lua_metatable[/code] property that returns a LuaObjectMetatable. This metatable will be used for the object when it is passed to Lua. If one does not exists the luaAPI.object_metatable will be used instead. + + + + + + + + + + The addition (+) operation. If any operand for an addition is not a number, Lua will try to call a metamethod. It starts by checking the first operand (even if it is a number); if that operand does not define a metamethod for __add, then Lua will check the second operand. If Lua can find a metamethod, it calls the metamethod with the two operands as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, if no metamethod is found, Lua raises an error. + + + + + + + + + The bitwise AND (&) operation. Behavior similar to the addition operation, except that Lua will try a metamethod if any operand is neither an integer nor a float coercible to an integer + + + + + + + + The bitwise NOT (unary ~) operation. Behavior similar to the bitwise AND operation. + + + + + + + + + The bitwise OR (|) operation. Behavior similar to the bitwise AND operation. + + + + + + + + + The bitwise exclusive OR (binary ~) operation. Behavior similar to the bitwise AND operation. + + + + + + + + + The bitwise NOT (unary ~) operation. Behavior similar to the bitwise AND operation. + + + + + + + + + The concatenation (..) operation. Behavior similar to the addition operation, except that Lua will try a metamethod if any operand is neither a string nor a number (which is always coercible to a string). + + + + + + + + + The division (/) operation. Behavior similar to the addition operation. + + + + + + + + + The equal (==) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are either both tables or both full userdata and they are not primitively equal. The result of the call is always converted to a boolean. + + + + + + + + https://www.lua.org/manual/5.4/manual.html#2.5.3 + + + + + + + + + The floor division (//) operation. Behavior similar to the addition operation. + + + + + + + + + The indexing access operation table[key]. This event happens when table is not a table or when key is not present in table. The metavalue is looked up in the metatable of table. + + The metavalue for this event can be either a function, a table, or any value with an __index metavalue. If it is a function, it is called with table and key as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, the final result is the result of indexing this metavalue with key. This indexing is regular, not raw, and therefore can trigger another __index metavalue. + + + + + + + + + The less equal (<=) operation. Behavior similar to the less than operation. + + + + + + + + The length (#) operation. If the object is not a string, Lua will try its metamethod. If there is a metamethod, Lua calls it with the object as argument, and the result of the call (always adjusted to one value) is the result of the operation. If there is no metamethod but the object is a table, then Lua uses the table length operation. Otherwise, Lua raises an error. + + + + + + + + + The less than (<) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are neither both numbers nor both strings. Moreover, the result of the call is always converted to a boolean. + + + + + + + + If object does not have a metatable, returns nil. Otherwise, if the object's metatable has a __metatable field, returns the associated value. Otherwise, returns the metatable of the given object. + + + + + + + + + The modulo (%) operation. Behavior similar to the addition operation. + + + + + + + + + The multiplication (*) operation. Behavior similar to the addition operation. + + + + + + + + + + The indexing assignment table[key] = value. Like the index event, this event happens when table is not a table or when key is not present in table. The metavalue is looked up in the metatable of table. + + Like with indexing, the metavalue for this event can be either a function, a table, or any value with an __newindex metavalue. If it is a function, it is called with table, key, and value as arguments. Otherwise, Lua repeats the indexing assignment over this metavalue with the same key and value. This assignment is regular, not raw, and therefore can trigger another __newindex metavalue. + + Whenever a __newindex metavalue is invoked, Lua does not perform the primitive assignment. If needed, the metamethod itself can call rawset to do the assignment. + + + + + + + + + The exponentiation (^) operation. Behavior similar to the addition operation. + + + + + + + + + The bitwise left shift (<<) operation. Behavior similar to the bitwise AND operation. + + + + + + + + + The bitwise right shift (>>) operation. Behavior similar to the bitwise AND operation. + + + + + + + + + The subtraction (-) operation. Behavior similar to the addition operation. + + + + + + + + The tostring operation. Used when tostring is called on the object. + + + + + + + + The negation (unary -) operation. Behavior similar to the addition operation. + + + + + + + + + + diff --git a/doc_classes/LuaTuple.xml b/doc_classes/LuaTuple.xml index 31fd7f00..fb7474f4 100644 --- a/doc_classes/LuaTuple.xml +++ b/doc_classes/LuaTuple.xml @@ -1,5 +1,5 @@ - + A Lua Tuple. diff --git a/project/testing/tests/LuaAPI.call_function.gd b/project/testing/tests/LuaAPI.call_function.gd index 3018ce43..e2243a87 100644 --- a/project/testing/tests/LuaAPI.call_function.gd +++ b/project/testing/tests/LuaAPI.call_function.gd @@ -8,7 +8,6 @@ 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 4dd86dc6..0a95cccc 100644 --- a/project/testing/tests/LuaAPI.expose_constructor.gd +++ b/project/testing/tests/LuaAPI.expose_constructor.gd @@ -12,7 +12,6 @@ func _ready(): id = 9950 lua = LuaAPI.new() - lua.permissive = true var err = lua.push_variant("TestObj", TestObject.new) 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 0340d312..80002638 100644 --- a/project/testing/tests/LuaAPI.expose_function.gd +++ b/project/testing/tests/LuaAPI.expose_function.gd @@ -40,7 +40,6 @@ 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 c7b5b50f..de481437 100644 --- a/project/testing/tests/LuaCoroutine.resume.gd +++ b/project/testing/tests/LuaCoroutine.resume.gd @@ -9,7 +9,6 @@ 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 5484c239..d483d8d1 100644 --- a/project/testing/tests/LuaCoroutine.yield_await.gd +++ b/project/testing/tests/LuaCoroutine.yield_await.gd @@ -13,7 +13,6 @@ 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 7bb82f21..0433cda7 100644 --- a/project/testing/tests/general.base_types.gd +++ b/project/testing/tests/general.base_types.gd @@ -8,7 +8,6 @@ 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 95c8567a..ab7015b1 100644 --- a/project/testing/tests/general.object_metamethods.gd +++ b/project/testing/tests/general.object_metamethods.gd @@ -16,7 +16,6 @@ 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 8bc3af16..b5ee1a74 100644 --- a/project/testing/tests/general.object_push.gd +++ b/project/testing/tests/general.object_push.gd @@ -14,7 +14,6 @@ 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 ac3d5f47..5a65639e 100644 --- a/project/testing/tests/luaAPI.do_file.gd +++ b/project/testing/tests/luaAPI.do_file.gd @@ -8,7 +8,6 @@ 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 a7581bd2..145ee018 100644 --- a/project/testing/tests/luaAPI.do_string.gd +++ b/project/testing/tests/luaAPI.do_string.gd @@ -8,7 +8,6 @@ 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 288c4ad0..b9d8f383 100644 --- a/project/testing/tests/luaAPI.pull_variant.gd +++ b/project/testing/tests/luaAPI.pull_variant.gd @@ -8,7 +8,6 @@ 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 92027840..0a30021e 100644 --- a/project/testing/tests/luaAPI.push_variant.gd +++ b/project/testing/tests/luaAPI.push_variant.gd @@ -8,7 +8,6 @@ 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/register_types.cpp b/register_types.cpp index b2d1aedf..c1ece786 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -3,6 +3,7 @@ #include "src/classes/luaCallableExtra.h" #include "src/classes/luaCoroutine.h" #include "src/classes/luaError.h" +#include "src/classes/luaObjectMetatable.h" #include "src/classes/luaTuple.h" #ifdef LAPI_GDEXTENSION @@ -18,6 +19,8 @@ void initialize_luaAPI_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); } diff --git a/src/classes/luaAPI.cpp b/src/classes/luaAPI.cpp index 83c641b0..9e804a2e 100644 --- a/src/classes/luaAPI.cpp +++ b/src/classes/luaAPI.cpp @@ -1,6 +1,7 @@ #include "luaAPI.h" #include "luaCoroutine.h" +#include "luaObjectMetatable.h" #include @@ -10,6 +11,9 @@ LuaAPI::LuaAPI() { lState = lua_newstate(&LuaAPI::luaAlloc, (void *)&luaAllocData); + Ref mt; + mt.instantiate(); + objectMetatable = mt; // Creating lua state instance state.setState(lState, this, true); @@ -37,13 +41,13 @@ 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); + ClassDB::bind_method(D_METHOD("set_object_metatable", "value"), &LuaAPI::setObjectMetatable); + ClassDB::bind_method(D_METHOD("get_object_metatable"), &LuaAPI::getObjectMetatable); ClassDB::bind_method(D_METHOD("set_memory_limit", "limit"), &LuaAPI::setMemoryLimit); ClassDB::bind_method(D_METHOD("get_memory_limit"), &LuaAPI::getMemoryLimit); - ADD_PROPERTY(PropertyInfo(Variant::INT, "permissive"), "set_permissive", "get_permissive"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "object_metatable"), "set_object_metatable", "get_object_metatable"); ADD_PROPERTY(PropertyInfo(Variant::INT, "memory_limit"), "set_memory_limit", "get_memory_limit"); BIND_ENUM_CONSTANT(HOOK_MASK_CALL); @@ -74,12 +78,12 @@ int LuaAPI::configureGC(int what, int data) { return lua_gc(lState, what, data); } -void LuaAPI::setPermissive(bool value) { - permissive = value; +void LuaAPI::setObjectMetatable(Ref value) { + objectMetatable = value; } -bool LuaAPI::getPermissive() const { - return permissive; +Ref LuaAPI::getObjectMetatable() const { + return objectMetatable; } void LuaAPI::setMemoryLimit(int limit) { @@ -240,19 +244,19 @@ void *LuaAPI::luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize) { } if (ptr == nullptr) { - if (data->memoryLimit != 0 && data->memoryUsed + nsize > data->memoryLimit) { + if (data->memoryLimit != 0 && data->memoryUsed + (int)nsize > data->memoryLimit) { return nullptr; } - data->memoryUsed += nsize; + data->memoryUsed += (int)nsize; return memalloc(nsize); } - if (data->memoryLimit != 0 && data->memoryUsed - osize + nsize > data->memoryLimit) { + if (data->memoryLimit != 0 && data->memoryUsed - (int)osize + (int)nsize > data->memoryLimit) { return nullptr; } - data->memoryUsed -= osize; - data->memoryUsed += nsize; + data->memoryUsed -= (int)osize; + data->memoryUsed += (int)nsize; return memrealloc(ptr, nsize); } \ No newline at end of file diff --git a/src/classes/luaAPI.h b/src/classes/luaAPI.h index effbae66..367caa40 100644 --- a/src/classes/luaAPI.h +++ b/src/classes/luaAPI.h @@ -18,6 +18,7 @@ using namespace godot; #endif class LuaCoroutine; +class LuaObjectMetatable; class LuaAPI : public RefCounted { GDCLASS(LuaAPI, RefCounted); @@ -32,8 +33,8 @@ class LuaAPI : public RefCounted { void bindLibraries(Array libs); void setHook(Callable hook, int mask, int count); - void setPermissive(bool value); - bool getPermissive() const; + void setObjectMetatable(Ref value); + Ref getObjectMetatable() const; void setMemoryLimit(int limit); int getMemoryLimit() const; @@ -79,6 +80,8 @@ class LuaAPI : public RefCounted { LuaState state; lua_State *lState = nullptr; + Ref objectMetatable; + static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize); struct LuaAllocData { @@ -88,8 +91,6 @@ class LuaAPI : public RefCounted { LuaAllocData luaAllocData; - bool permissive = true; - LuaError *execute(int handlerIndex); }; diff --git a/src/classes/luaObjectMetatable.cpp b/src/classes/luaObjectMetatable.cpp new file mode 100644 index 00000000..1564a283 --- /dev/null +++ b/src/classes/luaObjectMetatable.cpp @@ -0,0 +1,532 @@ +#include "luaObjectMetatable.h" + +#ifdef LAPI_GDEXTENSION +#define GDVIRTUAL_BIND(m, ...) BIND_VIRTUAL_METHOD(LuaObjectMetatable, m); +#endif + +void LuaObjectMetatable::_bind_methods() { + GDVIRTUAL_BIND(__index, "obj", "lua", "index"); + GDVIRTUAL_BIND(__newindex, "obj", "lua", "index", "value"); + GDVIRTUAL_BIND(__call, "obj", "lua", "args"); + GDVIRTUAL_BIND(__gc, "obj", "lua"); + GDVIRTUAL_BIND(__tostring, "obj", "lua"); + GDVIRTUAL_BIND(__metatable, "obj", "lua"); + GDVIRTUAL_BIND(__len, "obj", "lua"); + GDVIRTUAL_BIND(__unm, "obj", "lua"); + GDVIRTUAL_BIND(__add, "obj", "lua", "other"); + GDVIRTUAL_BIND(__sub, "obj", "lua", "other"); + GDVIRTUAL_BIND(__mul, "obj", "lua", "other"); + GDVIRTUAL_BIND(__div, "obj", "lua", "other"); + GDVIRTUAL_BIND(__idiv, "obj", "lua", "other"); + GDVIRTUAL_BIND(__mod, "obj", "lua", "other"); + GDVIRTUAL_BIND(__pow, "obj", "lua", "other"); + GDVIRTUAL_BIND(__band, "obj", "lua", "other"); + GDVIRTUAL_BIND(__bor, "obj", "lua", "other"); + GDVIRTUAL_BIND(__bxor, "obj", "lua", "other"); + GDVIRTUAL_BIND(__bnot, "obj", "lua"); + GDVIRTUAL_BIND(__shl, "obj", "lua", "other"); + GDVIRTUAL_BIND(__shr, "obj", "lua", "other"); + GDVIRTUAL_BIND(__concat, "obj", "lua", "other"); + GDVIRTUAL_BIND(__eq, "obj", "lua", "other"); + GDVIRTUAL_BIND(__lt, "obj", "lua", "other"); + GDVIRTUAL_BIND(__le, "obj", "lua", "other"); +} + +Variant LuaObjectMetatable::__index(Object *obj, Ref api, String index) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__index, obj, api, index, ret); +#else + ret = call("__index", obj, api, index); +#endif + return ret; +} + +LuaError *LuaObjectMetatable::__newindex(Object *obj, Ref api, String index, Variant value) { + LuaError *ret = nullptr; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__newindex, obj, api, index, value, ret); +#else + ret = dynamic_cast((Object *)call("__newindex", obj, api, index, value)); +#endif + return ret; +} + +Variant LuaObjectMetatable::__call(Object *obj, Ref api, Ref args) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__call, obj, api, args, ret); +#else + ret = call("__call", obj, api, args); +#endif + return ret; +} + +LuaError *LuaObjectMetatable::__gc(Object *obj, Ref api) { + LuaError *ret = nullptr; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__gc, obj, api, ret); +#else + ret = dynamic_cast((Object *)call("__gc", obj, api)); +#endif + return ret; +} + +String LuaObjectMetatable::__tostring(Object *obj, Ref api) { + String ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__tostring, obj, api, ret); +#else + ret = call("__tostring", obj, api); +#endif + return ret; +} + +Variant LuaObjectMetatable::__metatable(Object *obj, Ref api) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__metatable, obj, api, ret); +#else + ret = call("__metatable", obj, api); +#endif + return ret; +} + +int LuaObjectMetatable::__len(Object *obj, Ref api) { + int ret = 0; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__len, obj, api, ret); +#else + ret = call("__len", obj, api); +#endif + return ret; +} + +Variant LuaObjectMetatable::__unm(Object *obj, Ref api) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__unm, obj, api, ret); +#else + ret = call("__unm", obj, api); +#endif + return ret; +} + +Variant LuaObjectMetatable::__add(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__add, obj, api, other, ret); +#else + ret = call("__add", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__sub(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__sub, obj, api, other, ret); +#else + ret = call("__sub", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__mul(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__mul, obj, api, other, ret); +#else + ret = call("__mul", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__div(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__div, obj, api, other, ret); +#else + ret = call("__div", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__idiv(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__idiv, obj, api, other, ret); +#else + ret = call("__idiv", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__mod(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__mod, obj, api, other, ret); +#else + ret = call("__mod", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__pow(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__pow, obj, api, other, ret); +#else + ret = call("__pow", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__band(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__band, obj, api, other, ret); +#else + ret = call("__band", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__bor(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__bor, obj, api, other, ret); +#else + ret = call("__bor", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__bxor(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__bxor, obj, api, other, ret); +#else + ret = call("__bxor", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__bnot(Object *obj, Ref api) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__bnot, obj, api, ret); +#else + ret = call("__bnot", obj, api); +#endif + return ret; +} + +Variant LuaObjectMetatable::__shl(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__shl, obj, api, other, ret); +#else + ret = call("__shl", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__shr(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__shr, obj, api, other, ret); +#else + ret = call("__shr", obj, api, other); +#endif + return ret; +} + +Variant LuaObjectMetatable::__concat(Object *obj, Ref api, Variant other) { + Variant ret; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__concat, obj, api, other, ret); +#else + ret = call("__concat", obj, api, other); +#endif + return ret; +} + +bool LuaObjectMetatable::__eq(Object *obj, Ref api, Variant other) { + bool ret = false; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__eq, obj, api, other, ret); +#else + ret = call("__eq", obj, api, other); +#endif + return ret; +} + +bool LuaObjectMetatable::__lt(Object *obj, Ref api, Variant other) { + bool ret = false; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__lt, obj, api, other, ret); +#else + ret = call("__lt", obj, api, other); +#endif + return ret; +} + +bool LuaObjectMetatable::__le(Object *obj, Ref api, Variant other) { + bool ret = false; +#ifndef LAPI_GDEXTENSION + GDVIRTUAL_CALL(__le, obj, api, other, ret); +#else + ret = call("__le", obj, api, other); +#endif + return ret; +} + +// Default object metatable + +void LuaDefaultObjectMetatable::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_permissive"), &LuaDefaultObjectMetatable::getPermissive); + ClassDB::bind_method(D_METHOD("set_permissive", "value"), &LuaDefaultObjectMetatable::setPermissive); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "permissive"), "set_permissive", "get_permissive"); +} + +void LuaDefaultObjectMetatable::setPermissive(bool value) { + permissive = value; +} + +bool LuaDefaultObjectMetatable::getPermissive() const { + return permissive; +} + +Variant LuaDefaultObjectMetatable::__index(Object *obj, Ref api, String index) { + if (obj->has_method("__index")) { + return obj->call("__index", api, index); + } + + Array fields = Array(); + if (obj->has_method("lua_fields")) { + fields = obj->call("lua_fields"); + } + + if ((!permissive && fields.has(index)) || (permissive && !fields.has(index))) { + return obj->get(index); + } + + return Variant(); +} + +LuaError *LuaDefaultObjectMetatable::__newindex(Object *obj, Ref api, String index, Variant value) { + if (obj->has_method("__newindex")) { + Variant ret = obj->call("__newindex", api, index, value); + if (ret.get_type() == Variant::OBJECT) { +#ifndef LAPI_GDEXTENSION + return Object::cast_to(ret.operator Object *()); +#else + return dynamic_cast(ret.operator Object *()); +#endif + } + } + + Array fields = Array(); + if (obj->has_method("lua_fields")) { + fields = obj->call("lua_fields"); + } + + if ((!permissive && fields.has(index)) || (permissive && !fields.has(index))) { + obj->set(index, value); + return nullptr; + } + + return LuaError::newError(vformat("Attempt to set field '%s' on object of type '%s' which is not a valid field.", index, obj->get_class()), LuaError::ERR_RUNTIME); +} + +Variant LuaDefaultObjectMetatable::__call(Object *obj, Ref api, Ref args) { + if (obj->has_method("__call")) { + return obj->call("__call", api, args); + } + + return Variant(); +} + +LuaError *LuaDefaultObjectMetatable::__gc(Object *obj, Ref api) { + if (obj->has_method("__gc")) { + Variant ret = obj->call("__gc", api); + if (ret.get_type() == Variant::OBJECT) { +#ifndef LAPI_GDEXTENSION + return Object::cast_to(ret.operator Object *()); +#else + return dynamic_cast(ret.operator Object *()); +#endif + } + } + + return nullptr; +} + +String LuaDefaultObjectMetatable::__tostring(Object *obj, Ref api) { + if (obj->has_method("__tostring")) { + return obj->call("__tostring", api); + } + + return String(); +} + +Variant LuaDefaultObjectMetatable::__metatable(Object *obj, Ref api) { + if (obj->has_method("__metatable")) { + return obj->call("__metatable", api); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__unm(Object *obj, Ref api) { + if (obj->has_method("__unm")) { + return obj->call("__unm", api); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__add(Object *obj, Ref api, Variant other) { + if (obj->has_method("__add")) { + return obj->call("__add", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__sub(Object *obj, Ref api, Variant other) { + if (obj->has_method("__sub")) { + return obj->call("__sub", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__mul(Object *obj, Ref api, Variant other) { + if (obj->has_method("__mul")) { + return obj->call("__mul", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__div(Object *obj, Ref api, Variant other) { + if (obj->has_method("__div")) { + return obj->call("__div", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__idiv(Object *obj, Ref api, Variant other) { + if (obj->has_method("__idiv")) { + return obj->call("__idiv", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__mod(Object *obj, Ref api, Variant other) { + if (obj->has_method("__mod")) { + return obj->call("__mod", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__pow(Object *obj, Ref api, Variant other) { + if (obj->has_method("__pow")) { + return obj->call("__pow", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__band(Object *obj, Ref api, Variant other) { + if (obj->has_method("__band")) { + return obj->call("__band", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__bor(Object *obj, Ref api, Variant other) { + if (obj->has_method("__bor")) { + return obj->call("__bor", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__bxor(Object *obj, Ref api, Variant other) { + if (obj->has_method("__bxor")) { + return obj->call("__bxor", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__bnot(Object *obj, Ref api) { + if (obj->has_method("__bnot")) { + return obj->call("__bnot", api); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__shl(Object *obj, Ref api, Variant other) { + if (obj->has_method("__shl")) { + return obj->call("__shl", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__shr(Object *obj, Ref api, Variant other) { + if (obj->has_method("__shr")) { + return obj->call("__shr", api, other); + } + + return Variant(); +} + +Variant LuaDefaultObjectMetatable::__concat(Object *obj, Ref api, Variant other) { + if (obj->has_method("__concat")) { + return obj->call("__concat", api, other); + } + + return Variant(); +} + +int LuaDefaultObjectMetatable::__len(Object *obj, Ref api) { + if (obj->has_method("__len")) { + return obj->call("__len", api); + } + + return Variant(); +} + +bool LuaDefaultObjectMetatable::__eq(Object *obj, Ref api, Variant other) { + if (obj->has_method("__eq")) { + return obj->call("__eq", api, other); + } + + return Variant(); +} + +bool LuaDefaultObjectMetatable::__lt(Object *obj, Ref api, Variant other) { + if (obj->has_method("__lt")) { + return obj->call("__lt", api, other); + } + + return Variant(); +} + +bool LuaDefaultObjectMetatable::__le(Object *obj, Ref api, Variant other) { + if (obj->has_method("__le")) { + return obj->call("__le", api, other); + } + + return Variant(); +} diff --git a/src/classes/luaObjectMetatable.h b/src/classes/luaObjectMetatable.h new file mode 100644 index 00000000..fc5d6f63 --- /dev/null +++ b/src/classes/luaObjectMetatable.h @@ -0,0 +1,125 @@ +#ifndef LUAOBJECTMETATABLE_H +#define LUAOBJECTMETATABLE_H + +#ifndef LAPI_GDEXTENSION +#include "core/core_bind.h" +#include "core/object/ref_counted.h" +#else +#include +#endif + +#include "luaAPI.h" +#include "luaError.h" +#include "luaTuple.h" + +#ifdef LAPI_GDEXTENSION +using namespace godot; +#endif + +class LuaObjectMetatable : public RefCounted { + GDCLASS(LuaObjectMetatable, RefCounted); + +protected: + static void _bind_methods(); + +#ifndef LAPI_GDEXTENSION + GDVIRTUAL3R(Variant, __index, Object *, Ref, String); + GDVIRTUAL4R(LuaError *, __newindex, Object *, Ref, String, Variant); + GDVIRTUAL3R(Variant, __call, Object *, Ref, Ref); + GDVIRTUAL2R(LuaError *, __gc, Object *, Ref); + GDVIRTUAL2R(String, __tostring, Object *, Ref); + GDVIRTUAL2R(Variant, __metatable, Object *, Ref); + GDVIRTUAL2R(int, __len, Object *, Ref); + GDVIRTUAL2R(Variant, __unm, Object *, Ref); + GDVIRTUAL3R(Variant, __add, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __sub, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __mul, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __div, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __idiv, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __mod, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __pow, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __band, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __bor, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __bxor, Object *, Ref, Variant); + GDVIRTUAL2R(Variant, __bnot, Object *, Ref); + GDVIRTUAL3R(Variant, __shl, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __shr, Object *, Ref, Variant); + GDVIRTUAL3R(Variant, __concat, Object *, Ref, Variant); + GDVIRTUAL3R(bool, __eq, Object *, Ref, Variant); + GDVIRTUAL3R(bool, __lt, Object *, Ref, Variant); + GDVIRTUAL3R(bool, __le, Object *, Ref, Variant); +#endif + +public: + virtual Variant __index(Object *obj, Ref api, String index); + virtual LuaError *__newindex(Object *obj, Ref api, String index, Variant value); + virtual Variant __call(Object *obj, Ref api, Ref args); + virtual LuaError *__gc(Object *obj, Ref api); + virtual String __tostring(Object *obj, Ref api); + virtual Variant __metatable(Object *obj, Ref api); + virtual int __len(Object *obj, Ref api); + virtual Variant __unm(Object *obj, Ref api); + virtual Variant __add(Object *obj, Ref api, Variant other); + virtual Variant __sub(Object *obj, Ref api, Variant other); + virtual Variant __mul(Object *obj, Ref api, Variant other); + virtual Variant __div(Object *obj, Ref api, Variant other); + virtual Variant __idiv(Object *obj, Ref api, Variant other); + virtual Variant __mod(Object *obj, Ref api, Variant other); + virtual Variant __pow(Object *obj, Ref api, Variant other); + virtual Variant __band(Object *obj, Ref api, Variant other); + virtual Variant __bor(Object *obj, Ref api, Variant other); + virtual Variant __bxor(Object *obj, Ref api, Variant other); + virtual Variant __bnot(Object *obj, Ref api); + virtual Variant __shl(Object *obj, Ref api, Variant other); + virtual Variant __shr(Object *obj, Ref api, Variant other); + virtual Variant __concat(Object *obj, Ref api, Variant other); + virtual bool __eq(Object *obj, Ref api, Variant other); + virtual bool __lt(Object *obj, Ref api, Variant other); + virtual bool __le(Object *obj, Ref api, Variant other); + +private: +}; + +// Default object metatable + +class LuaDefaultObjectMetatable : public LuaObjectMetatable { + GDCLASS(LuaDefaultObjectMetatable, LuaObjectMetatable); + +protected: + static void _bind_methods(); + +public: + Variant __index(Object *obj, Ref api, String index) override; + LuaError *__newindex(Object *obj, Ref api, String index, Variant value) override; + Variant __call(Object *obj, Ref api, Ref args) override; + LuaError *__gc(Object *obj, Ref api) override; + String __tostring(Object *obj, Ref api) override; + Variant __metatable(Object *obj, Ref api) override; + int __len(Object *obj, Ref api) override; + Variant __unm(Object *obj, Ref api) override; + Variant __add(Object *obj, Ref api, Variant other) override; + Variant __sub(Object *obj, Ref api, Variant other) override; + Variant __mul(Object *obj, Ref api, Variant other) override; + Variant __div(Object *obj, Ref api, Variant other) override; + Variant __idiv(Object *obj, Ref api, Variant other) override; + Variant __mod(Object *obj, Ref api, Variant other) override; + Variant __pow(Object *obj, Ref api, Variant other) override; + Variant __band(Object *obj, Ref api, Variant other) override; + Variant __bor(Object *obj, Ref api, Variant other) override; + Variant __bxor(Object *obj, Ref api, Variant other) override; + Variant __bnot(Object *obj, Ref api) override; + Variant __shl(Object *obj, Ref api, Variant other) override; + Variant __shr(Object *obj, Ref api, Variant other) override; + Variant __concat(Object *obj, Ref api, Variant other) override; + bool __eq(Object *obj, Ref api, Variant other) override; + bool __lt(Object *obj, Ref api, Variant other) override; + bool __le(Object *obj, Ref api, Variant other) override; + + void setPermissive(bool permissive); + bool getPermissive() const; + +private: + bool permissive = true; +}; + +#endif diff --git a/src/metatables.cpp b/src/metatables.cpp index 501857ba..2958635a 100644 --- a/src/metatables.cpp +++ b/src/metatables.cpp @@ -2,6 +2,7 @@ #include #include +#include #include // These 2 macros helps us in constructing general metamethods. @@ -237,7 +238,6 @@ void LuaState::createRect2Metatable() { luaL_newmetatable(L, "mt_Rect2"); LUA_METAMETHOD_TEMPLATE(L, -1, "__index", { - // Index was not found, so check to see if there is a matching function if (arg1.has_method(arg2.operator String())) { lua_pushlightuserdata(inner_state, lua_touserdata(inner_state, 1)); LuaState::pushVariant(inner_state, arg2); @@ -386,48 +386,14 @@ void LuaState::createObjectMetatable() { LUA_METAMETHOD_TEMPLATE(L, -1, "__index", { Ref api = getAPI(inner_state); - - // If object overrides - if (arg1.has_method("__index")) { - LuaState::pushVariant(inner_state, arg1.call("__index", Ref(api), arg2)); - return 1; - } - - bool permissive = 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; + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - // If the functions is allowed and exists - 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 the field is allowed - if (allowedFields.has(arg2)) { - Variant var = arg1.get(arg2); - LuaState::pushVariant(inner_state, var); + if (mt.is_valid()) { + Variant ret = mt->__index(arg1, api, arg2); + LuaState::pushVariant(inner_state, ret); return 1; } @@ -436,269 +402,385 @@ void LuaState::createObjectMetatable() { LUA_METAMETHOD_TEMPLATE(L, -1, "__newindex", { Ref api = getAPI(inner_state); - - // If object overrides - if (arg1.has_method("__newindex")) { - LuaState::pushVariant(inner_state, arg1.call("__newindex", Ref(api), arg2, arg3)); - return 1; + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - bool permissive = api->getPermissive(); - Array allowedFields = Array(); - if (arg1.has_method("lua_fields")) { - allowedFields = arg1.call("lua_fields"); + if (mt.is_valid()) { + LuaError *err = mt->__newindex(arg1, api, arg2, arg3); + if (err != nullptr) { + LuaState::pushVariant(inner_state, err); + return 1; + } } - 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; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__call", { - if (!arg1.has_method("__call")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - int argc = lua_gettop(inner_state); - Array args; - for (int i = 1; i < argc; i++) { - args.push_back(LuaState::getVariant(inner_state, i + 1)); + if (mt.is_valid()) { + int argc = lua_gettop(inner_state); + + Array args; + for (int i = 1; i < argc; i++) { + args.push_back(LuaState::getVariant(inner_state, i + 1)); + } + + Variant ret = mt->__call(arg1, api, LuaTuple::fromArray(args)); + LuaState::pushVariant(inner_state, ret); + return 1; } - LuaState::pushVariant(inner_state, arg1.call("__call", Ref(getAPI(inner_state)), LuaTuple::fromArray(args))); - return 1; + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__gc", { - // We need to manually uncount the ref - if (Ref ref = Object::cast_to(arg1); ref.is_valid()) { - ref->~RefCounted(); + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + // Sometimes the api ref is cleaned up first, so we need to check for that + if (!mt.is_valid() && api.is_valid()) { + mt = api->getObjectMetatable(); } - if (!arg1.has_method("__gc")) { - return 0; + if (mt.is_valid()) { + LuaError *err = mt->__gc(arg1, api); + if (err != nullptr) { + LuaState::pushVariant(inner_state, err); + } } - // just in case they want to raise an error - Variant ret = arg1.call("__gc", Ref(getAPI(inner_state))); - if (LuaError *err = dynamic_cast(ret.operator Object *())) { - LuaState::pushVariant(inner_state, ret); + // We need to manually uncount the ref + if (Ref ref = Object::cast_to(arg1); ref.is_valid()) { + ref->~RefCounted(); } + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__tostring", { - // If object overrides - if (!arg1.has_method("__tostring")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__tostring", Ref(getAPI(inner_state)))); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__tostring(arg1, api)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__metatable", { - // If object overrides - if (!arg1.has_method("__metatable")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__metatable", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__metatable(arg1, api)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__len", { - // If object overrides - if (!arg1.has_method("__len")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__len", Ref(getAPI(inner_state)))); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__len(arg1, api)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__unm", { - // If object overrides - if (!arg1.has_method("__unm")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__unm", Ref(getAPI(inner_state)))); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__unm(arg1, api)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__add", { - // If object overrides - if (!arg1.has_method("__add")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__add", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__add(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__sub", { - // If object overrides - if (!arg1.has_method("__sub")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__sub", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__sub(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__mul", { - // If object overrides - if (!arg1.has_method("__mul")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__mul", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__mul(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__div", { - // If object overrides - if (!arg1.has_method("__div")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__div", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__div(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__idiv", { - // If object overrides - if (!arg1.has_method("__idiv")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__idiv", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__idiv(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__mod", { - // If object overrides - if (!arg1.has_method("__mod")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__mod", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__mod(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__pow", { - // If object overrides - if (!arg1.has_method("__pow")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__pow", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__pow(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__concat", { - // If object overrides - if (!arg1.has_method("__concat")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__concat", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__concat(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__band", { - // If object overrides - if (!arg1.has_method("__band")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__band", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__band(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__bor", { - // If object overrides - if (!arg1.has_method("__bor")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__bor", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__bor(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__bxor", { - // If object overrides - if (!arg1.has_method("__bxor")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__bxor", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__bxor(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__bnot", { - // If object overrides - if (!arg1.has_method("__bnot")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__bnot", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__bnot(arg1, api)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__shl", { - // If object overrides - if (!arg1.has_method("__shl")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__shl", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__shl(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__shr", { - // If object overrides - if (!arg1.has_method("__shr")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__shr", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__shr(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__eq", { - // If object overrides - if (!arg1.has_method("__eq")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__eq", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__eq(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__lt", { - // If object overrides - if (!arg1.has_method("__lt")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__lt", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__lt(arg1, api, arg2)); + return 1; + } + + return 0; }); LUA_METAMETHOD_TEMPLATE(L, -1, "__le", { - // If object overrides - if (!arg1.has_method("__le")) { - return 0; + Ref api = getAPI(inner_state); + Ref mt = arg1.get("lua_metatable"); + + if (!mt.is_valid()) { + mt = api->getObjectMetatable(); } - LuaState::pushVariant(inner_state, arg1.call("__le", Ref(getAPI(inner_state)), arg2)); - return 1; + if (mt.is_valid()) { + LuaState::pushVariant(inner_state, mt->__le(arg1, api, arg2)); + return 1; + } + + return 0; }); lua_pop(L, 1);