示例#1
0
        internal override void Generate(CodeGenerator cg)
        {
            /* Template:
             *  for (;MoveNext(enumerator);)
             *      $value = CurrentValue(enumerator);
             *      $key = CurrentKey(enumerator);
             *      {body}
             *  }
             */

            var lblMoveNext = new NamedLabel("MoveNext");
            var lblBody     = new object();

            //
            cg.Builder.DefineHiddenSequencePoint();
            cg.Builder.EmitBranch(ILOpCode.Br, lblMoveNext);
            cg.Builder.MarkLabel(lblBody);

            // $value, $key
            this.EnumereeEdge.EmitGetCurrent(cg, this.ValueVariable, this.KeyVariable);

            // {
            cg.GenerateScope(this.BodyBlock, NextBlock.Ordinal);
            // }

            // if (enumerator.MoveNext())
            cg.EmitSequencePoint(_moveSpan);
            cg.Builder.MarkLabel(lblMoveNext);
            this.EnumereeEdge.EmitMoveNext(cg); // bool
            cg.Builder.EmitBranch(ILOpCode.Brtrue, lblBody);

            //
            cg.Scope.ContinueWith(NextBlock);
        }
示例#2
0
        void EmitMultipleTypeCheck(CodeGenerator cg, ImmutableArray <BoundTypeRef> trefs)
        {
            var il = cg.Builder;

            // STACK : object

            var lblFound = new NamedLabel("filter_found");
            var lblEnd   = new NamedLabel("filter_end");

            for (int i = 0; i < trefs.Length; i++)
            {
                il.EmitOpCode(ILOpCode.Dup);

                // (ex is T) : bool
                EmitTypeCheck(cg, trefs[i]);

                // if (STACK) goto lblFound;
                il.EmitBranch(ILOpCode.Brtrue, lblFound);
            }

            il.EmitOpCode(ILOpCode.Pop);    // POP object
            il.EmitBoolConstant(false);
            il.EmitBranch(ILOpCode.Br, lblEnd);

            il.MarkLabel(lblFound);
            il.EmitOpCode(ILOpCode.Pop);    // POP object
            il.EmitBoolConstant(true);

            il.MarkLabel(lblEnd);

            // STACK: i4 (boolean)
        }
示例#3
0
        private static void EmitStateMachineMethodStart(CodeGenerator cg)
        {
            // local <state> = g._state that is switched on (can't switch on remote field)
            cg.EmitGeneratorInstance();
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetGeneratorState_Generator);

            var stateTmpLocal = cg.GetTemporaryLocal(cg.CoreTypes.Int32);

            cg.Builder.EmitLocalStore(stateTmpLocal);

            // g._state = -1 : running
            cg.EmitGeneratorInstance();
            cg.Builder.EmitIntConstant(-1);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.SetGeneratorState_Generator_int);

            // create label for situation when state doesn't correspond to continuation: 0 -> didn't run to first yield
            var noContinuationLabel = new NamedLabel("noStateContinuation");

            // prepare jump table from yields
            var yieldExLabels = new List <KeyValuePair <ConstantValue, object> >();

            foreach (var yield in cg.Routine.ControlFlowGraph.Yields)
            {
                // labels have 1-based index (zero is reserved for run to first yield)
                // label object is the BoundYieldStatement itself, it is Marked at the proper place within its Emit method
                Debug.Assert(yield.YieldIndex >= 1);
                yieldExLabels.Add(new KeyValuePair <ConstantValue, object>(ConstantValue.Create(yield.YieldIndex), yield));
            }

            // emit switch table that based on g._state jumps to appropriate continuation label
            cg.Builder.EmitIntegerSwitchJumpTable(yieldExLabels.ToArray(), noContinuationLabel, stateTmpLocal, Microsoft.Cci.PrimitiveTypeCode.Int32);
            cg.ReturnTemporaryLocal(stateTmpLocal);

            cg.Builder.MarkLabel(noContinuationLabel);
        }
