/* * This file is part of LuaInterface. * * Copyright (C) 2003-2005 Fabio Mascarenhas de Queiroz. * Copyright (C) 2012 Megax * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using System; using System.IO; using System.Threading; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using LuaInterface.Event; using LuaInterface.Method; using LuaInterface.Exceptions; using LuaInterface.Extensions; namespace LuaInterface { using LuaCore = KopiLua.Lua; /* * Main class of LuaInterface * Object-oriented wrapper to Lua API * * Author: Fabio Mascarenhas * Version: 1.0 * * // steffenj: important changes in Lua class: * - removed all Open*Lib() functions * - all libs automatically open in the Lua class constructor (just assign nil to unwanted libs) * */ [CLSCompliant(true)] public class Lua : IDisposable { #region lua debug functions /// /// Event that is raised when an exception occures during a hook call. /// /// Reinhard Ostermeier public event EventHandler HookException; /// /// Event when lua hook callback is called /// /// /// Is only raised if SetDebugHook is called before. /// /// Reinhard Ostermeier public event EventHandler DebugHook; /// /// lua hook calback delegate /// /// Reinhard Ostermeier private LuaCore.lua_Hook hookCallback = null; #endregion #region Globals auto-complete private readonly List globals = new List(); private bool globalsSorted; #endregion private /*readonly */ LuaCore.lua_State luaState; /// /// True while a script is being executed /// public bool IsExecuting { get { return executing; } } private LuaCore.lua_CFunction panicCallback; private ObjectTranslator translator; /// /// 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 init_luanet = "local metatable = {} \n" + "local import_type = luanet.import_type \n" + "local load_assembly = luanet.load_assembly \n" + " \n" + "-- Lookup a .NET identifier component. \n" + "function metatable:__index(key) -- key is e.g. \"Form\" \n" + " -- Get the fully-qualified name, e.g. \"System.Windows.Forms.Form\" \n" + " local fqn = ((rawget(self, \".fqn\") and rawget(self, \".fqn\") .. \n" + " \".\") or \"\") .. key \n" + " \n" + " -- Try to find either a luanet function or a CLR type \n" + " local obj = rawget(luanet, key) or import_type(fqn) \n" + " \n" + " -- If key is neither a luanet function or a CLR type, then it is simply \n" + " -- an identifier component. \n" + " if obj == nil then \n" + " -- It might be an assembly, so we load it too. \n" + " load_assembly(fqn) \n" + " obj = { [\".fqn\"] = fqn } \n" + " setmetatable(obj, metatable) \n" + " end \n" + " \n" + " -- Cache this lookup \n" + " rawset(self, key, obj) \n" + " return obj \n" + "end \n" + " \n" + "-- A non-type has been called; e.g. foo = System.Foo() \n" + "function metatable:__call(...) \n" + " error(\"No such type: \" .. rawget(self, \".fqn\"), 2) \n" + "end \n" + " \n" + "-- This is the root of the .NET namespace \n" + "luanet[\".fqn\"] = false \n" + "setmetatable(luanet, metatable) \n" + " \n" + "-- Preload the mscorlib assembly \n" + "luanet.load_assembly(\"mscorlib\") \n"; #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 = LuaLib.luaL_newstate(); // steffenj: Lua 5.1.1 API change (lua_open is gone) //LuaLib.luaopen_base(luaState); // steffenj: luaopen_* no longer used LuaLib.luaL_openlibs(luaState); // steffenj: Lua 5.1.1 API change (luaopen_base is gone, just open all libs right here) LuaLib.lua_pushstring(luaState, "LUAINTERFACE LOADED"); LuaLib.lua_pushboolean(luaState, true); LuaLib.lua_settable(luaState, (int)LuaIndexes.Registry); LuaLib.lua_newtable(luaState); LuaLib.lua_setglobal(luaState, "luanet"); LuaLib.lua_pushvalue(luaState, (int)LuaIndexes.Globals); LuaLib.lua_getglobal(luaState, "luanet"); LuaLib.lua_pushstring(luaState, "getmetatable"); LuaLib.lua_getglobal(luaState, "getmetatable"); LuaLib.lua_settable(luaState, -3); LuaLib.lua_replace(luaState, (int)LuaIndexes.Globals); translator = new ObjectTranslator(this, luaState); LuaLib.lua_replace(luaState, (int)LuaIndexes.Globals); LuaLib.luaL_dostring(luaState, Lua.init_luanet); // steffenj: lua_dostring renamed to luaL_dostring // We need to keep this in a managed reference so the delegate doesn't get garbage collected panicCallback = new LuaCore.lua_CFunction(PanicCallback); LuaLib.lua_atpanic(luaState, panicCallback); //LuaLib.lua_atlock(luaState, lockCallback = new LuaCore.lua_CFunction(LockCallback)); //LuaLib.lua_atunlock(luaState, unlockCallback = new LuaCore.lua_CFunction(UnlockCallback)); } /* * CAUTION: LuaInterface.Lua instances can't share the same lua state! */ public Lua(LuaCore.lua_State lState) { LuaLib.lua_pushstring(lState, "LUAINTERFACE LOADED"); LuaLib.lua_gettable(lState, (int)LuaIndexes.Registry); if(LuaLib.lua_toboolean(lState, -1)) { LuaLib.lua_settop(lState, -2); throw new LuaException("There is already a LuaInterface.Lua instance associated with this Lua state"); } else { LuaLib.lua_settop(lState, -2); LuaLib.lua_pushstring(lState, "LUAINTERFACE LOADED"); LuaLib.lua_pushboolean(lState, true); LuaLib.lua_settable(lState, (int)LuaIndexes.Registry); luaState = lState; LuaLib.lua_pushvalue(lState, (int)LuaIndexes.Globals); LuaLib.lua_getglobal(lState, "luanet"); LuaLib.lua_pushstring(lState, "getmetatable"); LuaLib.lua_getglobal(lState, "getmetatable"); LuaLib.lua_settable(lState, -3); LuaLib.lua_replace(lState, (int)LuaIndexes.Globals); translator = new ObjectTranslator(this, luaState); LuaLib.lua_replace(lState, (int)LuaIndexes.Globals); LuaLib.luaL_dostring(lState, Lua.init_luanet); // steffenj: lua_dostring renamed to luaL_dostring } _StatePassed = true; } /// /// Called for each lua_lock call /// /// /// Not yet used /*int LockCallback(LuaCore.lua_State luaState) { // Monitor.Enter(luaLock); return 0; }*/ /// /// Called for each lua_unlock call /// /// /// Not yet used /*int UnlockCallback(LuaCore.lua_State luaState) { // Monitor.Exit(luaLock); return 0; }*/ public void Close() { if(_StatePassed) return; ////// if(luaState != LuaCore.lua_State.Zero) if(!luaState.IsNull()) LuaCore.lua_close(luaState); //luaState = LuaCore.lua_State.Zero; <- suggested by Christopher Cebulski http://luaforge.net/forum/forum.php?thread_id = 44593&forum_id = 146 } static int PanicCallback(LuaCore.lua_State luaState) { // string desc = LuaLib.lua_tostring(luaState, 1); string reason = string.Format("unprotected error in call to Lua API ({0})", LuaLib.lua_tostring(luaState, -1)); // lua_tostring(L, -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); LuaLib.lua_settop(luaState, oldTop); // A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved) var luaEx = err as LuaScriptException; if(!luaEx.IsNull()) throw luaEx; // A non-wrapped Lua error (best interpreted as a string) - wrap it and throw it if(err.IsNull()) err = "Unknown Lua Error"; throw new LuaScriptException(err.ToString(), string.Empty); } /// /// 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.IsNull()) { translator.throwError(luaState, caughtExcept); LuaLib.lua_pushnil(luaState); return 1; } else return 0; } /// /// /// /// /// /// public LuaFunction LoadString(string chunk, string name) { int oldTop = LuaLib.lua_gettop(luaState); executing = true; try { if(LuaLib.luaL_loadbuffer(luaState, chunk, name) != 0) ThrowExceptionFromError(oldTop); } finally { executing = false; } var result = translator.getFunction(luaState, -1); translator.popValues(luaState, oldTop); return result; } /// /// /// /// /// public LuaFunction LoadFile(string fileName) { int oldTop = LuaLib.lua_gettop(luaState); if(LuaLib.luaL_loadfile(luaState, fileName) != 0) ThrowExceptionFromError(oldTop); var result = translator.getFunction(luaState, -1); translator.popValues(luaState, oldTop); return result; } /* * Excutes a Lua chunk and returns all the chunk's return * values in an array */ public object[] DoString(string chunk) { int oldTop = LuaLib.lua_gettop(luaState); if(LuaLib.luaL_loadbuffer(luaState, chunk, "chunk") == 0) { executing = true; try { if(LuaLib.lua_pcall(luaState, 0, -1, 0) == 0) return translator.popValues(luaState, oldTop); else ThrowExceptionFromError(oldTop); } finally { executing = false; } } else ThrowExceptionFromError(oldTop); return null; // Never reached - keeps compiler happy } /// /// Executes a Lua chnk and returns all the chunk's return values in an array. /// /// Chunk to execute /// Name to associate with the chunk /// public object[] DoString(string chunk, string chunkName) { int oldTop = LuaLib.lua_gettop(luaState); executing = true; if(LuaLib.luaL_loadbuffer(luaState, chunk, chunkName) == 0) { try { if(LuaLib.lua_pcall(luaState, 0, -1, 0) == 0) return translator.popValues(luaState, oldTop); else ThrowExceptionFromError(oldTop); } finally { executing = false; } } else ThrowExceptionFromError(oldTop); return null; // Never reached - keeps compiler happy } /* * Excutes a Lua file and returns all the chunk's return * values in an array */ public object[] DoFile(string fileName) { int oldTop = LuaLib.lua_gettop(luaState); if(LuaLib.luaL_loadfile(luaState, fileName) == 0) { executing = true; try { if(LuaLib.lua_pcall(luaState, 0, -1, 0) == 0) return translator.popValues(luaState, oldTop); else ThrowExceptionFromError(oldTop); } finally { executing = false; } } else ThrowExceptionFromError(oldTop); return null; // Never reached - keeps compiler happy } /* * 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 = LuaLib.lua_gettop(luaState); string[] path = fullPath.Split(new char[] { '.' }); LuaLib.lua_getglobal(luaState, path[0]); returnValue = translator.getObject(luaState, -1); if(path.Length>1) { string[] remainingPath = new string[path.Length-1]; Array.Copy(path, 1, remainingPath, 0, path.Length-1); returnValue = getObject(remainingPath); } LuaLib.lua_settop(luaState, oldTop); return returnValue; } set { int oldTop = LuaLib.lua_gettop(luaState); string[] path = fullPath.Split(new char[] { '.' }); if(path.Length == 1) { translator.push(luaState, value); LuaLib.lua_setglobal(luaState, fullPath); } else { LuaLib.lua_getglobal(luaState, path[0]); string[] remainingPath = new string[path.Length-1]; Array.Copy(path, 1, remainingPath, 0, path.Length-1); setObject(remainingPath, value); } LuaLib.lua_settop(luaState, oldTop); // Globals auto-complete if(value.IsNull()) { // 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(LuaCore.lua_CFunction)) { // 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)) { if( // Check that the LuaHideAttribute and LuaGlobalAttribute were not applied (method.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) && (method.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0) && // Exclude some generic .NET methods that wouldn't be very usefull in Lua method.Name != "GetType" && method.Name != "GetHashCode" && method.Name != "Equals" && method.Name != "ToString" && method.Name != "Clone" && method.Name != "Dispose" && method.Name != "GetEnumerator" && method.Name != "CopyTo" && !method.Name.StartsWith("get_", StringComparison.Ordinal) && !method.Name.StartsWith("set_", StringComparison.Ordinal) && !method.Name.StartsWith("add_", StringComparison.Ordinal) && !method.Name.StartsWith("remove_", StringComparison.Ordinal)) { // Format for easy method invocation string command = path + ":" + method.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).Length == 0) && (field.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0)) { // 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).Length == 0) && (property.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0) // Exclude some generic .NET properties that wouldn't be very usefull 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 */ internal object getObject(string[] remainingPath) { object returnValue = null; for(int i = 0; i < remainingPath.Length; i++) { LuaLib.lua_pushstring(luaState, remainingPath[i]); LuaLib.lua_gettable(luaState, -2); returnValue = translator.getObject(luaState, -1); if(returnValue.IsNull()) 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]; return (obj is LuaCore.lua_CFunction ? new LuaFunction((LuaCore.lua_CFunction)obj, this) : (LuaFunction)obj); } /* * Register a delegate type to be used to convert Lua funcitions 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); } /* * 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 = LuaLib.lua_gettop(luaState); if(!LuaLib.lua_checkstack(luaState, args.Length+6)) throw new LuaException("Lua stack overflow"); translator.push(luaState, function); if(!args.IsNull()) { nArgs = args.Length; for(int i = 0; i < args.Length; i++) translator.push(luaState, args[i]); } executing = true; try { int error = LuaLib.lua_pcall(luaState, nArgs, -1, 0); if(error != 0) ThrowExceptionFromError(oldTop); } finally { executing = false; } return !returnTypes.IsNull() ? translator.popValues(luaState, oldTop, returnTypes) : translator.popValues(luaState, oldTop); } /* * Navigates a table to set the value of one of its fields */ internal void setObject(string[] remainingPath, object val) { for(int i = 0; i < remainingPath.Length-1; i++) { LuaLib.lua_pushstring(luaState, remainingPath[i]); LuaLib.lua_gettable(luaState, -2); } LuaLib.lua_pushstring(luaState, remainingPath[remainingPath.Length-1]); translator.push(luaState, val); LuaLib.lua_settable(luaState, -3); } /* * Creates a new table as a global variable or as a field * inside an existing table */ public void NewTable(string fullPath) { string[] path = fullPath.Split(new char[] { '.' }); int oldTop = LuaLib.lua_gettop(luaState); if(path.Length == 1) { LuaLib.lua_newtable(luaState); LuaLib.lua_setglobal(luaState, fullPath); } else { LuaLib.lua_getglobal(luaState, path[0]); for(int i = 1; i < path.Length-1; i++) { LuaLib.lua_pushstring(luaState, path[i]); LuaLib.lua_gettable(luaState, -2); } LuaLib.lua_pushstring(luaState, path[path.Length-1]); LuaLib.lua_newtable(luaState); LuaLib.lua_settable(luaState, -3); } LuaLib.lua_settop(luaState, oldTop); } public ListDictionary GetTableDict(LuaTable table) { var dict = new ListDictionary(); int oldTop = LuaLib.lua_gettop(luaState); translator.push(luaState, table); LuaLib.lua_pushnil(luaState); while(LuaLib.lua_next(luaState, -2) != 0) { dict[translator.getObject(luaState, -2)] = translator.getObject(luaState, -1); LuaLib.lua_settop(luaState, -2); } LuaLib.lua_settop(luaState, 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 /// Reinhard Ostermeier public int SetDebugHook(EventMasks mask, int count) { if(hookCallback.IsNull()) { hookCallback = new LuaCore.lua_Hook(DebugHookCallback); return LuaCore.lua_sethook(luaState, hookCallback, (int)mask, count); } return -1; } /// /// Removes the debug hook /// /// see lua docs /// Reinhard Ostermeier public int RemoveDebugHook() { hookCallback = null; return LuaCore.lua_sethook(luaState, null, 0, 0); } /// /// Gets the hook mask. /// /// hook mask /// Reinhard Ostermeier public EventMasks GetHookMask() { return (EventMasks)LuaCore.lua_gethookmask(luaState); } /// /// Gets the hook count /// /// see lua docs /// Reinhard Ostermeier public int GetHookCount() { return LuaCore.lua_gethookcount(luaState); } /// /// Gets the stack entry on a given level /// /// level /// lua debug structure /// Returns true if level was allowed, false if level was invalid. /// Reinhard Ostermeier /*public bool GetStack(int level, out LuaCore.lua_Debug luaDebug) { luaDebug = new LuaDebug(); LuaCore.lua_State ld = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(luaDebug)); System.Runtime.InteropServices.Marshal.StructureToPtr(luaDebug, ld, false); try { return LuaLib.lua_getstack(luaState, level, luaDebug) != 0; } finally { luaDebug = (LuaDebug)System.Runtime.InteropServices.Marshal.PtrToStructure(ld, typeof(LuaDebug)); System.Runtime.InteropServices.Marshal.FreeHGlobal(ld); } }*/ /// /// Gets info (see lua docs) /// /// what (see lua docs) /// lua debug structure /// see lua docs /// Reinhard Ostermeier /*public int GetInfo(String what, ref LuaCore.lua_Debug luaDebug) { LuaCore.lua_State ld = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(luaDebug)); System.Runtime.InteropServices.Marshal.StructureToPtr(luaDebug, ld, false); try { return LuaLib.lua_getinfo(luaState, what, ld); } finally { luaDebug = (LuaDebug)System.Runtime.InteropServices.Marshal.PtrToStructure(ld, typeof(LuaDebug)); System.Runtime.InteropServices.Marshal.FreeHGlobal(ld); } }*/ /// /// Gets local (see lua docs) /// /// lua debug structure /// see lua docs /// see lua docs /// Reinhard Ostermeier public string GetLocal(LuaCore.lua_Debug luaDebug, int n) { return LuaCore.lua_getlocal(luaState, luaDebug, n).ToString(); } /// /// Sets local (see lua docs) /// /// lua debug structure /// see lua docs /// see lua docs /// Reinhard Ostermeier public string SetLocal(LuaCore.lua_Debug luaDebug, int n) { return LuaCore.lua_setlocal(luaState, luaDebug, n).ToString(); } /// /// Gets up value (see lua docs) /// /// see lua docs /// see lua docs /// see lua docs /// Reinhard Ostermeier public string GetUpValue(int funcindex, int n) { return LuaCore.lua_getupvalue(luaState, funcindex, n).ToString(); } /// /// Sets up value (see lua docs) /// /// see lua docs /// see lua docs /// see lua docs /// Reinhard Ostermeier public string SetUpValue(int funcindex, int n) { return LuaCore.lua_setupvalue(luaState, funcindex, n).ToString(); } /// /// Delegate that is called on lua hook callback /// /// lua state /// Pointer to LuaDebug (lua_debug) structure /// Reinhard Ostermeier private void DebugHookCallback(LuaCore.lua_State luaState, LuaCore.lua_Debug luaDebug) { try { var temp = DebugHook; if(!temp.IsNull()) temp(this, new DebugHookEventArgs(luaDebug)); } catch(Exception ex) { OnHookException(new HookExceptionEventArgs(ex)); } } private void OnHookException(HookExceptionEventArgs e) { var temp = HookException; if(!temp.IsNull()) temp(this, e); } /// /// Pops a value from the lua stack. /// /// Returns the top value from the lua stack. /// Reinhard Ostermeier public object Pop() { int top = LuaLib.lua_gettop(luaState); return translator.popValues(luaState, top - 1)[0]; } /// /// Pushes a value onto the lua stack. /// /// Value to push. /// Reinhard Ostermeier public void Push(object value) { translator.push(luaState, value); } #endregion internal void dispose(int reference) { if(!luaState.IsNull()) //Fix submitted by Qingrui Li LuaLib.lua_unref(luaState, 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 = LuaLib.lua_gettop(luaState); LuaLib.lua_getref(luaState, reference); LuaLib.lua_pushstring(luaState, field); LuaLib.lua_rawget(luaState, -2); object obj = translator.getObject(luaState, -1); LuaLib.lua_settop(luaState, 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 = LuaLib.lua_gettop(luaState); LuaLib.lua_getref(luaState, reference); object returnValue = getObject(field.Split(new char[] {'.'})); LuaLib.lua_settop(luaState, 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 = LuaLib.lua_gettop(luaState); LuaLib.lua_getref(luaState, reference); translator.push(luaState, field); LuaLib.lua_gettable(luaState, -2); object returnValue = translator.getObject(luaState, -1); LuaLib.lua_settop(luaState, 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 = LuaLib.lua_gettop(luaState); LuaLib.lua_getref(luaState, reference); setObject(field.Split(new char[] {'.'}), val); LuaLib.lua_settop(luaState, 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 = LuaLib.lua_gettop(luaState); LuaLib.lua_getref(luaState, reference); translator.push(luaState, field); translator.push(luaState, val); LuaLib.lua_settable(luaState, -3); LuaLib.lua_settop(luaState, oldTop); } /* * 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 = LuaLib.lua_gettop(luaState); var wrapper = new LuaMethodWrapper(translator, target, function.DeclaringType, function); translator.push(luaState, new LuaCore.lua_CFunction(wrapper.call)); this[path] = translator.getObject(luaState, -1); var f = GetFunction(path); LuaLib.lua_settop(luaState, oldTop); return f; } /* * Compares the two values referenced by ref1 and ref2 for equality */ internal bool compareRef(int ref1, int ref2) { int top = LuaLib.lua_gettop(luaState); LuaLib.lua_getref(luaState, ref1); LuaLib.lua_getref(luaState, ref2); int equal = LuaLib.lua_equal(luaState, -1, -2); LuaLib.lua_settop(luaState, top); return (equal != 0); } internal void pushCSFunction(LuaCore.lua_CFunction function) { translator.pushFunction(luaState, function); } #region IDisposable Members public virtual void Dispose() { if(!translator.IsNull()) { translator.pendingEvents.Dispose(); translator = null; } this.Close(); GC.Collect(); GC.WaitForPendingFinalizers(); } #endregion } }