internal void PushObject(object obj, IntPtr?stateOverride = null) { var state = stateOverride ?? MainState; Debug.Assert(LuaApi.GetMainState(state) == MainState, "State override did not match main state."); if (obj == null) { LuaApi.PushNil(state); return; } if (obj is IConvertible conv) { switch (conv.GetTypeCode()) { case TypeCode.Boolean: LuaApi.PushBoolean(state, (bool)obj); return; case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: LuaApi.PushInteger(state, conv.ToInt64(null)); return; case TypeCode.UInt64: LuaApi.PushInteger(state, (long)((ulong)conv)); return; case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: LuaApi.PushNumber(state, conv.ToDouble(null)); return; case TypeCode.Char: case TypeCode.String: LuaApi.PushString(state, conv.ToString()); return; default: ObjectBinder.PushNetObject(state, conv); return; } } else if (obj is LuaReference luaReference) { luaReference.PushOnto(state); } else { ObjectBinder.PushNetObject(state, obj); } }
private static int WrappedCall(IntPtr state, Func <int> function) { var oldTop = LuaApi.GetTop(state); try { LuaApi.PushBoolean(state, true); return(function() + 1); } catch (Exception e) { // If an exception occurs, then we need to reset the top. We don't know what state the stack is in! LuaApi.SetTop(state, oldTop); LuaApi.PushBoolean(state, false); if (e is LuaException) { LuaApi.PushString(state, e.Message); } else { LuaApi.PushString(state, $"unhandled .NET exception:\n{e}"); } return(2); } }
/// <summary> /// Initializes a new instance of the <see cref="ObjectBinder"/> class for the given <see cref="Lua"/> environment. /// </summary> /// <param name="lua">The <see cref="Lua"/> environment.</param> public ObjectBinder(Lua lua) { _lua = lua; // The __index metamethod can be cached in certain situations, such as methods and events. This can significantly improve // performance. var wrapIndexFunction = (LuaFunction)lua.DoString(@" local error = error local cache = setmetatable({}, { __mode = 'k' }) return function(fn) return function(obj, index) local objcache = cache[obj] if objcache == nil then objcache = {} cache[obj] = objcache end if objcache[index] ~= nil then return objcache[index] end local success, iscached, result = fn(obj, index) if not success then error(iscached, 2) end if iscached then objcache[index] = result end return result end end")[0]; // To raise Lua errors, we will do so directly in Lua. Doing so by P/Invoking luaL_error is problematic because luaL_error // performs a longjmp, which can destroy stack information. _wrapFunction = (LuaFunction)lua.DoString(@" local error = error local function helper(success, ...) if not success then error(..., 2) end return ... end return function(fn) return function(...) return helper(fn(...)) end end")[0]; // Storing the LuaCFunction delegates prevents the .NET GC from garbage collecting them. _objectMetamethods = new Dictionary <string, LuaCFunction> { ["__call"] = CallObject, ["__index"] = IndexObject, ["__newindex"] = NewIndexObject, ["__add"] = AddObject, ["__sub"] = SubObject, ["__mul"] = MulObject, ["__div"] = DivObject, ["__mod"] = ModObject, ["__band"] = BandObject, ["__bor"] = BorObject, ["__bxor"] = BxorObject, ["__shr"] = ShrObject, ["__shl"] = ShlObject, ["__eq"] = EqObject, ["__lt"] = LtObject, ["__le"] = LeObject, ["__unm"] = UnmObject, ["__bnot"] = BnotObject, ["__gc"] = Gc, ["__tostring"] = ToString }; _proxyCallObjectDelegate = ProxyCallObject; _typeMetamethods = new Dictionary <string, LuaCFunction> { ["__call"] = CallType, ["__index"] = IndexType, ["__newindex"] = NewIndexType, ["__gc"] = Gc, ["__tostring"] = ToString }; _proxyCallTypeDelegate = ProxyCallType; NewMetatable(ObjectMetatable, _objectMetamethods); NewMetatable(TypeMetatable, _typeMetamethods); void NewMetatable(string name, Dictionary <string, LuaCFunction> metamethods) { LuaApi.NewMetatable(lua.MainState, name); foreach (var kvp in metamethods) { LuaApi.PushString(lua.MainState, kvp.Key); var isWrapped = kvp.Key != "__gc" && kvp.Key != "__tostring"; if (kvp.Key == "__index") { wrapIndexFunction.PushOnto(lua.MainState); } else if (isWrapped) { _wrapFunction.PushOnto(lua.MainState); } LuaApi.PushCClosure(lua.MainState, kvp.Value, 0); if (isWrapped) { LuaApi.PCallK(lua.MainState, 1, 1); } LuaApi.SetTable(lua.MainState, -3); } // Setting __metatable to false will hide the metatable, protecting it from getmetatable and setmetatable. LuaApi.PushString(lua.MainState, "__metatable"); LuaApi.PushBoolean(lua.MainState, false); LuaApi.SetTable(lua.MainState, -3); LuaApi.Pop(lua.MainState, 1); } }