void EmitTraitInstanceInit(CodeGenerator cg, SynthesizedPhpCtorSymbol ctor, TraitUse t)
        {
            // Template: this.<>trait_T = new T(ctx, this, self)

            // PLACE: this.<>trait_T
            var instancePlace = new FieldPlace(cg.ThisPlaceOpt, t.TraitInstanceField, module: cg.Module);

            // .ctor(Context, object @this, RuntimeTypeHandle self)
            var tctor = t.Symbol.InstanceConstructors[0];

            Debug.Assert(tctor.ParameterCount == 2);
            Debug.Assert(tctor.Parameters[0].Type == cg.CoreTypes.Context);
            //Debug.Assert(tctor.Parameters[1].Type == TSelfParameter);

            // using trait in trait?
            var ctort = ctor as SynthesizedPhpTraitCtorSymbol;

            //
            instancePlace.EmitStorePrepare(cg.Builder);
            // Context:
            cg.EmitLoadContext();
            // this:
            if (ctort != null)
            {
                ctort.ThisParameter.EmitLoad(cg.Builder);                   // this is passed from caller
            }
            else
            {
                cg.EmitThis();
            }
            // new T<TSelf>(...)
            cg.EmitCall(ILOpCode.Newobj, tctor);

            instancePlace.EmitStore(cg.Builder);
        }
        void GenerateFieldAccessorProperty(Emit.PEModuleBuilder module, DiagnosticBag diagnostics, SourceFieldSymbol srcf, PropertySymbol paccessor)
        {
            //
            module.SynthesizedManager.AddProperty(this, paccessor);

            //
            var get_body = MethodGenerator.GenerateMethodBody(module, paccessor.GetMethod, (il) =>
            {
                // Template: return field;
                var place = new FieldPlace(new ArgPlace(this, 0), srcf.OverridenDefinition, module);
                place.EmitLoad(il);
                il.EmitRet(false);
            }, null, diagnostics, false);

            module.SetMethodBody(paccessor.GetMethod, get_body);
            module.SynthesizedManager.AddMethod(this, paccessor.GetMethod);

            //
            var set_body = MethodGenerator.GenerateMethodBody(module, paccessor.SetMethod, (il) =>
            {
                // Template: field = value;
                var place = new FieldPlace(new ArgPlace(this, 0), srcf.OverridenDefinition, module);
                place.EmitStorePrepare(il);
                new ArgPlace(this, 1).EmitLoad(il);
                place.EmitStore(il);
                il.EmitRet(true);
            }, null, diagnostics, false);

            module.SetMethodBody(paccessor.SetMethod, set_body);
            module.SynthesizedManager.AddMethod(this, paccessor.SetMethod);
        }
        internal override IList <MethodSymbol> SynthesizeStubs(PEModuleBuilder module, DiagnosticBag diagnostic)
        {
            var overloads = base.SynthesizeStubs(module, diagnostic);

            // synthesize RoutineInfo:
            var cctor = module.GetStaticCtorBuilder(_container);

            lock (cctor)
            {
                var field = new FieldPlace(null, this.EnsureRoutineInfoField(module), module);

                var ct = module.Compilation.CoreTypes;

                // {RoutineInfoField} = new PhpAnonymousRoutineInfo(name, handle)
                field.EmitStorePrepare(cctor);

                cctor.EmitStringConstant(this.MetadataName);
                cctor.EmitLoadToken(module, DiagnosticBag.GetInstance(), this, null);
                cctor.EmitCall(module, DiagnosticBag.GetInstance(), ILOpCode.Call, ct.Operators.Method("AnonymousRoutine", ct.String, ct.RuntimeMethodHandle));

                field.EmitStore(cctor);
            }

            //
            return(overloads);
        }
        internal override IList <MethodSymbol> SynthesizeStubs(PEModuleBuilder module, DiagnosticBag diagnostic)
        {
            var overloads = base.SynthesizeStubs(module, diagnostic);

            // synthesize RoutineInfo:
            var cctor = module.GetStaticCtorBuilder(_file);

            lock (cctor)
            {
                using (var cg = new CodeGenerator(
                           cctor, module, diagnostic, OptimizationLevel.Release, false, this.ContainingType,
                           contextPlace: null, thisPlace: null, routine: this))
                {
                    var field = new FieldPlace(null, this.EnsureRoutineInfoField(module), module);

                    // {RoutineInfoField} = RoutineInfo.CreateUserRoutine(name, handle, overloads[])
                    field.EmitStorePrepare(cctor);

                    cctor.EmitStringConstant(this.QualifiedName.ToString());
                    cctor.EmitLoadToken(module, DiagnosticBag.GetInstance(), this, null);
                    cg.Emit_NewArray(cg.CoreTypes.RuntimeMethodHandle, overloads.AsImmutable(), m => cg.EmitLoadToken(m, null));

                    cctor.EmitCall(module, DiagnosticBag.GetInstance(), ILOpCode.Call, cg.CoreMethods.Reflection.CreateUserRoutine_string_RuntimeMethodHandle_RuntimeMethodHandleArr);

                    field.EmitStore(cctor);
                }
            }

            //
            return(overloads);
        }
