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); // } //} } } }
internal override void EmitInit(CodeGenerator cg) { // TODO: ? if (cg.HasUnoptimizedLocals && $this) <locals>["this"] = ... var srcparam = _symbol as SourceParameterSymbol; if (srcparam != null) { var srcplace = new ParamPlace(_symbol); var routine = srcparam.Routine; if (cg.HasUnoptimizedLocals) { Debug.Assert(cg.LocalsPlaceOpt != null); // copy parameter to <locals>[Name] // <locals>[name] = value cg.LocalsPlaceOpt.EmitLoad(cg.Builder); // <locals> cg.EmitIntStringKey(new BoundLiteral(this.Name)); // [key] cg.EmitConvertToPhpValue(srcplace.EmitLoad(cg.Builder), 0); // value cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.SetItemValue_IntStringKey_PhpValue); // _isUnoptimized = true; } else { // TODO: copy parameter by value in case of PhpValue, Array, PhpString // create local variable in case of parameter type is not enough for its use within routine if (_symbol.Type != cg.CoreTypes.PhpValue && _symbol.Type != cg.CoreTypes.PhpAlias) { var tmask = routine.ControlFlowGraph.GetParamTypeMask(srcparam); var clrtype = cg.DeclaringCompilation.GetTypeFromTypeRef(routine, tmask); if (clrtype != _symbol.Type) // Assert: only if clrtype is not covered by _symbol.Type { // TODO: performance warning _lazyLocal = new BoundLocal(new SynthesizedLocalSymbol(routine, srcparam.Name, clrtype)); _lazyLocal.EmitInit(cg); var localplace = _lazyLocal.Place(cg.Builder); // <local> = <param> localplace.EmitStorePrepare(cg.Builder); cg.EmitConvert(srcplace.EmitLoad(cg.Builder), 0, clrtype); localplace.EmitStore(cg.Builder); } } } } }
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); } } }
internal override IPlace Place(ILBuilder il) { if (_lazyplace != null) { return(_lazyplace); } if (_isUnoptimized) { return(null); } IPlace place = new ParamPlace(_symbol); if (this.VariableKind == VariableKind.ThisParameter) { place = new ReadOnlyPlace(place); } return(place); }
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> /// 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; }
internal override void EmitInit(CodeGenerator cg) { var srcparam = _symbol as SourceParameterSymbol; if (srcparam == null) { // an implicit parameter return; } // TODO: check callable, iterable // TODO: ? if (cg.HasUnoptimizedLocals && $this) <locals>["this"] = ... var srcplace = new ParamPlace(_symbol); var routine = srcparam.Routine; if (cg.HasUnoptimizedLocals) { Debug.Assert(cg.LocalsPlaceOpt != null); // copy parameter to <locals>[Name] // <locals>[name] = value cg.LocalsPlaceOpt.EmitLoad(cg.Builder); // <locals> cg.EmitIntStringKey(new BoundLiteral(this.Name)); // [key] if (srcparam.Syntax.PassedByRef) { var srcpt = srcplace.EmitLoad(cg.Builder); // PhpAlias Debug.Assert(srcpt == cg.CoreTypes.PhpAlias); cg.EmitConvert(srcpt, 0, cg.CoreTypes.PhpAlias); cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.SetItemAlias_IntStringKey_PhpAlias); } else { if (_symbol.IsParams) { // (PhhpValue)new PhpArray( params ) cg.EmitConvertToPhpValue(cg.ArrayToPhpArray(srcplace, true), 0); } else { if (_symbol.Type == cg.CoreTypes.PhpValue) { // <param>.GetValue() srcplace.EmitLoadAddress(cg.Builder); cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.GetValue); } else { // (PhpValue)<param> cg.EmitConvertToPhpValue(srcplace.EmitLoad(cg.Builder), 0); // PhpValue } // copy <value> if (cg.IsCopiable(_symbol.Type)) { cg.EmitDeepCopy(cg.CoreTypes.PhpValue); } } cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.Add_IntStringKey_PhpValue); } // _isUnoptimized = true; } else { // create local variable in case of parameter type is not enough for its use within routine if (_symbol.Type != cg.CoreTypes.PhpValue && _symbol.Type != cg.CoreTypes.PhpAlias) { var tmask = routine.ControlFlowGraph.GetLocalTypeMask(srcparam.Name); var clrtype = cg.DeclaringCompilation.GetTypeFromTypeRef(routine, tmask); if (clrtype != _symbol.Type) // Assert: only if clrtype is not covered by _symbol.Type { // TODO: performance warning _lazyLocal = new BoundLocal(new SynthesizedLocalSymbol(routine, srcparam.Name, clrtype)); _lazyLocal.EmitInit(cg); var localplace = _lazyLocal.Place(cg.Builder); localplace.EmitStorePrepare(cg.Builder); if (_symbol.IsParams) { Debug.Assert(_symbol.Type.IsSZArray()); Debug.Assert(clrtype == cg.CoreTypes.PhpArray); // <local> = new PhpArray(){ ... } cg.ArrayToPhpArray(srcplace, true); } else { // <local> = <param> cg.EmitConvert(srcplace.EmitLoad(cg.Builder), 0, clrtype); } localplace.EmitStore(cg.Builder); } } else { if (_symbol.Type == cg.CoreTypes.PhpValue) { srcplace.EmitStorePrepare(cg.Builder); // dereference & copy // <param> = <param>.GetValue().DeepCopy() srcplace.EmitLoadAddress(cg.Builder); cg.EmitDeepCopy(cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.GetValue)); srcplace.EmitStore(cg.Builder); } else if (cg.IsCopiable(_symbol.Type)) { srcplace.EmitStorePrepare(cg.Builder); // copy // <param> = DeepCopy(<param>) cg.EmitDeepCopy(srcplace.EmitLoad(cg.Builder)); srcplace.EmitStore(cg.Builder); } } } }
/// <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; }