Exemple #1
0
        /// <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);
            }
        }