public MetaFunctions(lua.State L, ObjectTranslator translator) { Debug.Assert(translator.interpreter.IsSameLua(L)); this.translator = translator; this.interpreter = translator.interpreter; // used by BuildObjectMetatable _toString = this.toString; _index = this.index; _newindex = this.newIndex; luaL.checkstack(L, 2, "new MetaFunctions"); StackAssert.Start(L); // Creates the metatable for superclasses (the base field of registered tables) bool didnt_exist = luaclr.newrefmeta(L, "luaNet_searchbase", 3); Debug.Assert(didnt_exist); lua.pushcfunction(L, _toString); lua.setfield(L, -2, "__tostring"); lua.pushcfunction(L, _baseIndex = this.baseIndex); lua.setfield(L, -2, "__index"); lua.pushcfunction(L, _newindex); lua.setfield(L, -2, "__newindex"); lua.pop(L, 1); // Creates the metatable for type references didnt_exist = luaclr.newrefmeta(L, "luaNet_class", 4); Debug.Assert(didnt_exist); lua.pushcfunction(L, _toString); lua.setfield(L, -2, "__tostring"); lua.pushcfunction(L, _classIndex = this.classIndex); lua.setfield(L, -2, "__index"); lua.pushcfunction(L, _classNewindex = this.classNewIndex); lua.setfield(L, -2, "__newindex"); lua.pushcfunction(L, _classCall = this.classCall); lua.setfield(L, -2, "__call"); lua.pop(L, 1); StackAssert.End(); }
[TestMethod] public void InFunction() { using (var l = new Lua()) { var L = luanet.getstate(l); lua.cpcall(L, L2 => { Assert.AreEqual(1, L2.Length); lua.pop(L2, 1); Assert.AreEqual(3, L.Sum(si => si.Type == LUA.T.NONE ? 0 : 1)); Assert.AreEqual(LUA.T.NONE, L2[lua.upvalueindex(1)].Type); return(0); }, default(IntPtr)); Assert.AreEqual(0, L.Length); lua.CFunction cf = L2 => { Assert.AreEqual(0, L2.Length); Assert.AreEqual(4, L2.Sum(si => si.Type == LUA.T.NONE ? 0 : 1)); Assert.AreEqual(LUA.T.BOOLEAN, L2[lua.upvalueindex(1)].Type); return(0); }; lua.pushboolean(L, true); lua.pushcclosure(L, cf, 1); lua.call(L, 0, 0); GC.KeepAlive(cf); Assert.AreEqual(0, L.Length); } }
public void push() { _keys[_keys.Length - 1].VerifySupport(); var L = Parent.L; luanet.checkstack(L, 2, "StackIndexChild.push"); StackAssert.Start(L); Dbg.Assert(_keys.All(k => k.IsSupported)); var keys = _keys; lua.CFunction tryBlock = L2 => { for (int i = 0; i < keys.Length; ++i) { keys[i].push(L2); lua.gettable(L2, -2); lua.remove(L2, -2); } return(1); }; // parent should be pushed first. it might be pointing to the index we're about to fill this.Parent.push(); lua.pushcfunction(L, tryBlock); lua.insert(L, -2); var ex = luanet.pcall(L, 1, 1); if (ex != null) { StackAssert.End(); Trace.TraceError("push failed: " + ex.Message); throw ex; } GC.KeepAlive(tryBlock); StackAssert.End(1); }
/// <summary>[-n, +1, m] Pushes a new C closure onto the stack. <paramref name="n"/> can be up to 254; the last slot is reserved for a garbage collection userdata that keeps the delegate alive.</summary> public static void pushcclosure(lua.State L, lua.CFunction f, int n) { Debug.Assert(f != null); Debug.Assert(n <= 254 && n >= 0); luaL.checkstack(L, 2, "luaclr.pushcclosure"); luaclr.newref(L, f); luaclr.newrefmeta(L, "LuaCLR CFunction anchor", 0); // nothing to add to this metatable other than gc, it just needs to exist and then not exist lua.setmetatable(L, -2); lua.pushcclosure(L, f, n + 1); }
public ObjectTranslator(lua.State L, Lua interpreter) { Debug.Assert(interpreter.IsSameLua(L)); luaclr.checkstack(L, 1, "new ObjectTranslator"); this.interpreter = interpreter; typeChecker = new CheckType(interpreter); metaFunctions = new MetaFunctions(L, this); StackAssert.Start(L); lua.pushcfunction(L, _loadAssembly = this.loadAssembly); lua.setglobal(L, "load_assembly"); lua.pushcfunction(L, _importType = this.importType); lua.setglobal(L, "import_type"); lua.pushcfunction(L, _makeObject = this.makeObject); lua.setglobal(L, "make_object"); lua.pushcfunction(L, _freeObject = this.freeObject); lua.setglobal(L, "free_object"); lua.pushcfunction(L, _getMethodBysig = this.getMethodBysig); lua.setglobal(L, "get_method_bysig"); lua.pushcfunction(L, _getConstructorBysig = this.getConstructorBysig); lua.setglobal(L, "get_constructor_bysig"); lua.pushcfunction(L, _ctype = this.ctype); lua.setglobal(L, "ctype"); lua.pushcfunction(L, _enum = this.@enum); lua.setglobal(L, "enum"); StackAssert.End(); }
public void CallViaThunk(Action function) { if (function == null) { throw new ArgumentNullException("function"); } // todo: is it more performant to actually pass a GCHandle in through the ud parameter versus creating and GCing delegate thunks? lua.CFunction wrapper = L => { lua.settop(L, 0); // no need for luanet.entercfunction here because it's guaranteed to be the same state try { function(); } catch (LuaInternalException) { throw; } catch (Exception ex) { luanet.error(L, _interpreter, ex); } return(0); }; { var L = _L; if (lua.cpcall(L, wrapper, default(IntPtr)) != LUA.ERR.Success) { throw luanet.exceptionfromerror(L, _interpreter, -2); } } }
/// <summary>[-0, +2, e] /// Returns to Lua the value of a member or a delegate to call it, depending on the type of the member. /// Works with static or instance members. Uses reflection to find members, /// and stores the reflected MemberInfo object in a cache (indexed by <paramref name="objType"/> and <paramref name="methodName"/>). /// </summary> /// <exception cref="ArgumentNullException"><paramref name="objType"/> and <paramref name="methodName"/> are required</exception> int getMember(lua.State L, IReflect objType, object obj, string methodName, BindingFlags bindingType) { Debug.Assert(interpreter.IsSameLua(L)); Debug.Assert(objType != null && methodName != null); Debug.Assert((obj == null) == (objType is ProxyType)); Debug.Assert((obj == null) != (objType is Type)); Debug.Assert((obj == null) == ((bindingType & BindingFlags.Static) == BindingFlags.Static)); Debug.Assert((obj == null) != ((bindingType & BindingFlags.Instance) == BindingFlags.Instance)); MemberInfo member = null; object cachedMember = checkMemberCache(objType, methodName); if (cachedMember != null) { var cachedMethod = cachedMember as lua.CFunction; if (cachedMethod != null) { luaclr.pushcfunction(L, cachedMethod); lua.pushboolean(L, true); return(2); } Debug.Assert(cachedMember is MemberInfo); member = (MemberInfo)cachedMember; } else { MemberInfo[] members = objType.GetMember(methodName, bindingType | luanet.LuaBindingFlags); if (members.Length != 0) { member = members[0]; #if DEBUG if (!(members.Length == 1 || member is MethodBase)) // todo { return(luaL.error(L, "Overloads for members other than methods are not implemented.")); } #endif } } object value = null; switch (member == null ? MemberTypes.All : member.MemberType) { default: // not found or found a constructor // kevinh - we want to throw an error because merely returning 'nil' in this case // is not sufficient. valid data members may return nil and therefore there must be some // way to know the member just doesn't exist. return(luaL.error(L, string.Format("'{0}' does not contain a definition for '{1}'", objType.UnderlyingSystemType.FullName, methodName))); case MemberTypes.Method: var wrapper = new lua.CFunction((new LuaMethodWrapper(translator, objType, methodName, bindingType)).call); if (cachedMember == null) { setMemberCache(objType, methodName, wrapper); } luaclr.pushcfunction(L, wrapper); lua.pushboolean(L, true); return(2); case MemberTypes.Field: if (!translator.memberIsAllowed(member)) { return(luaL.error(L, "field read failed (access denied)")); } try { value = ((FieldInfo)member).GetValue(obj); } catch { goto default; } translator.push(L, value); break; case MemberTypes.Property: // todo: support indexed properties if (!translator.memberIsAllowed(member)) { return(luaL.error(L, "property call failed (access denied)")); } try { value = ((PropertyInfo)member).GetValue(obj, null); } catch (TargetInvocationException ex) { return(translator.throwError(L, luaclr.verifyex(ex.InnerException))); } catch { goto default; } translator.push(L, value); break; case MemberTypes.Event: if (!translator.memberIsAllowed(member)) { return(luaL.error(L, "event read failed (access denied)")); } value = new RegisterEventHandler(translator.pendingEvents, obj, (EventInfo)member); translator.push(L, value); break; case MemberTypes.NestedType: var nestedType = (Type)member; if (translator.FindType(nestedType)) // don't hand out class references unless loaded/whitelisted { ObjectTranslator.pushType(L, nestedType); } else { lua.pushnil(L); } break; } if (cachedMember == null) { setMemberCache(objType, methodName, member); } // push false because we are NOT returning a function (see luaIndexFunction) lua.pushboolean(L, false); return(2); }
[TestMethod] public void CFunctions() { const uint iterations = 2048 * 2048; using (var li = new Lua()) { var L = luanet.getstate(li); Trace.WriteLine(iterations.ToString("N0") + " iterations"); Noop(L); // jit var timer = Stopwatch.StartNew(); for (uint i = 0; i < iterations; ++i) { Noop(L); } timer.Stop(); Display(timer, "CLR -> CLR"); luaL.loadstring(L, @" local Noop = Noop for i=1," + iterations + @" do Noop() end " ); #pragma warning disable 618 luanet.CSFunction cs_function = Noop; luanet.pushstdcallcfunction(L, cs_function); lua.setglobal(L, "Noop"); lua.pushvalue(L, -1); timer.Reset(); timer.Start(); lua.call(L, 0, 0); timer.Stop(); Display(timer, "Lua -> stdcall -> CLR"); GC.KeepAlive(cs_function); #pragma warning restore 618 lua.CFunction c_function = Noop; lua.pushcfunction(L, c_function); lua.setglobal(L, "Noop"); lua.pushvalue(L, -1); timer.Reset(); timer.Start(); lua.call(L, 0, 0); timer.Stop(); Display(timer, "Lua -> cdecl -> CLR"); GC.KeepAlive(c_function); luaL.dostring(L, "function Noop() end"); lua.pushvalue(L, -1); timer.Reset(); timer.Start(); lua.call(L, 0, 0); timer.Stop(); Display(timer, "Lua -> Lua"); /* * 4,194,304 iterations * CLR -> CLR: 12ms * Lua -> stdcall -> CLR: 731ms * Lua -> cdecl -> CLR: 420ms * Lua -> Lua: 468ms */ } }
/// <summary>[-0, +1, m] Pushes a C function onto the stack just like lua_pushcfunction except the delegate will be automatically kept alive by storing an opaque userdata in an upvalue.</summary> [MethodImpl(INLINE)] public static void pushcfunction(lua.State L, lua.CFunction f) { luaclr.pushcclosure(L, f, 0); }