using System; using System.Linq; using System.Reflection; using System.Collections.Generic; using System.Runtime.InteropServices; using KeraLua; using NLua.Event; using NLua.Method; using NLua.Exceptions; using NLua.Extensions; #if __IOS__ || __TVOS__ || __WATCHOS__ using ObjCRuntime; #endif using LuaState = KeraLua.Lua; using LuaNativeFunction = KeraLua.LuaFunction; namespace NLua { public class Lua : IDisposable { #region lua debug functions /// /// Event that is raised when an exception occures during a hook call. /// public event EventHandler HookException; /// /// Event when lua hook callback is called /// /// /// Is only raised if SetDebugHook is called before. /// public event EventHandler DebugHook; /// /// lua hook calback delegate /// private LuaHookFunction hookCallback = null; #endregion #region Globals auto-complete private readonly List globals = new List(); private bool globalsSorted; #endregion private LuaState luaState; /// /// True while a script is being executed /// public bool IsExecuting { get { return executing; } } public LuaState State => luaState; private LuaNativeFunction panicCallback; private ObjectTranslator translator; /// /// Used to protect the (global) object translator pool during add/remove /// private static readonly object translatorPoolLock = new object(); /// /// Used to ensure multiple .net threads all get serialized by this single lock for access to the lua stack/objects /// //private object luaLock = new object(); private bool _StatePassed; private bool executing; static string initLuanet = @"local metatable = {} local rawget = rawget local import_type = luanet.import_type local load_assembly = luanet.load_assembly luanet.error, luanet.type = error, type -- Lookup a .NET identifier component. function metatable:__index(key) -- key is e.g. 'Form' -- Get the fully-qualified name, e.g. 'System.Windows.Forms.Form' local fqn = rawget(self,'.fqn') fqn = ((fqn and fqn .. '.') or '') .. key -- Try to find either a luanet function or a CLR type local obj = rawget(luanet,key) or import_type(fqn) -- If key is neither a luanet function or a CLR type, then it is simply -- an identifier component. if obj == nil then -- It might be an assembly, so we load it too. pcall(load_assembly,fqn) obj = { ['.fqn'] = fqn } setmetatable(obj, metatable) end -- Cache this lookup rawset(self, key, obj) return obj end -- A non-type has been called; e.g. foo = System.Foo() function metatable:__call(...) error('No such type: ' .. rawget(self,'.fqn'), 2) end -- This is the root of the .NET namespace luanet['.fqn'] = false setmetatable(luanet, metatable) -- Preload the mscorlib assembly luanet.load_assembly('mscorlib')"; static string clr_package = @"--- --- This lua module provides auto importing of .net classes into a named package. --- Makes for super easy use of LuaInterface glue --- --- example: --- Threading = CLRPackage(""System"", ""System.Threading"") --- Threading.Thread.Sleep(100) --- --- Extensions: --- import() is a version of CLRPackage() which puts the package into a list which is used by a global __index lookup, --- and thus works rather like C#'s using statement. It also recognizes the case where one is importing a local --- assembly, which must end with an explicit .dll extension. --- Alternatively, luanet.namespace can be used for convenience without polluting the global namespace: --- local sys,sysi = luanet.namespace {'System','System.IO'} -- sys.Console.WriteLine(""we are at {0}"",sysi.Directory.GetCurrentDirectory()) -- LuaInterface hosted with stock Lua interpreter will need to explicitly require this... if not luanet then require 'luanet' end local import_type, load_assembly = luanet.import_type, luanet.load_assembly local mt = { --- Lookup a previously unfound class and add it to our table __index = function(package, classname) local class = rawget(package, classname) if class == nil then class = import_type(package.packageName .. ""."" .. classname) if class == nil then class = import_type(classname) end package[classname] = class -- keep what we found around, so it will be shared end return class end } function luanet.namespace(ns) if type(ns) == 'table' then local res = {} for i = 1,#ns do res[i] = luanet.namespace(ns[i]) end return unpack(res) end -- FIXME - table.packageName could instead be a private index (see Lua 13.4.4) local t = { packageName = ns } setmetatable(t,mt) return t end local globalMT, packages local function set_global_mt() packages = {} globalMT = { __index = function(T,classname) for i,package in ipairs(packages) do local class = package[classname] if class then _G[classname] = class return class end end end } setmetatable(_G, globalMT) end --- Create a new Package class function CLRPackage(assemblyName, packageName) -- a sensible default... packageName = packageName or assemblyName local ok = pcall(load_assembly,assemblyName) -- Make sure our assembly is loaded return luanet.namespace(packageName) end function import (assemblyName, packageName) if not globalMT then set_global_mt() end if not packageName then local i = assemblyName:find('%.dll$') if i then packageName = assemblyName:sub(1,i-1) else packageName = assemblyName end end local t = CLRPackage(assemblyName,packageName) table.insert(packages,t) return t end function luanet.make_array (tp,tbl) local arr = tp[#tbl] for i,v in ipairs(tbl) do arr:SetValue(v,i-1) end return arr end function luanet.each(o) local e = o:GetEnumerator() return function() if e:MoveNext() then return e.Current end end end "; public bool UseTraceback { get; set; } = false; #region Globals auto-complete /// /// An alphabetically sorted list of all globals (objects, methods, etc.) externally added to this Lua instance /// /// Members of globals are also listed. The formatting is optimized for text input auto-completion. public IEnumerable Globals { get { // Only sort list when necessary if (!globalsSorted) { globals.Sort(); globalsSorted = true; } return globals; } } #endregion public Lua() { luaState = new LuaState(); Init(); // We need to keep this in a managed reference so the delegate doesn't get garbage collected panicCallback = PanicCallback; luaState.AtPanic(panicCallback); } /* * CAUTION: NLua.Lua instances can't share the same lua state! */ public Lua(LuaState luaState) { luaState.PushString("NLua_Loaded"); luaState.GetTable((int)LuaRegistry.Index); if (luaState.ToBoolean(-1)) { luaState.SetTop(-2); throw new LuaException("There is already a NLua.Lua instance associated with this Lua state"); } else { this.luaState = luaState; _StatePassed = true; luaState.SetTop(-2); Init(); } } void Init() { luaState.PushString("NLua_Loaded"); luaState.PushBoolean(true); luaState.SetTable((int)LuaRegistry.Index); if (_StatePassed == false) { luaState.NewTable(); luaState.SetGlobal("luanet"); } luaState.PushGlobalTable(); luaState.GetGlobal("luanet"); luaState.PushString("getmetatable"); luaState.GetGlobal("getmetatable"); luaState.SetTable(-3); luaState.PopGlobalTable(); translator = new ObjectTranslator(this, luaState); lock (translatorPoolLock) { ObjectTranslatorPool.Instance.Add(luaState, translator); } luaState.PopGlobalTable(); luaState.DoString(initLuanet); } public void Close() { if (_StatePassed) return; if (luaState != null) { lock (translatorPoolLock) { luaState.Close(); ObjectTranslatorPool.Instance.Remove(luaState); luaState = null; } } } #if __IOS__ || __TVOS__ || __WATCHOS__ [MonoPInvokeCallback(typeof(LuaNativeFunction))] #endif static int PanicCallback(IntPtr state) { var luaState = LuaState.FromIntPtr(state); string reason = string.Format("Unprotected error in call to Lua API ({0})", luaState.ToString(-1)); throw new LuaException(reason); } /// /// Assuming we have a Lua error string sitting on the stack, throw a C# exception out to the user's app /// /// Thrown if the script caused an exception private void ThrowExceptionFromError(int oldTop) { object err = translator.GetObject(luaState, -1); luaState.SetTop(oldTop); // A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved) var luaEx = err as LuaScriptException; if (luaEx != null) throw luaEx; // A non-wrapped Lua error (best interpreted as a string) - wrap it and throw it if (err == null) err = "Unknown Lua Error"; throw new LuaScriptException(err.ToString(), string.Empty); } /// /// Push a debug.traceback reference onto the stack, for a pcall function to use as error handler. (Remember to increment any top-of-stack markers!) /// private static int PushDebugTraceback(LuaState luaState, int argCount) { luaState.GetGlobal("debug"); luaState.GetField(-1, "traceback"); luaState.Remove(-2); int errindex = -argCount - 2; luaState.Insert(errindex); return errindex; } /// /// Return a debug.traceback() call result (a multi-line string, containing a full stack trace, including C calls. /// Note: it won't return anything unless the interpreter is in the middle of execution - that is, it only makes sense to call it from a method called from Lua, or during a coroutine yield. /// public string GetDebugTraceback() { int oldTop = luaState.GetTop(); luaState.GetGlobal("debug"); // stack: debug luaState.GetField(-1, "traceback"); // stack: debug,traceback luaState.Remove(-2); // stack: traceback luaState.PCall(0, -1, 0); return translator.PopValues(luaState, oldTop)[0] as string; } /// /// Convert C# exceptions into Lua errors /// /// num of things on stack /// null for no pending exception internal int SetPendingException(Exception e) { var caughtExcept = e; if (caughtExcept != null) { translator.ThrowError(luaState, caughtExcept); luaState.PushNil(); return 1; } else return 0; } /// /// /// /// /// /// public LuaFunction LoadString(string chunk, string name) { int oldTop = luaState.GetTop(); executing = true; try { if (luaState.LoadString(chunk, name) != LuaStatus.OK) ThrowExceptionFromError(oldTop); } finally { executing = false; } var result = translator.GetFunction(luaState, -1); translator.PopValues(luaState, oldTop); return result; } /// /// /// /// /// /// public LuaFunction LoadString(byte[] chunk, string name) { int oldTop = luaState.GetTop(); executing = true; try { if (luaState.LoadBuffer(chunk, name) != LuaStatus.OK) ThrowExceptionFromError(oldTop); } finally { executing = false; } var result = translator.GetFunction(luaState, -1); translator.PopValues(luaState, oldTop); return result; } /// /// Load a File on, and return a LuaFunction to execute the file loaded (useful to see if the syntax of a file is ok) /// /// /// public LuaFunction LoadFile(string fileName) { int oldTop = luaState.GetTop(); if (luaState.LoadFile(fileName) != LuaStatus.OK) ThrowExceptionFromError(oldTop); var result = translator.GetFunction(luaState, -1); translator.PopValues(luaState, oldTop); return result; } /// /// Executes a Lua chunk and returns all the chunk's return values in an array. /// /// Chunk to execute /// Name to associate with the chunk. Defaults to "chunk". /// public object[] DoString(byte[] chunk, string chunkName = "chunk") { int oldTop = luaState.GetTop(); executing = true; if (luaState.LoadBuffer(chunk, chunkName) == LuaStatus.OK) { int errfunction = 0; if (UseTraceback) { errfunction = PushDebugTraceback(luaState, 0); oldTop++; } try { if (luaState.PCall(0, -1, errfunction) == LuaStatus.OK) return translator.PopValues(luaState, oldTop); else ThrowExceptionFromError(oldTop); } finally { executing = false; } } else ThrowExceptionFromError(oldTop); return null; } /// /// Executes a Lua chunk and returns all the chunk's return values in an array. /// /// Chunk to execute /// Name to associate with the chunk. Defaults to "chunk". /// public object[] DoString(string chunk, string chunkName = "chunk") { int oldTop = luaState.GetTop(); executing = true; if (luaState.LoadString(chunk, chunkName) == LuaStatus.OK) { int errfunction = 0; if (UseTraceback) { errfunction = PushDebugTraceback(luaState, 0); oldTop++; } try { if (luaState.PCall(0, -1, errfunction) == LuaStatus.OK) return translator.PopValues(luaState, oldTop); else ThrowExceptionFromError(oldTop); } finally { executing = false; } } else ThrowExceptionFromError(oldTop); return null; } /* * Excutes a Lua file and returns all the chunk's return * values in an array */ public object[] DoFile(string fileName) { int oldTop = luaState.GetTop(); if (luaState.LoadFile(fileName) == LuaStatus.OK) { executing = true; int errfunction = 0; if (UseTraceback) { errfunction = PushDebugTraceback(luaState, 0); oldTop++; } try { if (luaState.PCall(0, -1, errfunction) == LuaStatus.OK) return translator.PopValues(luaState, oldTop); else ThrowExceptionFromError(oldTop); } finally { executing = false; } } else ThrowExceptionFromError(oldTop); return null; } /* * Indexer for global variables from the LuaInterpreter * Supports navigation of tables by using . operator */ public object this[string fullPath] { get { object returnValue = null; int oldTop = luaState.GetTop(); string[] path = FullPathToArray(fullPath); luaState.GetGlobal(path[0]); returnValue = translator.GetObject(luaState, -1); if (path.Length > 1) { var dispose = returnValue as LuaBase; string[] remainingPath = new string[path.Length - 1]; Array.Copy(path, 1, remainingPath, 0, path.Length - 1); returnValue = GetObject(remainingPath); if (dispose != null) dispose.Dispose(); } luaState.SetTop(oldTop); return returnValue; } set { int oldTop = luaState.GetTop(); string[] path = FullPathToArray(fullPath); if (path.Length == 1) { translator.Push(luaState, value); luaState.SetGlobal(fullPath); } else { luaState.GetGlobal(path[0]); string[] remainingPath = new string[path.Length - 1]; Array.Copy(path, 1, remainingPath, 0, path.Length - 1); SetObject(remainingPath, value); } luaState.SetTop(oldTop); // Globals auto-complete if (value == null) { // Remove now obsolete entries globals.Remove(fullPath); } else { // Add new entries if (!globals.Contains(fullPath)) RegisterGlobal(fullPath, value.GetType(), 0); } } } #region Globals auto-complete /// /// Adds an entry to (recursivley handles 2 levels of members) /// /// The index accessor path ot the entry /// The type of the entry /// How deep have we gone with recursion? private void RegisterGlobal(string path, Type type, int recursionCounter) { // If the type is a global method, list it directly if (type == typeof(LuaFunction)) { // Format for easy method invocation globals.Add(path + "("); } // If the type is a class or an interface and recursion hasn't been running too long, list the members else if ((type.IsClass || type.IsInterface) && type != typeof(string) && recursionCounter < 2) { #region Methods foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { string name = method.Name; if ( // Check that the LuaHideAttribute and LuaGlobalAttribute were not applied (!method.GetCustomAttributes(typeof(LuaHideAttribute), false).Any()) && (!method.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Any()) && // Exclude some generic .NET methods that wouldn't be very usefull in Lua name != "GetType" && name != "GetHashCode" && name != "Equals" && name != "ToString" && name != "Clone" && name != "Dispose" && name != "GetEnumerator" && name != "CopyTo" && !name.StartsWith("get_", StringComparison.Ordinal) && !name.StartsWith("set_", StringComparison.Ordinal) && !name.StartsWith("add_", StringComparison.Ordinal) && !name.StartsWith("remove_", StringComparison.Ordinal)) { // Format for easy method invocation string command = path + ":" + name + "("; if (method.GetParameters().Length == 0) command += ")"; globals.Add(command); } } #endregion #region Fields foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance)) { if ( // Check that the LuaHideAttribute and LuaGlobalAttribute were not applied (!field.GetCustomAttributes(typeof(LuaHideAttribute), false).Any()) && (!field.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Any())) { // Go into recursion for members RegisterGlobal(path + "." + field.Name, field.FieldType, recursionCounter + 1); } } #endregion #region Properties foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if ( // Check that the LuaHideAttribute and LuaGlobalAttribute were not applied (!property.GetCustomAttributes(typeof(LuaHideAttribute), false).Any()) && (!property.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Any()) // Exclude some generic .NET properties that wouldn't be very useful in Lua && property.Name != "Item") { // Go into recursion for members RegisterGlobal(path + "." + property.Name, property.PropertyType, recursionCounter + 1); } } #endregion } else globals.Add(path); // Otherwise simply add the element to the list // List will need to be sorted on next access globalsSorted = false; } #endregion /* * Navigates a table in the top of the stack, returning * the value of the specified field */ object GetObject(string[] remainingPath) { object returnValue = null; for (int i = 0; i < remainingPath.Length; i++) { luaState.PushString(remainingPath[i]); luaState.GetTable(-2); returnValue = translator.GetObject(luaState, -1); if (returnValue == null) break; } return returnValue; } /* * Gets a numeric global variable */ public double GetNumber(string fullPath) { return (double)this[fullPath]; } /* * Gets a string global variable */ public string GetString(string fullPath) { return this[fullPath].ToString(); } /* * Gets a table global variable */ public LuaTable GetTable(string fullPath) { return (LuaTable)this[fullPath]; } /* * Gets a table global variable as an object implementing * the interfaceType interface */ public object GetTable(Type interfaceType, string fullPath) { return CodeGeneration.Instance.GetClassInstance(interfaceType, GetTable(fullPath)); } /* * Gets a function global variable */ public LuaFunction GetFunction(string fullPath) { object obj = this[fullPath]; LuaFunction luaFunction = obj as LuaFunction; if (luaFunction != null) return luaFunction; luaFunction = new LuaFunction((LuaNativeFunction) obj, this); return luaFunction; } /* * Register a delegate type to be used to convert Lua functions to C# delegates (useful for iOS where there is no dynamic code generation) * type delegateType */ public void RegisterLuaDelegateType(Type delegateType, Type luaDelegateType) { CodeGeneration.Instance.RegisterLuaDelegateType(delegateType, luaDelegateType); } public void RegisterLuaClassType(Type klass, Type luaClass) { CodeGeneration.Instance.RegisterLuaClassType(klass, luaClass); } public void LoadCLRPackage() { luaState.DoString(Lua.clr_package); } /* * Gets a function global variable as a delegate of * type delegateType */ public Delegate GetFunction(Type delegateType, string fullPath) { return CodeGeneration.Instance.GetDelegate(delegateType, GetFunction(fullPath)); } /* * Calls the object as a function with the provided arguments, * returning the function's returned values inside an array */ internal object[] CallFunction(object function, object[] args) { return CallFunction(function, args, null); } /* * Calls the object as a function with the provided arguments and * casting returned values to the types in returnTypes before returning * them in an array */ internal object[] CallFunction(object function, object[] args, Type[] returnTypes) { int nArgs = 0; int oldTop = luaState.GetTop(); if (!luaState.CheckStack(args.Length + 6)) throw new LuaException("Lua stack overflow"); translator.Push(luaState, function); if (args.Length > 0) { nArgs = args.Length; for (int i = 0; i < args.Length; i++) translator.Push(luaState, args[i]); } executing = true; try { int errfunction = 0; if (UseTraceback) { errfunction = PushDebugTraceback(luaState, nArgs); oldTop++; } LuaStatus error = luaState.PCall(nArgs, -1, errfunction); if (error != LuaStatus.OK) ThrowExceptionFromError(oldTop); } finally { executing = false; } if (returnTypes != null) return translator.PopValues(luaState, oldTop, returnTypes); return translator.PopValues(luaState, oldTop); } /* * Navigates a table to set the value of one of its fields */ void SetObject(string[] remainingPath, object val) { for (int i = 0; i < remainingPath.Length - 1; i++) { luaState.PushString(remainingPath[i]); luaState.GetTable(-2); } luaState.PushString(remainingPath[remainingPath.Length - 1]); translator.Push(luaState, val); luaState.SetTable(-3); } string[] FullPathToArray(string fullPath) { return fullPath.SplitWithEscape('.', '\\').ToArray(); } /* * Creates a new table as a global variable or as a field * inside an existing table */ public void NewTable(string fullPath) { string[] path = FullPathToArray(fullPath); int oldTop = luaState.GetTop(); if (path.Length == 1) { luaState.NewTable(); luaState.SetGlobal(fullPath); } else { luaState.GetGlobal(path[0]); for (int i = 1; i < path.Length - 1; i++) { luaState.PushString(path[i]); luaState.GetTable(-2); } luaState.PushString(path[path.Length - 1]); luaState.NewTable(); luaState.SetTable(-3); } luaState.SetTop( oldTop); } public Dictionary GetTableDict(LuaTable table) { var dict = new Dictionary(); int oldTop = luaState.GetTop(); translator.Push(luaState, table); luaState.PushNil(); while (luaState.Next(-2)) { dict[translator.GetObject(luaState, -2)] = translator.GetObject(luaState, -1); luaState.SetTop(-2); } luaState.SetTop(oldTop); return dict; } /* * Lets go of a previously allocated reference to a table, function * or userdata */ #region lua debug functions /// /// Activates the debug hook /// /// Mask /// Count /// see lua docs. -1 if hook is already set public int SetDebugHook(LuaHookMask mask, int count) { if (hookCallback == null) { hookCallback = new LuaHookFunction(Lua.DebugHookCallback); luaState.SetHook(hookCallback, mask, count); } return -1; } /// /// Removes the debug hook /// /// see lua docs public void RemoveDebugHook() { hookCallback = null; luaState.SetHook(null, LuaHookMask.Disabled, 0); } /// /// Gets the hook mask. /// /// hook mask public LuaHookMask GetHookMask() { return luaState.HookMask; } /// /// Gets the hook count /// /// see lua docs public int GetHookCount() { return luaState.HookCount; } /// /// Gets local (see lua docs) /// /// lua debug structure /// see lua docs /// see lua docs public string GetLocal(LuaDebug luaDebug, int n) { return luaState.GetLocal(luaDebug, n); } /// /// Sets local (see lua docs) /// /// lua debug structure /// see lua docs /// see lua docs public string SetLocal(LuaDebug luaDebug, int n) { return luaState.SetLocal(luaDebug, n); } public int GetStack(int level, ref LuaDebug ar) { return luaState.GetStack(level, ref ar); } public bool GetInfo(string what, ref LuaDebug ar) { return luaState.GetInfo(what, ref ar); } /// /// Gets up value (see lua docs) /// /// see lua docs /// see lua docs /// see lua docs public string GetUpValue(int funcindex, int n) { return luaState.GetUpValue(funcindex, n); } /// /// Sets up value (see lua docs) /// /// see lua docs /// see lua docs /// see lua docs public string SetUpValue(int funcindex, int n) { return luaState.SetUpValue(funcindex, n); } /// /// Delegate that is called on lua hook callback /// /// lua state /// Pointer to LuaDebug (lua_debug) structure /// #if __IOS__ || __TVOS__ || __WATCHOS__ [MonoPInvokeCallback(typeof(LuaHookFunction))] #endif static void DebugHookCallback(IntPtr luaState, IntPtr luaDebug) { var state = LuaState.FromIntPtr(luaState); state.GetStack(0, luaDebug); if (!state.GetInfo("Snlu", luaDebug)) return; var debug = LuaDebug.FromIntPtr(luaDebug); ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(state); Lua lua = translator.Interpreter; lua.DebugHookCallbackInternal(state, debug); } private void DebugHookCallbackInternal(LuaState luaState, LuaDebug luaDebug) { try { var temp = DebugHook; if (temp != null) temp(this, new DebugHookEventArgs(luaDebug)); } catch (Exception ex) { OnHookException(new HookExceptionEventArgs(ex)); } } private void OnHookException(HookExceptionEventArgs e) { var temp = HookException; if (temp != null) temp(this, e); } /// /// Pops a value from the lua stack. /// /// Returns the top value from the lua stack. public object Pop() { int top = luaState.GetTop(); return translator.PopValues(luaState, top - 1)[0]; } /// /// Pushes a value onto the lua stack. /// /// Value to push. public void Push(object value) { translator.Push(luaState, value); } #endregion internal void DisposeInternal(int reference) { if (luaState != null) luaState.Unref(reference); } /* * Gets a field of the table corresponding to the provided reference * using rawget (do not use metatables) */ internal object RawGetObject(int reference, string field) { int oldTop = luaState.GetTop(); luaState.GetRef(reference); luaState.PushString(field); luaState.RawGet(-2); object obj = translator.GetObject(luaState, -1); luaState.SetTop(oldTop); return obj; } /* * Gets a field of the table or userdata corresponding to the provided reference */ internal object GetObject(int reference, string field) { int oldTop = luaState.GetTop(); luaState.GetRef(reference); object returnValue = GetObject(FullPathToArray(field)); luaState.SetTop(oldTop); return returnValue; } /* * Gets a numeric field of the table or userdata corresponding the the provided reference */ internal object GetObject(int reference, object field) { int oldTop = luaState.GetTop(); luaState.GetRef(reference); translator.Push(luaState, field); luaState.GetTable(-2); object returnValue = translator.GetObject(luaState, -1); luaState.SetTop(oldTop); return returnValue; } /* * Sets a field of the table or userdata corresponding the the provided reference * to the provided value */ internal void SetObject(int reference, string field, object val) { int oldTop = luaState.GetTop(); luaState.GetRef(reference); SetObject(FullPathToArray(field), val); luaState.SetTop(oldTop); } /* * Sets a numeric field of the table or userdata corresponding the the provided reference * to the provided value */ internal void SetObject(int reference, object field, object val) { int oldTop = luaState.GetTop(); luaState.GetRef(reference); translator.Push(luaState, field); translator.Push(luaState, val); luaState.SetTable(-3); luaState.SetTop(oldTop); } public LuaFunction RegisterFunction(string path, MethodBase function /*MethodInfo function*/) { return RegisterFunction(path, null, function); } /* * Registers an object's method as a Lua function (global or table field) * The method may have any signature */ public LuaFunction RegisterFunction(string path, object target, MethodBase function /*MethodInfo function*/) //CP: Fix for struct constructor by Alexander Kappner (link: http://luaforge.net/forum/forum.php?thread_id = 2859&forum_id = 145) { // We leave nothing on the stack when we are done int oldTop = luaState.GetTop(); var wrapper = new LuaMethodWrapper(translator, target, new ProxyType(function.DeclaringType), function); translator.Push(luaState, new LuaNativeFunction(wrapper.invokeFunction)); this[path] = translator.GetObject(luaState, -1); var f = GetFunction(path); luaState.SetTop(oldTop); return f; } /* * Compares the two values referenced by ref1 and ref2 for equality */ internal bool CompareRef(int ref1, int ref2) { int top = luaState.GetTop(); luaState.GetRef(ref1); luaState.GetRef(ref2); bool equal = luaState.AreEqual(-1, -2); luaState.SetTop(top); return equal; } internal void PushCSFunction(LuaNativeFunction function) { translator.PushFunction(luaState, function); } #region IDisposable Members public virtual void Dispose() { if (translator != null) { translator.pendingEvents.Dispose(); translator = null; } Close(); GC.WaitForPendingFinalizers(); } #endregion } }