/// <summary> /// Gets or sets the global with the given name. /// </summary> /// <param name="name">The name.</param> /// <returns>The value of the global.</returns> /// <exception cref="ArgumentException"> /// <paramref name="value"/> is a <see cref="LuaReference"/> which is tied to a different <see cref="Lua"/> environment. /// </exception> /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception> /// <exception cref="ObjectDisposedException">The <see cref="Lua"/> environment is disposed.</exception> public object this[string name] { get { if (name == null) { throw new ArgumentNullException(nameof(name)); } ThrowIfDisposed(); var type = LuaApi.GetGlobal(MainState, name); var result = ToObject(-1, type); LuaApi.Pop(MainState, 1); return(result); } set { if (name == null) { throw new ArgumentNullException(nameof(name)); } ThrowIfDisposed(); PushObject(value); LuaApi.SetGlobal(MainState, name); } }
/// <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); } }