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); }
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) }
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); }
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); }
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); } }
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); }
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); }
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); }