Beispiel #5
0
        /// <summary>
        /// Emits load of cached <c>RoutineInfo</c> corresponding to this method.
        /// </summary>
        /// <returns>Type symbol of <c>RoutineInfo</c>.</returns>
        internal virtual TypeSymbol EmitLoadRoutineInfo(CodeGenerator cg)
        {
            var name = MetadataName;
            var type = this.ContainingType;

            name = (type != null ? type.GetFullName() : "?") + "." + name;

            // cache the instance of RoutineInfo
            var tmpfld = cg.Module.SynthesizedManager.GetOrCreateSynthesizedField(
                cg.Module.ScriptType,
                cg.CoreTypes.RoutineInfo,
                "<>" + name,
                Accessibility.Internal,
                isstatic: true,
                @readonly: false);

            // Template: (tmpfld ?? tmpfld = CreateUserRoutine)
            var tmpplace = new FieldPlace(null, tmpfld, cg.Module);

            tmpplace.EmitLoad(cg.Builder);
            cg.EmitNullCoalescing((cg_) =>
            {
                // TODO: Interlocked(ref fld, CreateRoutine, null)
                tmpplace.EmitStorePrepare(cg_.Builder);
                EmitCreateRoutine(cg_, this);

                cg_.Builder.EmitOpCode(ILOpCode.Dup);
                tmpplace.EmitStore(cg_.Builder);
            });

            //
            return(tmpfld.Type
                   .Expect(cg.CoreTypes.RoutineInfo));
        }
