private Expression RewriteCallable <TException>(TException node, Expression[] arguments, Converter <TException, Expression> visitor, Func <TException, Expression[], TException> updater) where TException : Expression { var codeInsertionPoint = context.CurrentStatement.PrologueCodeInserter(); var newNode = visitor(node); if (newNode is TException typedExpr) { node = typedExpr; } else { return(newNode); } var hasAwait = false; for (var i = arguments.LongLength - 1L; i >= 0L; i--) { ref Expression arg = ref arguments[i]; if (hasAwait |= ExpressionAttributes.Get(arg)?.ContainsAwait ?? false) { var tempVar = NewStateSlot(arg.Type); codeInsertionPoint(Expression.Assign(tempVar, arg)); arg = tempVar; } }
private void AttachLabel(LabelTarget?target) { if (target is not null) { ExpressionAttributes.Get(CurrentStatement)?.Labels.Add(target); target.GetUserData().GetOrSet(StateIdPlaceholder).StateId = stateId; } }
private void ContainsAwait() { foreach (var attr in attributes) { if (ReferenceEquals(ExpressionAttributes.Get(CurrentStatement), attr)) { return; } attr.ContainsAwait = true; } }
protected override Expression VisitChildren(ExpressionVisitor visitor) { var filter = visitor.Visit(this.filter); if (ExpressionAttributes.Get(filter)?.ContainsAwait ?? false) { throw new NotSupportedException(ExceptionMessages.FilterHasAwait); } var handler = visitor.Visit(Content); handler = handler.AddPrologue(false, prologue).AddEpilogue(false, epilogue).AddEpilogue(false, FaultLabel.Goto()); return(IfThen(filter, handler)); }
protected override Expression VisitConditional(ConditionalExpression node) { if (node.Type == typeof(void)) { node = node.Update(node.Test, Statement.Wrap(node.IfTrue), Statement.Wrap(node.IfFalse)); return(context.Rewrite(node, base.VisitConditional)); } else if (node.IfTrue is BlockExpression && node.IfFalse is BlockExpression) { throw new NotSupportedException(ExceptionMessages.UnsupportedConditionalExpr); } else { /* * x = a ? await b() : c(); * --transformed into-- * var temp; * if(a) * temp = await b(); * else * temp = c(); * x = temp; */ var prologue = context.CurrentStatement.PrologueCodeInserter(); { var result = context.Rewrite(node, base.VisitConditional); if (result is ConditionalExpression conditional) { node = conditional; } else { return(result); } } if ((ExpressionAttributes.Get(node.IfTrue)?.ContainsAwait ?? false) || (ExpressionAttributes.Get(node.IfFalse)?.ContainsAwait ?? false)) { var tempVar = NewStateSlot(node.Type); prologue(Expression.Condition(node.Test, Expression.Assign(tempVar, node.IfTrue), Expression.Assign(tempVar, node.IfFalse), typeof(void))); return(tempVar); } else { return(node); } } }
private Expression RewriteBinary(BinaryExpression node) { var codeInsertionPoint = context.CurrentStatement.PrologueCodeInserter(); var newNode = base.VisitBinary(node); if (newNode is BinaryExpression binary) { node = binary; } else { return(newNode); } // do not place left operand at statement level because it has no side effects if (node.Left is ParameterExpression || node.Left is ConstantExpression || IsAssignment(node)) { return(node); } var leftIsAsync = ExpressionAttributes.Get(node.Left)?.ContainsAwait ?? false; var rightIsAsync = ExpressionAttributes.Get(node.Right)?.ContainsAwait ?? false; // left operand should be computed before right, so bump it before await expression if (rightIsAsync && !leftIsAsync) { /* * Method() + await a; * --transformed into-- * state.field = Method(); * state.awaiter = a.GetAwaiter(); * MoveNext(state.awaiter, newState); * return; * newState: state.field + state.awaiter.GetResult(); */ var leftTemp = NewStateSlot(node.Left.Type); codeInsertionPoint(Expression.Assign(leftTemp, node.Left)); node = node.Update(leftTemp, node.Conversion, node.Right); } return(node); }
internal IReadOnlyCollection <Expression> CreateJumpPrologue(GotoExpression @goto, ExpressionVisitor visitor) { var state = @goto.Target.GetUserData().GetOrSet(StateIdPlaceholder); var result = new LinkedList <Expression>(); // iterate through snapshot of statements because collection can be modified var statements = this.statements.ToArray(); foreach (var lookup in statements) { if (ExpressionAttributes.Get(lookup)?.Labels.Contains(@goto.Target) ?? false) { break; } if (lookup is TryCatchFinallyStatement statement) { result.AddLast(statement.InlineFinally(visitor, state)); } } Array.Clear(statements, 0, statements.Length); return(result); }