private void GenerateGeneratorMethod(CodeGenerator cg)
        {
            Debug.Assert(this.IsGeneratorMethod());

            var genSymbol = new SourceGeneratorSymbol(this);
            var il        = cg.Builder;

            /* Template:
             * return BuildGenerator( <ctx>, this, new PhpArray(){ p1, p2, ... }, new GeneratorStateMachineDelegate((IntPtr)<genSymbol>), (RuntimeMethodHandle)this )
             */

            cg.EmitLoadContext(); // ctx for generator
            cg.EmitThisOrNull();  // @this for generator

            // new PhpArray for generator's locals
            cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpArray);

            var generatorsLocals = cg.GetTemporaryLocal(cg.CoreTypes.PhpArray);

            cg.Builder.EmitLocalStore(generatorsLocals);

            // initialize parameters (set their _isOptimized and copy them to locals array)
            InitializeParametersForGeneratorMethod(cg, il, generatorsLocals);
            cg.Builder.EmitLoad(generatorsLocals);
            cg.ReturnTemporaryLocal(generatorsLocals);

            // new PhpArray for generator's synthesizedLocals
            cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpArray);

            // new GeneratorStateMachineDelegate(<genSymbol>) delegate for generator
            cg.Builder.EmitNullConstant();                                                                                           // null
            cg.EmitOpCode(ILOpCode.Ldftn);                                                                                           // method
            cg.EmitSymbolToken(genSymbol, null);
            cg.EmitCall(ILOpCode.Newobj, cg.CoreTypes.GeneratorStateMachineDelegate.Ctor(cg.CoreTypes.Object, cg.CoreTypes.IntPtr)); // GeneratorStateMachineDelegate(object @object, IntPtr method)

            // handleof(this)
            cg.EmitLoadToken(this, null);

            // create generator object via Operators factory method
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.BuildGenerator_Context_Object_PhpArray_PhpArray_GeneratorStateMachineDelegate_RuntimeMethodHandle);

            // .UseDynamicScope( scope ) : Generator
            if (this is SourceLambdaSymbol lambda)
            {
                lambda.GetCallerTypePlace().EmitLoad(cg.Builder); // RuntimeTypeContext
                cg.EmitCall(ILOpCode.Call, cg.CoreTypes.Operators.Method("UseDynamicScope", cg.CoreTypes.Generator, cg.CoreTypes.RuntimeTypeHandle))
                .Expect(cg.CoreTypes.Generator);
            }

            // Convert to return type (Generator or PhpValue, depends on analysis)
            cg.EmitConvert(cg.CoreTypes.Generator, 0, this.ReturnType);
            il.EmitRet(false);

            // Generate SM method. Must be generated after EmitInit of parameters (it sets their _isUnoptimized field).
            CreateStateMachineNextMethod(cg, genSymbol);
        }
        public virtual void Generate(CodeGenerator cg)
        {
            if (!this.IsGeneratorMethod())
            {
                //Proceed with normal method generation
                cg.GenerateScope(this.ControlFlowGraph.Start, int.MaxValue);
            }
            else
            {
                var genSymbol = new SourceGeneratorSymbol(this);
                var il        = cg.Builder;

                /* Template:
                 * return BuildGenerator( <ctx>, this, new PhpArray(){ p1, p2, ... }, new GeneratorStateMachineDelegate((IntPtr)<genSymbol>), (RuntimeMethodHandle)this )
                 */

                cg.EmitLoadContext(); // ctx for generator
                cg.EmitThisOrNull();  // @this for generator

                // new PhpArray for generator's locals
                cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpArray);

                var generatorsLocals = cg.GetTemporaryLocal(cg.CoreTypes.PhpArray);
                cg.Builder.EmitLocalStore(generatorsLocals);

                // initialize parameters (set their _isOptimized and copy them to locals array)
                InitializeParametersForGeneratorMethod(cg, il, generatorsLocals);
                cg.Builder.EmitLoad(generatorsLocals);
                cg.ReturnTemporaryLocal(generatorsLocals);

                // new PhpArray for generator's synthesizedLocals
                cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpArray);

                // new GeneratorStateMachineDelegate(<genSymbol>) delegate for generator
                cg.Builder.EmitNullConstant();                                                                                           // null
                cg.EmitOpCode(ILOpCode.Ldftn);                                                                                           // method
                cg.EmitSymbolToken(genSymbol, null);
                cg.EmitCall(ILOpCode.Newobj, cg.CoreTypes.GeneratorStateMachineDelegate.Ctor(cg.CoreTypes.Object, cg.CoreTypes.IntPtr)); // GeneratorStateMachineDelegate(object @object, IntPtr method)

                // handleof(this)
                cg.EmitLoadToken(this, null);

                // create generator object via Operators factory method
                cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.BuildGenerator_Context_Object_PhpArray_PhpArray_GeneratorStateMachineDelegate_RuntimeMethodHandle);

                // Convert to return type (Generator or PhpValue, depends on analysis)
                cg.EmitConvert(cg.CoreTypes.Generator, 0, this.ReturnType);
                il.EmitRet(false);

                // Generate SM method. Must be generated after EmitInit of parameters (it sets their _isUnoptimized field).
                CreateStateMachineNextMethod(cg, genSymbol);
            }
        }
        private void CreateStateMachineNextMethod(CodeGenerator cg, SourceGeneratorSymbol genSymbol)
        {
            cg.Module.SynthesizedManager.AddMethod(ContainingType, genSymbol); // save method symbol to module

            // generate generator's next method body
            var genMethodBody = MethodGenerator.GenerateMethodBody(cg.Module, genSymbol, (_il) =>
            {
                GenerateStateMachinesNextMethod(cg, _il, genSymbol);
            }
                                                                   , null, cg.Diagnostics, cg.EmitPdbSequencePoints);

            cg.Module.SetMethodBody(genSymbol, genMethodBody);
        }
        //Initialized a new CodeGenerator for generation of SourceGeneratorSymbol (state machine's next method)
        private void GenerateStateMachinesNextMethod(CodeGenerator cg, Microsoft.CodeAnalysis.CodeGen.ILBuilder _il, SourceGeneratorSymbol genSymbol)
        {
            // TODO: get correct ThisPlace, ReturnType etc. resolution & binding out of the box without GN_SGS hacks
            // using SourceGeneratorSymbol

            //Refactor parameters references to proper fields
            using (var stateMachineNextCg = new CodeGenerator(
                       _il, cg.Module, cg.Diagnostics,
                       cg.DeclaringCompilation.Options.OptimizationLevel,
                       cg.EmitPdbSequencePoints,
                       this.ContainingType,
                       contextPlace: new ParamPlace(genSymbol.ContextParameter),
                       thisPlace: new ParamPlace(genSymbol.ThisParameter),
                       routine: this,
                       locals: new ParamPlace(genSymbol.LocalsParameter),
                       localsInitialized: true,
                       tempLocals: new ParamPlace(genSymbol.TmpLocalsParameter)
                       )
            {
                GeneratorStateMachineMethod = genSymbol,    // Pass SourceGeneratorSymbol to CG for additional yield and StartBlock emit
            })
            {
                stateMachineNextCg.GenerateScope(this.ControlFlowGraph.Start, int.MaxValue);
            }
        }
