X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fasync.cs;h=6ae8027c0ab5eab7dfa1b203940eeb311d2b7d31;hb=f574f7b447e29c6f083fcad4e6dc5f89d3cb4b4d;hp=6c3cc72563ba2fdcdac0df7ead2cccf00d066008;hpb=cf196a58f7d528ee18c402ac62231ffe73bb4547;p=mono.git diff --git a/mcs/mcs/async.cs b/mcs/mcs/async.cs index 6c3cc72563b..6ae8027c0ab 100644 --- a/mcs/mcs/async.cs +++ b/mcs/mcs/async.cs @@ -1,4 +1,4 @@ -// +// // async.cs: Asynchronous functions // // Author: @@ -7,6 +7,7 @@ // Dual licensed under the terms of the MIT X11 or GNU GPL // // Copyright 2011 Novell, Inc. +// Copyright 2011 Xamarin Inc. // using System; @@ -22,7 +23,7 @@ using System.Reflection.Emit; namespace Mono.CSharp { - class Await : ExpressionStatement + public class Await : ExpressionStatement { Expression expr; AwaitStatement stmt; @@ -33,6 +34,12 @@ namespace Mono.CSharp this.loc = loc; } + public Expression Expr { + get { + return expr; + } + } + protected override void CloneTo (CloneContext clonectx, Expression target) { var t = (Await) target; @@ -52,16 +59,6 @@ namespace Mono.CSharp protected override Expression DoResolve (ResolveContext rc) { - if (rc.HasSet (ResolveContext.Options.FinallyScope)) { - rc.Report.Error (1984, loc, - "The `await' operator cannot be used in the body of a finally clause"); - } - - if (rc.HasSet (ResolveContext.Options.CatchScope)) { - rc.Report.Error (1985, loc, - "The `await' operator cannot be used in the body of a catch clause"); - } - if (rc.HasSet (ResolveContext.Options.LockScope)) { rc.Report.Error (1996, loc, "The `await' operator cannot be used in the body of a lock statement"); @@ -73,18 +70,12 @@ namespace Mono.CSharp } if (rc.IsUnsafe) { - // TODO: New error code - rc.Report.Error (-1900, loc, + rc.Report.Error (4004, loc, "The `await' operator cannot be used in an unsafe context"); } var bc = (BlockContext) rc; - if (!bc.CurrentBlock.ParametersBlock.IsAsync) { - // TODO: Should check for existence of await type but - // what to do with it - } - stmt = new AwaitStatement (expr, loc); if (!stmt.Resolve (bc)) return null; @@ -117,6 +108,11 @@ namespace Mono.CSharp { stmt.EmitStatement (ec); } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } } class AwaitStatement : YieldStatement @@ -130,12 +126,12 @@ namespace Mono.CSharp protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name) { - Error_WrongGetAwaiter (rc, loc, type); + Error_OperatorCannotBeApplied (rc, type); } protected override void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type) { - rc.Report.Error (1991, loc, "Cannot await `{0}' expression", type.GetSignatureForError ()); + rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ()); } } @@ -145,6 +141,7 @@ namespace Mono.CSharp : base (null, arguments) { mg = mge; + type = mg.BestCandidateReturnType; } public override Expression EmitToField (EmitContext ec) @@ -154,10 +151,11 @@ namespace Mono.CSharp } Field awaiter; - PropertyExpr is_completed; + PropertySpec is_completed; MethodSpec on_completed; MethodSpec get_result; TypeSpec type; + TypeSpec result_type; public AwaitStatement (Expression expr, Location loc) : base (expr, loc) @@ -166,6 +164,12 @@ namespace Mono.CSharp #region Properties + bool IsDynamic { + get { + return is_completed == null; + } + } + public TypeSpec Type { get { return type; @@ -174,7 +178,7 @@ namespace Mono.CSharp public TypeSpec ResultType { get { - return get_result.ReturnType; + return result_type; } } @@ -185,7 +189,7 @@ namespace Mono.CSharp GetResultExpression (ec).Emit (ec); } - public Invocation GetResultExpression (EmitContext ec) + public Expression GetResultExpression (EmitContext ec) { var fe_awaiter = new FieldExpr (awaiter, loc); fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); @@ -193,10 +197,15 @@ namespace Mono.CSharp // // result = awaiter.GetResult (); // - var mg_result = MethodGroupExpr.CreatePredefined (get_result, fe_awaiter.Type, loc); - mg_result.InstanceExpression = fe_awaiter; + if (IsDynamic) { + var rc = new ResolveContext (ec.MemberContext); + return new Invocation (new MemberAccess (fe_awaiter, "GetResult"), new Arguments (0)).Resolve (rc); + } else { + var mg_result = MethodGroupExpr.CreatePredefined (get_result, fe_awaiter.Type, loc); + mg_result.InstanceExpression = fe_awaiter; - return new GetResultInvocation (mg_result, new Arguments (0)); + return new GetResultInvocation (mg_result, new Arguments (0)); + } } public void EmitPrologue (EmitContext ec) @@ -211,83 +220,60 @@ namespace Mono.CSharp Label skip_continuation = ec.DefineLabel (); - is_completed.InstanceExpression = fe_awaiter; - is_completed.EmitBranchable (ec, skip_continuation, true); + Expression completed_expr; + if (IsDynamic) { + var rc = new ResolveContext (ec.MemberContext); + + Arguments dargs = new Arguments (1); + dargs.Add (new Argument (fe_awaiter)); + completed_expr = new DynamicMemberBinder ("IsCompleted", dargs, loc).Resolve (rc); + } else { + var pe = PropertyExpr.CreatePredefined (is_completed, loc); + pe.InstanceExpression = fe_awaiter; + completed_expr = pe; + } + + completed_expr.EmitBranchable (ec, skip_continuation, true); base.DoEmit (ec); - FieldSpec[] stack_fields = null; - TypeSpec[] stack = null; // - // Here is the clever bit. We know that await statement has to yield the control - // back but it can appear inside almost any expression. This means the stack can - // contain any depth of values and same values have to be present when the continuation - // handles control back. + // The stack has to be empty before calling await continuation. We handle this + // by lifting values which would be left on stack into class fields. The process + // is quite complicated and quite hard to test because any expression can possibly + // leave a value on the stack. // - // For example: await a + await b + // Following assert fails when some of expression called before is missing EmitToField + // or parent expression fails to find await in children expressions // - // In this case we fabricate a static stack forwarding method which moves the values - // from the stack to async storey fields. On re-entry point we restore exactly same - // stack using these fields. - // - // We fabricate a static method because we don't want to touch original stack and - // the instance method would require `this' as the first stack value on the stack - // - if (ec.StackHeight > 0) { -#if DEBUG - throw new InternalErrorException ("Await yield with non-empty stack"); -#else - var async_storey = (AsyncTaskStorey) machine_initializer.Storey; - - stack = ec.GetStackTypes (); - bool explicit_this; - var method = async_storey.GetStackForwarder (stack, out stack_fields, out explicit_this); - if (explicit_this) - ec.EmitThis (); - - ec.Emit (OpCodes.Call, method); -#endif - } + ec.AssertEmptyStack (); - var mg_completed = MethodGroupExpr.CreatePredefined (on_completed, fe_awaiter.Type, loc); - mg_completed.InstanceExpression = fe_awaiter; + var storey = (AsyncTaskStorey) machine_initializer.Storey; + var cont_field = storey.EmitContinuationInitialization (ec); var args = new Arguments (1); - var storey = (AsyncTaskStorey) machine_initializer.Storey; - var fe_cont = new FieldExpr (storey.Continuation, loc); - fe_cont.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + args.Add (new Argument (cont_field)); - args.Add (new Argument (fe_cont)); + if (IsDynamic) { + var rc = new ResolveContext (ec.MemberContext); + var mg_expr = new Invocation (new MemberAccess (fe_awaiter, "OnCompleted"), args).Resolve (rc); - // - // awaiter.OnCompleted (continuation); - // - mg_completed.EmitCall (ec, args); + ExpressionStatement es = (ExpressionStatement) mg_expr; + es.EmitStatement (ec); + } else { + var mg_completed = MethodGroupExpr.CreatePredefined (on_completed, fe_awaiter.Type, loc); + mg_completed.InstanceExpression = fe_awaiter; + + // + // awaiter.OnCompleted (continuation); + // + mg_completed.EmitCall (ec, args); + } // Return ok machine_initializer.EmitLeave (ec, unwind_protect); ec.MarkLabel (resume_point); - - if (stack_fields != null) { - for (int i = 0; i < stack_fields.Length; ++i) { - ec.EmitThis (); - - var field = stack_fields[i]; - - // - // We don't store `this' because it can be easily re-created - // - if (field == null) - continue; - - if (stack[i] is ReferenceContainer) - ec.Emit (OpCodes.Ldflda, field); - else - ec.Emit (OpCodes.Ldfld, field); - } - } - ec.MarkLabel (skip_continuation); } @@ -306,16 +292,9 @@ namespace Mono.CSharp } } - static void Error_WrongGetAwaiter (ResolveContext rc, Location loc, TypeSpec type) - { - rc.Report.Error (1986, loc, - "The `await' operand type `{0}' must have suitable GetAwaiter method", - type.GetSignatureForError ()); - } - void Error_WrongAwaiterPattern (ResolveContext rc, TypeSpec awaiter) { - rc.Report.Error (1999, loc, "The awaiter type `{0}' must have suitable IsCompleted, OnCompleted, and GetResult members", + rc.Report.Error (4011, loc, "The awaiter type `{0}' must have suitable IsCompleted, OnCompleted, and GetResult members", awaiter.GetSignatureForError ()); } @@ -324,13 +303,21 @@ namespace Mono.CSharp if (!base.Resolve (bc)) return false; + Arguments args = new Arguments (0); + type = expr.Type; // - // The task result is of dynamic type + // The await expression is of dynamic type // - if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) - throw new NotImplementedException ("dynamic await"); + if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + result_type = type; + + awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (type, loc); + + expr = new Invocation (new MemberAccess (expr, "GetAwaiter"), args).Resolve (bc); + return true; + } // // Check whether the expression is awaitable @@ -339,40 +326,31 @@ namespace Mono.CSharp if (ama == null) return false; - Arguments args = new Arguments (0); - var errors_printer = new SessionReportPrinter (); var old = bc.Report.SetPrinter (errors_printer); ama = new Invocation (ama, args).Resolve (bc); + bc.Report.SetPrinter (old); if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) { - bc.Report.SetPrinter (old); - Error_WrongGetAwaiter (bc, loc, expr.Type); + bc.Report.Error (1986, expr.Location, + "The `await' operand type `{0}' must have suitable GetAwaiter method", + expr.Type.GetSignatureForError ()); + return false; } var awaiter_type = ama.Type; awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (awaiter_type, loc); + expr = ama; // // Predefined: bool IsCompleted { get; } // - var is_completed_ma = new MemberAccess (expr, "IsCompleted").Resolve (bc); - if (is_completed_ma != null) { - is_completed = is_completed_ma as PropertyExpr; - if (is_completed != null && is_completed.Type.BuiltinType == BuiltinTypeSpec.Type.Bool && is_completed.IsInstance && is_completed.Getter != null) { - // valid - } else { - bc.Report.SetPrinter (old); - Error_WrongAwaiterPattern (bc, awaiter_type); - return false; - } - } - - bc.Report.SetPrinter (old); + is_completed = MemberCache.FindMember (awaiter_type, MemberFilter.Property ("IsCompleted", bc.Module.Compiler.BuiltinTypes.Bool), + BindingRestriction.InstanceOnly) as PropertySpec; - if (errors_printer.ErrorsCount > 0) { + if (is_completed == null || !is_completed.HasGet) { Error_WrongAwaiterPattern (bc, awaiter_type); return false; } @@ -405,6 +383,8 @@ namespace Mono.CSharp return false; } + result_type = get_result.ReturnType; + return true; } } @@ -446,14 +426,8 @@ namespace Mono.CSharp #endregion - public static void Create (ParametersBlock block, ParametersCompiled parameters, TypeContainer host, TypeSpec returnType, Location loc) + public static void Create (IMemberContext context, ParametersBlock block, ParametersCompiled parameters, TypeContainer host, TypeSpec returnType, Location loc) { - if (returnType != null && returnType.Kind != MemberKind.Void && - returnType != host.Module.PredefinedTypes.Task.TypeSpec && - !returnType.IsGenericTask) { - host.Compiler.Report.Error (1983, loc, "The return type of an async method must be void, Task, or Task"); - } - for (int i = 0; i < parameters.Count; i++) { Parameter p = parameters[i]; Parameter.Modifier mod = p.ModFlags; @@ -463,26 +437,25 @@ namespace Mono.CSharp return; } - // TODO: if (p is ArglistParameter) { - host.Compiler.Report.Error (1636, p.Location, - "__arglist is not allowed in parameter list of iterators"); + host.Compiler.Report.Error (4006, p.Location, + "__arglist is not allowed in parameter list of async methods"); return; } - // TODO: if (parameters.Types[i].IsPointer) { - host.Compiler.Report.Error (1637, p.Location, - "Iterators cannot have unsafe parameters or yield types"); + host.Compiler.Report.Error (4005, p.Location, + "Async methods cannot have unsafe parameters"); return; } } - // TODO: Warning - //if (!block.HasAwait) { - //} + if (!block.HasAwait) { + host.Compiler.Report.Warning (1998, 1, loc, + "Async block lacks `await' operator and will run synchronously"); + } - block.WrapIntoAsyncTask (host, returnType); + block.WrapIntoAsyncTask (context, host, returnType); } protected override BlockContext CreateBlockContext (ResolveContext rc) @@ -492,6 +465,7 @@ namespace Mono.CSharp if (lambda != null) return_inference = lambda.ReturnTypeInference; + ctx.StartFlowBranching (this, rc.CurrentBranching); return ctx; } @@ -515,17 +489,31 @@ namespace Mono.CSharp { var storey = (AsyncTaskStorey) Storey; storey.Instance.Emit (ec); - ec.Emit (OpCodes.Call, storey.StateMachineMethod.Spec); + var move_next_entry = storey.StateMachineMethod.Spec; + if (storey.MemberName.Arity > 0) { + move_next_entry = MemberCache.GetMember (storey.Instance.Type, move_next_entry); + } + + ec.Emit (OpCodes.Call, move_next_entry); + + // + // Emits return .$builder.Task; + // if (storey.Task != null) { - // - // async.$builder.Task; - // + var builder_field = storey.Builder.Spec; + var task_get = storey.Task.Get; + + if (storey.MemberName.Arity > 0) { + builder_field = MemberCache.GetMember (storey.Instance.Type, builder_field); + task_get = MemberCache.GetMember (builder_field.MemberType, task_get); + } + var pe_task = new PropertyExpr (storey.Task, loc) { - InstanceExpression = new FieldExpr (storey.Builder, loc) { + InstanceExpression = new FieldExpr (builder_field, loc) { InstanceExpression = storey.Instance }, - Getter = storey.Task.Get + Getter = task_get }; pe_task.Emit (ec); @@ -537,52 +525,19 @@ namespace Mono.CSharp class AsyncTaskStorey : StateMachine { - sealed class ParametersLoadStatement : Statement - { - readonly FieldSpec[] fields; - readonly TypeSpec[] parametersTypes; - readonly int thisParameterIndex; - - public ParametersLoadStatement (FieldSpec[] fields, TypeSpec[] parametersTypes, int thisParameterIndex) - { - this.fields = fields; - this.parametersTypes = parametersTypes; - this.thisParameterIndex = thisParameterIndex; - } - - protected override void CloneTo (CloneContext clonectx, Statement target) - { - throw new NotImplementedException (); - } - - protected override void DoEmit (EmitContext ec) - { - for (int i = 0; i < fields.Length; ++i) { - var field = fields[i]; - if (field == null) - continue; - - ec.EmitArgumentLoad (thisParameterIndex); - ec.EmitArgumentLoad (i); - if (parametersTypes[i] is ReferenceContainer) - ec.EmitLoadFromPtr (field.MemberType); - - ec.Emit (OpCodes.Stfld, field); - } - } - } - int awaiters; Field builder, continuation; readonly TypeSpec return_type; MethodSpec set_result; + MethodSpec set_exception; PropertySpec task; LocalVariable hoisted_return; - Dictionary> stack_forwarders; - List hoisted_stack_slots; + int locals_captured; + Dictionary> stack_fields; + TypeSpec action; - public AsyncTaskStorey (AsyncInitializer initializer, TypeSpec type) - : base (initializer.OriginalBlock, initializer.Host, null, null, "async") + public AsyncTaskStorey (IMemberContext context, AsyncInitializer initializer, TypeSpec type) + : base (initializer.OriginalBlock, initializer.Host,context.CurrentMemberDefinition as MemberBase, context.CurrentTypeParameters, "async") { return_type = type; } @@ -595,12 +550,6 @@ namespace Mono.CSharp } } - public Field Continuation { - get { - return continuation; - } - } - public LocalVariable HoistedReturn { get { return hoisted_return; @@ -623,50 +572,50 @@ namespace Mono.CSharp public Field AddAwaiter (TypeSpec type, Location loc) { - return AddCompilerGeneratedField ("$awaiter" + awaiters++.ToString ("X"), new TypeExpression (type, loc)); + return AddCapturedVariable ("$awaiter" + awaiters++.ToString ("X"), type); } - int locals_captured; - - public Field AddCapturedLocalVariable (TypeSpec type) + public StackField AddCapturedLocalVariable (TypeSpec type) { - var field = AddCompilerGeneratedField ("$" + locals_captured++.ToString ("X"), new TypeExpression (type, Location)); - field.Define (); - - return field; - } + if (mutator != null) + type = mutator.Mutate (type); - FieldSpec CreateStackValueField (TypeSpec type, BitArray usedFields) - { - if (hoisted_stack_slots == null) { - hoisted_stack_slots = new List (); - } else { - for (int i = 0; i < usedFields.Count; ++i) { - if (hoisted_stack_slots[i].MemberType == type && !usedFields[i]) { - usedFields.Set (i, true); - return hoisted_stack_slots[i]; + List existing_fields = null; + if (stack_fields == null) { + stack_fields = new Dictionary> (); + } else if (stack_fields.TryGetValue (type, out existing_fields)) { + foreach (var f in existing_fields) { + if (f.CanBeReused) { + f.CanBeReused = false; + return f; } } } - var field = AddCompilerGeneratedField ("$" + hoisted_stack_slots.Count.ToString ("X"), new TypeExpression (type, Location)); + const Modifiers mod = Modifiers.COMPILER_GENERATED | Modifiers.PRIVATE; + var field = new StackField (this, new TypeExpression (type, Location), mod, new MemberName ("$" + locals_captured++.ToString ("X"), Location)); + AddField (field); + field.Define (); - hoisted_stack_slots.Add (field.Spec); - return field.Spec; + if (existing_fields == null) { + existing_fields = new List (); + stack_fields.Add (type, existing_fields); + } + + existing_fields.Add (field); + + return field; } protected override bool DoDefineMembers () { - var action = Module.PredefinedTypes.Action.Resolve (); - if (action != null) { - continuation = AddCompilerGeneratedField ("$continuation", new TypeExpression (action, Location)); - continuation.ModFlags |= Modifiers.READONLY; - } + action = Module.PredefinedTypes.Action.Resolve (); PredefinedType builder_type; PredefinedMember bf; PredefinedMember sr; + PredefinedMember se; bool has_task_return_type = false; var pred_members = Module.PredefinedMembers; @@ -674,67 +623,62 @@ namespace Mono.CSharp builder_type = Module.PredefinedTypes.AsyncVoidMethodBuilder; bf = pred_members.AsyncVoidMethodBuilderCreate; sr = pred_members.AsyncVoidMethodBuilderSetResult; + se = pred_members.AsyncVoidMethodBuilderSetException; } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) { builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilder; bf = pred_members.AsyncTaskMethodBuilderCreate; sr = pred_members.AsyncTaskMethodBuilderSetResult; - task = pred_members.AsyncTaskMethodBuilderTask.Resolve (Location); + se = pred_members.AsyncTaskMethodBuilderSetException; + task = pred_members.AsyncTaskMethodBuilderTask.Get (); } else { builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilderGeneric; bf = pred_members.AsyncTaskMethodBuilderGenericCreate; sr = pred_members.AsyncTaskMethodBuilderGenericSetResult; - task = pred_members.AsyncTaskMethodBuilderGenericTask.Resolve (Location); + se = pred_members.AsyncTaskMethodBuilderGenericSetException; + task = pred_members.AsyncTaskMethodBuilderGenericTask.Get (); has_task_return_type = true; } - set_result = sr.Resolve (Location); - var builder_factory = bf.Resolve (Location); - var bt = builder_type.Resolve (); - if (bt == null || set_result == null || builder_factory == null) - return false; + set_result = sr.Get (); + set_exception = se.Get (); + var builder_factory = bf.Get (); + if (!builder_type.Define () || set_result == null || builder_factory == null || set_exception == null) { + Report.Error (1993, Location, + "Cannot find compiler required types for asynchronous functions support. Are you targeting the wrong framework version?"); + return base.DoDefineMembers (); + } + + var bt = builder_type.TypeSpec; // // Inflate generic Task types // if (has_task_return_type) { - bt = bt.MakeGenericType (Module, return_type.TypeArguments); + var task_return_type = return_type.TypeArguments; + if (mutator != null) + task_return_type = mutator.Mutate (task_return_type); + + bt = bt.MakeGenericType (Module, task_return_type); builder_factory = MemberCache.GetMember (bt, builder_factory); set_result = MemberCache.GetMember (bt, set_result); + set_exception = MemberCache.GetMember (bt, set_exception); if (task != null) task = MemberCache.GetMember (bt, task); } builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location)); - builder.ModFlags |= Modifiers.READONLY; if (!base.DoDefineMembers ()) return false; - MethodGroupExpr mg; var block = instance_constructors[0].Block; - // - // Initialize continuation with state machine method - // - if (continuation != null) { - var args = new Arguments (1); - mg = MethodGroupExpr.CreatePredefined (StateMachineMethod.Spec, spec, Location); - args.Add (new Argument (mg)); - - block.AddStatement ( - new StatementExpression (new SimpleAssign ( - new FieldExpr (continuation, Location), - new NewDelegate (action, args, Location), - Location - ))); - } - - mg = MethodGroupExpr.CreatePredefined (builder_factory, bt, Location); + var mg = MethodGroupExpr.CreatePredefined (builder_factory, bt, Location); block.AddStatement ( new StatementExpression (new SimpleAssign ( - new FieldExpr (builder, Location), - new Invocation (mg, new Arguments (0)), + new FieldExpr (builder, Location), + new Invocation (mg, new Arguments (0)), Location))); if (has_task_return_type) { @@ -744,6 +688,70 @@ namespace Mono.CSharp return true; } + public Expression EmitContinuationInitialization (EmitContext ec) + { + // + // When more than 1 awaiter has been used in the block we + // introduce class scope field to cache continuation delegate + // + if (awaiters > 1) { + if (continuation == null) { + continuation = AddCompilerGeneratedField ("$continuation", new TypeExpression (action, Location), true); + continuation.Define (); + } + + var fexpr = new FieldExpr (continuation, Location); + fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location); + + // + // if ($continuation == null) + // $continuation = new Action (MoveNext); + // + fexpr.Emit (ec); + + var skip_cont_init = ec.DefineLabel (); + ec.Emit (OpCodes.Brtrue_S, skip_cont_init); + + ec.EmitThis (); + EmitActionLoad (ec); + ec.Emit (OpCodes.Stfld, continuation.Spec); + ec.MarkLabel (skip_cont_init); + + return fexpr; + } + + // + // Otherwise simply use temporary local variable + // + var field = LocalVariable.CreateCompilerGenerated (action, OriginalSourceBlock, Location); + EmitActionLoad (ec); + field.EmitAssign (ec); + return new LocalVariableReference (field, Location); + } + + void EmitActionLoad (EmitContext ec) + { + ec.EmitThis (); + ec.Emit (OpCodes.Ldftn, StateMachineMethod.Spec); + ec.Emit (OpCodes.Newobj, (MethodSpec) MemberCache.FindMember (action, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly)); + } + + public void EmitSetException (EmitContext ec, LocalVariableReference exceptionVariable) + { + // + // $builder.SetException (Exception) + // + var mg = MethodGroupExpr.CreatePredefined (set_exception, set_exception.DeclaringType, Location); + mg.InstanceExpression = new FieldExpr (Builder, Location) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location) + }; + + Arguments args = new Arguments (1); + args.Add (new Argument (exceptionVariable)); + + mg.EmitCall (ec, args); + } + public void EmitSetResult (EmitContext ec) { // @@ -765,93 +773,31 @@ namespace Mono.CSharp mg.EmitCall (ec, args); } + } - // - // Fabricates stack forwarder based on stack types which copies all - // parameters to type fields - // - public MethodSpec GetStackForwarder (TypeSpec[] types, out FieldSpec[] fields, out bool explicitThisNeeded) + class StackField : Field + { + public StackField (DeclSpace parent, FullNamedExpression type, Modifiers mod, MemberName name) + : base (parent, type, mod, name, null) { - if (stack_forwarders == null) { - stack_forwarders = new Dictionary> (TypeSpecComparer.Default); - } else { - // - // Does same forwarder method with same types already exist - // - Tuple method; - if (stack_forwarders.TryGetValue (types, out method)) { - fields = method.Item2; - explicitThisNeeded = method.Item3; - return method.Item1; - } - } - - Parameter[] p = new Parameter[types.Length + 1]; - TypeSpec[] ptypes = new TypeSpec[p.Length]; - fields = new FieldSpec[types.Length]; - int this_argument_index = -1; - BitArray used_fields_map = null; - - for (int i = 0; i < types.Length; ++i) { - var t = types[i]; - - TypeSpec parameter_type = t; - if (parameter_type == InternalType.CurrentTypeOnStack) { - parameter_type = CurrentType; - } - - p[i] = new Parameter (new TypeExpression (parameter_type, Location), null, 0, null, Location); - ptypes[i] = parameter_type; - - if (t == InternalType.CurrentTypeOnStack) { - if (this_argument_index < 0) - this_argument_index = i; - - // Null means the type is `this' we can optimize by ignoring - continue; - } - - var reference = t as ReferenceContainer; - if (reference != null) - t = reference.Element; - - if (used_fields_map == null) - used_fields_map = new BitArray (hoisted_stack_slots == null ? 0 : hoisted_stack_slots.Count); - - fields[i] = CreateStackValueField (t, used_fields_map); - } - - // - // None of the arguments is `this' need to add an extra parameter to pass it - // to static forwarder method - // - if (this_argument_index < 0) { - explicitThisNeeded = true; - this_argument_index = types.Length; - var this_parameter = new Parameter (new TypeExpression (CurrentType, Location), null, 0, null, Location); - p[types.Length] = this_parameter; - ptypes[types.Length] = CurrentType; - } else { - explicitThisNeeded = false; - Array.Resize (ref p, p.Length - 1); - Array.Resize (ref ptypes, ptypes.Length - 1); - } - - var parameters = ParametersCompiled.CreateFullyResolved (p, ptypes); - - var m = new Method (this, null, new TypeExpression (Compiler.BuiltinTypes.Void, Location), - Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN, - new MemberName ("<>s__" + stack_forwarders.Count.ToString ("X")), parameters, null); + } - m.Block = new ToplevelBlock (Compiler, parameters, Location); - m.Block.AddScopeStatement (new ParametersLoadStatement (fields, ptypes, this_argument_index)); + public bool CanBeReused { get; set; } + } - m.Define (); - Methods.Add (m); + class StackFieldExpr : FieldExpr + { + public StackFieldExpr (Field field) + : base (field, Location.Null) + { + } - stack_forwarders.Add (types, Tuple.Create (m.Spec, fields, explicitThisNeeded)); + public override void Emit (EmitContext ec) + { + base.Emit (ec); - return m.Spec; + var field = (StackField) spec.MemberDefinition; + field.CanBeReused = true; } } }