public override void EmitStatement(EmitContext ec) { var storey = (AsyncTaskStorey)Storey; storey.Instance.Emit(ec); ec.Emit(OpCodes.Call, storey.StateMachineMethod.Spec); if (storey.Task != null) { // // async.$builder.Task; // var pe_task = new PropertyExpr(storey.Task, loc) { InstanceExpression = new FieldExpr(storey.Builder, loc) { InstanceExpression = storey.Instance }, Getter = storey.Task.Get }; pe_task.Emit(ec); } ec.Emit(OpCodes.Ret); }
internal static bool EmitStack(EmitContext ec, PropertyExpr property, string name, bool isadr) { if (property.PropertyInfo.Get == null) { ec.Report.Error(3664, "Asm code : the property " + name + " does not have a get accessor or does not exist"); return(false); } if (isadr) { ec.Report.Error(3664, "Asm code : the property " + name + " could not be used as POPARG"); return(false); } ec.Emit(OpCodes.Call, property.PropertyInfo.Get); //ec.Emit(OpCodes.Stloc_0); //ec.Emit(OpCodes.Ldloc_0); return(true); }
public override void EmitStatement(EmitContext ec) { var storey = (AsyncTaskStorey)Storey; storey.Instance.Emit(ec); 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 <async-storey-instance>.$builder.Task; // if (storey.Task != null) { 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(builder_field, loc) { InstanceExpression = storey.Instance }, Getter = task_get }; pe_task.Emit(ec); } ec.Emit(OpCodes.Ret); }
public void EmitInitializer(EmitContext ec) { // // Some predefined types are missing // if (builder == null) { return; } var instance = (TemporaryVariableReference)Instance; var builder_field = builder.Spec; if (MemberName.Arity > 0) { builder_field = MemberCache.GetMember(instance.Type, builder_field); } // // Inflated factory method when task is of generic type // if (builder_factory.DeclaringType.IsGeneric) { var task_return_type = return_type.TypeArguments; var bt = builder_factory.DeclaringType.MakeGenericType(Module, task_return_type); builder_factory = MemberCache.GetMember(bt, builder_factory); builder_start = MemberCache.GetMember(bt, builder_start); } // // stateMachine.$builder = AsyncTaskMethodBuilder<{task-type}>.Create(); // instance.AddressOf(ec, AddressOp.Store); ec.Emit(OpCodes.Call, builder_factory); ec.Emit(OpCodes.Stfld, builder_field); // // stateMachine.$builder.Start<{storey-type}>(ref stateMachine); // instance.AddressOf(ec, AddressOp.Store); ec.Emit(OpCodes.Ldflda, builder_field); if (Task != null) { ec.Emit(OpCodes.Dup); } instance.AddressOf(ec, AddressOp.Store); ec.Emit(OpCodes.Call, builder_start.MakeGenericMethod(Module, instance.Type)); // // Emits return stateMachine.$builder.Task; // if (Task != null) { var task_get = Task.Get; if (MemberName.Arity > 0) { task_get = MemberCache.GetMember(builder_field.MemberType, task_get); } var pe_task = new PropertyExpr(Task, Location) { InstanceExpression = EmptyExpression.Null, // Comes from the dup above Getter = task_get }; pe_task.Emit(ec); } }
public void EmitPrologue(EmitContext ec) { awaiter = ((AsyncTaskStorey)machine_initializer.Storey).AddAwaiter(expr.Type); var fe_awaiter = new FieldExpr(awaiter, loc); fe_awaiter.InstanceExpression = new CompilerGeneratedThis(ec.CurrentType, loc); Label skip_continuation = ec.DefineLabel(); using (ec.With(BuilderContext.Options.OmitDebugInfo, true)) { // // awaiter = expr.GetAwaiter (); // fe_awaiter.EmitAssign(ec, expr, false, false); 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); dargs = new Arguments(1); dargs.Add(new Argument(completed_expr)); completed_expr = new DynamicConversion(ec.Module.Compiler.BuiltinTypes.Bool, 0, dargs, loc).Resolve(rc); } else { var pe = PropertyExpr.CreatePredefined(awaiter_definition.IsCompleted, loc); pe.InstanceExpression = fe_awaiter; completed_expr = pe; } completed_expr.EmitBranchable(ec, skip_continuation, true); } base.DoEmit(ec); // // 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. // // Following assert fails when some of expression called before is missing EmitToField // or parent expression fails to find await in children expressions // ec.AssertEmptyStack(); var storey = (AsyncTaskStorey)machine_initializer.Storey; if (IsDynamic) { storey.EmitAwaitOnCompletedDynamic(ec, fe_awaiter); } else { storey.EmitAwaitOnCompleted(ec, fe_awaiter); } // Return ok machine_initializer.EmitLeave(ec, unwind_protect); ec.MarkLabel(resume_point); ec.MarkLabel(skip_continuation); }
public override bool Resolve (BlockContext ec) { bool is_dynamic = expr.Type == InternalType.Dynamic; if (is_dynamic) { expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc); } else if (TypeManager.IsNullableType (expr.Type)) { expr = new Nullable.UnwrapCall (expr).Resolve (ec); } var get_enumerator_mg = ResolveGetEnumerator (ec); if (get_enumerator_mg == null) { return false; } var get_enumerator = get_enumerator_mg.BestCandidate; enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc); enumerator_variable.Resolve (ec); // Prepare bool MoveNext () var move_next_mg = ResolveMoveNext (ec, get_enumerator); if (move_next_mg == null) { return false; } move_next_mg.InstanceExpression = enumerator_variable; // Prepare ~T~ Current { get; } var current_prop = ResolveCurrent (ec, get_enumerator); if (current_prop == null) { return false; } var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec); if (current_pe == null) return false; VarExpr ve = var_type as VarExpr; if (ve != null) { if (is_dynamic) { // Source type is dynamic, set element type to dynamic too var_type = new TypeExpression (InternalType.Dynamic, var_type.Location); } else { // Infer implicitly typed local variable from foreach enumerable type var_type = new TypeExpression (current_pe.Type, var_type.Location); } } else if (is_dynamic) { // Explicit cast of dynamic collection elements has to be done at runtime current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic); } var_type = var_type.ResolveAsTypeTerminal (ec, false); if (var_type == null) return false; variable.Type = var_type.Type; var init = new Invocation (get_enumerator_mg, null); statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)), new Body (var_type.Type, variable, current_pe, statement, loc), loc); var enum_type = enumerator_variable.Type; // // Add Dispose method call when enumerator can be IDisposable // if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) { if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) { // // Runtime Dispose check // var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc); vd.Initializer = init; statement = new Using (vd, statement, loc); } else { // // No Dispose call needed // this.init = new SimpleAssign (enumerator_variable, init); this.init.Resolve (ec); } } else { // // Static Dispose check // var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc); vd.Initializer = init; statement = new Using (vd, statement, loc); } return statement.Resolve (ec); }
public override void Emit (EmitContext ec) { pinned_string.CreateBuilder (ec); expr.Emit (ec); pinned_string.EmitAssign (ec); // TODO: Should use Binary::Add pinned_string.Emit (ec); ec.Emit (OpCodes.Conv_I); PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location); //pe.InstanceExpression = pinned_string; pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec); ec.Emit (OpCodes.Add); vi.EmitAssign (ec); }
public override bool Resolve (BlockContext ec) { bool is_dynamic = expr.Type == InternalType.Dynamic; if (is_dynamic) expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc); var get_enumerator_mg = ResolveGetEnumerator (ec); if (get_enumerator_mg == null) { return false; } var get_enumerator = get_enumerator_mg.BestCandidate; var enumerator = new TemporaryVariable (get_enumerator.ReturnType, loc); enumerator.Resolve (ec); // Prepare bool MoveNext () var move_next_mg = ResolveMoveNext (ec, get_enumerator); if (move_next_mg == null) { return false; } move_next_mg.InstanceExpression = enumerator; // Prepare ~T~ Current { get; } var current_prop = ResolveCurrent (ec, get_enumerator); if (current_prop == null) { return false; } var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator }.Resolve (ec); if (current_pe == null) return false; VarExpr ve = var_type as VarExpr; if (ve != null) { // Infer implicitly typed local variable from foreach enumerable type var_type = new TypeExpression (current_pe.Type, var_type.Location); } var_type = var_type.ResolveAsTypeTerminal (ec, false); if (var_type == null) return false; var init = new Invocation (get_enumerator_mg, null); init.Resolve (ec); statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)), new Body (var_type.Type, variable, current_pe, statement, loc), loc); var enum_type = enumerator.Type; // // Add Dispose method call when enumerator can be IDisposable // if (!enumerator.Type.ImplementsInterface (TypeManager.idisposable_type)) { if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) { // // Runtime Dispose check // var tv = new LocalTemporary (TypeManager.idisposable_type); statement = new Dispose (enumerator, tv, init, statement, loc); } else { // // No Dispose call needed // this.init = new SimpleAssign (enumerator, init); this.init.Resolve (ec); } } else { // // Static Dispose check // statement = new Dispose (enumerator, null, init, statement, loc); } return statement.Resolve (ec); }
public void EmitPrologue(EmitContext ec) { var fe_awaiter = new FieldExpr(awaiter, loc); fe_awaiter.InstanceExpression = new CompilerGeneratedThis(ec.CurrentType, loc); // // awaiter = expr.GetAwaiter (); // fe_awaiter.EmitAssign(ec, expr, false, false); Label skip_continuation = ec.DefineLabel(); 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); // // 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. // // Following assert fails when some of expression called before is missing EmitToField // or parent expression fails to find await in children expressions // ec.AssertEmptyStack(); 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(fe_cont)); if (IsDynamic) { var rc = new ResolveContext(ec.MemberContext); var mg_expr = new Invocation(new MemberAccess(fe_awaiter, "OnCompleted"), args).Resolve(rc); 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); ec.MarkLabel(skip_continuation); }
public override bool Resolve(BlockContext bc) { if (!base.Resolve(bc)) { return(false); } type = expr.Type; // // The task result is of dynamic type // if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { throw new NotImplementedException("dynamic await"); } // // Check whether the expression is awaitable // Expression ama = new AwaitableMemberAccess(expr).Resolve(bc); 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); if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression(ama.Type)) { bc.Report.SetPrinter(old); Error_WrongGetAwaiter(bc, loc, expr.Type); 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); if (errors_printer.ErrorsCount > 0) { Error_WrongAwaiterPattern(bc, awaiter_type); return(false); } // // Predefined: OnCompleted (Action) // if (bc.Module.PredefinedTypes.Action.Define()) { on_completed = MemberCache.FindMember(awaiter_type, MemberFilter.Method("OnCompleted", 0, ParametersCompiled.CreateFullyResolved(bc.Module.PredefinedTypes.Action.TypeSpec), bc.Module.Compiler.BuiltinTypes.Void), BindingRestriction.InstanceOnly) as MethodSpec; if (on_completed == null) { Error_WrongAwaiterPattern(bc, awaiter_type); return(false); } } // // Predefined: GetResult () // // The method return type is also result type of await expression // get_result = MemberCache.FindMember(awaiter_type, MemberFilter.Method("GetResult", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.InstanceOnly) as MethodSpec; if (get_result == null) { Error_WrongAwaiterPattern(bc, awaiter_type); return(false); } return(true); }
bool TryType (EmitContext ec, Type t) { MethodGroupExpr mg = Expression.MemberLookup ( ec.ContainerType, t, "GetEnumerator", MemberTypes.Method, Expression.AllBindingFlags, loc) as MethodGroupExpr; if (mg == null) return false; MethodInfo result = null; MethodInfo tmp_move_next = null; PropertyExpr tmp_get_cur = null; Type tmp_enumerator_type = enumerator_type; foreach (MethodInfo mi in mg.Methods) { if (TypeManager.GetParameterData (mi).Count != 0) continue; // Check whether GetEnumerator is public if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public) continue; if (IsOverride (mi)) continue; enumerator_found = true; if (!GetEnumeratorFilter (ec, mi)) continue; if (result != null) { if (TypeManager.IsGenericType (result.ReturnType)) { if (!TypeManager.IsGenericType (mi.ReturnType)) continue; MethodBase mb = TypeManager.DropGenericMethodArguments (mi); Report.SymbolRelatedToPreviousError (t); Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " + "because it contains multiple implementation of `{1}'. Try casting to a specific implementation", TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb)); return false; } // Always prefer generics enumerators if (!TypeManager.IsGenericType (mi.ReturnType)) { if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) || TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType)) continue; Report.SymbolRelatedToPreviousError (result); Report.SymbolRelatedToPreviousError (mi); Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'", TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi)); return false; } } result = mi; tmp_move_next = move_next; tmp_get_cur = get_current; tmp_enumerator_type = enumerator_type; if (mi.DeclaringType == t) break; } if (result != null) { move_next = tmp_move_next; get_current = tmp_get_cur; enumerator_type = tmp_enumerator_type; MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result }; get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc); if (t != expr.Type) { expr = Convert.ExplicitConversion ( ec, expr, t, loc); if (expr == null) throw new InternalErrorException (); } get_enumerator.InstanceExpression = expr; get_enumerator.IsBase = t != expr.Type; return true; } return false; }
// // Retrieves a `public T get_Current ()' method from the Type `t' // bool FetchGetCurrent (EmitContext ec, Type t) { PropertyExpr pe = Expression.MemberLookup ( ec.ContainerType, t, "Current", MemberTypes.Property, Expression.AllBindingFlags, loc) as PropertyExpr; if (pe == null) return false; get_current = pe; return true; }
bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi) { Type return_type = mi.ReturnType; // // Ok, we can access it, now make sure that we can do something // with this `GetEnumerator' // if (return_type == TypeManager.ienumerator_type || TypeManager.ienumerator_type.IsAssignableFrom (return_type) || (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) { // // If it is not an interface, lets try to find the methods ourselves. // For example, if we have: // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}} // We can avoid the iface call. This is a runtime perf boost. // even bigger if we have a ValueType, because we avoid the cost // of boxing. // // We have to make sure that both methods exist for us to take // this path. If one of the methods does not exist, we will just // use the interface. Sadly, this complex if statement is the only // way I could do this without a goto // if (TypeManager.bool_movenext_void == null) { TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod ( TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes); } if (TypeManager.ienumerator_getcurrent == null) { TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty ( TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type); } #if GMCS_SOURCE // // Prefer a generic enumerator over a non-generic one. // if (return_type.IsInterface && return_type.IsGenericType) { enumerator_type = return_type; if (!FetchGetCurrent (ec, return_type)) get_current = new PropertyExpr ( ec.ContainerType, TypeManager.ienumerator_getcurrent, loc); if (!FetchMoveNext (return_type)) move_next = TypeManager.bool_movenext_void; return true; } #endif if (return_type.IsInterface || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) { enumerator_type = return_type; move_next = TypeManager.bool_movenext_void; get_current = new PropertyExpr ( ec.ContainerType, TypeManager.ienumerator_getcurrent, loc); return true; } } else { // // Ok, so they dont return an IEnumerable, we will have to // find if they support the GetEnumerator pattern. // if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) { Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property", TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi)); return false; } } enumerator_type = return_type; return true; }
bool TryType(ResolveContext ec, TypeSpec t) { var mg = Expression.MemberLookup (ec.Compiler, ec.CurrentType, null, t, "GetEnumerator", 0, MemberKind.Method, BindingRestriction.NoOverrides | BindingRestriction.InstanceOnly, loc) as MethodGroupExpr; if (mg == null) return false; MethodSpec result = null; MethodSpec tmp_move_next = null; PropertyExpr tmp_get_cur = null; TypeSpec tmp_enumerator_type = enumerator_type; foreach (MethodSpec mi in mg.Methods) { if (!mi.Parameters.IsEmpty) continue; // Check whether GetEnumerator is public if ((mi.Modifiers & Modifiers.AccessibilityMask) != Modifiers.PUBLIC) continue; enumerator_found = true; if (!GetEnumeratorFilter (ec, mi)) continue; if (result != null) { if (TypeManager.IsGenericType (result.ReturnType)) { if (!TypeManager.IsGenericType (mi.ReturnType)) continue; ec.Report.SymbolRelatedToPreviousError (t); ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " + "because it contains multiple implementation of `{1}'. Try casting to a specific implementation", TypeManager.CSharpName (t), TypeManager.generic_ienumerable_type.GetSignatureForError ()); return false; } // Always prefer generics enumerators if (!TypeManager.IsGenericType (mi.ReturnType)) { if (mi.DeclaringType.ImplementsInterface (result.DeclaringType) || result.DeclaringType.ImplementsInterface (mi.DeclaringType)) continue; ec.Report.SymbolRelatedToPreviousError (result); ec.Report.SymbolRelatedToPreviousError (mi); ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'", TypeManager.CSharpName (t), "enumerable", result.GetSignatureForError (), mi.GetSignatureForError ()); return false; } } result = mi; tmp_move_next = move_next; tmp_get_cur = get_current; tmp_enumerator_type = enumerator_type; if (mi.DeclaringType == t) break; } if (result != null) { move_next = tmp_move_next; get_current = tmp_get_cur; enumerator_type = tmp_enumerator_type; get_enumerator = new MethodGroupExpr (result, enumerator_type, loc); if (t != expr.Type) { expr = Convert.ExplicitConversion ( ec, expr, t, loc); if (expr == null) throw new InternalErrorException (); } get_enumerator.InstanceExpression = expr; get_enumerator.IsBase = t != expr.Type; return true; } return false; }
// // Retrieves a `public T get_Current ()' method from the Type `t' // bool FetchGetCurrent(ResolveContext ec, TypeSpec t) { PropertyExpr pe = Expression.MemberLookup (ec.Compiler, ec.CurrentType, t, "Current", 0, MemberKind.Property, BindingRestriction.AccessibleOnly, loc) as PropertyExpr; if (pe == null) return false; get_current = pe; return true; }