void EmitInit(Emit.PEModuleBuilder module) { // void Init(Context) var tt = DeclaringCompilation.CoreTypes; var diagnostic = DiagnosticBag.GetInstance(); // override IStaticInit.Init(Context) { .. } var initMethod = new SynthesizedMethodSymbol(this, "Init", false, true, tt.Void, Accessibility.Public); initMethod.SetParameters(new SynthesizedParameterSymbol(initMethod, tt.Context, 0, RefKind.None, "ctx")); var body = MethodGenerator.GenerateMethodBody(module, initMethod, (il) => { var cg = new CodeGenerator(il, module, diagnostic, OptimizationLevel.Release, false, this, new ArgPlace(tt.Context, 1), new ArgPlace(this, 0)); foreach (var fld in this.Fields) { if (fld.RequiresContext) { fld.EmitInit(cg); } } // il.EmitRet(true); }, null, diagnostic, false); module.SetMethodBody(initMethod, body); module.SynthesizedManager.AddMethod(this, initMethod); }
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 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> /// Method that enumerates all referenced global types. /// EnumerateReferencedTypes(Action<string, RuntimeTypeHandle> callback) /// </summary> MethodSymbol CreateBuiltinTypesSymbol() { var compilation = DeclaringCompilation; var action_T = compilation.GetWellKnownType(WellKnownType.System_Action_T); var action_phptypeinfo = action_T.Construct(compilation.CoreTypes.PhpTypeInfo); var method = new SynthesizedMethodSymbol(this, "BuiltinTypes", true, false, compilation.CoreTypes.Void, Accessibility.Public); method.SetParameters(new SynthesizedParameterSymbol(method, action_phptypeinfo, 0, RefKind.None, "callback")); // return(method); }
/// <summary> /// Method that enumerates all script Main functions. /// EnumerateScripts(Action<string, RuntimeMethodHandle> callback) /// </summary> MethodSymbol CreateEnumerateScriptsSymbol() { var compilation = DeclaringCompilation; var action_T2 = compilation.GetWellKnownType(WellKnownType.System_Action_T2); var action_string_method = action_T2.Construct(compilation.CoreTypes.String, compilation.CoreTypes.RuntimeMethodHandle); var method = new SynthesizedMethodSymbol(this, "EnumerateScripts", true, false, compilation.CoreTypes.Void, Accessibility.Public); method.SetParameters(new SynthesizedParameterSymbol(method, action_string_method, 0, RefKind.None, "callback")); // return(method); }
/// <summary> /// Method that enumerates all app-wide global constants. /// EnumerateConstants(Action<string, PhpValue, bool> callback) /// </summary> MethodSymbol CreateEnumerateConstantsSymbol() { var compilation = DeclaringCompilation; var action_T3 = compilation.GetWellKnownType(WellKnownType.System_Action_T3); var action_string_value_bool = action_T3.Construct(compilation.CoreTypes.String, compilation.CoreTypes.PhpValue, compilation.CoreTypes.Boolean); var method = new SynthesizedMethodSymbol(this, "EnumerateConstants", true, false, compilation.CoreTypes.Void, Accessibility.Public); method.SetParameters(new SynthesizedParameterSymbol(method, action_string_value_bool, 0, RefKind.None, "callback")); // return(method); }
/// <summary> /// Method that enumerates all app-wide global constants. /// EnumerateConstants(Context.IConstantsComposition composer) /// </summary> MethodSymbol CreateEnumerateConstantsSymbol() { var compilation = DeclaringCompilation; var t = (TypeSymbol)compilation.GetTypeByMetadataName(CoreTypes.IConstantsCompositionFullName); Debug.Assert(t != null); var method = new SynthesizedMethodSymbol(this, "BuiltinConstants", true, false, compilation.CoreTypes.Void, Accessibility.Public); method.SetParameters(new SynthesizedParameterSymbol(method, t, 0, RefKind.None, name: "composer")); // return(method); }
public void EmitInit(Emit.PEModuleBuilder module, Action <Microsoft.CodeAnalysis.CodeGen.ILBuilder> builder) { Debug.Assert(_initMethod == null); var tt = DeclaringCompilation.CoreTypes; // override IStaticInit.Init(Context) _initMethod = new SynthesizedMethodSymbol(this, "Init", false, true, tt.Void, Accessibility.Public); _initMethod.SetParameters(new SynthesizedParameterSymbol(_initMethod, tt.Context, 0, RefKind.None, "ctx")); var body = CodeGen.MethodGenerator.GenerateMethodBody(module, _initMethod, builder, null, DiagnosticBag.GetInstance(), false); module.SetMethodBody(_initMethod, body); }
/// <summary> /// Main method wrapper in case it does not return PhpValue. /// </summary> /// <returns></returns> internal SynthesizedMethodSymbol EnsureMainMethodRegular() { if (_mainMethodRegularOpt == null && _mainMethod.ControlFlowGraph != null && // => main routine initialized _mainMethod.ReturnType != DeclaringCompilation.CoreTypes.PhpValue) { // PhpValue <Main>`0(parameters) _mainMethodRegularOpt = new SynthesizedMethodSymbol( this, WellKnownPchpNames.GlobalRoutineName + "`0", true, false, DeclaringCompilation.CoreTypes.PhpValue, Accessibility.Public); _mainMethodRegularOpt.SetParameters(_mainMethod.Parameters.Select(p => new SpecialParameterSymbol(_mainMethodRegularOpt, p.Type, p.Name, p.Ordinal)).ToArray()); } return(_mainMethodRegularOpt); }
/// <summary> /// In case the class implements <c>__invoke</c> method, we create special Invoke() method that is compatible with IPhpCallable interface. /// </summary> internal SynthesizedMethodSymbol EnsureInvokeMethod() { if (_lazyInvokeSymbol == null) { if (GetMembers(Pchp.Syntax.Name.SpecialMethodNames.Invoke.Value).Any(s => s is MethodSymbol)) { _lazyInvokeSymbol = new SynthesizedMethodSymbol(this, "IPhpCallable.Invoke", false, true, DeclaringCompilation.CoreTypes.PhpValue) { ExplicitOverride = (MethodSymbol)DeclaringCompilation.CoreTypes.IPhpCallable.Symbol.GetMembers("Invoke").Single(), }; _lazyInvokeSymbol.SetParameters( new SpecialParameterSymbol(_lazyInvokeSymbol, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, 0), new SpecialParameterSymbol(_lazyInvokeSymbol, ArrayTypeSymbol.CreateSZArray(ContainingAssembly, DeclaringCompilation.CoreTypes.PhpValue.Symbol), "arguments", 1)); _lazyMembers = _lazyMembers.Add(_lazyInvokeSymbol); } } return(_lazyInvokeSymbol); }
/// <summary> /// Creates ghost stub that calls current method. /// </summary> protected void CreateGhostOverload(PEModuleBuilder module, DiagnosticBag diagnostic, TypeSymbol ghostreturn, IEnumerable <ParameterSymbol> ghostparams, MethodSymbol explicitOverride = null) { var ghost = new SynthesizedMethodSymbol( this.ContainingType, this.Name, this.IsStatic, explicitOverride != null, ghostreturn, this.DeclaredAccessibility) { ExplicitOverride = explicitOverride, }; ghost.SetParameters(ghostparams.Select(p => new SynthesizedParameterSymbol(ghost, p.Type, p.Ordinal, p.RefKind, p.Name)).ToArray()); // save method symbol to module module.SynthesizedManager.AddMethod(this.ContainingType, ghost); // generate method body GenerateGhostBody(module, diagnostic, ghost); }
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); } }
/// <summary> /// Creates ghost stub that calls current method. /// </summary> protected void CreateGhostOverload(PEModuleBuilder module, DiagnosticBag diagnostic, TypeSymbol ghostreturn, IEnumerable<ParameterSymbol> ghostparams, MethodSymbol explicitOverride = null) { var ghost = new SynthesizedMethodSymbol( this.ContainingType, this.Name, this.IsStatic, explicitOverride != null, ghostreturn, this.DeclaredAccessibility) { ExplicitOverride = explicitOverride, }; ghost.SetParameters(ghostparams.Select(p => new SynthesizedParameterSymbol(ghost, p.Type, p.Ordinal, p.RefKind, p.Name)).ToArray()); // save method symbol to module module.SynthesizedManager.AddMethod(this.ContainingType, ghost); // generate method body GenerateGhostBody(module, diagnostic, ghost); }
/// <summary> /// Main method wrapper in case it does not return PhpValue. /// </summary> void SynthesizeMainMethodWrapper(PEModuleBuilder module, DiagnosticBag diagnostics) { if (this.ReturnType != DeclaringCompilation.CoreTypes.PhpValue) { // PhpValue <Main>`0(parameters) var wrapper = new SynthesizedMethodSymbol( this.ContainingFile, WellKnownPchpNames.GlobalRoutineName + "`0", true, false, DeclaringCompilation.CoreTypes.PhpValue, Accessibility.Public); wrapper.SetParameters(this.Parameters.Select(p => new SynthesizedParameterSymbol(wrapper, p.Type, p.Ordinal, p.RefKind, p.Name)).ToArray()); // save method symbol to module module.SynthesizedManager.AddMethod(this.ContainingFile, wrapper); // generate method body module.CreateMainMethodWrapper(wrapper, this, diagnostics); } }
/// <summary> /// In case the class implements <c>__invoke</c> method, we create special Invoke() method that is compatible with IPhpCallable interface. /// </summary> internal SynthesizedMethodSymbol EnsureInvokeMethod(Emit.PEModuleBuilder module) { if (_lazyInvokeSymbol == null) { if (IsInvokable) { _lazyInvokeSymbol = new SynthesizedMethodSymbol(this, "IPhpCallable.Invoke", false, true, DeclaringCompilation.CoreTypes.PhpValue) { ExplicitOverride = (MethodSymbol)DeclaringCompilation.CoreTypes.IPhpCallable.Symbol.GetMembers("Invoke").Single(), }; _lazyInvokeSymbol.SetParameters( new SpecialParameterSymbol(_lazyInvokeSymbol, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, 0), new SynthesizedParameterSymbol(_lazyInvokeSymbol, ArrayTypeSymbol.CreateSZArray(ContainingAssembly, DeclaringCompilation.CoreTypes.PhpValue.Symbol), 1, RefKind.None, "arguments")); // module.SynthesizedManager.AddMethod(this, _lazyInvokeSymbol); } } return(_lazyInvokeSymbol); }
void EmitInit(Emit.PEModuleBuilder module) { // void Init(Context) var tt = DeclaringCompilation.CoreTypes; var diagnostic = DiagnosticBag.GetInstance(); // override IStaticInit.Init(Context) { .. } var initMethod = new SynthesizedMethodSymbol(this, "Init", false, true, tt.Void, Accessibility.Public); initMethod.SetParameters(new SynthesizedParameterSymbol(initMethod, tt.Context, 0, RefKind.None, SpecialParameterSymbol.ContextName)); var body = MethodGenerator.GenerateMethodBody(module, initMethod, (il) => { var cg = new CodeGenerator(il, module, diagnostic, module.Compilation.Options.OptimizationLevel, false, this, new ArgPlace(tt.Context, 1), new ArgPlace(this, 0), initMethod) { CallerType = this.ContainingType, ContainingFile = _class.ContainingFile, }; foreach (var p in this.Fields.Cast <IPhpPropertySymbol>()) { if (p.RequiresContext) { p.EmitInit(cg); } } // il.EmitRet(true); }, null, diagnostic, false); module.SetMethodBody(initMethod, body); module.SynthesizedManager.AddMethod(this, initMethod); }
void EmitPhpCallable(Emit.PEModuleBuilder module, DiagnosticBag diagnostics) { var iphpcallable = DeclaringCompilation.CoreTypes.IPhpCallable; var __invoke = TryGetMagicInvoke(); if (__invoke == null || IsAlreadyImplemented(__invoke) || IsAlreadyImplemented(iphpcallable)) { // already implemented in a base class return; } // // IPhpCallable.Invoke(Context <ctx>, PhpVaue[] arguments) // var invoke = new SynthesizedMethodSymbol(this, iphpcallable.FullName + ".Invoke", false, true, DeclaringCompilation.CoreTypes.PhpValue, isfinal: true) { ExplicitOverride = (MethodSymbol)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, }; var rtype = invoke.ReturnType; // Template: return (T)__invoke() cg.EmitConvert(cg.EmitForwardCall(__invoke, invoke, callvirt: true), default, rtype);
/// <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> /// Generates ghost method body that calls <c>this</c> method. /// </summary> protected void GenerateGhostBody(PEModuleBuilder module, DiagnosticBag diagnostic, SynthesizedMethodSymbol ghost) { var body = MethodGenerator.GenerateMethodBody(module, ghost, (il) => { var cg = new CodeGenerator(il, module, diagnostic, OptimizationLevel.Release, false, this.ContainingType, this.GetContextPlace(), this.GetThisPlace()); // return (T){routine}(p0, ..., pN); cg.EmitConvert(cg.EmitThisCall(this, ghost), 0, ghost.ReturnType); cg.EmitRet(ghost.ReturnType); }, null, diagnostic, false); module.SetMethodBody(ghost, body); }
/// <summary> /// Create real CIL entry point, where it calls given method. /// </summary> internal void CreateEntryPoint(MethodSymbol method, DiagnosticBag diagnostic) { // "static int Main(string[] args)" var realmethod = new SynthesizedMethodSymbol(this.ScriptType, "Main", true, false, _compilation.CoreTypes.Int32, Accessibility.Private); realmethod.SetParameters(new SynthesizedParameterSymbol(realmethod, ArrayTypeSymbol.CreateSZArray(this.Compilation.SourceAssembly, this.Compilation.CoreTypes.String), 0, RefKind.None, "args")); // var body = MethodGenerator.GenerateMethodBody(this, realmethod, (il) => { var types = this.Compilation.CoreTypes; var methods = this.Compilation.CoreMethods; var args_place = new ParamPlace(realmethod.Parameters[0]); // AddScriptReference<Script>() var AddScriptReferenceMethod = (MethodSymbol)methods.Context.AddScriptReference_TScript.Symbol.Construct(this.ScriptType); il.EmitCall(this, diagnostic, ILOpCode.Call, AddScriptReferenceMethod); // int exitcode = 0; var exitcode_loc = il.LocalSlotManager.AllocateSlot(types.Int32.Symbol, LocalSlotConstraints.None); il.EmitIntConstant(0); il.EmitLocalStore(exitcode_loc); // create Context var ctx_loc = il.LocalSlotManager.AllocateSlot(types.Context.Symbol, LocalSlotConstraints.None); // ctx_loc = Context.Create***(args) args_place.EmitLoad(il); MethodSymbol create_method = (_compilation.Options.OutputKind == OutputKind.ConsoleApplication) ? _compilation.CoreTypes.Context.Symbol.LookupMember<MethodSymbol>("CreateConsole") : null; Debug.Assert(create_method != null); il.EmitOpCode(ILOpCode.Call, +1); il.EmitToken(create_method, null, diagnostic); il.EmitLocalStore(ctx_loc); // Template: // try { Main(...); } catch (ScriptDiedException) { } finally { ctx.Dispose; } il.OpenLocalScope(ScopeType.TryCatchFinally); // try { try ... } finally {} il.OpenLocalScope(ScopeType.Try); { // IL requires catches and finally block to be distinct try il.OpenLocalScope(ScopeType.TryCatchFinally); // try {} catch (ScriptDiedException) {} il.OpenLocalScope(ScopeType.Try); { // emit .call method; if (method.HasThis) { throw new NotImplementedException(); // TODO: create instance of ContainingType } // params foreach (var p in method.Parameters) { if (p.Type == types.Context && p.Name == SpecialParameterSymbol.ContextName) { // <ctx> il.EmitLocalLoad(ctx_loc); } else if (p.Type == types.PhpArray && p.Name == SpecialParameterSymbol.LocalsName) { // <ctx>.Globals il.EmitLocalLoad(ctx_loc); il.EmitCall(this, diagnostic, ILOpCode.Call, methods.Context.Globals.Getter) .Expect(p.Type); } else if (p.Type == types.Object && p.Name == SpecialParameterSymbol.ThisName) { // null il.EmitNullConstant(); } else { throw new NotImplementedException(); // TODO: default parameter } } if (il.EmitCall(this, diagnostic, ILOpCode.Call, method).SpecialType != SpecialType.System_Void) il.EmitOpCode(ILOpCode.Pop); } il.CloseLocalScope(); // /Try il.AdjustStack(1); // Account for exception on the stack. il.OpenLocalScope(ScopeType.Catch, Compilation.CoreTypes.ScriptDiedException.Symbol); { // exitcode = <exception>.ProcessStatus(ctx) il.EmitLocalLoad(ctx_loc); il.EmitCall(this, diagnostic, ILOpCode.Callvirt, Compilation.CoreTypes.ScriptDiedException.Symbol.LookupMember<MethodSymbol>("ProcessStatus")); il.EmitLocalStore(exitcode_loc); } il.CloseLocalScope(); // /Catch il.CloseLocalScope(); // /TryCatch } il.CloseLocalScope(); // /Try il.OpenLocalScope(ScopeType.Finally); { // ctx.Dispose il.EmitLocalLoad(ctx_loc); il.EmitOpCode(ILOpCode.Call, -1); il.EmitToken(methods.Context.Dispose.Symbol, null, diagnostic); } il.CloseLocalScope(); // /Finally il.CloseLocalScope(); // /TryCatch // return ctx.ExitCode il.EmitLocalLoad(exitcode_loc); il.EmitRet(false); }, null, diagnostic, false); SetMethodBody(realmethod, body); // this.ScriptType.EntryPointSymbol = realmethod; }
/// <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); } } } } }
/// <summary> /// Collects methods that has to be overriden and matches with this declaration. /// Missing overrides are reported, needed ghost stubs are synthesized. /// </summary> public void FinalizeMethodTable(Emit.PEModuleBuilder module, DiagnosticBag diagnostics) { // creates ghost stubs for overrides that do not match the signature foreach (var info in this.ResolveOverrides(diagnostics)) { // note: unresolved abstracts already reported by ResolveOverrides // is ghost stub needed? if (ReferenceEquals(info.OverrideCandidate?.ContainingType, this) || // candidate not matching exactly the signature in this type info.ImplementsInterface) // explicitly implement the interface { if (info.HasOverride) { if (info.ImplementsInterface && info.Override != null && info.Override.IsVirtual) { // create explicit override only if the interface method is implemented with a class method that does not implement the interface /* * interface I { foo } // METHOD * class X { foo } // OVERRIDE * class Y : X, I { explicit I.foo override } // GHOST */ if (info.Override.ContainingType.ImplementsInterface(info.Method.ContainingType)) { /* => X implements I */ // explicit method override is not needed continue; } } /* * class A { * TReturn1 foo(A, B); * } * class B : A { * TReturn2 foo(A2, B2); * * // SYNTHESIZED GHOST: * override TReturn foo(A, B){ return (TReturn)foo((A2)A, (B2)B); * } */ // override method with a ghost that calls the override (info.Override ?? info.OverrideCandidate).CreateGhostOverload( this, module, diagnostics, info.Method.ReturnType, info.Method.Parameters, phphidden: true, explicitOverride: info.Method); } else { // synthesize abstract method implementing unresolved interface member if (info.IsUnresolvedAbstract && info.ImplementsInterface && this.IsAbstract && !this.IsInterface) { var method = info.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(method.Parameters.Select(p => SynthesizedParameterSymbol.Create(ghost, p)).ToArray()); module.SynthesizedManager.AddMethod(this, ghost); } } } // setup synthesized methods explicit override as resolved if (info.Override is SynthesizedMethodSymbol sm && sm.ExplicitOverride == null && sm.ContainingType == this) { sm.ExplicitOverride = info.Method; } } // synthesized field accessors: foreach (var srcf in GetMembers().OfType <SourceFieldSymbol>()) { var paccessor = srcf.FieldAccessorProperty; if (paccessor != null) { GenerateFieldAccessorProperty(module, diagnostics, srcf, paccessor); } } }
/// <summary> /// In case the class implements <c>__invoke</c> method, we create special Invoke() method that is compatible with IPhpCallable interface. /// </summary> internal SynthesizedMethodSymbol EnsureInvokeMethod(Emit.PEModuleBuilder module) { if (_lazyInvokeSymbol == null) { if (IsInvokable) { _lazyInvokeSymbol = new SynthesizedMethodSymbol(this, "IPhpCallable.Invoke", false, true, DeclaringCompilation.CoreTypes.PhpValue) { ExplicitOverride = (MethodSymbol)DeclaringCompilation.CoreTypes.IPhpCallable.Symbol.GetMembers("Invoke").Single(), }; _lazyInvokeSymbol.SetParameters( new SpecialParameterSymbol(_lazyInvokeSymbol, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, 0), new SynthesizedParameterSymbol(_lazyInvokeSymbol, ArrayTypeSymbol.CreateSZArray(ContainingAssembly, DeclaringCompilation.CoreTypes.PhpValue.Symbol), 1, RefKind.None, "arguments")); // module.SynthesizedManager.AddMethod(this, _lazyInvokeSymbol); } } return _lazyInvokeSymbol; }
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); }
/// <summary> /// Method that enumerates all script Main functions. /// EnumerateScripts(Action<string, RuntimeMethodHandle> callback) /// </summary> MethodSymbol CreateEnumerateScriptsSymbol() { var compilation = DeclaringCompilation; var action_T2 = compilation.GetWellKnownType(WellKnownType.System_Action_T2); var action_string_method = action_T2.Construct(compilation.CoreTypes.String, compilation.CoreTypes.RuntimeMethodHandle); var method = new SynthesizedMethodSymbol(this, "EnumerateScripts", true, false, compilation.CoreTypes.Void, Accessibility.Public); method.SetParameters(new SynthesizedParameterSymbol(method, action_string_method, 0, RefKind.None, "callback")); // return method; }
/// <summary> /// Method that enumerates all app-wide global constants. /// EnumerateConstants(Action<string, PhpValue, bool> callback) /// </summary> MethodSymbol CreateEnumerateConstantsSymbol() { var compilation = DeclaringCompilation; var action_T3 = compilation.GetWellKnownType(WellKnownType.System_Action_T3); var action_string_value_bool = action_T3.Construct(compilation.CoreTypes.String, compilation.CoreTypes.PhpValue, compilation.CoreTypes.Boolean); var method = new SynthesizedMethodSymbol(this, "EnumerateConstants", true, false, compilation.CoreTypes.Void, Accessibility.Public); method.SetParameters(new SynthesizedParameterSymbol(method, action_string_value_bool, 0, RefKind.None, "callback")); // return method; }
public void EmitInit(Emit.PEModuleBuilder module, Action<Microsoft.CodeAnalysis.CodeGen.ILBuilder> builder) { Debug.Assert(_initMethod == null); var tt = DeclaringCompilation.CoreTypes; // override IStaticInit.Init(Context) _initMethod = new SynthesizedMethodSymbol(this, "Init", false, true, tt.Void, Accessibility.Public); _initMethod.SetParameters(new SynthesizedParameterSymbol(_initMethod, tt.Context, 0, RefKind.None, "ctx")); var body = CodeGen.MethodGenerator.GenerateMethodBody(module, _initMethod, builder, null, DiagnosticBag.GetInstance(), false); module.SetMethodBody(_initMethod, body); }
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); }