示例#4
0
        internal override void Emit(CodeGenerator cg)
        {
            Debug.Assert(cg.Routine.ControlFlowGraph.Yields != null);

            // yieldIndex is 1-based because zero is reserved for to-first-yield-run.
            var yieldEx    = this.PhpSyntax;
            var yieldIndex = Array.IndexOf(cg.Routine.ControlFlowGraph.Yields, this) + 1;

            Debug.Assert(yieldIndex >= 1);

            var il = cg.Builder;


            // sets currValue and currKey on generator object
            setAsPhpValueOnGenerator(cg, YieldedValue, cg.CoreMethods.Operators.SetGeneratorCurrValue_Generator_PhpValue);
            setAsPhpValueOnGenerator(cg, YieldedKey, cg.CoreMethods.Operators.SetGeneratorCurrKey_Generator_PhpValue);


            // generator._userKeyReturned = (YieldedKey != null)
            var userKeyReturned = (YieldedKey != null);

            il.EmitLoadArgumentOpcode(3);
            cg.EmitLoadConstant(userKeyReturned, cg.CoreTypes.Boolean);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.SetGeneratorReturnedUserKey_Generator_bool);


            //generator._state = yieldIndex
            il.EmitLoadArgumentOpcode(3);
            cg.EmitLoadConstant(yieldIndex, cg.CoreTypes.Int32);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.SetGeneratorState_Generator_int);


            // return & set continuation point just after that
            il.EmitRet(true);
            il.MarkLabel(this);

            // if(generator._currException != null) throw ex;
            il.EmitLoadArgumentOpcode(3);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetGeneratorThrownException_Generator);

            var excNotNull = new NamedLabel("generator._currException == null");

            il.EmitBranch(ILOpCode.Brfalse, excNotNull);

            // load the exception to be thrown on stack (so it can be nulled)
            il.EmitLoadArgumentOpcode(3);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetGeneratorThrownException_Generator);

            //g._curException = null : clear the field after throwing the exception
            il.EmitLoadArgumentOpcode(3);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.NullGeneratorThrownException_Generator);

            il.EmitThrow(false);

            il.MarkLabel(excNotNull);
        }
示例#5
0
        void EmitJumpTable(CodeGenerator cg)
        {
            var yields = cg.Routine.ControlFlowGraph.Yields;

            if (yields.IsDefaultOrEmpty)
            {
                return;
            }

            // local <state> = g._state that is switched on (can't switch on remote field)
            Debug.Assert(cg.GeneratorStateLocal != null);

            // create label for situation when state doesn't correspond to continuation: 0 -> didn't run to first yield
            var noContinuationLabel = new NamedLabel("noStateContinuation");

            // prepare jump table from yields
            var yieldExLabels = new List <KeyValuePair <ConstantValue, object> >();

            foreach (var yield in yields)
            {
                // only applies to yields inside this "try" block
                var node = yield.ContainingTryScopes.First;
                while (node != null && node.Value != this)
                {
                    node = node.Next;
                }
                if (node == null)
                {
                    continue;
                }

                // jump to next nested "try" or inside "yield" itself
                var target = (object)node.Next?.Value /*next try block*/ ?? yield /*inside yield*/;

                // case YieldIndex: goto target;
                yieldExLabels.Add(new KeyValuePair <ConstantValue, object>(ConstantValue.Create(yield.YieldIndex), target));
            }

            if (yieldExLabels.Count != 0)
            {
                // emit switch table that based on g._state jumps to appropriate continuation label
                cg.Builder.EmitIntegerSwitchJumpTable(yieldExLabels.ToArray(), noContinuationLabel, cg.GeneratorStateLocal, Microsoft.Cci.PrimitiveTypeCode.Int32);

                cg.Builder.MarkLabel(noContinuationLabel);
            }
        }
