/// <summary> /// Creates ghost stub that calls method. /// </summary> public static void CreateGhostOverload(this MethodSymbol method, NamedTypeSymbol containingtype, PEModuleBuilder module, DiagnosticBag diagnostic, TypeSymbol ghostreturn, IEnumerable <ParameterSymbol> ghostparams, MethodSymbol explicitOverride = null) { var ghost = new SynthesizedMethodSymbol( containingtype, method.Name, method.IsStatic, explicitOverride != null, ghostreturn, method.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(containingtype, ghost); // generate method body GenerateGhostBody(module, diagnostic, method, ghost); }
/// <summary> /// Creates ghost stub that calls method. /// </summary> public static void CreateGhostOverload(this MethodSymbol method, NamedTypeSymbol containingtype, PEModuleBuilder module, DiagnosticBag diagnostic, TypeSymbol ghostreturn, IEnumerable <ParameterSymbol> ghostparams, MethodSymbol explicitOverride = null) { string prefix = (explicitOverride != null && explicitOverride.ContainingType.IsInterface) ? (explicitOverride.ContainingType.GetFullName() + ".") // explicit interface override : null; var ghost = new SynthesizedMethodSymbol( containingtype, prefix + method.Name, method.IsStatic, explicitOverride != null, ghostreturn, method.DeclaredAccessibility) { ExplicitOverride = explicitOverride, ForwardedCall = method, }; ghost.SetParameters(ghostparams.Select(p => SynthesizedParameterSymbol.Create(ghost, p)).ToArray()); // save method symbol to module module.SynthesizedManager.AddMethod(containingtype, ghost); // generate method body GenerateGhostBody(module, diagnostic, method, ghost); }
/// <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]); // this.EmitBootstrap(il); // 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); var ex_loc = il.LocalSlotManager.AllocateSlot(types.Exception.Symbol, LocalSlotConstraints.None); var onUnhandledException_method = types.Context.Symbol.LookupMember <MethodSymbol>("OnUnhandledException"); if (_compilation.Options.OutputKind == OutputKind.ConsoleApplication) { // CreateConsole(string mainscript, params string[] args) var create_method = types.Context.Symbol.LookupMember <MethodSymbol>("CreateConsole", m => { return (m.ParameterCount == 2 && m.Parameters[0].Type == types.String && // string mainscript m.Parameters[1].Type == args_place.Type); // params string[] args }); Debug.Assert(create_method != null); il.EmitStringConstant(EntryPointScriptName(method)); // mainscript args_place.EmitLoad(il); // args il.EmitOpCode(ILOpCode.Call, +2); il.EmitToken(create_method, null, diagnostic); } else { // CreateEmpty(args) MethodSymbol create_method = types.Context.Symbol.LookupMember <MethodSymbol>("CreateEmpty"); Debug.Assert(create_method != null); Debug.Assert(create_method.ParameterCount == 1); Debug.Assert(create_method.Parameters[0].Type == args_place.Type); args_place.EmitLoad(il); // args il.EmitOpCode(ILOpCode.Call, +1); il.EmitToken(create_method, null, diagnostic); } il.EmitLocalStore(ctx_loc); // Template: // try { Main(...); } catch (ScriptDiedException) { } catch (Exception) { ... } 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) { switch (p.Name) { case SpecialParameterSymbol.ContextName: // <ctx> il.EmitLocalLoad(ctx_loc); break; case SpecialParameterSymbol.LocalsName: // <ctx>.Globals il.EmitLocalLoad(ctx_loc); il.EmitCall(this, diagnostic, ILOpCode.Call, methods.Context.Globals.Getter) .Expect(p.Type); break; case SpecialParameterSymbol.ThisName: // null il.EmitNullConstant(); break; case SpecialParameterSymbol.SelfName: // default(RuntimeTypeHandle) var runtimetypehandle_loc = il.LocalSlotManager.AllocateSlot(types.RuntimeTypeHandle.Symbol, LocalSlotConstraints.None); il.EmitValueDefault(this, diagnostic, runtimetypehandle_loc); il.LocalSlotManager.FreeSlot(runtimetypehandle_loc); break; default: throw new ArgumentException(p.Name); } } 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, types.ScriptDiedException.Symbol); { // exitcode = <exception>.ProcessStatus(ctx) il.EmitLocalLoad(ctx_loc); il.EmitCall(this, diagnostic, ILOpCode.Callvirt, types.ScriptDiedException.Symbol.LookupMember <MethodSymbol>("ProcessStatus")); il.EmitLocalStore(exitcode_loc); } il.CloseLocalScope(); // /Catch if (onUnhandledException_method != null) // only if runtime defines the method (backward compat.) { il.OpenLocalScope(ScopeType.Catch, types.Exception.Symbol); { // <ex_loc> = <stack> il.EmitLocalStore(ex_loc); // <ctx_loc>.OnUnhandledException( <ex_loc> ) : bool il.EmitLocalLoad(ctx_loc); il.EmitLocalLoad(ex_loc); il.EmitCall(this, diagnostic, ILOpCode.Callvirt, onUnhandledException_method) .Expect(SpecialType.System_Boolean); // if ( !<bool> ) // { var lbl_end = new object(); il.EmitBranch(ILOpCode.Brtrue, lbl_end); // rethrow <ex_loc>; il.EmitLocalLoad(ex_loc); il.EmitThrow(true); // } il.MarkLabel(lbl_end); } 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> /// Generates ghost method body that calls <c>this</c> method. /// </summary> static void GenerateGhostBody(PEModuleBuilder module, DiagnosticBag diagnostic, MethodSymbol method, SynthesizedMethodSymbol ghost) { var containingtype = ghost.ContainingType; var body = MethodGenerator.GenerateMethodBody(module, ghost, (il) => { // $this var thisPlace = ghost.HasThis ? new ArgPlace(containingtype, 0) : null; // Context var ctxPlace = thisPlace != null && ghost.ContainingType is SourceTypeSymbol sourcetype ? new FieldPlace(thisPlace, sourcetype.ContextStore) : (IPlace) new ArgPlace(module.Compilation.CoreTypes.Context, 0); var cg = new CodeGenerator(il, module, diagnostic, module.Compilation.Options.OptimizationLevel, false, containingtype, ctxPlace, thisPlace); // return (T){routine}(p0, ..., pN); cg.EmitConvert(cg.EmitForwardCall(method, ghost), 0, ghost.ReturnType); cg.EmitRet(ghost.ReturnType); }, null, diagnostic, false); module.SetMethodBody(ghost, body); }
/// <summary> /// Generates ghost method body that calls <c>this</c> method. /// </summary> static void GenerateGhostBody(PEModuleBuilder module, DiagnosticBag diagnostic, MethodSymbol method, SynthesizedMethodSymbol ghost) { var containingtype = ghost.ContainingType; var body = MethodGenerator.GenerateMethodBody(module, ghost, (il) => { // $this var thisPlace = ghost.HasThis ? new ArgPlace(containingtype, 0) : null; // Context var ctxPlace = thisPlace != null && ghost.ContainingType is SourceTypeSymbol sourcetype ? (sourcetype.ContextStore != null ? new FieldPlace(thisPlace, sourcetype.ContextStore, module) : null) : (IPlace) new ArgPlace(module.Compilation.CoreTypes.Context, 0); // .callvirt bool callvirt = ghost.ExplicitOverride != null && ghost.ExplicitOverride.ContainingType.IsInterface; // implementing interface, otherwise we should be able to call specific method impl. non-virtually via ghost var cg = new CodeGenerator(il, module, diagnostic, module.Compilation.Options.OptimizationLevel, false, containingtype, ctxPlace, thisPlace) { DebugRoutine = ghost, }; // return (T){routine}(p0, ..., pN); cg.EmitConvert(cg.EmitForwardCall(method, ghost, callvirt: callvirt), 0, ghost.ReturnType); cg.EmitRet(ghost.ReturnType); }, null, diagnostic, false); module.SetMethodBody(ghost, body); }
/// <summary> /// Emit body of enumeration of app-wide global constants. /// </summary> internal void CreateEnumerateConstantsSymbol(DiagnosticBag diagnostic) { var method = this.ScriptType.EnumerateConstantsSymbol; var consts = this.Compilation.GlobalSemantics.GetExportedConstants(); // void (Action<string, RuntimeMethodHandle> callback) var body = MethodGenerator.GenerateMethodBody(this, method, (il) => { var cg = new CodeGenerator(il, this, diagnostic, this.Compilation.Options.OptimizationLevel, false, this.ScriptType, null, null); // interface IConstantsComposition var constantscomposition = method.Parameters[0].Type; Debug.Assert(constantscomposition.Name == "IConstantsComposition"); // Define(string, ...) var define_xxx = constantscomposition .GetMembers("Define") .OfType <MethodSymbol>() .Where(m => m.ParameterCount == 2) .ToArray(); var define_string = define_xxx.Single(m => m.Parameters[1].Type.SpecialType == SpecialType.System_String); var define_long = define_xxx.Single(m => m.Parameters[1].Type.SpecialType == SpecialType.System_Int64); var define_double = define_xxx.Single(m => m.Parameters[1].Type.SpecialType == SpecialType.System_Double); var define_func = define_xxx.Single(m => m.Parameters[1].Type.Name == "Func"); // Func<PhpValue> var define_value = define_xxx.Single(m => m.Parameters[1].Type == cg.CoreTypes.PhpValue); foreach (var c in consts.OfType <Symbol>()) { // composer.Define(c.Name, c.Value) il.EmitLoadArgumentOpcode(0); // string : name il.EmitStringConstant(c.MetadataName); if (c is FieldSymbol fld) { // PhpValue : value TypeSymbol consttype; var constvalue = fld.GetConstantValue(false); if (constvalue != null) { consttype = cg.EmitLoadConstant(constvalue.Value, cg.CoreTypes.PhpValue); } else { consttype = new FieldPlace(null, fld, this).EmitLoad(il); } // Define(...) if (consttype.SpecialType == SpecialType.System_Int32) { // i4 -> i8 il.EmitOpCode(ILOpCode.Conv_i8); consttype = cg.CoreTypes.Long; } MethodSymbol define_method = null; if (consttype.SpecialType == SpecialType.System_String) { define_method = define_string; } else if (consttype.SpecialType == SpecialType.System_Int64) { define_method = define_long; } else if (consttype.SpecialType == SpecialType.System_Double) { define_method = define_double; } else { cg.EmitConvertToPhpValue(consttype, 0); define_method = define_value; } il.EmitCall(this, diagnostic, ILOpCode.Callvirt, define_method); } else if (c is PropertySymbol prop) { MethodSymbol getter_func; // Func<PhpValue> if (prop.Type == cg.CoreTypes.PhpValue) { getter_func = prop.GetMethod; } else { // static PhpValue get_XXX => get_prop(); getter_func = new SynthesizedMethodSymbol(ScriptType, "get_" + prop.Name, true, false, cg.CoreTypes.PhpValue, Accessibility.Internal); SynthesizedManager.AddMethod(ScriptType, getter_func); SetMethodBody(getter_func, MethodGenerator.GenerateMethodBody(this, getter_func, _il => { var _cg = new CodeGenerator(_il, this, diagnostic, this.Compilation.Options.OptimizationLevel, false, ScriptType, null, null); _cg.EmitRet(_cg.EmitConvertToPhpValue(_cg.EmitForwardCall(prop.GetMethod, getter_func, callvirt: false), 0)); }, null, diagnostic, false)); } // new Func<PhpValue>(object @object = null, IntPtr method = getter_func) cg.Builder.EmitNullConstant(); // null cg.EmitOpCode(ILOpCode.Ldftn); // method cg.EmitSymbolToken(getter_func, null); cg.EmitCall(ILOpCode.Newobj, Compilation.GetWellKnownType(WellKnownType.System_Func_T).Construct(cg.CoreTypes.PhpValue).InstanceConstructors[0]); // il.EmitCall(this, diagnostic, ILOpCode.Callvirt, define_func); } else { throw ExceptionUtilities.UnexpectedValue(c); } } // il.EmitRet(true); }, null, diagnostic, false); SetMethodBody(method, 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 args_place = new ParamPlace(realmethod.Parameters[0]); // AddScriptReference<Script>() var AddScriptReferenceMethod = (MethodSymbol)this.Compilation.CoreMethods.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, this.Compilation.CoreMethods.Context.get_Globals) .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(this.Compilation.CoreMethods.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; }