Example #5
0
        private void GenerateGeneratorMethod(CodeGenerator cg)
        {
            Debug.Assert(this.IsGeneratorMethod());

            var genSymbol = new SourceGeneratorSymbol(this);
            //var genConstructed = (genSymbol.ContainingType is SourceTraitTypeSymbol st)
            //    ? genSymbol.AsMember(st.Construct(st.TypeArguments))
            //    : genSymbol;

            var il     = cg.Builder;
            var lambda = this as SourceLambdaSymbol;

            /* Template:
             * return BuildGenerator( <ctx>, new PhpArray(){ p1, p2, ... }, new GeneratorStateMachineDelegate((IntPtr)<genSymbol>), (RuntimeMethodHandle)this )
             */

            cg.EmitLoadContext(); // ctx for generator

            // new PhpArray for generator's locals
            cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpArray);

            var generatorsLocals = cg.GetTemporaryLocal(cg.CoreTypes.PhpArray);

            cg.Builder.EmitLocalStore(generatorsLocals);

            // initialize parameters (set their _isOptimized and copy them to locals array)
            InitializeParametersForGeneratorMethod(cg, il, generatorsLocals);
            cg.Builder.EmitLoad(generatorsLocals);
            cg.ReturnTemporaryLocal(generatorsLocals);

            // new PhpArray for generator's synthesizedLocals
            cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpArray);

            // new GeneratorStateMachineDelegate(<genSymbol>) delegate for generator
            cg.Builder.EmitNullConstant(); // null
            cg.EmitOpCode(ILOpCode.Ldftn); // method
            cg.EmitSymbolToken(genSymbol, null);

            cg.EmitCall(ILOpCode.Newobj, cg.CoreTypes.GeneratorStateMachineDelegate.Ctor(cg.CoreTypes.Object, cg.CoreTypes.IntPtr)); // GeneratorStateMachineDelegate(object @object, IntPtr method)

            // handleof(this)
            cg.EmitLoadToken(this, null);

            // create generator object via Operators factory method
            cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.BuildGenerator_Context_PhpArray_PhpArray_GeneratorStateMachineDelegate_RuntimeMethodHandle);

            // .SetGeneratorThis( object ) : Generator
            if (!this.IsStatic || (lambda != null && lambda.UseThis))
            {
                GetPhpThisVariablePlaceWithoutGenerator(cg.Module).EmitLoad(cg.Builder);
                cg.EmitCastClass(cg.DeclaringCompilation.GetSpecialType(SpecialType.System_Object));
                cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.SetGeneratorThis_Generator_Object)
                .Expect(cg.CoreTypes.Generator);
            }

            // .SetGeneratorLazyStatic( PhpTypeInfo ) : Generator
            if ((this.Flags & RoutineFlags.UsesLateStatic) != 0 && this.IsStatic)
            {
                cg.EmitLoadStaticPhpTypeInfo();
                cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.SetGeneratorLazyStatic_Generator_PhpTypeInfo)
                .Expect(cg.CoreTypes.Generator);
            }

            // .SetGeneratorDynamicScope( scope ) : Generator
            if (lambda != null)
            {
                lambda.GetCallerTypePlace().EmitLoad(cg.Builder); // RuntimeTypeContext
                cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.SetGeneratorDynamicScope_Generator_RuntimeTypeHandle)
                .Expect(cg.CoreTypes.Generator);
            }

            // Convert to return type (Generator or PhpValue, depends on analysis)
            cg.EmitConvert(cg.CoreTypes.Generator, 0, this.ReturnType);
            il.EmitRet(false);

            // Generate SM method. Must be generated after EmitInit of parameters (it sets their _isUnoptimized field).
            CreateStateMachineNextMethod(cg, genSymbol);
        }
