private void pushObject(Object o) { GCHandle gch = GCHandle.Alloc(o); // Store the GCHandle so that it won't get garbage collected. mLuaReferences.Add(gch); // Push a userdata onto the stack (we don't care about the returned address) // This is needed because we cannot create a metatable for a lightuserdata. // So we make the light userdata a member of it's metatable. IntPtr p = LuaDll.lua_newuserdata(L, 8); // Write the IntPtr of the object handle to the userdata. Marshal.WriteIntPtr(p, GCHandle.ToIntPtr(gch)); // Pushes the metatable of the given class onto the stack pushMetatable(o.GetType()); // We should have a metatable of this object's class on the top of the stack. // Now register the object, which is below that (-2) // o.__metatable = mt LuaDll.lua_setmetatable(L, -2); // The exported object should now be on the top of the stack. }
/// <summary> /// Returns the value at stack index i as a C# object. /// </summary> /// <param name="i">Index of object</param> /// <returns>Returns object at index i</returns> private object getValue(int i) { object result = null; int t = LuaDll.lua_type(L, i); switch (t) { case LuaDll.LUA_TSTRING: /* strings */ result = LuaDll.lua_tostring(L, i); break; case LuaDll.LUA_TBOOLEAN: /* booleans */ result = LuaDll.lua_toboolean(L, i); break; case LuaDll.LUA_TNUMBER: /* numbers */ result = LuaDll.lua_tonumber(L, i); break; case LuaDll.LUA_TUSERDATA: /* user data */ IntPtr p = LuaDll.lua_touserdata(L, i); result = GCHandle.FromIntPtr(Marshal.ReadIntPtr(p)).Target; break; default: /* other values */ Console.WriteLine("getValue({0}) : Unsupported type [{1}]", i, LuaDll.lua_typename(L, t)); break; } return(result); }
private int pushFunctionLut(System.Reflection.MethodInfo mi, string bindName) { int top = LuaDll.lua_gettop(L); // Push global lut to the stack string globalLutName = "__Lua.cs__LUT__"; LuaDll.lua_getfield(L, LuaDll.LUA_REGISTRYINDEX, globalLutName); if (LuaDll.lua_isnil(L, -1)) { // If the global lut does not exist then create it and push it onto the stack. // Pop the nil value LuaDll.lua_pop(L, 1); // Push a new table and set it as the global globalLutName LuaDll.lua_newtable(L); LuaDll.lua_pushvalue(L, -1); // Dupe for setglobal call. LuaDll.lua_setfield(L, LuaDll.LUA_REGISTRYINDEX, globalLutName); } int lut = LuaDll.lua_gettop(L); Debug.Assert(lut == (top + 1)); // Push function lut to the stack. string fullName = mi.ReflectedType.FullName + "." + bindName; LuaDll.lua_getfield(L, lut, fullName); if (LuaDll.lua_isnil(L, -1)) { // If the function lut does not exist then create it. // Pop off the nil result. LuaDll.lua_pop(L, 1); // Push in a new table LuaDll.lua_newtable(L); // Add the name to the table as kFunctionNameKey LuaDll.lua_pushstring(L, bindName); LuaDll.lua_setfield(L, -2, kFunctionNameKey); // Add this to the global lut, keyed on FullName LuaDll.lua_pushvalue(L, -1); LuaDll.lua_setfield(L, lut, fullName); } // Remove the global lut LuaDll.lua_remove(L, lut); // Now we have the function lut at the top of the stack. int result = LuaDll.lua_gettop(L); Debug.Assert(result == top + 1); // Return the stack position of the lut. return(result); }
/// <summary> /// Pushes the object v into the lua stack and returns the number of values that were pushed. /// If v is a MultiValue then it might push more than one value. /// Will automatically register the class of the object if not yet registered /// </summary> /// <param name="v">Object to push onto the stack</param> /// <returns></returns> private int pushValue(object v) { if (v == null) { return(0); } Type t = v.GetType(); if (t == typeof(int)) { LuaDll.lua_pushinteger(L, (int)v); return(1); } else if ((t == typeof(double)) || (t == typeof(float))) { LuaDll.lua_pushnumber(L, (double)v); return(1); } else if (t == typeof(string)) { LuaDll.lua_pushstring(L, (string)v); return(1); } else if (t == typeof(bool)) { LuaDll.lua_pushboolean(L, ((bool)v)?1:0); return(1); } else if (t == typeof(MultiValue)) { // Special case to push multiple values onto the stack MultiValue mv = (MultiValue)v; int result = 0; for (int i = 0; i < mv.Length; ++i) { // Since this is recursive, we could end up with arrays inside arrays. // Should we catch that? result += pushValue(mv[i]); } return(result); } else if (t.IsClass) { // If the is an object, then store a reference to it in the static // list of lua references. It can be removed from this list once // lua is done with it. pushObject(v); return(1); } else { // Error, unexpected type, cannot push. Console.WriteLine("pushValue() Cannot push type [{0}]", t.FullName); return(0); } }
private void registerFunctionAs(System.Reflection.MethodInfo mi, string name) { // Push the function delgate pushFunction(mi, name); // Set the function as a global in the lua scope with the same name. LuaDll.lua_setglobal(L, name); }
public void registerObject(string name, Object o) { // Push the object onto the stack (this will also register the class as needed) pushObject(o); // Now set the object (which is now at the top of the stack) to be a global // with the given name. LuaDll.lua_setglobal(L, name); }
private void registerClass(Type t) { string className = t.FullName; if (LuaDll.luaL_newmetatable(L, className) == 1) { int top = LuaDll.lua_gettop(L); Console.WriteLine("Registering class " + className); foreach (System.Reflection.MethodInfo mi in t.GetMethods()) { if (mi.IsPublic) { // Check that this method has been declared as bindable? if (isBindable(mi)) { // If the name is set, then bind using that name. string bindName = getBindableName(mi, mi.Name); // Push the method pushFunction(mi, bindName); // Set the pushed closure as a member of our table. // The method should be at the top of the stack (-1), and the table below that (-2) // mt[mi.Name] = method; LuaDll.lua_setfield(L, -2, bindName); } } } // Register some luaisms // __gc LuaDll.lua_pushcfunction(L, mgcAdapterDelegate); LuaDll.lua_setfield(L, -2, "__gc"); // __tostring LuaDll.lua_pushcfunction(L, mtostringAdapterDelegate); LuaDll.lua_setfield(L, -2, "__tostring"); // __eq // Comparison operator LuaDll.lua_pushcfunction(L, meqAdapterDelegate); LuaDll.lua_setfield(L, -2, "__eq"); LuaDll.lua_pushstring(L, "__index"); // Push "__index" LuaDll.lua_pushvalue(L, -2); // Copy the metatable to the top of the stack LuaDll.lua_settable(L, -3); // mt["__index"] = mt // metatable should be at top of stack. Debug.Assert(top == LuaDll.lua_gettop(L), "Top is wrong"); } }
/// <summary> /// Implementation of __tostring metamethod. /// Used to convert a userdata object to a string for printing. /// </summary> /// <param name="L">Lua state</param> /// <returns>Returns one string on the stack</returns> private int tostringAdapter(lua_State L) { // Get the object IntPtr ud = LuaDll.lua_touserdata(L, 1); GCHandle gh = GCHandle.FromIntPtr(Marshal.ReadIntPtr(ud)); // Generate the string result string result = String.Format("{0}: {1:X}", gh.Target.ToString(), ud.ToInt32()); // Push the string and return it (return 1) LuaDll.lua_pushstring(L, result); return(1); }
/// <summary> /// Implementation of __eq metamethod. /// This is used to compare two objects of the same type for equality. /// </summary> /// <param name="L">Lua state</param> /// <returns>Returns 0 on the stack if the arguments are not equal, 1 otherwise</returns> private int eqAdapter(lua_State L) { // Get the objects IntPtr ud1 = LuaDll.lua_touserdata(L, 1); GCHandle gh1 = GCHandle.FromIntPtr(Marshal.ReadIntPtr(ud1)); IntPtr ud2 = LuaDll.lua_touserdata(L, 2); GCHandle gh2 = GCHandle.FromIntPtr(Marshal.ReadIntPtr(ud2)); bool result = gh1.Target.Equals(gh2.Target); LuaDll.lua_pushboolean(L, result ? 1 : 0); return(1); }
/// <summary> /// Implementation of __gc metamethod. /// Called when a userdata object is being released by the lua garbage collector. /// </summary> /// <param name="L">Lua state</param> /// <returns>Returns 0</returns> private int gcAdapter(lua_State L) { // Remove the referenced object from the global reference list so that // it can be cleaned up by the GC. IntPtr ud = LuaDll.lua_touserdata(L, 1); GCHandle gh = GCHandle.FromIntPtr(Marshal.ReadIntPtr(ud)); Console.WriteLine("[gcAdapter] - Releasing " + gh.Target.ToString()); bool result = mLuaReferences.Remove(gh); Debug.Assert(result, "[gcAdapter] - garbage collector release object not in mLuaReferences"); return(0); }
public MultiValue dostring(string s) { int top = LuaDll.lua_gettop(L); int ret = LuaDll.luaL_dostring(L, s); if (ret != 0) { string err = LuaDll.lua_tostring(L, -1); Console.WriteLine("ERROR: " + err); LuaDll.lua_pop(L, 1); } else { int newtop = LuaDll.lua_gettop(L); return(getValues(top + 1, newtop - top)); } return(null); }
/////////////////////////////////////////////////////////////////////// /// <summary> /// Constructor - creates a new lua state. /// </summary> public LuaState() { mLuaFunctionCallAdapterDelegate = functionCallAdapter; mgcAdapterDelegate = gcAdapter; mtostringAdapterDelegate = tostringAdapter; meqAdapterDelegate = eqAdapter; L = LuaDll.luaL_newstate(); LuaDll.luaL_openlibs(L); LuaDll.lua_atpanic(L, mPanicFunctionDelegate); // Redirect stderr from the Lua DLL to an error file. //FileStream fs = new FileStream("error.txt", FileMode.Create); //mOutputRef = new HandleRef(fs, fs.Handle); //SetStdHandle(-12, mOutputRef); // Install a new print function (to capture stdout) LuaDll.lua_register(L, "print", sLuaPrintDelegate); }
private void pushFunction(System.Reflection.MethodInfo mi, string bindName) { int top = LuaDll.lua_gettop(L); // Generate unique name based on parameter list. string mangle = buildMangledNameFromMethodInfo(mi, bindName); // Push the Look Up Table for this function onto the Lua stack. int flut = pushFunctionLut(mi, bindName); // Lookup the manged name. LuaDll.lua_getfield(L, flut, mangle); // If not already defined then go ahead and define it if (LuaDll.lua_isnil(L, -1)) { // Pop nil value LuaDll.lua_pop(L, 1); // Store the function in a GCHandle in a global list so it // doesn't get garbage collected. GCHandle h = GCHandle.Alloc(mi); mLuaReferences.Add(h); // functionLut[mangledName] = MethodInfo LuaDll.lua_pushlightuserdata(L, GCHandle.ToIntPtr(h)); LuaDll.lua_setfield(L, flut, mangle); // Push the function call delegate with the function lut as an up value. LuaDll.lua_pushcclosure(L, mLuaFunctionCallAdapterDelegate, 1); } else { System.Reflection.MethodInfo oldMi = (System.Reflection.MethodInfo)GCHandle.FromIntPtr(LuaDll.lua_touserdata(L, -1)).Target; throw new Exception(String.Format("Duplicate function registration of '{0}', old={1}, new={2}", demangleName(mangle), oldMi, mi)); } Debug.Assert(LuaDll.lua_gettop(L) == top + 1); }
public void inspectClass(Object o) { int top = LuaDll.lua_gettop(L); // check if this class is registered and inspect the metatable LuaDll.luaL_getmetatable(L, o.GetType().FullName); if (LuaDll.lua_isnil(L, -1)) { Console.WriteLine("inspectClass - metatable not found for class [{0}]", o.GetType().FullName); return; } Console.WriteLine("Inspecting {0}", o.GetType().FullName); /* table is in the stack at index 't' */ int t = LuaDll.lua_gettop(L); LuaDll.lua_pushnil(L); /* first key */ while (LuaDll.lua_next(L, t) != 0) { // Use built-in "print" function to print the two values on the stack. LuaDll.lua_getglobal(L, "print"); LuaDll.lua_pushstring(L, "\t"); LuaDll.lua_pushvalue(L, -4); LuaDll.lua_pushvalue(L, -4); LuaDll.lua_call(L, 3, 0); /* removes 'value'; keeps 'key' for next iteration */ LuaDll.lua_pop(L, 1); } // Pop the metatable LuaDll.lua_pop(L, 1); Debug.Assert(top == LuaDll.lua_gettop(L), "Error in stack"); }
/// <summary> /// Build a mangled name from a lua function call. /// Assumes that the function arguments are on the stack. /// This is to allow each unique overloaded method in C# to have a unique name in lua. /// Method names must be in the same format generated by buildMangledNameFromMethodInfo. /// </summary> /// <param name="simpleName">Unmangled name of method</param> /// <returns>Returns a string containing a unique name of the given method</returns> /// <see>buildMangledNameFromMethodInfo</see> private string buildMangledNameFromLuaParameters(string simpleName) { StringBuilder sb = new StringBuilder(simpleName + "_"); int paramcount = LuaDll.lua_gettop(L); Type[] types = new Type[paramcount]; for (int i = 0; i < paramcount; ++i) { int luaType = LuaDll.lua_type(L, i + 1); switch (luaType) { case LuaDll.LUA_TBOOLEAN: sb.Append("b"); break; case LuaDll.LUA_TNUMBER: sb.Append("n"); break; case LuaDll.LUA_TSTRING: sb.Append("s"); break; case LuaDll.LUA_TUSERDATA: sb.Append("c"); break; default: Console.WriteLine("Invalid parameter #{0} as {1}", i + 1, LuaDll.lua_typename(L, luaType)); break; } } return(sb.ToString()); }
public MultiValue call(string func, params object[] args) { int top = LuaDll.lua_gettop(L); MultiValue mv = new MultiValue(args); LuaDll.lua_getfield(L, LuaDll.LUA_GLOBALSINDEX, func); int count = pushValue(mv); int ret = LuaDll.lua_pcall(L, count, LuaDll.LUA_MULTRET, 0); if (ret != 0) { string err = LuaDll.lua_tostring(L, -1); Console.WriteLine("ERROR: " + err); LuaDll.lua_pop(L, 1); } else { int newtop = LuaDll.lua_gettop(L); return(getValues(top + 1, newtop - top)); } return(null); }
/// <summary> /// Single lua function used for all C# function or method calls from lua. /// </summary> /// <param name="L">Lua state</param> /// <returns>Returns number of results pushed onto the stack</returns> private int functionCallAdapter(lua_State L) { // Figure out which function to call. // Our upvalue is a table of all methods with the same name as this one. // Get the function name. LuaDll.lua_getfield(L, LuaDll.lua_upvalueindex(1), kFunctionNameKey); string simpleName = LuaDll.lua_tostring(L, -1); LuaDll.lua_pop(L, 1); // Lookup the mangled name from the lut stored at upvalueindex. string mangledName = buildMangledNameFromLuaParameters(simpleName); LuaDll.lua_getfield(L, LuaDll.lua_upvalueindex(1), mangledName); if (LuaDll.lua_isnil(L, -1)) { LuaDll.luaL_error(L, "Unknown function " + mangledName + " \"" + demangleName(mangledName) + "\""); return(0); } // Get the function delegate from the stack. GCHandle h = GCHandle.FromIntPtr(LuaDll.lua_topointer(L, -1)); LuaDll.lua_pop(L, 1); System.Reflection.MethodInfo fd = (System.Reflection.MethodInfo)h.Target; System.Reflection.ParameterInfo[] pinfo = fd.GetParameters(); int firstParam = 0; Object self = null; // Check if this is a static method or not. If it is not static, then the first // parameter must be an instance of the class. if (!fd.IsStatic) { // Get the pointer to the object (and verify the type). IntPtr ud = LuaDll.luaL_checkudata(L, 1, fd.ReflectedType.FullName); // Copy the address of the object from the userdata. GCHandle hSelf = GCHandle.FromIntPtr(Marshal.ReadIntPtr(ud)); // Since this is a non-static method, it will have an extra parameter. firstParam = 1; self = hSelf.Target; } // Verify that we are getting the correct number of parameters if (pinfo.Length != (LuaDll.lua_gettop(L) - firstParam)) { LuaDll.luaL_error(L, "Invalid number of parameters for function '{0}'", fd.Name); return(1); } Object[] parms = new Object[pinfo.Length]; int firstLuaParam = firstParam + 1; for (int i = 0; i < pinfo.Length; ++i) { if (pinfo[i].ParameterType == typeof(int)) { parms[i] = LuaDll.luaL_checkinteger(L, i + firstLuaParam); } else if (pinfo[i].ParameterType == typeof(double)) { parms[i] = LuaDll.luaL_checknumber(L, i + firstLuaParam); } else if (pinfo[i].ParameterType == typeof(string)) { parms[i] = LuaDll.luaL_checkstring(L, i + firstLuaParam); } else if (pinfo[i].ParameterType == typeof(bool)) { LuaDll.luaL_checktype(L, i + firstLuaParam, LuaDll.LUA_TBOOLEAN); parms[i] = LuaDll.lua_toboolean(L, i + firstLuaParam); } else { // Get the pointer to the object. IntPtr ud = LuaDll.lua_touserdata(L, i + firstLuaParam); parms[i] = GCHandle.FromIntPtr(Marshal.ReadIntPtr(ud)).Target; } } object result = fd.Invoke(self, parms); return(pushValue(result)); }