protected virtual IEnumerable <ParameterSymbol> CreateParameters(IEnumerable <ParameterSymbol> baseparams) { int index = 0; // Context <ctx> yield return(new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, index++)); // same parameters as PHP constructor foreach (var p in baseparams) { if (!SpecialParameterSymbol.IsContextParameter(p)) { yield return(new SynthesizedParameterSymbol(this, p.Type, index++, p.RefKind, p.Name, p.IsParams, explicitDefaultConstantValue: p.ExplicitDefaultConstantValue)); } } }
/// <summary> /// Parameters for <see cref="SourceGeneratorSymbol"/> method are defined by <c>GeneratorStateMachineDelegate</c>. /// </summary> ParameterSymbol[] CreateParameters(SourceRoutineSymbol originalRoutine) { // resolve type of $this var thisType = originalRoutine.GetPhpThisVariablePlaceWithoutGenerator()?.Type ?? (TypeSymbol)originalRoutine.DeclaringCompilation.ObjectType; // yield sm method signature var index = 0; return(new[] { ContextParameter = new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, index++), ThisParameter = new SpecialParameterSymbol(this, thisType, SpecialParameterSymbol.ThisName, index++), LocalsParameter = new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.PhpArray, SpecialParameterSymbol.LocalsName, index++), TmpLocalsParameter = new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.PhpArray, SpecialParameterSymbol.TemporaryLocalsName, index++), GeneratorParameter = new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Generator, "generator", index++), }); }
void EmitTraitImplementations(Emit.PEModuleBuilder module) { foreach (var t in TraitUses) { foreach (var m in t.GetMembers().OfType <SynthesizedMethodSymbol>()) { Debug.Assert(m.ForwardedCall != null); module.SetMethodBody(m, MethodGenerator.GenerateMethodBody(module, m, il => { IPlace thisPlace = null; IPlace traitInstancePlace = null; IPlace ctxPlace; if (m.IsStatic) { // Template: return TRAIT.method(...) Debug.Assert(SpecialParameterSymbol.IsContextParameter(m.Parameters[0])); ctxPlace = new ParamPlace(m.Parameters[0]); } else { // Template: return this.<>trait.method(...) thisPlace = new ArgPlace(this, 0); // this ctxPlace = new FieldPlace(thisPlace, this.ContextStore, module); // this.<ctx> traitInstancePlace = new FieldPlace(thisPlace, t.TraitInstanceField, module); // this.<>trait } using (var cg = new CodeGenerator(il, module, DiagnosticBag.GetInstance(), module.Compilation.Options.OptimizationLevel, false, this, ctxPlace, thisPlace) { CallerType = this, }) { var forwarded_type = cg.EmitForwardCall(m.ForwardedCall, m, thisPlaceExplicit: traitInstancePlace); var target_type = m.ReturnType; cg.EmitConvert(forwarded_type, 0, target_type); // always (forwarded_type === target_type) cg.EmitRet(target_type); } }, null, DiagnosticBag.GetInstance(), false)); module.SynthesizedManager.AddMethod(this, m); } } }
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)); }
/// <summary> /// Gets the special <c><static></c> parameter of given method if any. Otherwise <c>null</c>. /// </summary> internal static ParameterSymbol LateStaticParameter(this MethodSymbol method) { // in source routines, we can iterate just the implicit parameters and not populating the source parameters var ps = method is SourceRoutineSymbol sr ? sr.ImplicitParameters : method.Parameters; foreach (var p in ps) { if (SpecialParameterSymbol.IsLateStaticParameter(p)) { return(p); } else if (!p.IsImplicitlyDeclared) { break; } } return(null); }
protected virtual IEnumerable<ParameterSymbol> CreateParameters(IEnumerable<ParameterSymbol> baseparams) { int index = 0; // Context <ctx> yield return new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, index++); if (IsInitFieldsOnly) { // DummyFieldsOnlyCtor _ yield return new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.DummyFieldsOnlyCtor, "_", index++); } // same parameters as PHP constructor foreach (var p in baseparams) { if (SpecialParameterSymbol.IsContextParameter(p)) continue; if (SpecialParameterSymbol.IsDummyFieldsOnlyCtorParameter(p)) continue; yield return SynthesizedParameterSymbol.Create(this, p, index++); } }
/// <summary> /// Gets additional flags of the caller routine. /// </summary> public static RoutineFlags InvocationFlags(this IPhpRoutineSymbol routine) { var f = RoutineFlags.None; var ps = /*routine is SourceRoutineSymbol sr ? sr.ImplicitParameters :*/ routine.Parameters; foreach (var p in ps) { if (p.IsImplicitlyDeclared) { if (SpecialParameterSymbol.IsImportValueParameter(p, out var spec)) { switch (spec) { case SpecialParameterSymbol.ValueSpec.CallerArgs: f |= RoutineFlags.UsesArgs; break; case SpecialParameterSymbol.ValueSpec.Locals: f |= RoutineFlags.UsesLocals; break; case SpecialParameterSymbol.ValueSpec.CallerStaticClass: f |= RoutineFlags.UsesLateStatic; break; } } } else { // implicit parameters are at begining only break; } } return(f); }
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 ResolveBaseCtorAndParameters() { if (!_parameters.IsDefaultOrEmpty) { return; } // var phpctor = this.PhpCtor; // this tells us what parameters are provided to resolve base .ctor that can be called var basephpnew = this.ContainingType.BaseType.PhpNewMethodSymbol; // base..phpnew() to be called if provided var basectors = (basephpnew != null) ? ImmutableArray.Create(basephpnew) : this.ContainingType.BaseType.InstanceConstructors .Where(c => c.DeclaredAccessibility != Accessibility.Private) .OrderByDescending(c => c.ParameterCount) // longest ctors first .AsImmutable(); // Context <ctx> var ps = new List <ParameterSymbol>(1) { new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, 0) // Context <ctx> }; var givenparams = (phpctor != null) ? phpctor.Parameters.Where(p => !p.IsImplicitlyDeclared).ToArray() : EmptyArray <ParameterSymbol> .Instance; // find best matching basector foreach (var c in basectors) { var calledparams = c.Parameters.Where(p => !p.IsImplicitlyDeclared).ToArray(); if (CanBePassedTo(givenparams, calledparams)) { // we have found base constructor with most parameters we can call with given parameters _lazyBaseCtor = c; break; } } if (_lazyBaseCtor == null) { throw new InvalidOperationException("Base .ctor cannot be resolved with provided constructor."); } // Debug.Assert(SpecialParameterSymbol.IsContextParameter(ps[0])); Debug.Assert(_lazyBaseCtor != null && !_lazyBaseCtor.IsStatic); foreach (var p in _lazyBaseCtor.Parameters) { if (SpecialParameterSymbol.IsContextParameter(p)) { continue; } ps.Add(new SynthesizedParameterSymbol(this, p.Type, ps.Count, p.RefKind, p.Name, explicitDefaultConstantValue: p.ExplicitDefaultConstantValue)); } // _parameters = ps.AsImmutable(); }