// when [<expr>, ...] *<array> // // generates this code: // // IEnumerator<object>/*!*/ enumVar = RubyOps.Unsplat(<array>).GetEnumerator(); // bool result = false; // while (enumVar.MoveNext()) { // if (<MakeTest>(enumVar.Current)) { // result = true; // break; // } // } private static MSA.Expression /*!*/ MakeArrayTest(AstGenerator /*!*/ gen, MSA.Expression /*!*/ array, MSA.Expression value) { MSA.Expression enumVariable = gen.CurrentScope.DefineHiddenVariable("#case-enumerator", typeof(IEnumerator <object>)); MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#case-compare-result", typeof(bool)); MSA.LabelTarget label = Ast.Label(); return(AstFactory.Block( Ast.Assign(enumVariable, Ast.Call( Methods.Unsplat.OpCall(AstFactory.Box(array)), Methods.IEnumerable_Of_Object_GetEnumerator )), Ast.Assign(resultVariable, Ast.Constant(false)), AstUtils.While( Ast.Call(enumVariable, Methods.IEnumerator_MoveNext), AstUtils.If( MakeTest(gen, Ast.Call(enumVariable, Methods.IEnumerator_get_Current), value), Ast.Block( Ast.Assign(resultVariable, Ast.Constant(true)), Ast.Break(label), Ast.Empty() ) ), null, label, null ), resultVariable )); }
internal override MSAst.Expression Transform(MSAst.Expression body) { return(GlobalParent.AddDebugInfoAndVoid( AstUtils.If( GlobalParent.Convert(typeof(bool), ConversionResultKind.ExplicitCast, Test), body ), Span )); }
protected override Expression VisitTry(TryExpression node) { MSAst.Expression b = Visit(node.Body); ReadOnlyCollection <CatchBlock> h = Visit(node.Handlers, VisitCatchBlock); MSAst.Expression y = Visit(node.Finally); MSAst.Expression f; _insideConditionalBlock = true; try { f = Visit(node.Fault); } finally { _insideConditionalBlock = false; } node = Ast.MakeTry(node.Type, b, y, f, h); List <MSAst.CatchBlock> newHandlers = null; MSAst.Expression newFinally = null; // If the TryStatement has any Catch blocks we need to insert the exception // event as a first statement so that we can be notified of first-chance exceptions. if (node.Handlers != null && node.Handlers.Count > 0) { newHandlers = new List <CatchBlock>(); foreach (var catchBlock in node.Handlers) { ParameterExpression exceptionVar = catchBlock.Variable ?? Ast.Parameter(catchBlock.Test, null); Expression debugMarker, thread; if (_transformToGenerator) { debugMarker = Ast.Call( typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.GetCurrentSequencePointForGeneratorFrame)), _frame ); thread = Ast.Call(typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.GetThread)), _frame); } else { debugMarker = _debugMarker; thread = _thread; } MSAst.Expression exceptionEvent = Ast.Block( // Rethrow ForceToGeneratorLoopException AstUtils.If( Ast.TypeIs( exceptionVar, typeof(ForceToGeneratorLoopException) ), Ast.Throw(exceptionVar) ), AstUtils.If( Ast.Equal(_globalDebugMode, AstUtils.Constant((int)DebugMode.FullyEnabled)), _pushFrame ?? Ast.Empty(), Ast.Call( typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.OnTraceEvent)), thread, debugMarker, exceptionVar ) ) ); newHandlers.Add(Ast.MakeCatchBlock( catchBlock.Test, exceptionVar, Ast.Block( exceptionEvent, catchBlock.Body ), catchBlock.Filter )); } } if (!_transformToGenerator && node.Finally != null) { // Prevent the user finally block from running if the frame is currently remapping to generator newFinally = AstUtils.If( Ast.Not( Ast.Call( typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.IsCurrentLeafFrameRemappingToGenerator)), _thread ) ), node.Finally ); } if (newHandlers != null || newFinally != null) { node = Ast.MakeTry( node.Type, node.Body, newFinally ?? node.Finally, node.Fault, newHandlers != null ? (IEnumerable <CatchBlock>)newHandlers : node.Handlers ); } return(node); }
// see Ruby Language.doc/Runtime/Control Flow Implementation/While-Until internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#loop-result", typeof(object)); MSA.Expression redoVariable = gen.CurrentScope.DefineHiddenVariable("#skip-condition", typeof(bool)); MSA.ParameterExpression blockUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(BlockUnwinder)); MSA.ParameterExpression evalUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(EvalUnwinder)); bool isInnerLoop = gen.CurrentLoop != null; MSA.LabelTarget breakLabel = Ast.Label(); MSA.LabelTarget continueLabel = Ast.Label(); gen.EnterLoop(redoVariable, resultVariable, breakLabel, continueLabel); MSA.Expression transformedBody = gen.TransformStatements(_statements, ResultOperation.Ignore); MSA.Expression transformedCondition = AstFactory.IsTrue(_condition.TransformRead(gen)); gen.LeaveLoop(); MSA.Expression conditionPositiveStmt, conditionNegativeStmt; if (_isWhileLoop) { conditionPositiveStmt = Ast.Empty(); conditionNegativeStmt = Ast.Break(breakLabel); } else { conditionPositiveStmt = Ast.Break(breakLabel); conditionNegativeStmt = Ast.Empty(); } // make the loop first: MSA.Expression loop = Ast.Block( Ast.Assign(redoVariable, Ast.Constant(_isPostTest)), AstFactory.Infinite(breakLabel, continueLabel, AstUtils.Try( AstUtils.If(redoVariable, Ast.Assign(redoVariable, Ast.Constant(false)) ).ElseIf(transformedCondition, conditionPositiveStmt ).Else( conditionNegativeStmt ), transformedBody ).Catch(blockUnwinder, // redo = u.IsRedo Ast.Assign(redoVariable, Ast.Field(blockUnwinder, BlockUnwinder.IsRedoField)) ).Filter(evalUnwinder, Ast.Equal(Ast.Field(evalUnwinder, EvalUnwinder.ReasonField), AstFactory.BlockReturnReasonBreak), // result = unwinder.ReturnValue Ast.Assign(resultVariable, Ast.Field(evalUnwinder, EvalUnwinder.ReturnValueField)), Ast.Break(breakLabel) ) ), Ast.Empty() ); // wrap it to try finally that updates RFC state: if (!isInnerLoop) { loop = AstUtils.Try( Ast.Assign(Ast.Field(gen.CurrentRfcVariable, RuntimeFlowControl.InLoopField), Ast.Constant(true)), loop ).Finally( Ast.Assign(Ast.Field(gen.CurrentRfcVariable, RuntimeFlowControl.InLoopField), Ast.Constant(false)) ); } return(AstFactory.Block(loop, resultVariable)); }