Ejemplo n.º 1
0
        private static int ToString(IntPtr state)
        {
            var obj = LuaApi.ToHandle(state, 1).Target;

            LuaApi.PushString(state, obj.ToString() ?? "");
            return(1);
        }
Ejemplo n.º 2
0
        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);
            }
        }
Ejemplo n.º 3
0
        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);
            }
        }
Ejemplo n.º 4
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);
            }
        }