Beispiel #6
0
        void EmitInit(Emit.PEModuleBuilder module, DiagnosticBag diagnostic, PhpCompilation compilation, SynthesizedStaticLocHolder holder, BoundExpression initializer)
        {
            var loctype = holder.ValueField.Type;

            bool requiresContext = initializer != null && initializer.RequiresContext;

            if (requiresContext)
            {
                // emit Init only if it needs Context

                holder.EmitInit(module, (il) =>
                {
                    var cg = new CodeGenerator(il, module, diagnostic, OptimizationLevel.Release, false,
                                               holder.ContainingType, new ArgPlace(compilation.CoreTypes.Context, 1), new ArgPlace(holder, 0));

                    var valuePlace = new FieldPlace(cg.ThisPlaceOpt, holder.ValueField);

                    // Template: this.value = <initilizer>;

                    valuePlace.EmitStorePrepare(il);
                    cg.EmitConvert(initializer, valuePlace.TypeOpt);
                    valuePlace.EmitStore(il);

                    //
                    il.EmitRet(true);
                });
            }

            // default .ctor
            holder.EmitCtor(module, (il) =>
            {
                if (!requiresContext)
                {
                    // emit default value only if it won't be initialized by Init above

                    var cg = new CodeGenerator(il, module, diagnostic, OptimizationLevel.Release, false,
                                               holder.ContainingType, null, new ArgPlace(holder, 0));

                    var valuePlace = new FieldPlace(cg.ThisPlaceOpt, holder.ValueField);

                    // Template: this.value = default(T);

                    valuePlace.EmitStorePrepare(il);
                    if (initializer != null)
                    {
                        cg.EmitConvert(initializer, valuePlace.TypeOpt);
                    }
                    else
                    {
                        cg.EmitLoadDefault(valuePlace.TypeOpt, 0);
                    }
                    valuePlace.EmitStore(il);
                }

                //
                il.EmitRet(true);
            });
        }
        void IPhpPropertySymbol.EmitInit(CodeGenerator cg)
        {
            // this.{FIELD} = {ORIGINAL_FIELD}

            var fldplace = new FieldPlace(IsStatic ? null : new ArgPlace(ContainingType, 0), this, cg.Module);

            fldplace.EmitStorePrepare(cg.Builder);
            cg.EmitConvert(EmitLoadSourceValue(cg), 0, this.Type);
            fldplace.EmitStore(cg.Builder);
        }
        static void EmitTraitCtorInit(CodeGenerator cg, SynthesizedPhpTraitCtorSymbol tctor)
        {
            var il = cg.Builder;

            // this.<>this = @this
            var thisFieldPlace = new FieldPlace(cg.ThisPlaceOpt, tctor.ContainingType.RealThisField, module: cg.Module);

            thisFieldPlace.EmitStorePrepare(il);
            tctor.ThisParameter.EmitLoad(il);
            thisFieldPlace.EmitStore(il);
        }
Beispiel #9
0
        void EmitPhpCtors(ImmutableArray <MethodSymbol> instancectors, Emit.PEModuleBuilder module, DiagnosticBag diagnostics)
        {
            foreach (SynthesizedPhpCtorSymbol ctor in instancectors)
            {
                module.SetMethodBody(ctor, MethodGenerator.GenerateMethodBody(module, ctor, il =>
                {
                    Debug.Assert(SpecialParameterSymbol.IsContextParameter(ctor.Parameters[0]));

                    var cg = new CodeGenerator(il, module, diagnostics, OptimizationLevel.Release, false, this, new ParamPlace(ctor.Parameters[0]), new ArgPlace(this, 0));

                    Debug.Assert(ctor.BaseCtor != null);

                    // base..ctor or this..ctor
                    cg.EmitPop(cg.EmitThisCall(ctor.BaseCtor, ctor));

                    if (ctor.PhpConstructor == null)
                    {
                        // initialize <ctx> field, if field is declared within this type
                        var ctxField = this.ContextStore;
                        if (ctxField != null && object.ReferenceEquals((object)ctxField.ContainingType, this))
                        {
                            var ctxFieldPlace = new FieldPlace(cg.ThisPlaceOpt, ctxField);

                            // Debug.Assert(<ctx> != null)
                            cg.EmitDebugAssertNotNull(cg.ContextPlaceOpt, "Context cannot be null.");

                            // <this>.<ctx> = <ctx>
                            ctxFieldPlace.EmitStorePrepare(il);
                            cg.EmitLoadContext();
                            ctxFieldPlace.EmitStore(il);
                        }

                        // initialize class fields
                        foreach (var fld in this.EnsureMembers().OfType <SourceFieldSymbol>().Where(fld => !fld.RequiresHolder && !fld.IsStatic && !fld.IsConst))
                        {
                            fld.EmitInit(cg);
                        }
                    }
                    else
                    {
                        Debug.Assert(ctor.BaseCtor.ContainingType == this);

                        // this.__construct
                        cg.EmitPop(cg.EmitThisCall(ctor.PhpConstructor, ctor));
                    }

                    // ret
                    Debug.Assert(ctor.ReturnsVoid);
                    cg.EmitRet(ctor.ReturnType);
                }, null, diagnostics, false));
            }
        }
        internal void EmitInit(Emit.PEModuleBuilder module)
        {
            var cctor = module.GetStaticCtorBuilder(_file);
            var field = new FieldPlace(null, this.EnsureRoutineInfoField(module));

            // {RoutineInfoField} = RoutineInfo.CreateUserRoutine(name, handle)
            field.EmitStorePrepare(cctor);

            cctor.EmitStringConstant(this.QualifiedName.ToString());
            cctor.EmitLoadToken(module, DiagnosticBag.GetInstance(), this, null);
            cctor.EmitCall(module, DiagnosticBag.GetInstance(), System.Reflection.Metadata.ILOpCode.Call, module.Compilation.CoreMethods.Reflection.CreateUserRoutine_string_RuntimeMethodHandle);

            field.EmitStore(cctor);
        }
        internal void EmitInit(PEModuleBuilder module)
        {
            var cctor = module.GetStaticCtorBuilder(_container);
            var field = new FieldPlace(null, this.EnsureRoutineInfoField(module), module);

            var ct = module.Compilation.CoreTypes;

            // {RoutineInfoField} = new PhpAnonymousRoutineInfo(name, handle)
            field.EmitStorePrepare(cctor);

            cctor.EmitStringConstant(this.MetadataName);
            cctor.EmitLoadToken(module, DiagnosticBag.GetInstance(), this, null);
            cctor.EmitCall(module, DiagnosticBag.GetInstance(), ILOpCode.Call, ct.Operators.Method("AnonymousRoutine", ct.String, ct.RuntimeMethodHandle));

            field.EmitStore(cctor);
        }
