protected override void DoEmit(EmitContext ec) { Label label_init = ec.DefineLabel(); ec.EmitThis(); ec.Emit(OpCodes.Ldflda, host.PC.Spec); ec.EmitInt((int)State.Start); ec.EmitInt((int)State.Uninitialized); var m = ec.Module.PredefinedMembers.InterlockedCompareExchange.Resolve(loc); if (m != null) { ec.Emit(OpCodes.Call, m); } ec.EmitInt((int)State.Uninitialized); ec.Emit(OpCodes.Bne_Un_S, label_init); ec.EmitThis(); ec.Emit(OpCodes.Ret); ec.MarkLabel(label_init); new_storey.Emit(ec); ec.Emit(OpCodes.Ret); }
// // Called back from YieldStatement // public virtual void InjectYield(EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point) { // // Guard against being disposed meantime // Label disposed = ec.DefineLabel(); var iterator = storey as IteratorStorey; if (iterator != null) { ec.EmitThis(); ec.Emit(OpCodes.Ldfld, iterator.DisposingField.Spec); ec.Emit(OpCodes.Brtrue_S, disposed); } // // store resume program-counter // ec.EmitThis(); ec.EmitInt(resume_pc); ec.Emit(OpCodes.Stfld, storey.PC.Spec); if (iterator != null) { ec.MarkLabel(disposed); } // mark finally blocks as disabled if (unwind_protect && skip_finally != null) { ec.EmitInt(1); ec.Emit(OpCodes.Stloc, skip_finally); } }
/// <summary> /// C# allows this kind of scenarios: /// interface I { void M (); } /// class X { public void M (); } /// class Y : X, I { } /// /// For that case, we create an explicit implementation function /// I.M in Y. /// </summary> void DefineProxy(TypeSpec iface, MethodSpec base_method, MethodSpec iface_method) { // TODO: Handle nested iface names string proxy_name; var ns = iface.MemberDefinition.Namespace; if (string.IsNullOrEmpty(ns)) { proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name; } else { proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name; } var param = iface_method.Parameters; MethodBuilder proxy = container.TypeBuilder.DefineMethod( proxy_name, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.CheckAccessOnOverride | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.Standard | CallingConventions.HasThis, base_method.ReturnType.GetMetaInfo(), param.GetMetaInfo()); if (iface_method.IsGeneric) { var gnames = iface_method.GenericDefinition.TypeParameters.Select(l => l.Name).ToArray(); proxy.DefineGenericParameters(gnames); } for (int i = 0; i < param.Count; i++) { string name = param.FixedParameters [i].Name; ParameterAttributes attr = ParametersCompiled.GetParameterAttribute(param.FixedParameters [i].ModFlags); proxy.DefineParameter(i + 1, attr, name); } int top = param.Count; var ec = new EmitContext(new ProxyMethodContext(container), proxy.GetILGenerator(), null, null); ec.EmitThis(); // TODO: GetAllParametersArguments for (int i = 0; i < top; i++) { ec.EmitArgumentLoad(i); } ec.Emit(OpCodes.Call, base_method); ec.Emit(OpCodes.Ret); container.TypeBuilder.DefineMethodOverride(proxy, (MethodInfo)iface_method.GetMetaInfo()); }
void EmitMoveNext_NoResumePoints(EmitContext ec) { ec.EmitThis(); ec.Emit(OpCodes.Ldfld, storey.PC.Spec); ec.EmitThis(); ec.EmitInt((int)IteratorStorey.State.After); ec.Emit(OpCodes.Stfld, storey.PC.Spec); // We only care if the PC is zero (start executing) or non-zero (don't do anything) ec.Emit(OpCodes.Brtrue, move_next_error); BodyEnd = ec.DefineLabel(); var async_init = this as AsyncInitializer; if (async_init != null) { ec.BeginExceptionBlock(); } block.EmitEmbedded(ec); if (async_init != null) { async_init.EmitCatchBlock(ec); } ec.MarkLabel(BodyEnd); EmitMoveNextEpilogue(ec); ec.MarkLabel(move_next_error); if (ReturnType.Kind != MemberKind.Void) { ec.EmitInt(0); ec.Emit(OpCodes.Ret); } ec.MarkLabel(move_next_ok); }
public void EmitCatchBlock(EmitContext ec) { var catch_value = LocalVariable.CreateCompilerGenerated(ec.Module.Compiler.BuiltinTypes.Exception, block, Location); ec.BeginCatchBlock(catch_value.Type); catch_value.EmitAssign(ec); ec.EmitThis(); ec.EmitInt((int)IteratorStorey.State.After); ec.Emit(OpCodes.Stfld, storey.PC.Spec); ((AsyncTaskStorey)Storey).EmitSetException(ec, new LocalVariableReference(catch_value, Location)); ec.Emit(OpCodes.Leave, move_next_ok); ec.EndExceptionBlock(); }
void EmitMoveNext(EmitContext ec) { move_next_ok = ec.DefineLabel(); move_next_error = ec.DefineLabel(); if (resume_points == null) { EmitMoveNext_NoResumePoints(ec); return; } current_pc = ec.GetTemporaryLocal(ec.BuiltinTypes.UInt); ec.EmitThis(); ec.Emit(OpCodes.Ldfld, storey.PC.Spec); ec.Emit(OpCodes.Stloc, current_pc); // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit ec.EmitThis(); ec.EmitInt((int)IteratorStorey.State.After); ec.Emit(OpCodes.Stfld, storey.PC.Spec); Label[] labels = new Label[1 + resume_points.Count]; labels[0] = ec.DefineLabel(); bool need_skip_finally = false; for (int i = 0; i < resume_points.Count; ++i) { ResumableStatement s = resume_points[i]; need_skip_finally |= s is ExceptionStatement; labels[i + 1] = s.PrepareForEmit(ec); } if (need_skip_finally) { skip_finally = ec.GetTemporaryLocal(ec.BuiltinTypes.Bool); ec.EmitInt(0); ec.Emit(OpCodes.Stloc, skip_finally); } var async_init = this as AsyncInitializer; if (async_init != null) { ec.BeginExceptionBlock(); } ec.Emit(OpCodes.Ldloc, current_pc); ec.Emit(OpCodes.Switch, labels); ec.Emit(async_init != null ? OpCodes.Leave : OpCodes.Br, move_next_error); ec.MarkLabel(labels[0]); BodyEnd = ec.DefineLabel(); block.EmitEmbedded(ec); ec.MarkLabel(BodyEnd); if (async_init != null) { async_init.EmitCatchBlock(ec); } ec.Mark(Block.Original.EndLocation); ec.EmitThis(); ec.EmitInt((int)IteratorStorey.State.After); ec.Emit(OpCodes.Stfld, storey.PC.Spec); EmitMoveNextEpilogue(ec); ec.MarkLabel(move_next_error); if (ReturnType.Kind != MemberKind.Void) { ec.EmitInt(0); ec.Emit(OpCodes.Ret); } ec.MarkLabel(move_next_ok); if (ReturnType.Kind != MemberKind.Void) { ec.EmitInt(1); ec.Emit(OpCodes.Ret); } }
public void EmitDispose(EmitContext ec) { if (resume_points == null) { return; } Label end = ec.DefineLabel(); Label[] labels = null; for (int i = 0; i < resume_points.Count; ++i) { ResumableStatement s = resume_points[i]; Label ret = s.PrepareForDispose(ec, end); if (ret.Equals(end) && labels == null) { continue; } if (labels == null) { labels = new Label[resume_points.Count + 1]; for (int j = 0; j <= i; ++j) { labels[j] = end; } } labels[i + 1] = ret; } if (labels != null) { current_pc = ec.GetTemporaryLocal(ec.BuiltinTypes.UInt); ec.EmitThis(); ec.Emit(OpCodes.Ldfld, storey.PC.Spec); ec.Emit(OpCodes.Stloc, current_pc); } ec.EmitThis(); ec.EmitInt(1); ec.Emit(OpCodes.Stfld, ((IteratorStorey)storey).DisposingField.Spec); ec.EmitThis(); ec.EmitInt((int)IteratorStorey.State.After); ec.Emit(OpCodes.Stfld, storey.PC.Spec); if (labels != null) { //SymbolWriter.StartIteratorDispatcher (ec.ig); ec.Emit(OpCodes.Ldloc, current_pc); ec.Emit(OpCodes.Switch, labels); //SymbolWriter.EndIteratorDispatcher (ec.ig); foreach (ResumableStatement s in resume_points) { s.EmitForDispose(ec, current_pc, end, true); } } ec.MarkLabel(end); }
public override void Emit (EmitContext ec) { // // Use same anonymous method implementation for scenarios where same // code is used from multiple blocks, e.g. field initializers // if (method == null) { // // Delay an anonymous method definition to avoid emitting unused code // for unreachable blocks or expression trees // method = DoCreateMethodHost (ec); method.Define (); method.PrepareEmit (); } bool is_static = (method.ModFlags & Modifiers.STATIC) != 0; if (is_static && am_cache == null && !ec.IsStaticConstructor) { // // Creates a field cache to store delegate instance if it's not generic // if (!method.MemberName.IsGeneric) { var parent = method.Parent.PartialContainer; int id = parent.AnonymousMethodsCounter++; var cache_type = storey != null && storey.Mutator != null ? storey.Mutator.Mutate (type) : type; am_cache = new Field (parent, new TypeExpression (cache_type, loc), Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "am$cache", id), loc), null); am_cache.Define (); parent.AddField (am_cache); } else { // TODO: Implement caching of generated generic static methods // // Idea: // // Some extra class is needed to capture variable generic type // arguments. Maybe we could re-use anonymous types, with a unique // anonymous method id, but they are quite heavy. // // Consider : "() => typeof(T);" // // We need something like // static class Wrap<Tn, Tm, DelegateType> { // public static DelegateType cache; // } // // We then specialize local variable to capture all generic parameters // and delegate type, e.g. "Wrap<Ta, Tb, DelegateTypeInst> cache;" // } } Label l_initialized = ec.DefineLabel (); if (am_cache != null) { ec.Emit (OpCodes.Ldsfld, am_cache.Spec); ec.Emit (OpCodes.Brtrue_S, l_initialized); } // // Load method delegate implementation // if (is_static) { ec.EmitNull (); } else if (storey != null) { Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext)); if (e != null) { e.Emit (ec); } } else { ec.EmitThis (); // // Special case for value type storey where this is not lifted but // droped off to parent class // if (ec.CurrentAnonymousMethod != null && ec.AsyncTaskStorey != null) ec.Emit (OpCodes.Ldfld, ec.AsyncTaskStorey.HoistedThis.Field.Spec); } var delegate_method = method.Spec; if (storey != null && storey.MemberName.IsGeneric) { TypeSpec t = storey.Instance.Type; // // Mutate anonymous method instance type if we are in nested // hoisted generic anonymous method storey // if (ec.IsAnonymousStoreyMutateRequired) { t = storey.Mutator.Mutate (t); } ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ())); } else { if (delegate_method.IsGeneric) { TypeParameterSpec[] tparams; var sm = ec.CurrentAnonymousMethod == null ? null : ec.CurrentAnonymousMethod.Storey as StateMachine; if (sm != null && sm.OriginalTypeParameters != null) { tparams = sm.CurrentTypeParameters.Types; } else { tparams = method.TypeParameters; } delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, tparams); } ec.Emit (OpCodes.Ldftn, delegate_method); } var constructor_method = Delegate.GetConstructor (type); ec.Emit (OpCodes.Newobj, constructor_method); if (am_cache != null) { ec.Emit (OpCodes.Stsfld, am_cache.Spec); ec.MarkLabel (l_initialized); ec.Emit (OpCodes.Ldsfld, am_cache.Spec); } }
/// <summary> /// C# allows this kind of scenarios: /// interface I { void M (); } /// class X { public void M (); } /// class Y : X, I { } /// /// For that case, we create an explicit implementation function /// I.M in Y. /// </summary> void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method) { // TODO: Handle nested iface names string proxy_name; var ns = iface.MemberDefinition.Namespace; if (string.IsNullOrEmpty (ns)) proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name; else proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name; var param = iface_method.Parameters; MethodBuilder proxy = container.TypeBuilder.DefineMethod ( proxy_name, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.CheckAccessOnOverride | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.Standard | CallingConventions.HasThis, base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ()); if (iface_method.IsGeneric) { var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray (); proxy.DefineGenericParameters (gnames); } for (int i = 0; i < param.Count; i++) { string name = param.FixedParameters [i].Name; ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags); proxy.DefineParameter (i + 1, attr, name); } int top = param.Count; var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null, null); ec.EmitThis (); // TODO: GetAllParametersArguments for (int i = 0; i < top; i++) ec.EmitArgumentLoad (i); ec.Emit (OpCodes.Call, base_method); ec.Emit (OpCodes.Ret); container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ()); }