示例#6
0
        private static void EmitStateMachineMethodStart(CodeGenerator cg)
        {
            // local <state> = g._state that is switched on (can't switch on remote field)
            cg.EmitGeneratorInstance();
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetGeneratorState_Generator);

            var stateLocal = cg.GeneratorStateLocal = cg.GetTemporaryLocal(cg.CoreTypes.Int32);

            cg.Builder.EmitLocalStore(stateLocal);

            // g._state = -1 : running
            cg.EmitGeneratorInstance();
            cg.Builder.EmitIntConstant(-1);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.SetGeneratorState_Generator_int);

            // create label for situation when state doesn't correspond to continuation: 0 -> didn't run to first yield
            var noContinuationLabel = new NamedLabel("noStateContinuation");

            // prepare jump table from yields
            var yieldExLabels = new List <KeyValuePair <ConstantValue, object> >();

            foreach (var yield in cg.Routine.ControlFlowGraph.Yields)
            {
                // labels have 1-based index (zero is reserved for run to first yield)
                // label object is the BoundYieldStatement itself, it is Marked at the proper place within its Emit method
                Debug.Assert(yield.YieldIndex >= 1);

                // yield statements inside "try" block are handled at beginning of try block itself (we cannot branch directly inside "try" from outside)
                var target = (object)yield.ContainingTryScopes.First?.Value ?? yield;

                // case YieldIndex: goto target;
                yieldExLabels.Add(new KeyValuePair <ConstantValue, object>(ConstantValue.Create(yield.YieldIndex), target));
            }

            if (yieldExLabels.Count != 0) // yields might get optimized out, resulting in generator method with no yields, and no jump table
            {
                // emit switch table that based on g._state jumps to appropriate continuation label
                cg.Builder.EmitIntegerSwitchJumpTable(yieldExLabels.ToArray(), noContinuationLabel, stateLocal, Cci.PrimitiveTypeCode.Int32);
            }

            cg.Builder.MarkLabel(noContinuationLabel);
        }
示例#7
0
        private static void EmitStateMachineMethodStart(CodeGenerator cg)
        {
            // local <state> = g._state that is switched on (can't switch on remote field)
            cg.Builder.EmitLoadArgumentOpcode(3);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetGeneratorState_Generator);

            var stateTmpLocal = cg.GetTemporaryLocal(cg.CoreTypes.Int32);

            cg.Builder.EmitLocalStore(stateTmpLocal);


            // g._state = -1 : running
            cg.Builder.EmitLoadArgumentOpcode(3);
            cg.EmitLoadConstant(-1, cg.CoreTypes.Int32);
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.SetGeneratorState_Generator_int);


            // create label for situation when state doesn't correspond to continuation: 0 -> didn't run to first yield
            var noContinuationLabel = new NamedLabel("noStateContinuation");

            // prepare jump table from yields
            var yields        = cg.Routine.ControlFlowGraph.Yields;
            var yieldExLabels = new List <KeyValuePair <ConstantValue, object> >();

            for (var i = 0; i < yields.Length; i++)
            {
                // i+1 because labels have 1-based index (zero is reserved for run to first yield)
                yieldExLabels.Add(new KeyValuePair <ConstantValue, object>(ConstantValue.Create(i + 1), yields[i]));
            }

            // emit switch table that based on g._state jumps to appropriate continuation label
            cg.Builder.EmitIntegerSwitchJumpTable(yieldExLabels.ToArray(), noContinuationLabel, stateTmpLocal, Microsoft.Cci.PrimitiveTypeCode.Int32);
            cg.ReturnTemporaryLocal(stateTmpLocal);


            cg.Builder.MarkLabel(noContinuationLabel);
        }