Beispiel #12
0
        internal void EmitInit(CodeGenerator cg)
        {
            var fldplace = new FieldPlace(IsStatic ? null : new ArgPlace(ContainingType, 0), this);

            if (this.Initializer != null)
            {
                // fld = <initializer>
                fldplace.EmitStorePrepare(cg.Builder);
                cg.EmitConvert(this.Initializer, this.Type);
                fldplace.EmitStore(cg.Builder);
            }
            else
            {
                // fld = default(type)
                cg.EmitInitializePlace(fldplace);
            }
        }
Beispiel #13
0
        void IPhpPropertySymbol.EmitInit(CodeGenerator cg)
        {
            // skip initialization if we can use default
            if (this.Initializer == null &&
                this.OverridenDefinition == null &&
                !this.Type.Is_PhpAlias())
            {
                // does not have to be explicitly initialized
                // default is ok
                return;
            }

            //
            cg.TypeRefContext = EnsureTypeRefContext();

            //
            var fldplace = new FieldPlace(IsStatic ? null : new ArgPlace(ContainingType, 0), this, cg.Module);

            // fld = <initializer>
            fldplace.EmitStorePrepare(cg.Builder);

            if (this.Initializer != null)
            {
                cg.EmitSequencePoint(this.Initializer.PhpSyntax);

                // INITIALIZER
                cg.EmitConvert(this.Initializer, fldplace.Type);

                if (cg.EmitPdbSequencePoints)
                {
                    cg.Builder.EmitOpCode(ILOpCode.Nop);
                }
            }
            else
            {
                // default
                cg.EmitLoadDefault(fldplace.Type);
            }

            fldplace.EmitStore(cg.Builder);

            //
            cg.TypeRefContext = null;
        }
        void EmitPhpNew(SynthesizedPhpNewMethodSymbol phpnew, Emit.PEModuleBuilder module)
        {
            if (phpnew == null)
            {
                return;                 // static class
            }
            module.SetMethodBody(phpnew, MethodGenerator.GenerateMethodBody(module, phpnew, il =>
            {
                Debug.Assert(SpecialParameterSymbol.IsContextParameter(phpnew.Parameters[0]));

                var cg = new CodeGenerator(il, module, DiagnosticBag.GetInstance(), OptimizationLevel.Release, false, this, new ParamPlace(phpnew.Parameters[0]), new ArgPlace(this, 0));

                // initialize <ctx> field,
                // if field is declared within this type
                var ctxField = this.ContextField;
                if (ctxField != null && object.ReferenceEquals(ctxField.ContainingType, this))
                {
                    var ctxFieldPlace = new FieldPlace(cg.ThisPlaceOpt, ctxField);

                    // Debug.Assert(<ctx> != null)
                    cg.EmitDebugAssertNotNull(cg.ContextPlaceOpt, "Context cannot be null.");

                    // <this>.<ctx> = <ctx>
                    ctxFieldPlace.EmitStorePrepare(il);
                    cg.EmitLoadContext();
                    ctxFieldPlace.EmitStore(il);
                }

                // initialize class fields,
                // default(PhpValue) is not a valid value, its TypeTable must not be null
                foreach (var fld in this.GetFieldsToEmit().OfType <SourceFieldSymbol>().Where(fld => !fld.IsStatic && !fld.IsConst))
                {
                    fld.EmitInit(cg);
                }

                // base..phpnew ?? base..ctor
                var basenew = phpnew.BasePhpNew;
                Debug.Assert(basenew != null);
                cg.EmitPop(cg.EmitThisCall(basenew, phpnew));

                Debug.Assert(phpnew.ReturnsVoid);
                cg.EmitRet(true);
            }, null, DiagnosticBag.GetInstance(), false));
        }
        void IPhpPropertySymbol.EmitInit(CodeGenerator cg)
        {
            cg.TypeRefContext = EnsureTypeRefContext();

            //

            var fldplace = new FieldPlace(IsStatic ? null : new ArgPlace(ContainingType, 0), this, cg.Module);

            if (this.Initializer != null)
            {
                // fld = <initializer>
                fldplace.EmitStorePrepare(cg.Builder);
                cg.EmitConvert(this.Initializer, this.Type);
                fldplace.EmitStore(cg.Builder);
            }
            else
            {
                // fld = default(type)
                cg.EmitInitializePlace(fldplace);
            }

            //
            cg.TypeRefContext = null;
        }
