예제 #1
0
        /// <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"));
        }
예제 #2
0
        /// <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;
        }
예제 #3
0
        /// <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;
        }
예제 #4
0
        /// <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;
        }