/// <summary> /// Gets place of PHP <c>$this</c> variable. /// </summary> public virtual IPlace GetPhpThisVariablePlace(PEModuleBuilder module = null) { var thisPlace = GetThisPlace(); if (thisPlace != null) { if (this.IsGeneratorMethod()) { // $this ~ arg1 thisPlace = new ArgPlace(thisPlace.Type, 1); } else if (this.ContainingType.IsTraitType()) { // $this ~ this.<>this thisPlace = new FieldPlace(thisPlace, ((SourceTraitTypeSymbol)this.ContainingType).RealThisField, module); } // return(thisPlace); } else { return(null); } }
void EmitPhpCallable(Emit.PEModuleBuilder module, DiagnosticBag diagnostics) { var __invoke = TryGetMagicInvoke(); if (__invoke == null || IsAlreadyImplemented(__invoke) || IsAlreadyImplemented(DeclaringCompilation.CoreTypes.IPhpCallable)) { // already implemented in a base class return; } // // IPhpCallable.Invoke(Context <ctx>, PhpVaue[] arguments) // var invoke = new SynthesizedMethodSymbol(this, "IPhpCallable.Invoke", false, true, DeclaringCompilation.CoreTypes.PhpValue, isfinal: false) { ExplicitOverride = (MethodSymbol)DeclaringCompilation.CoreTypes.IPhpCallable.Symbol.GetMembers("Invoke").Single(), ForwardedCall = __invoke, }; invoke.SetParameters( new SpecialParameterSymbol(invoke, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, 0), new SynthesizedParameterSymbol(invoke, ArrayTypeSymbol.CreateSZArray(ContainingAssembly, DeclaringCompilation.CoreTypes.PhpValue.Symbol), 1, RefKind.None, name: "arguments", isParams: true)); module.SetMethodBody(invoke, MethodGenerator.GenerateMethodBody(module, invoke, il => { var cg = new CodeGenerator(il, module, diagnostics, module.Compilation.Options.OptimizationLevel, false, this, new ParamPlace(invoke.Parameters[0]), new ArgPlace(this, 0)) { CallerType = this, }; cg.EmitRet(cg.EmitForwardCall(__invoke, invoke, callvirt: true)); }, 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 ctxPlace = new FieldPlace(thisPlace, this.ContextStore, module); var cg = new CodeGenerator(il, module, diagnostics, module.Compilation.Options.OptimizationLevel, false, this, ctxPlace, 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); }
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); // NOTE: following is not needed anymore: //// ghost stubs: // ... resolve this already in SourceTypeSymbol.GetMembers(), now it does not get overloaded properly //var ps = m.Parameters; //for (int i = 0; i < ps.Length; i++) //{ // if (ps[i].HasUnmappedDefaultValue()) // => ConstantValue couldn't be resolved for optional parameter // { // // create ghost stub foo(p0, .. pi-1) => foo(p0, .. , pN) // GhostMethodBuilder.CreateGhostOverload(m, module, DiagnosticBag.GetInstance(), i); // } //} } } }
void EmitToString(Emit.PEModuleBuilder module) { if (this.IsInterface || this.IsTrait) { return; } var __tostring = this.GetMembersByPhpName(SpecialMethodNames.Tostring.Value).OfType <MethodSymbol>().FirstOrDefault(); if (__tostring != null) // implement ToString if: there is __toString() function { // lookup base string ToString() var overriden = this.LookupMember <MethodSymbol>( WellKnownMemberNames.ObjectToString, m => OverrideHelper.SignaturesMatch(m, (MethodSymbol)DeclaringCompilation.GetSpecialTypeMember(SpecialMember.System_Object__ToString))); Debug.Assert(overriden != null); if (overriden == null || overriden.IsSealed || overriden.ContainingType == this) { // cannot be overriden return; } // public sealed override string ToString() var tostring = new SynthesizedMethodSymbol(this, WellKnownMemberNames.ObjectToString, false, true, DeclaringCompilation.CoreTypes.String, Accessibility.Public, isfinal: false, phphidden: true) { ExplicitOverride = overriden, ForwardedCall = __tostring, }; module.SetMethodBody(tostring, MethodGenerator.GenerateMethodBody(module, tostring, il => { var thisPlace = new ArgPlace(this, 0); var cg = new CodeGenerator(il, module, DiagnosticBag.GetInstance(), module.Compilation.Options.OptimizationLevel, false, this, new FieldPlace(thisPlace, this.ContextStore, module), thisPlace); if (__tostring != null) { // __tostring().ToString() cg.EmitConvert(cg.EmitForwardCall(__tostring, tostring, callvirt: true), 0, tostring.ReturnType); } else { // PhpException.ObjectToStringNotSupported(this) cg.EmitThis(); cg.EmitPop(cg.EmitCall(ILOpCode.Call, cg.CoreTypes.PhpException.Method("ObjectToStringNotSupported", cg.CoreTypes.Object))); // return "" cg.Builder.EmitStringConstant(string.Empty); } cg.EmitRet(tostring.ReturnType); }, null, DiagnosticBag.GetInstance(), false)); module.SynthesizedManager.AddMethod(this, tostring); } }
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 EmitToString(Emit.PEModuleBuilder module) { if (this.IsInterface || this.IsTrait) { return; } var __tostring = this.GetMembers(SpecialMethodNames.Tostring.Value, true).OfType <MethodSymbol>().FirstOrDefault(); if (__tostring != null || this.Syntax.BaseClass == null) // implement ToString if: there is __tostring() function or ToString is not overriden yet { // public override string ToString() // Note, there might be two ToString methods with same parameters only differing by their return type, CLR allows that var tostring = new SynthesizedMethodSymbol(this, "ToString", false, true, DeclaringCompilation.CoreTypes.String, Accessibility.Public, isfinal: false) { ExplicitOverride = (MethodSymbol)DeclaringCompilation.GetSpecialTypeMember(SpecialMember.System_Object__ToString), ForwardedCall = __tostring, }; module.SetMethodBody(tostring, MethodGenerator.GenerateMethodBody(module, tostring, il => { var thisPlace = new ArgPlace(this, 0); var cg = new CodeGenerator(il, module, DiagnosticBag.GetInstance(), module.Compilation.Options.OptimizationLevel, false, this, new FieldPlace(thisPlace, this.ContextStore, module), thisPlace); if (__tostring != null) { // __tostring().ToString() cg.EmitConvert(cg.EmitForwardCall(__tostring, tostring, callvirt: true), 0, tostring.ReturnType); } else { // PhpException.ObjectToStringNotSupported(this) cg.EmitThis(); cg.EmitPop(cg.EmitCall(ILOpCode.Call, cg.CoreTypes.PhpException.Method("ObjectToStringNotSupported", cg.CoreTypes.Object))); // return "" cg.Builder.EmitStringConstant(string.Empty); } cg.EmitRet(tostring.ReturnType); }, null, DiagnosticBag.GetInstance(), false)); module.SynthesizedManager.AddMethod(this, tostring); } }
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); }
/// <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 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); }