Beispiel #16
0
        void EmitInit(Emit.PEModuleBuilder module, DiagnosticBag diagnostic, PhpCompilation compilation, SynthesizedStaticLocHolder holder, BoundExpression initializer)
        {
            var requiresContext = initializer != null && initializer.RequiresContext;

            if (requiresContext)
            {
                // emit Init only if it needs Context

                holder.EmitInit(module, (il) =>
                {
                    var cg = new CodeGenerator(il, module, diagnostic, compilation.Options.OptimizationLevel, false,
                                               holder.ContainingType, new ArgPlace(compilation.CoreTypes.Context, 1), new ArgPlace(holder, 0));

                    var valuePlace = new FieldPlace(cg.ThisPlaceOpt, holder.ValueField, module);

                    // Template: this.value = <initilizer>;

                    valuePlace.EmitStorePrepare(il);
                    cg.EmitConvert(initializer, valuePlace.TypeOpt);
                    valuePlace.EmitStore(il);

                    //
                    il.EmitRet(true);
                });
            }

            // default .ctor
            holder.EmitCtor(module, (il) =>
            {
                // base..ctor()
                var ctor = holder.BaseType.InstanceConstructors.Single();
                il.EmitLoadArgumentOpcode(0);                         // this
                il.EmitCall(module, diagnostic, ILOpCode.Call, ctor); // .ctor()

                if (!requiresContext)
                {
                    // emit default value only if it won't be initialized by Init above

                    var cg = new CodeGenerator(il, module, diagnostic, compilation.Options.OptimizationLevel, false,
                                               holder.ContainingType, null, new ArgPlace(holder, 0));

                    var valuePlace = new FieldPlace(cg.ThisPlaceOpt, holder.ValueField, module);

                    // Template: this.value = default(T);

                    valuePlace.EmitStorePrepare(il);
                    if (initializer != null)
                    {
                        cg.EmitConvert(initializer, valuePlace.TypeOpt);
                    }
                    else
                    {
                        cg.EmitLoadDefault(valuePlace.TypeOpt, 0);
                    }
                    valuePlace.EmitStore(il);
                }

                //
                il.EmitRet(true);
            });
        }
        /// <summary>
        /// Emits initializers of all parameter's non-standard default values (such as PhpArray)
        /// within the type's static .cctor
        /// </summary>
        private void EmitParametersDefaultValue(PEModuleBuilder module, DiagnosticBag diagnostics)
        {
            foreach (var p in this.SourceParameters)
            {
                var field = p.DefaultValueField;
                if (field is SynthesizedFieldSymbol)
                {
                    Debug.Assert(p.Initializer != null);

                    module.SynthesizedManager.AddField(field.ContainingType, field);

                    // .cctor() {

                    var cctor = module.GetStaticCtorBuilder(field.ContainingType);
                    lock (cctor)
                    {
                        SynthesizedMethodSymbol func = null;

                        if (field.Type.Is_Func_Context_PhpValue())  // Func<Context, PhpValue>
                        {
                            // private static PhpValue func(Context) => INITIALIZER()
                            func = new SynthesizedMethodSymbol(field.ContainingType, field.Name + "Func", isstatic: true, isvirtual: false, DeclaringCompilation.CoreTypes.PhpValue, isfinal: true);
                            func.SetParameters(new SynthesizedParameterSymbol(func, DeclaringCompilation.CoreTypes.Context, 0, RefKind.None, name: SpecialParameterSymbol.ContextName));

                            //
                            module.SetMethodBody(func, MethodGenerator.GenerateMethodBody(module, func, il =>
                            {
                                var ctxPlace = new ArgPlace(DeclaringCompilation.CoreTypes.Context, 0);
                                var cg       = new CodeGenerator(il, module, diagnostics, module.Compilation.Options.OptimizationLevel, false, field.ContainingType, ctxPlace, null)
                                {
                                    CallerType                = ContainingType,
                                    ContainingFile            = ContainingFile,
                                    IsInCachedArrayExpression = true,   // do not cache array initializers twice
                                };

                                // return {Initializer}
                                cg.EmitConvert(p.Initializer, func.ReturnType);
                                cg.EmitRet(func.ReturnType);
                            }, null, diagnostics, false));

                            module.SynthesizedManager.AddMethod(func.ContainingType, func);
                        }

                        using (var cg = new CodeGenerator(cctor, module, diagnostics, module.Compilation.Options.OptimizationLevel, false, field.ContainingType,
                                                          contextPlace: null,
                                                          thisPlace: null)
                        {
                            CallerType = ContainingType,
                            ContainingFile = ContainingFile,
                            IsInCachedArrayExpression = true,   // do not cache array initializers twice
                        })
                        {
                            var fldplace = new FieldPlace(null, field, module);
                            fldplace.EmitStorePrepare(cg.Builder);
                            if (func == null)
                            {
                                // {field} = {Initializer};
                                cg.EmitConvert(p.Initializer, field.Type);
                            }
                            else
                            {
                                MethodSymbol funcsymbol = func;

                                // bind func in case it is generic
                                if (func.ContainingType is SourceTraitTypeSymbol st)
                                {
                                    funcsymbol = func.AsMember(st.Construct(st.TypeArguments));
                                }

                                // Func<,>(object @object, IntPtr method)
                                var func_ctor = ((NamedTypeSymbol)field.Type).InstanceConstructors.Single(m =>
                                                                                                          m.ParameterCount == 2 &&
                                                                                                          m.Parameters[0].Type.SpecialType == SpecialType.System_Object &&
                                                                                                          m.Parameters[1].Type.SpecialType == SpecialType.System_IntPtr
                                                                                                          );

                                // {field} = new Func<Context, PhpValue>( {func} )
                                cg.Builder.EmitNullConstant();                                                                         // null
                                cg.EmitOpCode(ILOpCode.Ldftn);                                                                         // method
                                cg.Builder.EmitToken(module.Translate(funcsymbol, null, cg.Diagnostics, false), null, cg.Diagnostics); // !! needDeclaration: false
                                cg.EmitCall(ILOpCode.Newobj, func_ctor);
                            }
                            fldplace.EmitStore(cg.Builder);
                        }
                    }
                }
            }
        }
        void EmitPhpCtors(ImmutableArray <MethodSymbol> instancectors, Emit.PEModuleBuilder module, DiagnosticBag diagnostics)
        {
            foreach (SynthesizedPhpCtorSymbol ctor in instancectors)
            {
                module.SetMethodBody(ctor, MethodGenerator.GenerateMethodBody(module, ctor, il =>
                {
                    if (ctor is SynthesizedParameterlessPhpCtorSymbol)
                    {
                        EmitParameterlessCtor(ctor, il, module, diagnostics);
                        return;
                    }

                    Debug.Assert(SpecialParameterSymbol.IsContextParameter(ctor.Parameters[0]));

                    var cg = new CodeGenerator(il, module, diagnostics, module.Compilation.Options.OptimizationLevel, false, this, new ParamPlace(ctor.Parameters[0]), new ArgPlace(this, 0))
                    {
                        CallerType     = this,
                        ContainingFile = ContainingFile,
                    };

                    Debug.Assert(ctor.BaseCtor != null);

                    // base..ctor or this..ctor
                    cg.EmitPop(cg.EmitForwardCall(ctor.BaseCtor, ctor));

                    if (ctor.PhpConstructor == null)
                    {
                        // initialize <ctx> field, if field is declared within this type
                        var ctxField = this.ContextStore;
                        if (ctxField != null && object.ReferenceEquals((object)ctxField.ContainingType, this))
                        {
                            var ctxFieldPlace = new FieldPlace(cg.ThisPlaceOpt, ctxField, module);

                            // Debug.Assert(<ctx> != null)
                            cg.EmitDebugAssertNotNull(cg.ContextPlaceOpt, "Context cannot be null.");

                            // <this>.<ctx> = <ctx>
                            ctxFieldPlace.EmitStorePrepare(il);
                            cg.EmitLoadContext();
                            ctxFieldPlace.EmitStore(il);
                        }

                        // trait specific:
                        if (ctor is SynthesizedPhpTraitCtorSymbol tctor)
                        {
                            EmitTraitCtorInit(cg, tctor);
                        }

                        // trait instances:
                        foreach (var t in this.TraitUses)
                        {
                            EmitTraitInstanceInit(cg, ctor, t);
                        }

                        // initialize instance fields:
                        foreach (var f in this.GetMembers().OfType <IPhpPropertySymbol>().Where(f => f.FieldKind == PhpPropertyKind.InstanceField))
                        {
                            Debug.Assert(f.ContainingStaticsHolder == null);
                            f.EmitInit(cg);
                        }
                    }
                    else
                    {
                        Debug.Assert(ctor.BaseCtor.ContainingType == this);

                        // this.__construct
                        cg.EmitPop(cg.EmitForwardCall(ctor.PhpConstructor, ctor));
                    }

                    // ret
                    Debug.Assert(ctor.ReturnsVoid);
                    cg.EmitRet(ctor.ReturnType);
                }, null, diagnostics, false));
            }
        }
        void EmitDisposable(Emit.PEModuleBuilder module, DiagnosticBag diagnostics)
        {
            var __destruct = TryGetDestruct();

            if (__destruct == null ||
                IsAlreadyImplemented(__destruct) ||
                IsAlreadyImplemented(DeclaringCompilation.GetSpecialType(SpecialType.System_IDisposable)))
            {
                // already implemented in a base class
                return;
            }

            //
            // IDisposable.Dispose()
            //
            var dispose = new SynthesizedMethodSymbol(this, "IDisposable.Dispose", false, true, DeclaringCompilation.GetSpecialType(SpecialType.System_Void), isfinal: true)
            {
                ExplicitOverride = (MethodSymbol)DeclaringCompilation.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose),
                ForwardedCall    = __destruct,
            };

            module.SetMethodBody(dispose, MethodGenerator.GenerateMethodBody(module, dispose, il =>
            {
                var thisPlace = new ArgPlace(this, 0);
                var ctxPlace  = new FieldPlace(thisPlace, this.ContextStore, module);
                var cg        = new CodeGenerator(il, module, diagnostics, module.Compilation.Options.OptimizationLevel, false, this, ctxPlace, thisPlace)
                {
                    CallerType = this,
                };

                // private bool <>b_disposed;
                var disposedField = cg.Module.SynthesizedManager.GetOrCreateSynthesizedField(this, DeclaringCompilation.CoreTypes.Boolean, WellKnownPchpNames.SynthesizedDisposedFieldName, Accessibility.Private, false, false, false);
                var disposedPlace = new FieldPlace(thisPlace, disposedField, cg.Module);

                // if (<>b_disposed) return;
                var lblContinue = new object();
                disposedPlace.EmitLoad(cg.Builder);
                cg.Builder.EmitBranch(ILOpCode.Brfalse, lblContinue);
                cg.EmitRet(DeclaringCompilation.CoreTypes.Void);
                cg.Builder.MarkLabel(lblContinue);

                // <>b_disposed = true;
                disposedPlace.EmitStorePrepare(cg.Builder);
                cg.Builder.EmitBoolConstant(true);
                disposedPlace.EmitStore(cg.Builder);

                // __destruct()
                cg.EmitPop(cg.EmitForwardCall(__destruct, dispose, callvirt: true));

                // .ret
                cg.EmitRet(DeclaringCompilation.GetSpecialType(SpecialType.System_Void));
            }, null, diagnostics, false));

            module.SynthesizedManager.AddMethod(this, dispose);

            ////
            //// Finalize()
            ////
            //var finalize = new SynthesizedFinalizeSymbol(this)
            //{
            //    ForwardedCall = dispose,
            //};

            //Debug.Assert(finalize.OverriddenMethod != null);

            //module.SetMethodBody(finalize, MethodGenerator.GenerateMethodBody(module, finalize, il =>
            //{
            //    var thisPlace = new ArgPlace(this, 0);
            //    var ctxPlace = new FieldPlace(thisPlace, this.ContextStore, module);
            //    var cg = new CodeGenerator(il, module, diagnostics, module.Compilation.Options.OptimizationLevel, false, this, ctxPlace, thisPlace)
            //    {
            //        CallerType = this,
            //    };

            //    //
            //    cg.Builder.OpenLocalScope(ScopeType.TryCatchFinally);
            //    // try {
            //    cg.Builder.OpenLocalScope(ScopeType.Try);

            //    // Dispose()
            //    cg.EmitPop(cg.EmitForwardCall(dispose, finalize, callvirt: false));
            //    //if (cg.EmitPdbSequencePoints) cg.Builder.EmitOpCode(ILOpCode.Nop);

            //    // }
            //    cg.Builder.CloseLocalScope();
            //    // finally {
            //    cg.Builder.OpenLocalScope(ScopeType.Finally);

            //    // base.Finalize()
            //    thisPlace.EmitLoad(cg.Builder);
            //    cg.EmitCall(ILOpCode.Call, finalize.ExplicitOverride);
            //    //if (cg.EmitPdbSequencePoints) cg.Builder.EmitOpCode(ILOpCode.Nop);

            //    // }
            //    cg.Builder.CloseLocalScope();
            //    cg.Builder.CloseLocalScope();

            //    // .ret
            //    cg.EmitRet(DeclaringCompilation.GetSpecialType(SpecialType.System_Void));

            //}, null, diagnostics, false));

            //module.SynthesizedManager.AddMethod(this, finalize);
        }