Example #6
0
        //Initialized a new CodeGenerator for generation of SourceGeneratorSymbol (state machine's next method)
        private void generateStateMachinesNextMethod(CodeGenerator cg, Microsoft.CodeAnalysis.CodeGen.ILBuilder _il, SourceGeneratorSymbol genSymbol)
        {
            // TODO: Pass SourceGeneratorSymbol to CG instead of this to get correct ThisPlace, ReturnType etc. resolution & binding out of the box without GN_SGS hacks
            // ..can't do that easily beacuse CodeGenerator accepts only SourceRoutineSymbol and SGS derives from SynthesizedMethodSymbol (shim over too general MethodSymbol)

            //Refactor parameters references to proper fields
            using (var stateMachineNextCg = new CodeGenerator(
                       _il, cg.Module, cg.Diagnostics,
                       cg.DeclaringCompilation.Options.OptimizationLevel,
                       cg.EmitPdbSequencePoints,
                       this.ContainingType,
                       contextPlace: new ParamPlace(genSymbol.Parameters[0]),
                       thisPlace: new ParamPlace(genSymbol.Parameters[1]),
                       routine: this,
                       locals: new ParamPlace(genSymbol.Parameters[2]),
                       localsInitialized: true
                       ))
            {
                stateMachineNextCg.GenerateScope(this.ControlFlowGraph.Start, int.MaxValue);
            }
        }