/// <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> /// Called when the item is a function definition item. /// </summary> /// <param name="target">The object that was passed to IParseItem.Visit.</param> /// <returns>The passed target or a modification of it.</returns> /// <exception cref="System.ArgumentNullException">If target is null.</exception> public IParseItem Visit(FuncDefItem target) { if (target == null) throw new ArgumentNullException("target"); var gen = compiler.CurrentGenerator; ChunkBuilder.VarDefinition field = null; string name = null; bool store = false; if (target.Local) { // local function definition. if (target.InstanceName != null) throw new SyntaxException(Resources.InstanceLocalMethod, target.Debug); if (!(target.Prefix is NameItem)) throw new SyntaxException(Resources.IndexerLocalMethod, target.Debug); NameItem namei = (NameItem)target.Prefix; name = namei.Name; field = compiler.DefineLocal(namei); field.StartSet(); } else if (target.Prefix != null) { if (target.InstanceName != null) { // instance function definition. name = null; if (target.Prefix is NameItem) name = ((NameItem)target.Prefix).Name; else name = (string)((LiteralItem)((IndexerItem)target.Prefix).Expression).Value; name += ":" + target.InstanceName; // {Prefix}.SetIndex({InstanceName}, {ImplementFunction(..)}) target.Prefix.Accept(this); gen.Emit(OpCodes.Ldarg_1); gen.Emit(OpCodes.Callvirt, typeof(ILuaEnvironment).GetMethod("get_Runtime")); gen.Emit(OpCodes.Ldstr, target.InstanceName); gen.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod("CreateValue")); store = true; } else if (target.Prefix is IndexerItem) { // global function definition with indexer // {Prefix}.SetIndex({Expression}, {ImplementFunction(..)}) IndexerItem index = (IndexerItem)target.Prefix; name = (string)((LiteralItem)index.Expression).Value; index.Prefix.Accept(this); index.Expression.Accept(this); store = true; } else { // global function definition with name name = ((NameItem)target.Prefix).Name; field = compiler.FindVariable((NameItem)target.Prefix); field.StartSet(); } } compiler.ImplementFunction(this, target, name); if (field != null) field.EndSet(); else if (store) gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod("SetIndex")); return target; }
/// <summary> /// Called when the item is a function definition item. /// </summary> /// <param name="target">The object that was passed to IParseItem.Visit.</param> /// <returns>The passed target or a modification of it.</returns> /// <exception cref="System.ArgumentNullException">If target is null.</exception> public IParseItem Visit(FuncDefItem target) { if (target == null) throw new ArgumentNullException("target"); if (target.Local) { tree.DefineLocal(new[] { target.Prefix as NameItem }); } using (tree.DefineFunc()) { tree.DefineLocal(target.Arguments); target.Block.Accept(this); } target.FunctionInformation = tree.EndFunc(); return target; }
/// <summary> /// Reads a function from the input. Input must either be on the word 'function' or /// on the next token. If it is on 'function' and canName is true, it will give /// the function the read name; otherwise it will give it a null name. /// </summary> /// <param name="input">Where to read input from.</param> /// <param name="token">The token to append the read Token to.</param> /// <param name="canName">True if the function can have a name, otherwise false.</param> /// <param name="local">True if this function is a local definition, otherwise false.</param> /// <returns>The function definition that was read.</returns> protected virtual FuncDefItem ReadFunctionHelper(ITokenizer input, ref Token token, bool canName, bool local) { IParseVariable name = null; string inst = null; Token last = input.Peek(), debug = last; if (last.Value == "function") { input.Read(); // read 'function' last = input.Peek(); if (IsName(last.Value)) { Token nameTok = input.Read(); // read name name = new NameItem(last.Value) { Debug = last }; // handle indexers last = input.Peek(); while (last.Value == ".") { Read(input, ref nameTok); // read '.' last = input.Peek(); if (!IsName(last.Value)) break; name = new IndexerItem(name, new LiteralItem(last.Value) { Debug = last }) { Debug = nameTok }; Read(input, ref nameTok); } if (input.Peek().Value == ":") { Read(input, ref nameTok); inst = Read(input, ref nameTok).Value; if (!IsName(inst)) throw new SyntaxException(string.Format(Resources.TokenInvalid, last.Value, "function"), input.Name, last); } debug.Append(nameTok); } } if (name != null && !canName) throw new SyntaxException(Resources.FunctionCantHaveName, input.Name, debug); FuncDefItem ret = new FuncDefItem(name, local); ret.InstanceName = inst; last = Read(input, ref debug); if (last.Value != "(") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting, last.Value, "function", "("), input.Name, last); last = input.Peek(); while (last.Value != ")") { Token temp = Read(input, ref debug); // read the name if (!IsName(last.Value) && last.Value != "...") throw new SyntaxException(string.Format(Resources.TokenInvalid, last.Value, "function"), input.Name, temp); ret.AddArgument(new NameItem(last.Value) { Debug = last }); last = input.Peek(); if (last.Value == ",") Read(input, ref debug); else if (last.Value != ")") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting2, last.Value, "function", ",", ")"), input.Name, last); last = input.Peek(); } if (last.Value != ")") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting, last.Value, "function", ")"), input.Name, last); Read(input, ref debug); // read ')' BlockItem chunk = ReadBlock(input, ref debug); chunk.Return = chunk.Return ?? new ReturnItem(); ret.Block = chunk; last = Read(input, ref debug); if (last.Value != "end") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting, last.Value, "function", "end"), input.Name, last); token.Append(debug); ret.Debug = debug; return ret; }