示例#8
0
        void EmitPhpCallable(Emit.PEModuleBuilder module, DiagnosticBag diagnostics)
        {
            var __invoke = TryGetMagicInvoke();

            if (__invoke == null || __invoke.OverriddenMethod != null)
            {
                return;
            }

            //
            // IPhpCallable.Invoke(Context, PhpVaue[])
            //
            var invoke = new SynthesizedMethodSymbol(this, "IPhpCallable.Invoke", false, true, DeclaringCompilation.CoreTypes.PhpValue, isfinal: false)
            {
                ExplicitOverride = (MethodSymbol)DeclaringCompilation.CoreTypes.IPhpCallable.Symbol.GetMembers("Invoke").Single(),
            };

            invoke.SetParameters(
                new SpecialParameterSymbol(invoke, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, 0),
                new SynthesizedParameterSymbol(invoke, ArrayTypeSymbol.CreateSZArray(ContainingAssembly, DeclaringCompilation.CoreTypes.PhpValue.Symbol), 1, RefKind.None, "arguments"));

            module.SetMethodBody(invoke, MethodGenerator.GenerateMethodBody(module, invoke, il =>
            {
                var cg = new CodeGenerator(il, module, diagnostics, OptimizationLevel.Release, false, this, new ParamPlace(invoke.Parameters[0]), new ArgPlace(this, 0));

                var argsplace    = new ParamPlace(invoke.Parameters[1]);
                var args_element = ((ArrayTypeSymbol)argsplace.TypeOpt).ElementType;
                var ps           = __invoke.Parameters;

                // Template: this.__invoke(args[0], args[1], ...)

                cg.EmitThis();

                for (int i = 0; i < ps.Length; i++)
                {
                    var p = ps[i];

                    // LOAD args[i]
                    // Template: (i < args.Length) ? (T)args[i] : default(T)

                    var lbldefault = new NamedLabel("args_default");
                    var lblend     = new NamedLabel("args_end");

                    cg.Builder.EmitIntConstant(i);  // <i>
                    argsplace.EmitLoad(cg.Builder); // <args>
                    cg.EmitArrayLength();           // .Length
                    cg.Builder.EmitBranch(ILOpCode.Bge, lbldefault);

                    // (T)args[i]
                    if (p.IsImplicitlyDeclared)
                    {
                        throw new NotImplementedException();
                    }
                    else if (p.Type == cg.CoreTypes.PhpAlias)
                    {
                        // args[i].EnsureAlias()
                        argsplace.EmitLoad(cg.Builder);          // <args>
                        cg.Builder.EmitIntConstant(i);           // <i>
                        cg.Builder.EmitOpCode(ILOpCode.Ldelema); // ref args[i]
                        cg.EmitSymbolToken(args_element, null);
                        cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.EnsureAlias);
                    }
                    else
                    {
                        // (T)args[i]
                        argsplace.EmitLoad(cg.Builder);         // <args>
                        cg.Builder.EmitIntConstant(i);          // <i>
                        cg.Builder.EmitOpCode(ILOpCode.Ldelem); // args[i]
                        cg.EmitSymbolToken(args_element, null);
                        cg.EmitConvert(args_element, 0, p.Type);
                    }

                    cg.Builder.EmitBranch(ILOpCode.Br, lblend);

                    // default(T)
                    cg.Builder.MarkLabel(lbldefault);
                    cg.EmitParameterDefaultValue(p);

                    //
                    cg.Builder.MarkLabel(lblend);
                }

                cg.EmitCall(ILOpCode.Callvirt, __invoke);
                cg.EmitRet(invoke.ReturnType);
            }, null, diagnostics, false));

            module.SynthesizedManager.AddMethod(this, invoke);

            //
            // IPhpCallable.ToPhpValue()
            //
            var tophpvalue = new SynthesizedMethodSymbol(this, "IPhpCallable.ToPhpValue", false, true, DeclaringCompilation.CoreTypes.PhpValue, isfinal: false)
            {
                ExplicitOverride = (MethodSymbol)DeclaringCompilation.CoreTypes.IPhpCallable.Symbol.GetMembers("ToPhpValue").Single(),
            };

            //
            module.SetMethodBody(tophpvalue, MethodGenerator.GenerateMethodBody(module, tophpvalue, il =>
            {
                var thisPlace = new ArgPlace(this, 0);
                var cg        = new CodeGenerator(il, module, diagnostics, OptimizationLevel.Release, false, this, new FieldPlace(thisPlace, this.ContextStore), thisPlace);

                // return PhpValue.FromClass(this)
                cg.EmitThis();
                cg.EmitRet(cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.FromClass_Object));
            }, null, diagnostics, false));

            module.SynthesizedManager.AddMethod(this, tophpvalue);
        }