private Expression GenerateIteratorStatement(VariablePath iteratorVariablePath, Func<Expression> generateMoveNextUpdatePosition, int iteratorTupleIndex, LabeledStatementAst stmt, Action<List<Expression>, Expression> generateBody) { // We convert: // foreach ($x in $enumerable) {} // Into: // try // { // $oldforeach = $foreach // $enumerable = condition // $foreach = GetEnumerator $enumerable // if ($foreach == $null && $enumerable != $null) // { // $foreach = (new object[] { $enumerable }).GetEnumerator() // } // if ($foreach != $null) // { // while ($foreach.MoveNext()) // { // $x = $foreach.Current // } // } // } // finally // { // $foreach = $oldforeach // } // The translation for switch is similar. var temps = new List<ParameterExpression>(); var exprs = new List<Expression>(); var avs = new AutomaticVarSaver(this, iteratorVariablePath, iteratorTupleIndex); bool generatingForeach = stmt is ForEachStatementAst; exprs.Add(avs.SaveAutomaticVar()); // $enumerable = condition // $foreach/$switch = GetEnumerator $enumerable var enumerable = NewTemp(typeof(object), "enumerable"); temps.Add(enumerable); if (generatingForeach) { exprs.Add(UpdatePosition(stmt.Condition)); } exprs.Add( Expression.Assign(enumerable, GetRangeEnumerator(stmt.Condition.GetPureExpression()) ?? CaptureStatementResults(stmt.Condition, CaptureAstContext.Enumerable).Convert(typeof(object)))); var iteratorTemp = NewTemp(typeof(IEnumerator), iteratorVariablePath.UnqualifiedPath); temps.Add(iteratorTemp); exprs.Add(Expression.Assign(iteratorTemp, DynamicExpression.Dynamic(PSEnumerableBinder.Get(), typeof(IEnumerator), enumerable))); // In a foreach, generate: // if ($foreach == $null && $enumerable != $null) // { // $foreach = (new object[] { $enumerable }).GetEnumerator() // } // In a switch, generate: // if ($switch == $null) // { // $switch = (new object[] { $enumerable }).GetEnumerator() // } var testNeedScalarToEnumerable = generatingForeach ? Expression.AndAlso( Expression.Equal(iteratorTemp, ExpressionCache.NullConstant), Expression.NotEqual(enumerable, ExpressionCache.NullConstant)) : Expression.Equal(iteratorTemp, ExpressionCache.NullConstant); var scalarToEnumerable = Expression.Assign(iteratorTemp, Expression.Call(Expression.NewArrayInit(typeof(object), Expression.Convert(enumerable, typeof(object))), CachedReflectionInfo.IEnumerable_GetEnumerator)); exprs.Add(Expression.IfThen(testNeedScalarToEnumerable, scalarToEnumerable)); exprs.Add(avs.SetNewValue(iteratorTemp)); var moveNext = Expression.Block( generateMoveNextUpdatePosition(), Expression.Call(iteratorTemp, CachedReflectionInfo.IEnumerator_MoveNext)); var loop = GenerateWhileLoop(stmt.Label, () => moveNext, (loopBody, breakTarget, continueTarget) => generateBody(loopBody, Expression.Property(iteratorTemp, CachedReflectionInfo.IEnumerator_Current))); // With a foreach, the enumerator may never get assigned, in which case we skip the loop entirely. // Generate that test. // With a switch, the switch body is never skipped, so skip generating that test, and skip creating // target block. if (generatingForeach) { exprs.Add(Expression.IfThen(Expression.NotEqual(iteratorTemp, ExpressionCache.NullConstant), loop)); } else { exprs.Add(loop); } return Expression.Block( temps.Concat(avs.GetTemps()), Expression.TryFinally(Expression.Block(exprs), avs.RestoreAutomaticVar())); }
private Expression GenerateIteratorStatement(VariablePath iteratorVariablePath, Func<Expression> generateMoveNextUpdatePosition, int iteratorTupleIndex, LabeledStatementAst stmt, Action<List<Expression>, Expression> generateBody) { List<ParameterExpression> first = new List<ParameterExpression>(); List<Expression> expressions = new List<Expression>(); AutomaticVarSaver saver = new AutomaticVarSaver(this, iteratorVariablePath, iteratorTupleIndex); bool flag = stmt is ForEachStatementAst; expressions.Add(saver.SaveAutomaticVar()); ParameterExpression item = this.NewTemp(typeof(object), "enumerable"); first.Add(item); if (flag) { expressions.Add(this.UpdatePosition(stmt.Condition)); } expressions.Add(Expression.Assign(item, this.GetRangeEnumerator(stmt.Condition.GetPureExpression()) ?? this.CaptureStatementResults(stmt.Condition, CaptureAstContext.Enumerable, null).Convert(typeof(object)))); ParameterExpression iteratorTemp = this.NewTemp(typeof(IEnumerator), iteratorVariablePath.UnqualifiedPath); first.Add(iteratorTemp); expressions.Add(Expression.Assign(iteratorTemp, Expression.Dynamic(PSEnumerableBinder.Get(), typeof(IEnumerator), item))); BinaryExpression test = flag ? Expression.AndAlso(Expression.Equal(iteratorTemp, ExpressionCache.NullConstant), Expression.NotEqual(item, ExpressionCache.NullConstant)) : Expression.Equal(iteratorTemp, ExpressionCache.NullConstant); BinaryExpression ifTrue = Expression.Assign(iteratorTemp, Expression.Call(Expression.NewArrayInit(typeof(object), new Expression[] { Expression.Convert(item, typeof(object)) }), CachedReflectionInfo.IEnumerable_GetEnumerator)); expressions.Add(Expression.IfThen(test, ifTrue)); expressions.Add(saver.SetNewValue(iteratorTemp)); BlockExpression moveNext = Expression.Block(generateMoveNextUpdatePosition(), Expression.Call(iteratorTemp, CachedReflectionInfo.IEnumerator_MoveNext)); Expression expression4 = this.GenerateWhileLoop(stmt.Label, () => moveNext, delegate (List<Expression> loopBody, LabelTarget breakTarget, LabelTarget continueTarget) { generateBody(loopBody, Expression.Property(iteratorTemp, CachedReflectionInfo.IEnumerator_Current)); }, null); if (flag) { expressions.Add(Expression.IfThen(Expression.NotEqual(iteratorTemp, ExpressionCache.NullConstant), expression4)); } else { expressions.Add(expression4); } return Expression.Block(first.Concat<ParameterExpression>(saver.GetTemps()), new Expression[] { Expression.TryFinally(Expression.Block(expressions), saver.RestoreAutomaticVar()) }); }