namespace LuaInterface { using System; using System.IO; using System.Collections; using System.Reflection; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Diagnostics; /* * Passes objects from the CLR to Lua and vice-versa * * Author: Fabio Mascarenhas * Version: 1.0 */ public class ObjectTranslator { internal CheckType typeChecker; // object # to object (FIXME - it should be possible to get object address as an object #) public readonly Dictionary objects = new Dictionary(); // object to object # public readonly Dictionary objectsBackMap = new Dictionary(); internal LuaState interpreter; public MetaFunctions metaFunctions; public List assemblies; private LuaCSFunction registerTableFunction,unregisterTableFunction,getMethodSigFunction, getConstructorSigFunction,importTypeFunction,loadAssemblyFunction, ctypeFunction, enumFromIntFunction; internal EventHandlerContainer pendingEvents = new EventHandlerContainer(); public static ObjectTranslator FromState(IntPtr luaState) { LuaDLL.lua_getglobal(luaState, "_translator"); IntPtr thisptr = LuaDLL.lua_touserdata(luaState, -1); LuaDLL.lua_pop(luaState, 1); GCHandle handle = GCHandle.FromIntPtr(thisptr); ObjectTranslator translator = (ObjectTranslator)handle.Target; return translator; } public ObjectTranslator(LuaState interpreter,IntPtr luaState) { this.interpreter=interpreter; typeChecker=new CheckType(this); metaFunctions=new MetaFunctions(this); assemblies=new List(); assemblies.Add(Assembly.GetExecutingAssembly()); importTypeFunction=new LuaCSFunction(importType); loadAssemblyFunction=new LuaCSFunction(loadAssembly); registerTableFunction=new LuaCSFunction(registerTable); unregisterTableFunction=new LuaCSFunction(unregisterTable); getMethodSigFunction=new LuaCSFunction(getMethodSignature); getConstructorSigFunction=new LuaCSFunction(getConstructorSignature); ctypeFunction = new LuaCSFunction(ctype); enumFromIntFunction = new LuaCSFunction(enumFromInt); createLuaObjectList(luaState); createIndexingMetaFunction(luaState); createBaseClassMetatable(luaState); createClassMetatable(luaState); createFunctionMetatable(luaState); setGlobalFunctions(luaState); } /* * Sets up the list of objects in the Lua side */ private void createLuaObjectList(IntPtr luaState) { LuaDLL.lua_pushstring(luaState,"luaNet_objects"); LuaDLL.lua_newtable(luaState); LuaDLL.lua_newtable(luaState); LuaDLL.lua_pushstring(luaState,"__mode"); LuaDLL.lua_pushstring(luaState,"v"); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_setmetatable(luaState,-2); LuaDLL.lua_settable(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX); } /* * Registers the indexing function of CLR objects * passed to Lua */ private void createIndexingMetaFunction(IntPtr luaState) { LuaDLL.lua_pushstring(luaState,"luaNet_indexfunction"); LuaDLL.luaL_dostring(luaState,MetaFunctions.luaIndexFunction); //LuaDLL.lua_pushstdcallcfunction(luaState,indexFunction); LuaDLL.lua_rawset(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX); } /* * Creates the metatable for superclasses (the base * field of registered tables) */ private void createBaseClassMetatable(IntPtr luaState) { LuaDLL.luaL_newmetatable(luaState,"luaNet_searchbase"); LuaDLL.lua_pushstring(luaState,"__gc"); LuaDLL.lua_pushstdcallcfunction(luaState, metaFunctions.gcFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_pushstring(luaState,"__tostring"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_pushstring(luaState,"__index"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.baseIndexFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_pushstring(luaState,"__newindex"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.newindexFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_settop(luaState,-2); } /* * Creates the metatable for type references */ private void createClassMetatable(IntPtr luaState) { LuaDLL.luaL_newmetatable(luaState,"luaNet_class"); LuaDLL.lua_pushstring(luaState,"__gc"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_pushstring(luaState,"__tostring"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_pushstring(luaState,"__index"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.classIndexFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_pushstring(luaState,"__newindex"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.classNewindexFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_pushstring(luaState,"__call"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.callConstructorFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_settop(luaState,-2); } /* * Registers the global functions used by LuaInterface */ private void setGlobalFunctions(IntPtr luaState) { LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.indexFunction); LuaDLL.lua_setglobal(luaState,"get_object_member"); LuaDLL.lua_pushstdcallcfunction(luaState,importTypeFunction); LuaDLL.lua_setglobal(luaState,"import_type"); LuaDLL.lua_pushstdcallcfunction(luaState,loadAssemblyFunction); LuaDLL.lua_setglobal(luaState,"load_assembly"); LuaDLL.lua_pushstdcallcfunction(luaState,registerTableFunction); LuaDLL.lua_setglobal(luaState,"make_object"); LuaDLL.lua_pushstdcallcfunction(luaState,unregisterTableFunction); LuaDLL.lua_setglobal(luaState,"free_object"); LuaDLL.lua_pushstdcallcfunction(luaState,getMethodSigFunction); LuaDLL.lua_setglobal(luaState,"get_method_bysig"); LuaDLL.lua_pushstdcallcfunction(luaState,getConstructorSigFunction); LuaDLL.lua_setglobal(luaState,"get_constructor_bysig"); LuaDLL.lua_pushstdcallcfunction(luaState,ctypeFunction); LuaDLL.lua_setglobal(luaState,"ctype"); LuaDLL.lua_pushstdcallcfunction(luaState,enumFromIntFunction); LuaDLL.lua_setglobal(luaState,"enum"); } /* * Creates the metatable for delegates */ private void createFunctionMetatable(IntPtr luaState) { LuaDLL.luaL_newmetatable(luaState,"luaNet_function"); LuaDLL.lua_pushstring(luaState,"__gc"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_pushstring(luaState,"__call"); LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.execDelegateFunction); LuaDLL.lua_settable(luaState,-3); LuaDLL.lua_settop(luaState,-2); } /* * Passes errors (argument e) to the Lua interpreter */ internal void throwError(IntPtr luaState, object e) { // We use this to remove anything pushed by luaL_where int oldTop = LuaDLL.lua_gettop(luaState); // Stack frame #1 is our C# wrapper, so not very interesting to the user // Stack frame #2 must be the lua code that called us, so that's what we want to use LuaDLL.luaL_where(luaState, 1); object[] curlev = popValues(luaState, oldTop); // Determine the position in the script where the exception was triggered string errLocation = ""; if (curlev.Length > 0) errLocation = curlev[0].ToString(); string message = e as string; if (message != null) { // Wrap Lua error (just a string) and store the error location e = new LuaScriptException(message, errLocation); } else { Exception ex = e as Exception; if (ex != null) { // Wrap generic .NET exception as an InnerException and store the error location e = new LuaScriptException(ex, errLocation); } } push(luaState, e); LuaDLL.lua_error(luaState); } /* * Implementation of load_assembly. Throws an error * if the assembly is not found. */ [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] public static int loadAssembly(IntPtr luaState) { ObjectTranslator translator = ObjectTranslator.FromState(luaState); try { string assemblyName=LuaDLL.lua_tostring(luaState,1); Assembly assembly = null; //assembly = Assembly.GetExecutingAssembly(); try { assembly = Assembly.Load(assemblyName); } catch (BadImageFormatException) { // The assemblyName was invalid. It is most likely a path. } if (assembly == null) { assembly = Assembly.Load(AssemblyName.GetAssemblyName(assemblyName)); } if (assembly != null && !translator.assemblies.Contains(assembly)) { translator.assemblies.Add(assembly); } } catch(Exception e) { translator.throwError(luaState,e); } return 0; } internal Type FindType(string className) { foreach(Assembly assembly in assemblies) { Type klass=assembly.GetType(className); if(klass!=null) { return klass; } } return null; } /* * Implementation of import_type. Returns nil if the * type is not found. */ [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] public static int importType(IntPtr luaState) { ObjectTranslator translator = ObjectTranslator.FromState(luaState); string className=LuaDLL.lua_tostring(luaState,1); Type klass=translator.FindType(className); if(klass!=null) translator.pushType(luaState,klass); else LuaDLL.lua_pushnil(luaState); return 1; } /* * Implementation of make_object. Registers a table (first * argument in the stack) as an object subclassing the * type passed as second argument in the stack. */ [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] public static int registerTable(IntPtr luaState) { ObjectTranslator translator = ObjectTranslator.FromState(luaState); #if __NOGEN__ translator.throwError(luaState, "Tables as Objects not implemnented"); #else if(LuaDLL.lua_type(luaState,1)==LuaTypes.LUA_TTABLE) { LuaTable luaTable=translator.getTable(luaState,1); string superclassName = LuaDLL.lua_tostring(luaState, 2); if (superclassName != null) { Type klass = translator.FindType(superclassName); if (klass != null) { // Creates and pushes the object in the stack, setting // it as the metatable of the first argument object obj = CodeGeneration.Instance.GetClassInstance(klass, luaTable); translator.pushObject(luaState, obj, "luaNet_metatable"); LuaDLL.lua_newtable(luaState); LuaDLL.lua_pushstring(luaState, "__index"); LuaDLL.lua_pushvalue(luaState, -3); LuaDLL.lua_settable(luaState, -3); LuaDLL.lua_pushstring(luaState, "__newindex"); LuaDLL.lua_pushvalue(luaState, -3); LuaDLL.lua_settable(luaState, -3); LuaDLL.lua_setmetatable(luaState, 1); // Pushes the object again, this time as the base field // of the table and with the luaNet_searchbase metatable LuaDLL.lua_pushstring(luaState, "base"); int index = translator.addObject(obj); translator.pushNewObject(luaState, obj, index, "luaNet_searchbase"); LuaDLL.lua_rawset(luaState, 1); } else translator.throwError(luaState, "register_table: can not find superclass '" + superclassName + "'"); } else translator.throwError(luaState, "register_table: superclass name can not be null"); } else translator.throwError(luaState,"register_table: first arg is not a table"); #endif return 0; } /* * Implementation of free_object. Clears the metatable and the * base field, freeing the created object for garbage-collection */ [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] public static int unregisterTable(IntPtr luaState) { ObjectTranslator translator = ObjectTranslator.FromState(luaState); try { if(LuaDLL.lua_getmetatable(luaState,1)!=0) { LuaDLL.lua_pushstring(luaState,"__index"); LuaDLL.lua_gettable(luaState,-2); object obj=translator.getRawNetObject(luaState,-1); if(obj==null) translator.throwError(luaState,"unregister_table: arg is not valid table"); FieldInfo luaTableField=obj.GetType().GetField("__luaInterface_luaTable"); if(luaTableField==null) translator.throwError(luaState,"unregister_table: arg is not valid table"); luaTableField.SetValue(obj,null); LuaDLL.lua_pushnil(luaState); LuaDLL.lua_setmetatable(luaState,1); LuaDLL.lua_pushstring(luaState,"base"); LuaDLL.lua_pushnil(luaState); LuaDLL.lua_settable(luaState,1); } else translator.throwError(luaState,"unregister_table: arg is not valid table"); } catch(Exception e) { translator.throwError(luaState,e.Message); } return 0; } /* * Implementation of get_method_bysig. Returns nil * if no matching method is not found. */ [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] public static int getMethodSignature(IntPtr luaState) { ObjectTranslator translator = ObjectTranslator.FromState(luaState); IReflect klass; object target; int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class"); if(udata!=-1) { klass=(IReflect)translator.objects[udata]; target=null; } else { target=translator.getRawNetObject(luaState,1); if(target==null) { translator.throwError(luaState,"get_method_bysig: first arg is not type or object reference"); LuaDLL.lua_pushnil(luaState); return 1; } klass=target.GetType(); } string methodName=LuaDLL.lua_tostring(luaState,2); Type[] signature=new Type[LuaDLL.lua_gettop(luaState)-2]; for(int i=0;i /// Given the Lua int ID for an object remove it from our maps /// /// internal void collectObject(int udata) { object o; bool found = objects.TryGetValue(udata, out o); // The other variant of collectObject might have gotten here first, in that case we will silently ignore the missing entry if (found) { // Debug.WriteLine("Removing " + o.ToString() + " @ " + udata); objects.Remove(udata); objectsBackMap.Remove(o); } } /// /// Given an object reference, remove it from our maps /// /// void collectObject(object o, int udata) { // Debug.WriteLine("Removing " + o.ToString() + " @ " + udata); objects.Remove(udata); objectsBackMap.Remove(o); } /// /// We want to ensure that objects always have a unique ID /// int nextObj = 0; int addObject(object obj) { // New object: inserts it in the list int index = nextObj++; // Debug.WriteLine("Adding " + obj.ToString() + " @ " + index); objects[index] = obj; objectsBackMap[obj] = index; return index; } /* * Gets an object from the Lua stack according to its Lua type. */ internal object getObject(IntPtr luaState,int index) { LuaTypes type=LuaDLL.lua_type(luaState,index); switch(type) { case LuaTypes.LUA_TNUMBER: { return LuaDLL.lua_tonumber(luaState,index); } case LuaTypes.LUA_TSTRING: { return LuaDLL.lua_tostring(luaState,index); } case LuaTypes.LUA_TBOOLEAN: { return LuaDLL.lua_toboolean(luaState,index); } case LuaTypes.LUA_TTABLE: { return getTable(luaState,index); } case LuaTypes.LUA_TFUNCTION: { return getFunction(luaState,index); } case LuaTypes.LUA_TUSERDATA: { int udata=LuaDLL.luanet_tonetobject(luaState,index); if(udata!=-1) return objects[udata]; else return getUserData(luaState,index); } default: return null; } } /* * Gets the table in the index positon of the Lua stack. */ internal LuaTable getTable(IntPtr luaState,int index) { LuaDLL.lua_pushvalue(luaState,index); return new LuaTable(LuaDLL.luaL_ref(luaState,LuaIndexes.LUA_REGISTRYINDEX),interpreter); } /* * Gets the userdata in the index positon of the Lua stack. */ internal LuaUserData getUserData(IntPtr luaState,int index) { LuaDLL.lua_pushvalue(luaState,index); return new LuaUserData(LuaDLL.luaL_ref(luaState,LuaIndexes.LUA_REGISTRYINDEX),interpreter); } /* * Gets the function in the index positon of the Lua stack. */ public LuaFunction getFunction(IntPtr luaState,int index) { LuaDLL.lua_pushvalue(luaState,index); return new LuaFunction(LuaDLL.luaL_ref(luaState,LuaIndexes.LUA_REGISTRYINDEX),interpreter); } /* * Gets the CLR object in the index positon of the Lua stack. Returns * delegates as Lua functions. */ internal object getNetObject(IntPtr luaState,int index) { int idx=LuaDLL.luanet_tonetobject(luaState,index); if(idx!=-1) return objects[idx]; else return null; } /* * Gets the CLR object in the index positon of the Lua stack. Returns * delegates as is. */ internal object getRawNetObject(IntPtr luaState,int index) { int udata=LuaDLL.luanet_rawnetobj(luaState,index); if(udata!=-1) { return objects[udata]; } return null; } /* * Pushes the entire array into the Lua stack and returns the number * of elements pushed. */ internal int returnValues(IntPtr luaState, object[] returnValues) { if(LuaDLL.lua_checkstack(luaState,returnValues.Length+5)) { for(int i=0;i