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); } }
/// <summary> /// Matches all methods that can be overriden (non-static, public or protected, abstract or virtual) /// within this type sub-tree (this type, its base and interfaces) /// with its override. /// Methods without an override are either abstract or a ghost stup has to be synthesized. /// </summary> /// <param name="diagnostics"></param> internal OverrideInfo[] ResolveOverrides(DiagnosticBag diagnostics) { if (_lazyOverrides != null) { // already resolved return(_lazyOverrides); } // inherit abstracts from base type // ignoring System.Object (we don't override its methods from PHP) var overrides = new List <OverrideInfo>(); if (BaseType != null && BaseType.SpecialType != SpecialType.System_Object) { overrides.AddRange(BaseType.ResolveOverrides(diagnostics)); } // collect this type declared methods including synthesized methods var members = this.GetMembers(); // resolve overrides of inherited members for (int i = 0; i < overrides.Count; i++) { var m = overrides[i]; if (m.HasOverride == false) { // update override info of the inherited member overrides[i] = new OverrideInfo(m.Method, OverrideHelper.ResolveMethodImplementation(m.Method, members)); } else { // clear the interface flag of inherited override info m.ImplementsInterface = false; overrides[i] = m; } } // resolve overrides of interface methods foreach (var iface in Interfaces) { // skip interfaces implemented by base type or other interfaces, // we don't want to add redundant override entries: if ((BaseType != null && BaseType.ImplementsInterface(iface)) || Interfaces.Any(x => x != iface && x.ImplementsInterface(iface))) { // iface is already handled within overrides => skip // note: iface can be ignored in metadata at all actually continue; } var iface_abstracts = iface.ResolveOverrides(diagnostics); foreach (var m in iface_abstracts) { if (BaseType != null && m.Method.ContainingType != iface && BaseType.ImplementsInterface(m.Method.ContainingType)) { // iface {m.Method.ContainingType} already handled within overrides => skip continue; } // ignore interface method that is already implemented: if (overrides.Any(o => OverrideHelper.SignaturesMatch(o.Method, m.Method))) { continue; } // add interface member, // resolve its override overrides.Add(new OverrideInfo(m.Method, this.IsInterface ? null : OverrideHelper.ResolveMethodImplementation(m.Method, this)) { ImplementsInterface = true }); } } // add overrideable routines from this type foreach (var s in members) { if (s is MethodSymbol m && m.IsOverrideable()) { overrides.Add(new OverrideInfo(m)); } } // handle unresolved abstracts for (int i = 0; i < overrides.Count; i++) { var m = overrides[i]; if (m.IsUnresolvedAbstract && this is SourceTypeSymbol srct && !this.IsInterface) { if (!this.IsAbstract) { // Class '{0}' doesn't implement abstract method {1}::{2}() diagnostics.Add(DiagnosticBagExtensions.ParserDiagnostic(srct.ContainingFile.SyntaxTree, srct.Syntax.HeadingSpan, Devsense.PHP.Errors.Errors.AbstractMethodNotImplemented, srct.FullName.ToString(), ((IPhpTypeSymbol)m.Method.ContainingType).FullName.ToString(), m.RoutineName)); } else if (m.ImplementsInterface /*&& this.IsAbstract*/) { m.ImplementsInterface = false; var method = m.Method; Debug.Assert(!method.IsStatic); Debug.Assert(method.DeclaredAccessibility != Accessibility.Private); Debug.Assert(method.ContainingType.IsInterface); // Template: abstract function {name}({parameters}) var ghost = new SynthesizedMethodSymbol(this, method.RoutineName, isstatic: false, isvirtual: true, isabstract: true, isfinal: false, returnType: method.ReturnType, accessibility: method.DeclaredAccessibility); ghost.SetParameters(SynthesizedParameterSymbol.Create(ghost, method.Parameters)); //module.SynthesizedManager.AddMethod(this, ghost); // will be added to synthesized manager by FinalizeMethodTable m.Method = ghost; // replace the interface method with synthesized abstract method // update overrides overrides[i] = m; } } } // cache & return return(_lazyOverrides = overrides.ToArray()); }
/// <summary> /// Matches all methods that can be overriden (non-static, public or protected, abstract or virtual) /// within this type sub-tree (this type, its base and interfaces) /// with its override. /// Methods without an override are either abstract or a ghost stup has to be synthesized. /// </summary> /// <param name="diagnostics"></param> internal OverrideInfo[] ResolveOverrides(DiagnosticBag diagnostics) { if (_lazyOverrides != null) { // already resolved return(_lazyOverrides); } // TODO: ignore System.Object ? // inherit abstracts from base type var overrides = new List <OverrideInfo>(); if (BaseType != null) { overrides.AddRange(BaseType.ResolveOverrides(diagnostics)); } // collect this type declared methods including synthesized methods var methods = this.GetMembers().OfType <MethodSymbol>(); var methodslookup = methods.Where(OverrideHelper.CanOverride).ToLookup(m => m.RoutineName, StringComparer.OrdinalIgnoreCase); // resolve overrides of inherited members for (int i = 0; i < overrides.Count; i++) { var m = overrides[i]; if (m.HasOverride == false) { // update override info of the inherited member overrides[i] = new OverrideInfo(m.Method, OverrideHelper.ResolveMethodImplementation(m.Method, methodslookup[m.RoutineName])); } else { // clear the interface flag of inherited override info m.ImplementsInterface = false; overrides[i] = m; } } // resolve overrides of interface methods foreach (var iface in Interfaces) { // skip interfaces implemented by base type or other interfaces, // we don't want to add redundant override entries: if ((BaseType != null && BaseType.ImplementsInterface(iface)) || Interfaces.Any(x => x != iface && x.ImplementsInterface(iface))) { // iface is already handled within overrides => skip // note: iface can be ignored in metadata at all actually continue; } var iface_abstracts = iface.ResolveOverrides(diagnostics); foreach (var m in iface_abstracts) { if (BaseType != null && m.Method.ContainingType != iface && BaseType.ImplementsInterface(m.Method.ContainingType)) { // iface {m.Method.ContainingType} already handled within overrides => skip continue; } // ignore interface method that is already implemented: if (overrides.Any(o => OverrideHelper.SignaturesMatch(o.Method, m.Method))) { continue; } // add interface member, // resolve its override overrides.Add(new OverrideInfo(m.Method, this.IsInterface ? null : OverrideHelper.ResolveMethodImplementation(m.Method, this)) { ImplementsInterface = true }); } } // add overrideable routines from this type foreach (var m in methods) { if (m.IsOverrideable()) { overrides.Add(new OverrideInfo(m)); } } // report unresolved abstracts if (!this.IsAbstract && !this.IsInterface && this is SourceTypeSymbol srct) { foreach (var m in overrides) { if (m.IsUnresolvedAbstract) { // Class '{0}' doesn't implement abstract method {1}::{2}() diagnostics.Add(DiagnosticBagExtensions.ParserDiagnostic(srct.ContainingFile.SyntaxTree, srct.Syntax.HeadingSpan, Devsense.PHP.Errors.Errors.AbstractMethodNotImplemented, srct.FullName.ToString(), ((IPhpTypeSymbol)m.Method.ContainingType).FullName.ToString(), m.RoutineName)); } } } // cache & return return(_lazyOverrides = overrides.ToArray()); }