// // 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); } }
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); iterator_body_end = ec.DefineLabel(); block.EmitEmbedded(ec); ec.MarkLabel(iterator_body_end); EmitMoveNextEpilogue(ec); ec.MarkLabel(move_next_error); if (ReturnType.Kind != MemberKind.Void) { ec.EmitInt(0); ec.Emit(OpCodes.Ret); } }
protected override void DoEmit(EmitContext ec) { Label label_init = ec.DefineLabel(); ec.Emit(OpCodes.Ldarg_0); ec.Emit(OpCodes.Ldflda, host.PC.Spec); ec.EmitInt((int)Iterator.State.Start); ec.EmitInt((int)Iterator.State.Uninitialized); var m = ec.Module.PredefinedMembers.InterlockedCompareExchange.Resolve(loc); if (m != null) { ec.Emit(OpCodes.Call, m); } ec.EmitInt((int)Iterator.State.Uninitialized); ec.Emit(OpCodes.Bne_Un_S, label_init); ec.Emit(OpCodes.Ldarg_0); 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(); ec.Emit(OpCodes.Ldarg_0); ec.Emit(OpCodes.Ldfld, storey.DisposingField.Spec); ec.Emit(OpCodes.Brtrue_S, disposed); // // store resume program-counter // ec.Emit(OpCodes.Ldarg_0); ec.EmitInt(resume_pc); ec.Emit(OpCodes.Stfld, storey.PC.Spec); ec.MarkLabel(disposed); // mark finally blocks as disabled if (unwind_protect && skip_finally != null) { ec.EmitInt(1); ec.Emit(OpCodes.Stloc, skip_finally); } // Return ok ec.Emit(unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok); ec.MarkLabel(resume_point); }
public void EmitAwaitOnCompletedDynamic(EmitContext ec, FieldExpr awaiter) { var critical = Module.PredefinedTypes.ICriticalNotifyCompletion; if (!critical.Define()) { throw new NotImplementedException(); } var temp_critical = new LocalTemporary(critical.TypeSpec); var label_critical = ec.DefineLabel(); var label_end = ec.DefineLabel(); // // Special path for dynamic awaiters // // var awaiter = this.$awaiter as ICriticalNotifyCompletion; // if (awaiter == null) { // var completion = (INotifyCompletion) this.$awaiter; // this.$builder.AwaitOnCompleted (ref completion, ref this); // } else { // this.$builder.AwaitUnsafeOnCompleted (ref awaiter, ref this); // } // awaiter.Emit(ec); ec.Emit(OpCodes.Isinst, critical.TypeSpec); temp_critical.Store(ec); temp_critical.Emit(ec); ec.Emit(OpCodes.Brtrue_S, label_critical); var temp = new LocalTemporary(Module.PredefinedTypes.INotifyCompletion.TypeSpec); awaiter.Emit(ec); ec.Emit(OpCodes.Castclass, temp.Type); temp.Store(ec); EmitOnCompleted(ec, temp, false); temp.Release(ec); ec.Emit(OpCodes.Br_S, label_end); ec.MarkLabel(label_critical); EmitOnCompleted(ec, temp_critical, true); ec.MarkLabel(label_end); temp_critical.Release(ec); }
public void EmitDispose(EmitContext ec) { Label end = ec.DefineLabel(); Label [] labels = null; int n_resume_points = resume_points == null ? 0 : resume_points.Count; for (int i = 0; i < n_resume_points; ++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.Emit(OpCodes.Ldarg_0); ec.Emit(OpCodes.Ldfld, IteratorHost.PC.Spec); ec.Emit(OpCodes.Stloc, current_pc); } ec.Emit(OpCodes.Ldarg_0); ec.EmitInt(1); ec.Emit(OpCodes.Stfld, IteratorHost.DisposingField.Spec); ec.Emit(OpCodes.Ldarg_0); ec.EmitInt((int)State.After); ec.Emit(OpCodes.Stfld, IteratorHost.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, this, end, true); } } ec.MarkLabel(end); }
protected void EmitConditionalAccess(EmitContext ec) { var a_expr = arguments [0].Expr; var des = a_expr as DynamicExpressionStatement; if (des != null) { des.EmitConditionalAccess(ec); } if (HasConditionalAccess()) { var NullOperatorLabel = ec.DefineLabel(); if (ExpressionAnalyzer.IsInexpensiveLoad(a_expr)) { a_expr.Emit(ec); } else { var lt = new LocalTemporary(a_expr.Type); lt.EmitAssign(ec, a_expr, true, false); Arguments [0].Expr = lt; } ec.Emit(OpCodes.Brtrue_S, NullOperatorLabel); if (!ec.ConditionalAccess.Statement) { if (ec.ConditionalAccess.Type.IsNullableType) { Nullable.LiftedNull.Create(ec.ConditionalAccess.Type, Location.Null).Emit(ec); } else { ec.EmitNull(); } } ec.Emit(OpCodes.Br, ec.ConditionalAccess.EndLabel); ec.MarkLabel(NullOperatorLabel); return; } if (a_expr.HasConditionalAccess()) { var lt = new LocalTemporary(a_expr.Type); lt.EmitAssign(ec, a_expr, false, false); Arguments [0].Expr = lt; } }
public void EmitResultLift(EmitContext ec, TypeSpec type, bool statement) { if (!NullShortCircuit) { throw new InternalErrorException(); } bool value_rt = TypeSpec.IsValueType(type); TypeSpec lifted; if (value_rt) { if (type.IsNullableType) { lifted = type; } else { lifted = Nullable.NullableInfo.MakeType(ec.Module, type); ec.Emit(OpCodes.Newobj, Nullable.NullableInfo.GetConstructor(lifted)); } } else { lifted = null; } var end = ec.DefineLabel(); if (value_on_stack || !statement) { ec.Emit(OpCodes.Br_S, end); } ec.MarkLabel(NullOperatorLabel); if (value_on_stack) { ec.Emit(OpCodes.Pop); } if (!statement) { if (value_rt) { Nullable.LiftedNull.Create(lifted, Location.Null).Emit(ec); } else { ec.EmitNull(); } } ec.MarkLabel(end); }
public override void Emit (EmitContext ec) { Label l_initialized = ec.DefineLabel (); if (mg_cache != null) { ec.Emit (OpCodes.Ldsfld, mg_cache.Spec); ec.Emit (OpCodes.Brtrue_S, l_initialized); } base.Emit (ec); if (mg_cache != null) { ec.Emit (OpCodes.Stsfld, mg_cache.Spec); ec.MarkLabel (l_initialized); ec.Emit (OpCodes.Ldsfld, mg_cache.Spec); } }
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 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 override void EmitStatement(EmitContext ec) { if (conditionalAccessReceiver) { ec.ConditionalAccess = new ConditionalAccessContext(type, ec.DefineLabel()) { Statement = true }; } var call = new CallEmitter(); call.InstanceExpression = InstanceExpr; call.EmitStatement(ec, method, arguments, loc); if (conditionalAccessReceiver) { ec.CloseConditionalAccess(null); } }
protected override void DoEmit(EmitContext ec) { Label label_init = ec.DefineLabel(); ec.Emit(OpCodes.Ldarg_0); ec.Emit(OpCodes.Ldflda, host.PC.Spec); ec.EmitInt((int)Iterator.State.Start); ec.EmitInt((int)Iterator.State.Uninitialized); ec.Emit(OpCodes.Call, TypeManager.int_interlocked_compare_exchange); ec.EmitInt((int)Iterator.State.Uninitialized); ec.Emit(OpCodes.Bne_Un_S, label_init); ec.Emit(OpCodes.Ldarg_0); ec.Emit(OpCodes.Ret); ec.MarkLabel(label_init); new_storey.Emit(ec); ec.Emit(OpCodes.Ret); }
public override void Emit(EmitContext ec) { if (conditionalAccessReceiver) { ec.ConditionalAccess = new ConditionalAccessContext(type, ec.DefineLabel()); } // // Invocation on delegates call the virtual Invoke member // so we are always `instance' calls // var call = new CallEmitter(); call.InstanceExpression = InstanceExpr; call.Emit(ec, method, arguments, loc); if (conditionalAccessReceiver) { ec.CloseConditionalAccess(type.IsNullableType && type != method.ReturnType ? type : null); } }
public void Emit(EmitContext ec) { Nullable.Unwrap unwrap; if (NullShortCircuit) { NullOperatorLabel = ec.DefineLabel(); unwrap = instance as Nullable.Unwrap; } else { unwrap = null; } if (unwrap != null) { unwrap.Store(ec); unwrap.EmitCheck(ec); ec.Emit(OpCodes.Brfalse, NullOperatorLabel); unwrap.Emit(ec); var tmp = ec.GetTemporaryLocal(unwrap.Type); ec.Emit(OpCodes.Stloc, tmp); ec.Emit(OpCodes.Ldloca, tmp); ec.FreeTemporaryLocal(tmp, unwrap.Type); return; } EmitLoad(ec); if (NullShortCircuit) { ec.Emit(OpCodes.Dup); ec.Emit(OpCodes.Brfalse, NullOperatorLabel); } value_on_stack = true; }
public override void Emit(EmitContext ec) { if (conditional_access_receiver) { ec.ConditionalAccess = new ConditionalAccessContext(type, ec.DefineLabel()); } if (method_group.InstanceExpression == null) { ec.EmitNull(); } else { var ie = new InstanceEmitter(method_group.InstanceExpression, false); ie.Emit(ec, method_group.ConditionalAccess); } var delegate_method = method_group.BestCandidate; // Any delegate must be sealed if (!delegate_method.DeclaringType.IsDelegate && delegate_method.IsVirtual && !method_group.IsBase) { ec.Emit(OpCodes.Dup); ec.Emit(OpCodes.Ldvirtftn, delegate_method); } else { ec.Emit(OpCodes.Ldftn, delegate_method); } ec.Emit(OpCodes.Newobj, constructor_method); if (conditional_access_receiver) { ec.CloseConditionalAccess(null); } }
public override void Emit (EmitContext ec) { if (conditionalAccessReceiver) { ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); } // // Invocation on delegates call the virtual Invoke member // so we are always `instance' calls // var call = new CallEmitter (); call.InstanceExpression = InstanceExpr; call.Emit (ec, method, arguments, loc); if (conditionalAccessReceiver) ec.CloseConditionalAccess (type.IsNullableType && type != method.ReturnType ? type : null); }
public override void Emit (EmitContext ec) { if (conditional_access_receiver) ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); if (method_group.InstanceExpression == null) { ec.EmitNull (); } else { var ie = new InstanceEmitter (method_group.InstanceExpression, false); ie.Emit (ec, method_group.ConditionalAccess); } var delegate_method = method_group.BestCandidate; // Any delegate must be sealed if (!delegate_method.DeclaringType.IsDelegate && delegate_method.IsVirtual && !method_group.IsBase) { ec.Emit (OpCodes.Dup); ec.Emit (OpCodes.Ldvirtftn, delegate_method); } else { ec.Emit (OpCodes.Ldftn, delegate_method); } ec.Emit (OpCodes.Newobj, constructor_method); if (conditional_access_receiver) ec.CloseConditionalAccess (null); }
protected void EmitConditionalAccess (EmitContext ec) { var a_expr = arguments [0].Expr; var des = a_expr as DynamicExpressionStatement; if (des != null) { des.EmitConditionalAccess (ec); } if (HasConditionalAccess ()) { var NullOperatorLabel = ec.DefineLabel (); if (ExpressionAnalyzer.IsInexpensiveLoad (a_expr)) { a_expr.Emit (ec); } else { var lt = new LocalTemporary (a_expr.Type); lt.EmitAssign (ec, a_expr, true, false); Arguments [0].Expr = lt; } ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); if (!ec.ConditionalAccess.Statement) { if (ec.ConditionalAccess.Type.IsNullableType) Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec); else ec.EmitNull (); } ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel); ec.MarkLabel (NullOperatorLabel); return; } if (a_expr.HasConditionalAccess ()) { var lt = new LocalTemporary (a_expr.Type); lt.EmitAssign (ec, a_expr, false, false); Arguments [0].Expr = lt; } }
protected override void DoEmit (EmitContext ec) { Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd; ec.LoopBegin = ec.DefineLabel (); ec.LoopEnd = ec.DefineLabel (); statement.Emit (ec); ec.LoopBegin = old_begin; ec.LoopEnd = old_end; }
protected override void DoEmit (EmitContext ec) { copy.EmitAssign (ec, for_each.expr); int rank = length_exprs.Length; Label[] test = new Label [rank]; Label[] loop = new Label [rank]; for (int i = 0; i < rank; i++) { test [i] = ec.DefineLabel (); loop [i] = ec.DefineLabel (); if (lengths != null) lengths [i].EmitAssign (ec, length_exprs [i]); } IntConstant zero = new IntConstant (0, loc); for (int i = 0; i < rank; i++) { variables [i].EmitAssign (ec, zero); ec.Emit (OpCodes.Br, test [i]); ec.MarkLabel (loop [i]); } variable.local_info.CreateBuilder (ec); variable.EmitAssign (ec, conv, false, false); statement.Emit (ec); ec.MarkLabel (ec.LoopBegin); for (int i = rank - 1; i >= 0; i--){ counter [i].Emit (ec); ec.MarkLabel (test [i]); variables [i].Emit (ec); if (lengths != null) lengths [i].Emit (ec); else length_exprs [i].Emit (ec); ec.Emit (OpCodes.Blt, loop [i]); } ec.MarkLabel (ec.LoopEnd); }
public override Label PrepareForDispose (EmitContext ec, Label end) { if (!prepared_for_dispose) { prepared_for_dispose = true; dispose_try_block = ec.DefineLabel (); } return dispose_try_block; }
public override void Emit (EmitContext ec) { if (Report.Errors > 0) return; #if PRODUCTION try { #endif if (ec.HasReturnLabel) ec.ReturnLabel = ec.DefineLabel (); base.Emit (ec); ec.Mark (EndLocation); if (ec.HasReturnLabel) ec.MarkLabel (ec.ReturnLabel); if (ec.return_value != null) { ec.Emit (OpCodes.Ldloc, ec.return_value); ec.Emit (OpCodes.Ret); } else { // // If `HasReturnLabel' is set, then we already emitted a // jump to the end of the method, so we must emit a `ret' // there. // // Unfortunately, System.Reflection.Emit automatically emits // a leave to the end of a finally block. This is a problem // if no code is following the try/finally block since we may // jump to a point after the end of the method. // As a workaround, we're always creating a return label in // this case. // if (ec.HasReturnLabel || !unreachable) { if (ec.ReturnType != TypeManager.void_type) ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ()); ec.Emit (OpCodes.Ret); } } #if PRODUCTION } catch (Exception e){ Console.WriteLine ("Exception caught by the compiler while emitting:"); Console.WriteLine (" Block that caused the problem begin at: " + block.loc); Console.WriteLine (e.GetType ().FullName + ": " + e.Message); throw; } #endif }
protected override void DoEmit (EmitContext ec) { Label false_target = ec.DefineLabel (); Label end; // // If we're a boolean constant, Resolve() already // eliminated dead code for us. // Constant c = expr as Constant; if (c != null){ c.EmitSideEffect (ec); if (!c.IsDefaultValue) TrueStatement.Emit (ec); else if (FalseStatement != null) FalseStatement.Emit (ec); return; } expr.EmitBranchable (ec, false_target, false); TrueStatement.Emit (ec); if (FalseStatement != null){ bool branch_emitted = false; end = ec.DefineLabel (); if (!is_true_ret){ ec.Emit (OpCodes.Br, end); branch_emitted = true; } ec.MarkLabel (false_target); FalseStatement.Emit (ec); if (branch_emitted) ec.MarkLabel (end); } else { ec.MarkLabel (false_target); } }
protected override void EmitFinallyBody (EmitContext ec) { Label call_dispose = ec.DefineLabel (); if (dispose != null) { local_copy.Emit (ec, false); ec.Emit (OpCodes.Isinst, dispose.Type); dispose.Store (ec); } base.EmitFinallyBody (ec); if (dispose != null) { ec.MarkLabel (call_dispose); dispose.Release (ec); } }
void EmitMoveNext(EmitContext ec) { move_next_ok = ec.DefineLabel(); move_next_error = ec.DefineLabel(); if (resume_points == null) { EmitMoveNext_NoResumePoints(ec, block); return; } current_pc = ec.GetTemporaryLocal(ec.BuiltinTypes.UInt); ec.Emit(OpCodes.Ldarg_0); ec.Emit(OpCodes.Ldfld, IteratorHost.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.Emit(OpCodes.Ldarg_0); ec.EmitInt((int)State.After); ec.Emit(OpCodes.Stfld, IteratorHost.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.Emit(OpCodes.Ldc_I4_0); ec.Emit(OpCodes.Stloc, skip_finally); } SymbolWriter.StartIteratorDispatcher(ec); ec.Emit(OpCodes.Ldloc, current_pc); ec.Emit(OpCodes.Switch, labels); ec.Emit(OpCodes.Br, move_next_error); SymbolWriter.EndIteratorDispatcher(ec); ec.MarkLabel(labels [0]); SymbolWriter.StartIteratorBody(ec); block.Emit(ec); SymbolWriter.EndIteratorBody(ec); SymbolWriter.StartIteratorDispatcher(ec); ec.Emit(OpCodes.Ldarg_0); ec.EmitInt((int)State.After); ec.Emit(OpCodes.Stfld, IteratorHost.PC.Spec); ec.MarkLabel(move_next_error); ec.EmitInt(0); ec.Emit(OpCodes.Ret); ec.MarkLabel(move_next_ok); ec.Emit(OpCodes.Ldc_I4_1); ec.Emit(OpCodes.Ret); SymbolWriter.EndIteratorDispatcher(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(); is_completed.InstanceExpression = fe_awaiter; is_completed.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. // // For example: await a + await b // // 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) { var async_storey = (AsyncTaskStorey)machine_initializer.Storey; stack = ec.GetStackTypes(); var method = async_storey.GetStackForwarder(stack, out stack_fields); ec.EmitThis(); ec.Emit(OpCodes.Call, method); } var mg_completed = MethodGroupExpr.CreatePredefined(on_completed, fe_awaiter.Type, loc); mg_completed.InstanceExpression = fe_awaiter; 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)); // // 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); }
public void Emit (EmitContext ec, bool conditionalAccess) { Label NullOperatorLabel; Nullable.Unwrap unwrap; if (conditionalAccess && Expression.IsNeverNull (instance)) conditionalAccess = false; if (conditionalAccess) { NullOperatorLabel = ec.DefineLabel (); unwrap = instance as Nullable.Unwrap; } else { NullOperatorLabel = new Label (); unwrap = null; } IMemoryLocation instance_address = null; bool conditional_access_dup = false; if (unwrap != null) { unwrap.Store (ec); unwrap.EmitCheck (ec); ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); } else { if (conditionalAccess && addressRequired) { // // Don't allocate temp variable when instance load is cheap and load and load-address // operate on same memory // instance_address = instance as VariableReference; if (instance_address == null) instance_address = instance as LocalTemporary; if (instance_address == null) { EmitLoad (ec, false); ec.Emit (OpCodes.Dup); ec.EmitLoadFromPtr (instance.Type); conditional_access_dup = true; } else { instance.Emit (ec); } } else { EmitLoad (ec, !conditionalAccess); if (conditionalAccess) { conditional_access_dup = !IsInexpensiveLoad (); if (conditional_access_dup) ec.Emit (OpCodes.Dup); } } if (conditionalAccess) { if (instance.Type.Kind == MemberKind.TypeParameter) ec.Emit (OpCodes.Box, instance.Type); ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); if (conditional_access_dup) ec.Emit (OpCodes.Pop); } } if (conditionalAccess) { if (!ec.ConditionalAccess.Statement) { if (ec.ConditionalAccess.Type.IsNullableType) Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec); else ec.EmitNull (); } ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel); ec.MarkLabel (NullOperatorLabel); if (instance_address != null) { instance_address.AddressOf (ec, AddressOp.Load); } else if (unwrap != null) { unwrap.Emit (ec); var tmp = ec.GetTemporaryLocal (unwrap.Type); ec.Emit (OpCodes.Stloc, tmp); ec.Emit (OpCodes.Ldloca, tmp); ec.FreeTemporaryLocal (tmp, unwrap.Type); } else if (!conditional_access_dup) { instance.Emit (ec); } } }
public Label GetILLabel (EmitContext ec) { if (!il_label_set){ il_label = ec.DefineLabel (); il_label_set = true; } return il_label; }
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 Label GetILLabelCode (EmitContext ec) { if (!il_label_code_set){ il_label_code = ec.DefineLabel (); il_label_code_set = true; } return il_label_code; }
public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher) { if (emitted_dispose) return; emitted_dispose = true; Label end_of_try = ec.DefineLabel (); // Ensure that the only way we can get into this code is through a dispatcher if (have_dispatcher) ec.Emit (OpCodes.Br, end); ec.BeginExceptionBlock (); ec.MarkLabel (dispose_try_block); Label [] labels = null; for (int i = 0; i < resume_points.Count; ++i) { ResumableStatement s = (ResumableStatement) resume_points [i]; Label ret = s.PrepareForDispose (ec, end_of_try); if (ret.Equals (end_of_try) && labels == null) continue; if (labels == null) { labels = new Label [resume_points.Count]; for (int j = 0; j < i; ++j) labels [j] = end_of_try; } labels [i] = ret; } if (labels != null) { int j; for (j = 1; j < labels.Length; ++j) if (!labels [0].Equals (labels [j])) break; bool emit_dispatcher = j < labels.Length; if (emit_dispatcher) { //SymbolWriter.StartIteratorDispatcher (ec.ig); ec.Emit (OpCodes.Ldloc, iterator.CurrentPC); ec.EmitInt (first_resume_pc); ec.Emit (OpCodes.Sub); ec.Emit (OpCodes.Switch, labels); //SymbolWriter.EndIteratorDispatcher (ec.ig); } foreach (ResumableStatement s in resume_points) s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher); } ec.MarkLabel (end_of_try); ec.BeginFinallyBlock (); EmitFinallyBody (ec); ec.EndExceptionBlock (); }
protected override void DoEmit (EmitContext ec) { Label loop = ec.DefineLabel (); Label old_begin = ec.LoopBegin; Label old_end = ec.LoopEnd; ec.LoopBegin = ec.DefineLabel (); ec.LoopEnd = ec.DefineLabel (); ec.MarkLabel (loop); EmbeddedStatement.Emit (ec); ec.MarkLabel (ec.LoopBegin); // // Dead code elimination // if (expr is Constant){ bool res = !((Constant) expr).IsDefaultValue; expr.EmitSideEffect (ec); if (res) ec.Emit (OpCodes.Br, loop); } else expr.EmitBranchable (ec, loop, true); ec.MarkLabel (ec.LoopEnd); ec.LoopBegin = old_begin; ec.LoopEnd = old_end; }
protected override void DoEmit (EmitContext ec) { if (InitStatement != null) InitStatement.Emit (ec); if (empty) { Test.EmitSideEffect (ec); return; } Label old_begin = ec.LoopBegin; Label old_end = ec.LoopEnd; Label loop = ec.DefineLabel (); Label test = ec.DefineLabel (); ec.LoopBegin = ec.DefineLabel (); ec.LoopEnd = ec.DefineLabel (); ec.Emit (OpCodes.Br, test); ec.MarkLabel (loop); Statement.Emit (ec); ec.MarkLabel (ec.LoopBegin); Increment.Emit (ec); ec.MarkLabel (test); // // If test is null, there is no test, and we are just // an infinite loop // if (Test != null){ // // The Resolve code already catches the case for // Test == Constant (false) so we know that // this is true // if (Test is Constant) { Test.EmitSideEffect (ec); ec.Emit (OpCodes.Br, loop); } else { Test.EmitBranchable (ec, loop, true); } } else ec.Emit (OpCodes.Br, loop); ec.MarkLabel (ec.LoopEnd); ec.LoopBegin = old_begin; ec.LoopEnd = old_end; }
/// <summary> /// This method emits code for a lookup-based switch statement (non-string) /// Basically it groups the cases into blocks that are at least half full, /// and then spits out individual lookup opcodes for each block. /// It emits the longest blocks first, and short blocks are just /// handled with direct compares. /// </summary> /// <param name="ec"></param> /// <param name="val"></param> /// <returns></returns> void TableSwitchEmit (EmitContext ec, Expression val) { int element_count = Elements.Count; object [] element_keys = new object [element_count]; Elements.Keys.CopyTo (element_keys, 0); Array.Sort (element_keys); // initialize the block list with one element per key var key_blocks = new List<KeyBlock> (element_count); foreach (object key in element_keys) key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key))); KeyBlock current_kb; // iteratively merge the blocks while they are at least half full // there's probably a really cool way to do this with a tree... while (key_blocks.Count > 1) { var key_blocks_new = new List<KeyBlock> (); current_kb = (KeyBlock) key_blocks [0]; for (int ikb = 1; ikb < key_blocks.Count; ikb++) { KeyBlock kb = (KeyBlock) key_blocks [ikb]; if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb)) { // merge blocks current_kb.last = kb.last; current_kb.Size += kb.Size; } else { // start a new block key_blocks_new.Add (current_kb); current_kb = kb; } } key_blocks_new.Add (current_kb); if (key_blocks.Count == key_blocks_new.Count) break; key_blocks = key_blocks_new; } // initialize the key lists foreach (KeyBlock kb in key_blocks) kb.element_keys = new List<object> (); // fill the key lists int iBlockCurr = 0; if (key_blocks.Count > 0) { current_kb = (KeyBlock) key_blocks [0]; foreach (object key in element_keys) { bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last : System.Convert.ToInt64 (key) > current_kb.last; if (next_block) current_kb = (KeyBlock) key_blocks [++iBlockCurr]; current_kb.element_keys.Add (key); } } // sort the blocks so we can tackle the largest ones first key_blocks.Sort (); // okay now we can start... Label lbl_end = ec.DefineLabel (); // at the end ;-) Label lbl_default = default_target; Type type_keys = null; if (element_keys.Length > 0) type_keys = element_keys [0].GetType (); // used for conversions TypeSpec compare_type; if (TypeManager.IsEnumType (SwitchType)) compare_type = EnumSpec.GetUnderlyingType (SwitchType); else compare_type = SwitchType; for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock) { KeyBlock kb = ((KeyBlock) key_blocks [iBlock]); lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel (); if (kb.Length <= 2) { foreach (object key in kb.element_keys) { SwitchLabel sl = (SwitchLabel) Elements [key]; if (key is int && (int) key == 0) { val.EmitBranchable (ec, sl.GetILLabel (ec), false); } else { val.Emit (ec); EmitObjectInteger (ec, key); ec.Emit (OpCodes.Beq, sl.GetILLabel (ec)); } } } else { // TODO: if all the keys in the block are the same and there are // no gaps/defaults then just use a range-check. if (compare_type == TypeManager.int64_type || compare_type == TypeManager.uint64_type) { // TODO: optimize constant/I4 cases // check block range (could be > 2^31) val.Emit (ec); EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys)); ec.Emit (OpCodes.Blt, lbl_default); val.Emit (ec); EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys)); ec.Emit (OpCodes.Bgt, lbl_default); // normalize range val.Emit (ec); if (kb.first != 0) { EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys)); ec.Emit (OpCodes.Sub); } ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels! } else { // normalize range val.Emit (ec); int first = (int) kb.first; if (first > 0) { ec.EmitInt (first); ec.Emit (OpCodes.Sub); } else if (first < 0) { ec.EmitInt (-first); ec.Emit (OpCodes.Add); } } // first, build the list of labels for the switch int iKey = 0; int cJumps = kb.Length; Label [] switch_labels = new Label [cJumps]; for (int iJump = 0; iJump < cJumps; iJump++) { object key = kb.element_keys [iKey]; if (System.Convert.ToInt64 (key) == kb.first + iJump) { SwitchLabel sl = (SwitchLabel) Elements [key]; switch_labels [iJump] = sl.GetILLabel (ec); iKey++; } else switch_labels [iJump] = lbl_default; } // emit the switch opcode ec.Emit (OpCodes.Switch, switch_labels); } // mark the default for this block if (iBlock != 0) ec.MarkLabel (lbl_default); } // TODO: find the default case and emit it here, // to prevent having to do the following jump. // make sure to mark other labels in the default section // the last default just goes to the end if (element_keys.Length > 0) ec.Emit (OpCodes.Br, lbl_default); // now emit the code for the sections bool found_default = false; foreach (SwitchSection ss in Sections) { foreach (SwitchLabel sl in ss.Labels) { if (sl.Converted == SwitchLabel.NullStringCase) { ec.MarkLabel (null_target); } else if (sl.Label == null) { ec.MarkLabel (lbl_default); found_default = true; if (!has_null_case) ec.MarkLabel (null_target); } ec.MarkLabel (sl.GetILLabel (ec)); ec.MarkLabel (sl.GetILLabelCode (ec)); } ss.Block.Emit (ec); } if (!found_default) { ec.MarkLabel (lbl_default); if (!has_null_case) { ec.MarkLabel (null_target); } } ec.MarkLabel (lbl_end); }
public Label LabelTarget (EmitContext ec) { if (defined) return label; label = ec.DefineLabel (); defined = true; return label; }
protected override void EmitFinallyBody (EmitContext ec) { Label skip = ec.DefineLabel (); bool emit_null_check = !TypeManager.IsValueType (var.Type); if (emit_null_check) { var.Emit (ec); ec.Emit (OpCodes.Brfalse, skip); } Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc); if (emit_null_check) ec.MarkLabel (skip); }
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); }
internal void EmitMoveNext(EmitContext ec, Block original_block) { move_next_ok = ec.DefineLabel (); move_next_error = ec.DefineLabel (); if (resume_points == null) { EmitMoveNext_NoResumePoints (ec, original_block); return; } current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type); ec.Emit (OpCodes.Ldarg_0); ec.Emit (OpCodes.Ldfld, IteratorHost.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.Emit (OpCodes.Ldarg_0); ec.EmitInt ((int) State.After); ec.Emit (OpCodes.Stfld, IteratorHost.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 (TypeManager.bool_type); ec.Emit (OpCodes.Ldc_I4_0); ec.Emit (OpCodes.Stloc, skip_finally); } SymbolWriter.StartIteratorDispatcher (ec); ec.Emit (OpCodes.Ldloc, current_pc); ec.Emit (OpCodes.Switch, labels); ec.Emit (OpCodes.Br, move_next_error); SymbolWriter.EndIteratorDispatcher (ec); ec.MarkLabel (labels [0]); SymbolWriter.StartIteratorBody (ec); original_block.Emit (ec); SymbolWriter.EndIteratorBody (ec); SymbolWriter.StartIteratorDispatcher (ec); ec.Emit (OpCodes.Ldarg_0); ec.EmitInt ((int) State.After); ec.Emit (OpCodes.Stfld, IteratorHost.PC.Spec); ec.MarkLabel (move_next_error); ec.EmitInt (0); ec.Emit (OpCodes.Ret); ec.MarkLabel (move_next_ok); ec.Emit (OpCodes.Ldc_I4_1); ec.Emit (OpCodes.Ret); SymbolWriter.EndIteratorDispatcher (ec); }
protected void EmitCall (EmitContext ec, Expression binder, Arguments arguments, bool isStatement) { // // This method generates all internal infrastructure for a dynamic call. The // reason why it's quite complicated is the mixture of dynamic and anonymous // methods. Dynamic itself requires a temporary class (ContainerX) and anonymous // methods can generate temporary storey as well (AnonStorey). Handling MVAR // type parameters rewrite is non-trivial in such case as there are various // combinations possible therefore the mutator is not straightforward. Secondly // we need to keep both MVAR(possibly VAR for anon storey) and type VAR to emit // correct Site field type and its access from EmitContext. // int dyn_args_count = arguments == null ? 0 : arguments.Count; int default_args = isStatement ? 1 : 2; var module = ec.Module; bool has_ref_out_argument = false; var targs = new TypeExpression[dyn_args_count + default_args]; targs[0] = new TypeExpression (module.PredefinedTypes.CallSite.TypeSpec, loc); TypeExpression[] targs_for_instance = null; TypeParameterMutator mutator; var site_container = ec.CreateDynamicSite (); if (context_mvars != null) { TypeParameters tparam; TypeContainer sc = site_container; do { tparam = sc.CurrentTypeParameters; sc = sc.Parent; } while (tparam == null); mutator = new TypeParameterMutator (context_mvars, tparam); if (!ec.IsAnonymousStoreyMutateRequired) { targs_for_instance = new TypeExpression[targs.Length]; targs_for_instance[0] = targs[0]; } } else { mutator = null; } for (int i = 0; i < dyn_args_count; ++i) { Argument a = arguments[i]; if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref) has_ref_out_argument = true; var t = a.Type; // Convert any internal type like dynamic or null to object if (t.Kind == MemberKind.InternalCompilerType) t = ec.BuiltinTypes.Object; if (targs_for_instance != null) targs_for_instance[i + 1] = new TypeExpression (t, loc); if (mutator != null) t = t.Mutate (mutator); targs[i + 1] = new TypeExpression (t, loc); } TypeExpr del_type = null; TypeExpr del_type_instance_access = null; if (!has_ref_out_argument) { string d_name = isStatement ? "Action" : "Func"; TypeSpec te = null; Namespace type_ns = module.GlobalRootNamespace.GetNamespace ("System", true); if (type_ns != null) { te = type_ns.LookupType (module, d_name, dyn_args_count + default_args, LookupMode.Normal, loc); } if (te != null) { if (!isStatement) { var t = type; if (t.Kind == MemberKind.InternalCompilerType) t = ec.BuiltinTypes.Object; if (targs_for_instance != null) targs_for_instance[targs_for_instance.Length - 1] = new TypeExpression (t, loc); if (mutator != null) t = t.Mutate (mutator); targs[targs.Length - 1] = new TypeExpression (t, loc); } del_type = new GenericTypeExpr (te, new TypeArguments (targs), loc); if (targs_for_instance != null) del_type_instance_access = new GenericTypeExpr (te, new TypeArguments (targs_for_instance), loc); else del_type_instance_access = del_type; } } // // Create custom delegate when no appropriate predefined delegate has been found // Delegate d; if (del_type == null) { TypeSpec rt = isStatement ? ec.BuiltinTypes.Void : type; Parameter[] p = new Parameter[dyn_args_count + 1]; p[0] = new Parameter (targs[0], "p0", Parameter.Modifier.NONE, null, loc); var site = ec.CreateDynamicSite (); int index = site.Containers == null ? 0 : site.Containers.Count; if (mutator != null) rt = mutator.Mutate (rt); for (int i = 1; i < dyn_args_count + 1; ++i) { p[i] = new Parameter (targs[i], "p" + i.ToString ("X"), arguments[i - 1].Modifier, null, loc); } d = new Delegate (site, new TypeExpression (rt, loc), Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED, new MemberName ("Container" + index.ToString ("X")), new ParametersCompiled (p), null); d.CreateContainer (); d.DefineContainer (); d.Define (); d.PrepareEmit (); site.AddTypeContainer (d); // // Add new container to inflated site container when the // member cache already exists // if (site.CurrentType is InflatedTypeSpec && index > 0) site.CurrentType.MemberCache.AddMember (d.CurrentType); del_type = new TypeExpression (d.CurrentType, loc); if (targs_for_instance != null) { del_type_instance_access = null; } else { del_type_instance_access = del_type; } } else { d = null; } var site_type_decl = new GenericTypeExpr (module.PredefinedTypes.CallSiteGeneric.TypeSpec, new TypeArguments (del_type), loc); var field = site_container.CreateCallSiteField (site_type_decl, loc); if (field == null) return; if (del_type_instance_access == null) { var dt = d.CurrentType.DeclaringType.MakeGenericType (module, context_mvars.Types); del_type_instance_access = new TypeExpression (MemberCache.GetMember (dt, d.CurrentType), loc); } var instanceAccessExprType = new GenericTypeExpr (module.PredefinedTypes.CallSiteGeneric.TypeSpec, new TypeArguments (del_type_instance_access), loc); if (instanceAccessExprType.ResolveAsType (ec.MemberContext) == null) return; bool inflate_using_mvar = context_mvars != null && ec.IsAnonymousStoreyMutateRequired; TypeSpec gt; if (inflate_using_mvar || context_mvars == null) { gt = site_container.CurrentType; } else { gt = site_container.CurrentType.MakeGenericType (module, context_mvars.Types); } // When site container already exists the inflated version has to be // updated manually to contain newly created field if (gt is InflatedTypeSpec && site_container.AnonymousMethodsCounter > 1) { var tparams = gt.MemberDefinition.TypeParametersCount > 0 ? gt.MemberDefinition.TypeParameters : TypeParameterSpec.EmptyTypes; var inflator = new TypeParameterInflator (module, gt, tparams, gt.TypeArguments); gt.MemberCache.AddMember (field.InflateMember (inflator)); } FieldExpr site_field_expr = new FieldExpr (MemberCache.GetMember (gt, field), loc); BlockContext bc = new BlockContext (ec.MemberContext, null, ec.BuiltinTypes.Void); Arguments args = new Arguments (1); args.Add (new Argument (binder)); StatementExpression s = new StatementExpression (new SimpleAssign (site_field_expr, new Invocation (new MemberAccess (instanceAccessExprType, "Create"), args))); using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { var conditionalAccessReceiver = IsConditionalAccessReceiver; var ca = ec.ConditionalAccess; if (conditionalAccessReceiver) { ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()) { Statement = isStatement }; // // Emit conditional access expressions before dynamic call // is initialized. It pushes site_field_expr on stack before // the actual instance argument is emited which would cause // jump from non-empty stack. // EmitConditionalAccess (ec); } if (s.Resolve (bc)) { Statement init = new If (new Binary (Binary.Operator.Equality, site_field_expr, new NullLiteral (loc)), s, loc); init.Emit (ec); } args = new Arguments (1 + dyn_args_count); args.Add (new Argument (site_field_expr)); if (arguments != null) { int arg_pos = 1; foreach (Argument a in arguments) { if (a is NamedArgument) { // Name is not valid in this context args.Add (new Argument (a.Expr, a.ArgType)); } else { args.Add (a); } if (inflate_using_mvar && a.Type != targs[arg_pos].Type) a.Expr.Type = targs[arg_pos].Type; ++arg_pos; } } var target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (bc), args, false, loc).Resolve (bc); if (target != null) { target.Emit (ec); } if (conditionalAccessReceiver) { ec.CloseConditionalAccess (!isStatement && type.IsNullableType ? type : null); ec.ConditionalAccess = ca; } } }
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); }
void EmitMoveNext(EmitContext ec) { move_next_ok = ec.DefineLabel(); move_next_error = ec.DefineLabel(); if (resume_points == null) { EmitMoveNext_NoResumePoints(ec, block); 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(); } SymbolWriter.StartIteratorDispatcher(ec); ec.Emit(OpCodes.Ldloc, current_pc); ec.Emit(OpCodes.Switch, labels); ec.Emit(async_init != null ? OpCodes.Leave : OpCodes.Br, move_next_error); SymbolWriter.EndIteratorDispatcher(ec); ec.MarkLabel(labels[0]); iterator_body_end = ec.DefineLabel(); SymbolWriter.StartIteratorBody(ec); block.Emit(ec); SymbolWriter.EndIteratorBody(ec); SymbolWriter.StartIteratorDispatcher(ec); ec.MarkLabel(iterator_body_end); if (async_init != null) { 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)async_init.Storey).EmitSetException(ec, new LocalVariableReference(catch_value, Location)); ec.Emit(OpCodes.Leave, move_next_ok); ec.EndExceptionBlock(); } 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); } SymbolWriter.EndIteratorDispatcher(ec); }
public override void EmitStatement (EmitContext ec) { if (conditionalAccessReceiver) { ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()) { Statement = true }; } var call = new CallEmitter (); call.InstanceExpression = InstanceExpr; call.EmitStatement (ec, method, arguments, loc); if (conditionalAccessReceiver) ec.CloseConditionalAccess (null); }
protected override void DoEmit (EmitContext ec) { if (empty) { expr.EmitSideEffect (ec); return; } Label old_begin = ec.LoopBegin; Label old_end = ec.LoopEnd; ec.LoopBegin = ec.DefineLabel (); ec.LoopEnd = ec.DefineLabel (); // // Inform whether we are infinite or not // if (expr is Constant){ // expr is 'true', since the 'empty' case above handles the 'false' case ec.MarkLabel (ec.LoopBegin); expr.EmitSideEffect (ec); Statement.Emit (ec); ec.Emit (OpCodes.Br, ec.LoopBegin); // // Inform that we are infinite (ie, `we return'), only // if we do not `break' inside the code. // ec.MarkLabel (ec.LoopEnd); } else { Label while_loop = ec.DefineLabel (); ec.Emit (OpCodes.Br, ec.LoopBegin); ec.MarkLabel (while_loop); Statement.Emit (ec); ec.MarkLabel (ec.LoopBegin); ec.Mark (loc); expr.EmitBranchable (ec, while_loop, true); ec.MarkLabel (ec.LoopEnd); } ec.LoopBegin = old_begin; ec.LoopEnd = old_end; }
public void Emit(EmitContext ec, bool conditionalAccess) { Label NullOperatorLabel; Nullable.Unwrap unwrap; if (conditionalAccess && Expression.IsNeverNull(instance)) { conditionalAccess = false; } if (conditionalAccess) { NullOperatorLabel = ec.DefineLabel(); unwrap = instance as Nullable.Unwrap; } else { NullOperatorLabel = new Label(); unwrap = null; } IMemoryLocation instance_address = null; bool conditional_access_dup = false; if (unwrap != null) { unwrap.Store(ec); unwrap.EmitCheck(ec); ec.Emit(OpCodes.Brtrue_S, NullOperatorLabel); } else { if (conditionalAccess && addressRequired) { // // Don't allocate temp variable when instance load is cheap and load and load-address // operate on same memory // instance_address = instance as VariableReference; if (instance_address == null) { instance_address = instance as LocalTemporary; } if (instance_address == null) { EmitLoad(ec, false); ec.Emit(OpCodes.Dup); ec.EmitLoadFromPtr(instance.Type); conditional_access_dup = true; } else { instance.Emit(ec); } } else { EmitLoad(ec, !conditionalAccess); if (conditionalAccess) { conditional_access_dup = !IsInexpensiveLoad(); if (conditional_access_dup) { ec.Emit(OpCodes.Dup); } } } if (conditionalAccess) { if (instance.Type.Kind == MemberKind.TypeParameter) { ec.Emit(OpCodes.Box, instance.Type); } ec.Emit(OpCodes.Brtrue_S, NullOperatorLabel); if (conditional_access_dup) { ec.Emit(OpCodes.Pop); } } } if (conditionalAccess) { if (!ec.ConditionalAccess.Statement) { if (ec.ConditionalAccess.Type.IsNullableType) { Nullable.LiftedNull.Create(ec.ConditionalAccess.Type, Location.Null).Emit(ec); } else { ec.EmitNull(); } } ec.Emit(OpCodes.Br, ec.ConditionalAccess.EndLabel); ec.MarkLabel(NullOperatorLabel); if (instance_address != null) { instance_address.AddressOf(ec, AddressOp.Load); } else if (unwrap != null) { unwrap.Emit(ec); var tmp = ec.GetTemporaryLocal(unwrap.Type); ec.Emit(OpCodes.Stloc, tmp); ec.Emit(OpCodes.Ldloca, tmp); ec.FreeTemporaryLocal(tmp, unwrap.Type); } else if (!conditional_access_dup) { instance.Emit(ec); } } }
public void Emit(EmitContext ec, bool conditionalAccess) { Label NullOperatorLabel; Nullable.Unwrap unwrap; if (conditionalAccess && Expression.IsNeverNull(instance)) { conditionalAccess = false; } if (conditionalAccess) { NullOperatorLabel = ec.DefineLabel(); unwrap = instance as Nullable.Unwrap; } else { NullOperatorLabel = new Label(); unwrap = null; } if (unwrap != null) { unwrap.Store(ec); unwrap.EmitCheck(ec); ec.Emit(OpCodes.Brtrue_S, NullOperatorLabel); } else { EmitLoad(ec); if (conditionalAccess) { ec.Emit(OpCodes.Dup); ec.Emit(OpCodes.Brtrue_S, NullOperatorLabel); ec.Emit(OpCodes.Pop); } } if (conditionalAccess) { if (!ec.ConditionalAccess.Statement) { if (ec.ConditionalAccess.Type.IsNullableType) { Nullable.LiftedNull.Create(ec.ConditionalAccess.Type, Location.Null).Emit(ec); } else { ec.EmitNull(); } } ec.Emit(OpCodes.Br, ec.ConditionalAccess.EndLabel); ec.MarkLabel(NullOperatorLabel); if (unwrap != null) { unwrap.Emit(ec); var tmp = ec.GetTemporaryLocal(unwrap.Type); ec.Emit(OpCodes.Stloc, tmp); ec.Emit(OpCodes.Ldloca, tmp); ec.FreeTemporaryLocal(tmp, unwrap.Type); } } }
public void EmitDispose(EmitContext ec) { Label end = ec.DefineLabel (); Label [] labels = null; int n_resume_points = resume_points == null ? 0 : resume_points.Count; for (int i = 0; i < n_resume_points; ++i) { ResumableStatement s = (ResumableStatement) 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 (TypeManager.uint32_type); ec.Emit (OpCodes.Ldarg_0); ec.Emit (OpCodes.Ldfld, IteratorHost.PC.Spec); ec.Emit (OpCodes.Stloc, current_pc); } ec.Emit (OpCodes.Ldarg_0); ec.EmitInt ((int) State.After); ec.Emit (OpCodes.Stfld, IteratorHost.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, this, end, true); } ec.MarkLabel (end); }
public Label PrepareForEmit (EmitContext ec) { if (!prepared) { prepared = true; resume_point = ec.DefineLabel (); } return resume_point; }
protected override void DoEmit(EmitContext ec) { Label label_init = ec.DefineLabel (); ec.Emit (OpCodes.Ldarg_0); ec.Emit (OpCodes.Ldflda, host.PC.Spec); ec.EmitInt ((int) Iterator.State.Start); ec.EmitInt ((int) Iterator.State.Uninitialized); ec.Emit (OpCodes.Call, TypeManager.int_interlocked_compare_exchange); ec.EmitInt ((int) Iterator.State.Uninitialized); ec.Emit (OpCodes.Bne_Un_S, label_init); ec.Emit (OpCodes.Ldarg_0); ec.Emit (OpCodes.Ret); ec.MarkLabel (label_init); new_storey.Emit (ec); ec.Emit (OpCodes.Ret); }
protected sealed override void DoEmit (EmitContext ec) { EmitPreTryBody (ec); if (resume_points != null) { ec.EmitInt ((int) Iterator.State.Running); ec.Emit (OpCodes.Stloc, iter.CurrentPC); } ec.BeginExceptionBlock (); if (resume_points != null) { ec.MarkLabel (resume_point); // For normal control flow, we want to fall-through the Switch // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above ec.Emit (OpCodes.Ldloc, iter.CurrentPC); ec.EmitInt (first_resume_pc); ec.Emit (OpCodes.Sub); Label [] labels = new Label [resume_points.Count]; for (int i = 0; i < resume_points.Count; ++i) labels [i] = resume_points [i].PrepareForEmit (ec); ec.Emit (OpCodes.Switch, labels); } EmitTryBody (ec); ec.BeginFinallyBlock (); Label start_finally = ec.DefineLabel (); if (resume_points != null) { ec.Emit (OpCodes.Ldloc, iter.SkipFinally); ec.Emit (OpCodes.Brfalse_S, start_finally); ec.Emit (OpCodes.Endfinally); } ec.MarkLabel (start_finally); EmitFinallyBody (ec); ec.EndExceptionBlock (); }
void DoEmitStringSwitch (LocalTemporary value, EmitContext ec) { Label l_initialized = ec.DefineLabel (); // // Skip initialization when value is null // value.EmitBranchable (ec, null_target, false); // // Check if string dictionary is initialized and initialize // switch_cache_field.EmitBranchable (ec, l_initialized, true); string_dictionary.EmitStatement (ec); ec.MarkLabel (l_initialized); LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type); ResolveContext rc = new ResolveContext (ec.MemberContext); if (TypeManager.generic_ienumerable_type != null) { Arguments get_value_args = new Arguments (2); get_value_args.Add (new Argument (value)); get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out)); Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc); if (get_item == null) return; // // A value was not found, go to default case // get_item.EmitBranchable (ec, default_target, false); } else { Arguments get_value_args = new Arguments (1); get_value_args.Add (new Argument (value)); Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc); if (get_item == null) return; LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type); get_item_object.EmitAssign (ec, get_item, true, false); ec.Emit (OpCodes.Brfalse, default_target); ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable, new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc); get_item_int.EmitStatement (ec); get_item_object.Release (ec); } TableSwitchEmit (ec, string_switch_variable); string_switch_variable.Release (ec); }
protected void EmitCall(EmitContext ec, Expression binder, Arguments arguments, bool isStatement) { // // This method generates all internal infrastructure for a dynamic call. The // reason why it's quite complicated is the mixture of dynamic and anonymous // methods. Dynamic itself requires a temporary class (ContainerX) and anonymous // methods can generate temporary storey as well (AnonStorey). Handling MVAR // type parameters rewrite is non-trivial in such case as there are various // combinations possible therefore the mutator is not straightforward. Secondly // we need to keep both MVAR(possibly VAR for anon storey) and type VAR to emit // correct Site field type and its access from EmitContext. // int dyn_args_count = arguments == null ? 0 : arguments.Count; int default_args = isStatement ? 1 : 2; var module = ec.Module; bool has_ref_out_argument = false; var targs = new TypeExpression[dyn_args_count + default_args]; targs[0] = new TypeExpression(module.PredefinedTypes.CallSite.TypeSpec, loc); TypeExpression[] targs_for_instance = null; TypeParameterMutator mutator; var site_container = ec.CreateDynamicSite(); if (context_mvars != null) { TypeParameters tparam; TypeContainer sc = site_container; do { tparam = sc.CurrentTypeParameters; sc = sc.Parent; } while (tparam == null); mutator = new TypeParameterMutator(context_mvars, tparam); if (!ec.IsAnonymousStoreyMutateRequired) { targs_for_instance = new TypeExpression[targs.Length]; targs_for_instance[0] = targs[0]; } } else { mutator = null; } for (int i = 0; i < dyn_args_count; ++i) { Argument a = arguments[i]; if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref) { has_ref_out_argument = true; } var t = a.Type; // Convert any internal type like dynamic or null to object if (t.Kind == MemberKind.InternalCompilerType) { t = ec.BuiltinTypes.Object; } if (targs_for_instance != null) { targs_for_instance[i + 1] = new TypeExpression(t, loc); } if (mutator != null) { t = t.Mutate(mutator); } targs[i + 1] = new TypeExpression(t, loc); } TypeExpr del_type = null; TypeExpr del_type_instance_access = null; if (!has_ref_out_argument) { string d_name = isStatement ? "Action" : "Func"; TypeSpec te = null; Namespace type_ns = module.GlobalRootNamespace.GetNamespace("System", true); if (type_ns != null) { te = type_ns.LookupType(module, d_name, dyn_args_count + default_args, LookupMode.IgnoreAccessibility, loc); } if (te != null) { if (!isStatement) { var t = type; if (t.Kind == MemberKind.InternalCompilerType) { t = ec.BuiltinTypes.Object; } if (targs_for_instance != null) { targs_for_instance[targs_for_instance.Length - 1] = new TypeExpression(t, loc); } if (mutator != null) { t = t.Mutate(mutator); } targs[targs.Length - 1] = new TypeExpression(t, loc); } del_type = new GenericTypeExpr(te, new TypeArguments(targs), loc); if (targs_for_instance != null) { del_type_instance_access = new GenericTypeExpr(te, new TypeArguments(targs_for_instance), loc); } else { del_type_instance_access = del_type; } } } // // Create custom delegate when no appropriate predefined delegate has been found // Delegate d; if (del_type == null) { TypeSpec rt = isStatement ? ec.BuiltinTypes.Void : type; Parameter[] p = new Parameter[dyn_args_count + 1]; p[0] = new Parameter(targs[0], "p0", Parameter.Modifier.NONE, null, loc); var site = ec.CreateDynamicSite(); int index = site.Containers == null ? 0 : site.Containers.Count; if (mutator != null) { rt = mutator.Mutate(rt); } for (int i = 1; i < dyn_args_count + 1; ++i) { p[i] = new Parameter(targs[i], "p" + i.ToString("X"), arguments[i - 1].Modifier, null, loc); } d = new Delegate(site, new TypeExpression(rt, loc), Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED, new MemberName("Container" + index.ToString("X")), new ParametersCompiled(p), null); d.CreateContainer(); d.DefineContainer(); d.Define(); d.PrepareEmit(); site.AddTypeContainer(d); // // Add new container to inflated site container when the // member cache already exists // if (site.CurrentType is InflatedTypeSpec && index > 0) { site.CurrentType.MemberCache.AddMember(d.CurrentType); } del_type = new TypeExpression(d.CurrentType, loc); if (targs_for_instance != null) { del_type_instance_access = null; } else { del_type_instance_access = del_type; } } else { d = null; } var site_type_decl = new GenericTypeExpr(module.PredefinedTypes.CallSiteGeneric.TypeSpec, new TypeArguments(del_type), loc); var field = site_container.CreateCallSiteField(site_type_decl, loc); if (field == null) { return; } if (del_type_instance_access == null) { var dt = d.CurrentType.DeclaringType.MakeGenericType(module, context_mvars.Types); del_type_instance_access = new TypeExpression(MemberCache.GetMember(dt, d.CurrentType), loc); } var instanceAccessExprType = new GenericTypeExpr(module.PredefinedTypes.CallSiteGeneric.TypeSpec, new TypeArguments(del_type_instance_access), loc); if (instanceAccessExprType.ResolveAsType(ec.MemberContext) == null) { return; } bool inflate_using_mvar = context_mvars != null && ec.IsAnonymousStoreyMutateRequired; TypeSpec gt; if (inflate_using_mvar || context_mvars == null) { gt = site_container.CurrentType; } else { gt = site_container.CurrentType.MakeGenericType(module, context_mvars.Types); } // When site container already exists the inflated version has to be // updated manually to contain newly created field if (gt is InflatedTypeSpec && site_container.AnonymousMethodsCounter > 1) { var tparams = gt.MemberDefinition.TypeParametersCount > 0 ? gt.MemberDefinition.TypeParameters : TypeParameterSpec.EmptyTypes; var inflator = new TypeParameterInflator(module, gt, tparams, gt.TypeArguments); gt.MemberCache.AddMember(field.InflateMember(inflator)); } FieldExpr site_field_expr = new FieldExpr(MemberCache.GetMember(gt, field), loc); BlockContext bc = new BlockContext(ec.MemberContext, null, ec.BuiltinTypes.Void); Arguments args = new Arguments(1); args.Add(new Argument(binder)); StatementExpression s = new StatementExpression(new SimpleAssign(site_field_expr, new Invocation(new MemberAccess(instanceAccessExprType, "Create"), args))); using (ec.With(BuilderContext.Options.OmitDebugInfo, true)) { var conditionalAccessReceiver = IsConditionalAccessReceiver; var ca = ec.ConditionalAccess; if (conditionalAccessReceiver) { ec.ConditionalAccess = new ConditionalAccessContext(type, ec.DefineLabel()) { Statement = isStatement }; // // Emit conditional access expressions before dynamic call // is initialized. It pushes site_field_expr on stack before // the actual instance argument is emited which would cause // jump from non-empty stack. // EmitConditionalAccess(ec); } if (s.Resolve(bc)) { Statement init = new If(new Binary(Binary.Operator.Equality, site_field_expr, new NullLiteral(loc)), s, loc); init.Emit(ec); } args = new Arguments(1 + dyn_args_count); args.Add(new Argument(site_field_expr)); if (arguments != null) { int arg_pos = 1; foreach (Argument a in arguments) { if (a is NamedArgument) { // Name is not valid in this context args.Add(new Argument(a.Expr, a.ArgType)); } else { args.Add(a); } if (inflate_using_mvar && a.Type != targs[arg_pos].Type) { a.Expr.Type = targs[arg_pos].Type; } ++arg_pos; } } var target = new DelegateInvocation(new MemberAccess(site_field_expr, "Target", loc).Resolve(bc), args, false, loc).Resolve(bc); if (target != null) { target.Emit(ec); } if (conditionalAccessReceiver) { ec.CloseConditionalAccess(!isStatement && type.IsNullableType ? type : null); ec.ConditionalAccess = ca; } } }
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 (); } bool is_static = (method.ModFlags & Modifiers.STATIC) != 0; if (is_static && am_cache == null) { // // 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 // for (var b = Block.Parent; b != null; b = b.Parent) { if (b.ParametersBlock.StateMachine != null) { ec.Emit (OpCodes.Ldfld, b.ParametersBlock.StateMachine.HoistedThis.Field.Spec); break; } } } 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) delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, method.TypeParameters); 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); } }
protected override void DoEmit (EmitContext ec) { // // Needed to emit anonymous storey initialization // Otherwise it does not contain any statements for now // block.Emit (ec); default_target = ec.DefineLabel (); null_target = ec.DefineLabel (); // Store variable for comparission purposes // TODO: Don't duplicate non-captured VariableReference LocalTemporary value; if (HaveUnwrap) { value = new LocalTemporary (SwitchType); unwrap.EmitCheck (ec); ec.Emit (OpCodes.Brfalse, null_target); new_expr.Emit (ec); value.Store (ec); } else if (!is_constant) { value = new LocalTemporary (SwitchType); new_expr.Emit (ec); value.Store (ec); } else value = null; // // Setup the codegen context // Label old_end = ec.LoopEnd; Switch old_switch = ec.Switch; ec.LoopEnd = ec.DefineLabel (); ec.Switch = this; // Emit Code. if (is_constant) { if (constant_section != null) constant_section.Block.Emit (ec); } else if (string_dictionary != null) { DoEmitStringSwitch (value, ec); } else { TableSwitchEmit (ec, value); } if (value != null) value.Release (ec); // Restore context state. ec.MarkLabel (ec.LoopEnd); // // Restore the previous context // ec.LoopEnd = old_end; ec.Switch = old_switch; }