/// <summary> /// Defines a new local variable and returns an object used to get/set it's value. There are /// two possible variable types: Local and Captured. Which one is chosen depends on whether the /// variable is captured in the FunctionInfo used to create the current function. /// </summary> /// <param name="name">The name of the variable.</param> /// <returns>An object used to get/set it's value.</returns> public IVarDefinition DefineLocal(NameItem name) { return(_curNest.DefineLocal(name)); }
/// <summary> /// Implements a function definition based on a given function definition. /// </summary> /// <param name="funcName">The simple name of the function, can be null.</param> /// <param name="visitor">The current visitor object.</param> /// <param name="function">The function to generate for.</param> public void ImplementFunction(IParseItemVisitor visitor, FuncDefItem function, string funcName) { NameItem[] args = function.Arguments.ToArray(); if (function.InstanceName != null) args = new[] { new NameItem("self") }.Union(args).ToArray(); // ILuaMultiValue function(ILuaEnvironment E, ILuaMultiValue args, ILuaValue target, bool memberCall); funcName = funcName ?? "<>__" + (_mid++); string name = curNest.members.Contains(funcName) ? funcName + "_" + (_mid++) : funcName; MethodBuilder mb = curNest.TypeDef.DefineMethod(name, MethodAttributes.Public, typeof(ILuaMultiValue), new Type[] { typeof(ILuaEnvironment), typeof(ILuaMultiValue), typeof(ILuaValue), typeof(bool) }); var gen = mb.GetILGenerator(); curNest = new NestInfo(curNest, gen, function.FunctionInformation.CapturedLocals, function.FunctionInformation.HasNested, function.FunctionInformation.CapturesParrent); // if this is an instance method, create a BaseAccessor object to help types. if (function.InstanceName != null) { // TODO: Add base accessor back. //var field = curNest.DefineLocal(new NameItem("base")); } // If this was an instance call, the first Lua argument is the 'target'; // otherwise the it is the zero'th index in args. // int c = 0; var c = gen.DeclareLocal(typeof(int)); if (args.Length > 0) { var field = curNest.DefineLocal(args[0]); var end = gen.DefineLabel(); var else_ = gen.DefineLabel(); // if (!memberCall) c = 1; // {field_0} = (memberCall ? target : args[0]); field.StartSet(); gen.Emit(OpCodes.Ldarg, 4); gen.Emit(OpCodes.Brfalse, else_); gen.Emit(OpCodes.Ldarg_3); gen.Emit(OpCodes.Br, end); gen.MarkLabel(else_); gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Stloc, c); gen.Emit(OpCodes.Ldarg_2); if (args[0].Name != "...") { gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item")); } else { if (args.Length != 1) throw new InvalidOperationException("Variable arguments (...) only valid at end of argument list."); } gen.MarkLabel(end); field.EndSet(); } for (int i = 1; i < args.Length; i++) { var field = curNest.DefineLocal(args[i]); if (args[i].Name == "...") { if (i != args.Length - 1) throw new InvalidOperationException("Variable arguments (...) only valid at end of argument list."); // {field} = E.Runtime.CreateMultiValue(args.Skip({args.Length - 1}); field.StartSet(); gen.Emit(OpCodes.Ldarg_1); gen.Emit(OpCodes.Callvirt, typeof(ILuaEnvironment).GetMethod("get_Runtime")); gen.Emit(OpCodes.Ldarg_2); gen.Emit(OpCodes.Ldc_I4, args.Length - 1); gen.Emit(OpCodes.Call, typeof(Enumerable).GetMethod("Skip").MakeGenericMethod(typeof(object))); gen.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod("CreateMultiValue")); field.EndSet(); } else { // {field} = args[{i - 1} + c]; field.StartSet(); gen.Emit(OpCodes.Ldarg_2); gen.Emit(OpCodes.Ldc_I4, i-1); gen.Emit(OpCodes.Ldloc, c); gen.Emit(OpCodes.Add); gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item")); field.EndSet(); } } function.Block.Accept(visitor); if (curNest.TypeDef != null) curNest.TypeDef.CreateType(); curNest = curNest.Parrent; // push a pointer to the new method onto the stack of the previous nest method // the above line restores the nest to the previous state and this code will // push the new method. //! push E.Runtime.CreateFunctionValue({name}, {nest.TypeDef}.GetMethod({name}), {nest.ThisInst != null ? nest.NestInst : this} ); curNest.Generator.Emit(OpCodes.Ldarg_1); curNest.Generator.Emit(OpCodes.Callvirt, typeof(ILuaEnvironment).GetMethod("get_Runtime")); curNest.Generator.Emit(OpCodes.Ldstr, name); curNest.Generator.Emit(OpCodes.Ldtoken, curNest.TypeDef); curNest.Generator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) })); curNest.Generator.Emit(OpCodes.Ldstr, name); curNest.Generator.Emit(OpCodes.Callvirt, typeof(Type).GetMethod("GetMethod", new[] { typeof(string) })); if (curNest.ThisInst != null) curNest.Generator.Emit(OpCodes.Ldloc, curNest.ThisInst); else curNest.Generator.Emit(OpCodes.Ldarg_0); curNest.Generator.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod("CreateImplementationFunction")); }
/// <summary> /// Implements a function definition based on a given function definition. /// </summary> /// <param name="funcName">The simple name of the function, can be null.</param> /// <param name="visitor">The current visitor object.</param> /// <param name="function">The function to generate for.</param> public void ImplementFunction(IParseItemVisitor visitor, FuncDefItem function, string funcName) { NameItem[] args = function.Arguments.ToArray(); if (function.InstanceName != null) { args = new[] { new NameItem("self") }.Concat(args).ToArray(); } // ILuaMultiValue function(ILuaEnvironment E, ILuaMultiValue args, ILuaValue target, // bool memberCall); funcName ??= "<>__" + (_mid++); string name = _curNest.Members.Contains(funcName) ? funcName + "_" + (_mid++) : funcName; MethodBuilder mb = _curNest.TypeDef.DefineMethod( name, MethodAttributes.Public, typeof(ILuaMultiValue), new Type[] { typeof(ILuaEnvironment), typeof(ILuaMultiValue), typeof(ILuaValue), typeof(bool) }); var gen = mb.GetILGenerator(); _curNest = new NestInfo( _curNest, gen, function.FunctionInformation.CapturedLocals, function.FunctionInformation.HasNested, function.FunctionInformation.CapturesParent); // if this is an instance method, create a BaseAccessor object to help types. if (function.InstanceName != null) { // TODO: Add base accessor back. //var field = curNest.DefineLocal(new NameItem("base")); } // If this was an instance call, the first Lua argument is the 'target'; // otherwise the it is the zero'th index in args. // int c = 0; var c = gen.DeclareLocal(typeof(int)); if (args.Length > 0) { var field = _curNest.DefineLocal(args[0]); var end = gen.DefineLabel(); var else_ = gen.DefineLabel(); // if (!memberCall) c = 1; // {field_0} = (memberCall ? target : args[0]); field.StartSet(); gen.Emit(OpCodes.Ldarg, 4); gen.Emit(OpCodes.Brfalse, else_); gen.Emit(OpCodes.Ldarg_3); gen.Emit(OpCodes.Br, end); gen.MarkLabel(else_); gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Stloc, c); gen.Emit(OpCodes.Ldarg_2); if (args[0].Name != "...") { gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item")); } else { if (args.Length != 1) { throw new InvalidOperationException( "Variable arguments (...) only valid at end of argument list."); } } gen.MarkLabel(end); field.EndSet(); } for (int i = 1; i < args.Length; i++) { var field = _curNest.DefineLocal(args[i]); if (args[i].Name == "...") { if (i != args.Length - 1) { throw new InvalidOperationException( "Variable arguments (...) only valid at end of argument list."); } // {field} = E.Runtime.CreateMultiValue(args.Skip({args.Length - 1}); field.StartSet(); gen.Emit(OpCodes.Ldarg_1); gen.Emit( OpCodes.Callvirt, typeof(ILuaEnvironment).GetProperty(nameof(ILuaEnvironment.Runtime)).GetGetMethod()); gen.Emit(OpCodes.Ldarg_2); gen.Emit(OpCodes.Ldc_I4, args.Length - 1); gen.Emit(OpCodes.Call, typeof(Enumerable).GetMethod(nameof(Enumerable.Skip)) .MakeGenericMethod(typeof(object))); gen.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod(nameof(ILuaRuntime.CreateMultiValue))); field.EndSet(); } else { // {field} = args[{i - 1} + c]; field.StartSet(); gen.Emit(OpCodes.Ldarg_2); gen.Emit(OpCodes.Ldc_I4, i - 1); gen.Emit(OpCodes.Ldloc, c); gen.Emit(OpCodes.Add); gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item")); field.EndSet(); } } function.Block.Accept(visitor); if (_curNest.TypeDef != null) { _curNest.TypeDef.CreateType(); } _curNest = _curNest.Parent; // push a pointer to the new method onto the stack of the previous nest method // the above line restores the nest to the previous state and this code will // push the new method. //! push E.Runtime.CreateFunctionValue({name}, {nest.TypeDef}.GetMethod({name}), // {nest.ThisInst != null ? nest.NestInst : this} ); _curNest.Generator.Emit(OpCodes.Ldarg_1); _curNest.Generator.Emit( OpCodes.Callvirt, typeof(ILuaEnvironment).GetProperty(nameof(ILuaEnvironment.Runtime)).GetGetMethod()); _curNest.Generator.Emit(OpCodes.Ldstr, name); _curNest.Generator.Emit(OpCodes.Ldtoken, _curNest.TypeDef); _curNest.Generator.Emit( OpCodes.Call, typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), new Type[] { typeof(RuntimeTypeHandle) })); _curNest.Generator.Emit(OpCodes.Ldstr, name); _curNest.Generator.Emit( OpCodes.Callvirt, typeof(Type).GetMethod(nameof(Type.GetMethod), new[] { typeof(string) })); if (_curNest.ThisInst != null) { _curNest.Generator.Emit(OpCodes.Ldloc, _curNest.ThisInst); } else { _curNest.Generator.Emit(OpCodes.Ldarg_0); } _curNest.Generator.Emit( OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod(nameof(ILuaRuntime.CreateImplementationFunction))); }