// see Ruby Language.doc/Runtime/Control Flow Implementation/Redo internal override MSA.Expression /*!*/ Transform(AstGenerator /*!*/ gen) { // eval: if (gen.CompilerOptions.IsEval) { return(Methods.EvalRedo.OpCall(gen.CurrentScopeVariable)); } // loop: if (gen.CurrentLoop != null) { return(Ast.Block( Ast.Assign(gen.CurrentLoop.RedoVariable, AstUtils.Constant(true)), Ast.Continue(gen.CurrentLoop.ContinueLabel), AstUtils.Empty() )); } // block: if (gen.CurrentBlock != null) { return(Ast.Continue(gen.CurrentBlock.RedoLabel)); } // method: return(Methods.MethodRedo.OpCall(gen.CurrentScopeVariable)); }
internal static MSA.Expression /*!*/ TransformRetry(AstGenerator /*!*/ gen) { // eval: if (gen.CompilerOptions.IsEval) { return(Methods.EvalRetry.OpCall(gen.CurrentRfcVariable)); } // rescue clause: if (gen.CurrentRescue != null) { return(Ast.Block( Ast.Assign(gen.CurrentRescue.RetryingVariable, Ast.Constant(true)), Ast.Continue(gen.CurrentRescue.ContinueLabel), Ast.Empty() )); } // block: if (gen.CurrentBlock != null) { return(gen.Return(Methods.BlockRetry.OpCall(gen.CurrentBlock.BfcVariable))); } // primary frame: return(gen.Return(Methods.MethodRetry.OpCall(gen.CurrentRfcVariable, gen.MakeMethodBlockParameterRead()))); }
// see Ruby Language.doc/Runtime/Control Flow Implementation/Next internal override MSA.Expression /*!*/ Transform(AstGenerator /*!*/ gen) { MSA.Expression transformedReturnValue = TransformReturnValue(gen); // eval: if (gen.CompilerOptions.IsEval) { return(Methods.EvalNext.OpCall(gen.CurrentScopeVariable, AstUtils.Box(transformedReturnValue))); } // loop: if (gen.CurrentLoop != null) { return(Ast.Block( transformedReturnValue, // evaluate for side-effects Ast.Continue(gen.CurrentLoop.ContinueLabel), AstUtils.Empty() )); } // block: if (gen.CurrentBlock != null) { return(gen.Return(transformedReturnValue)); } // method: return(Methods.MethodNext.OpCall(gen.CurrentScopeVariable, AstUtils.Box(transformedReturnValue))); }
protected override bool Walk(ReturnStatement node) { Variable var; var mce = node.Expression as MethodCallExpression; if (mce != null && IsTCE(mce, out var)) { if (!(Current.Body is LabeledStatement)) { Current.Body = Ast.Labeled(Current.Body); } var ee = new List <Expression>(); int i = 0; var temps = new List <Variable>(); foreach (var par in Current.Parameters) { var v = Current.CreateLocalVariable((SymbolId)Builtins.GenSym(par.Name), par.Type); ee.Add(Ast.Assign(v, mce.Arguments[i++])); temps.Add(v); } i = 0; foreach (var par in Current.Parameters) { ee.Add(Ast.Assign(par, Ast.Read(temps[i++]))); } ee.Add(Ast.Void(Ast.Continue(mce.Span))); node.Expression = Ast.Comma(ee); if (var != null) { var.Lift = false; fixups.Add(var.Block); } Current.Bind(); } return(base.Walk(node)); }
private MSA.Expression /*!*/ TransformExceptionHandling(AstGenerator /*!*/ gen, ResultOperation resultOperation) { Assert.NotNull(gen); MSA.Expression exceptionThrownVariable = gen.CurrentScope.DefineHiddenVariable("#exception-thrown", typeof(bool)); MSA.Expression exceptionRethrowVariable = gen.CurrentScope.DefineHiddenVariable("#exception-rethrow", typeof(bool)); MSA.Expression retryingVariable = gen.CurrentScope.DefineHiddenVariable("#retrying", typeof(bool)); MSA.Expression oldExceptionVariable = gen.CurrentScope.DefineHiddenVariable("#old-exception", typeof(Exception)); MSA.ParameterExpression unwinder, exceptionVariable; MSA.Expression transformedBody; MSA.Expression transformedEnsure; MSA.Expression transformedElse; if (_ensureStatements != null) { transformedEnsure = Ast.Block( // ensure: Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), gen.TransformStatements(_ensureStatements, ResultOperation.Ignore), Methods.SetCurrentException.OpCall(gen.CurrentScopeVariable, oldExceptionVariable), // rethrow: AstUtils.IfThen( Ast.AndAlso( exceptionRethrowVariable, Ast.NotEqual(oldExceptionVariable, AstUtils.Constant(null)) ), Ast.Throw(oldExceptionVariable) ) ); } else { // rethrow: transformedEnsure = AstUtils.IfThen( Ast.AndAlso( exceptionRethrowVariable, Ast.NotEqual( Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), AstUtils.Constant(null, typeof(Exception))) ), Ast.Throw(oldExceptionVariable) ); } if (_elseStatements != null) { transformedElse = gen.TransformStatements(_elseStatements, resultOperation); } else { transformedElse = AstUtils.Empty(); } // body should do return, but else-clause is present => we cannot do return from the guarded statements: // (the value of the last expression in the body cannot be the last executed expression statement => we can ignore it): transformedBody = gen.TransformStatements(_statements, (_elseStatements != null) ? ResultOperation.Ignore : resultOperation); MSA.Expression enterRescue = null, leaveRescue = null; var retryLabel = Ast.Label("retry"); // make rescue clause: MSA.Expression transformedRescue; if (_rescueClauses != null) { // outer-most EH blocks sets and clears runtime flag RuntimeFlowControl.InTryRescue: if (gen.CurrentRescue == null) { enterRescue = Methods.EnterRescue.OpCall(gen.CurrentScopeVariable); leaveRescue = Methods.LeaveRescue.OpCall(gen.CurrentScopeVariable); } else { enterRescue = leaveRescue = AstUtils.Empty(); } gen.EnterRescueClause(retryingVariable, retryLabel); var handlers = new IfStatementTest[_rescueClauses.Count]; for (int i = 0; i < handlers.Length; i++) { handlers[i] = _rescueClauses[i].Transform(gen, resultOperation); } transformedRescue = AstUtils.Try( enterRescue, AstUtils.If(handlers, Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true))) ).Filter(unwinder = Ast.Parameter(typeof(EvalUnwinder), "#u"), Ast.Equal(Ast.Field(unwinder, EvalUnwinder.ReasonField), AstUtils.Constant(BlockReturnReason.Retry)), Ast.Block( Ast.Assign(retryingVariable, AstUtils.Constant(true)), Ast.Continue(retryLabel), AstUtils.Empty() ) ); gen.LeaveRescueClause(); } else { transformedRescue = Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true)); } if (_elseStatements != null) { transformedElse = AstUtils.Unless(exceptionThrownVariable, transformedElse); } var result = Ast.Block( Ast.Label(retryLabel), AstUtils.Try( Ast.Assign(exceptionThrownVariable, AstUtils.Constant(false)), Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(false)), Ast.Assign(retryingVariable, AstUtils.Constant(false)), // save exception (old_$! is not used unless there is a rescue clause): (_rescueClauses == null) ? (MSA.Expression)AstUtils.Empty() : Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), AstUtils.Try( Ast.Block(transformedBody, AstUtils.Empty()) ).Filter(exceptionVariable = Ast.Parameter(typeof(Exception), "#e"), Methods.CanRescue.OpCall(gen.CurrentScopeVariable, exceptionVariable), Ast.Assign(exceptionThrownVariable, AstUtils.Constant(true)), transformedRescue, AstUtils.Empty() ).FinallyIf((_rescueClauses != null), // restore previous exception if the current one has been handled: AstUtils.Unless(exceptionRethrowVariable, Methods.SetCurrentException.OpCall(gen.CurrentScopeVariable, oldExceptionVariable) ), leaveRescue ), // unless (exception_thrown) do <else-statements> end transformedElse, AstUtils.Empty() ).FilterIf((_rescueClauses != null || _elseStatements != null), exceptionVariable = Ast.Parameter(typeof(Exception), "#e"), Methods.CanRescue.OpCall(gen.CurrentScopeVariable, exceptionVariable), Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true)), AstUtils.Empty() ).FinallyWithJumps( AstUtils.Unless(retryingVariable, transformedEnsure) ) ); return(result); }
static Statement Rewrite(Statement body) { if (body is LabeledStatement) { var ls = (LabeledStatement)body; return(ls.Mark(Rewrite(ls.Statement))); } if (body is BlockStatement) { var node = body as BlockStatement; var newbody = new List <Statement>(); foreach (var stmt in node.Statements) { var ns = Rewrite(stmt); if (ns is BlockStatement) { newbody.AddRange(((BlockStatement)ns).Statements); } else { newbody.Add(ns); } } return(Ast.Block(newbody)); } if (body is WriteStatement) { var ws = (WriteStatement)body; if (ws.Value is CommaExpression) { var ce = ws.Value as CommaExpression; var block = RewriteExpressions(ce.Expressions, x => Ast.Write(ws.Variable, x)); return(Rewrite(Ast.Block(block))); } else if (ws.Value == null) { var cb = ws.Variable.Block; if (cb != null) { cb.RemoveVariables(new List <Variable>(new[] { ws.Variable })); cb.Bind(); } return(Ast.Empty()); } else if (ws.Variable.Block == null) { //var cb = ws.Variable.Block; //cb.RemoveVariables(new List<Variable>(new[] { ws.Variable })); //cb.Bind(); return(Ast.Empty()); } else if (ws.HasNoRef) { //var cb = ws.Variable.Block; //cb.RemoveVariables(new List<Variable>(new[] { ws.Variable })); //cb.Bind(); //return Ast.Empty(); } } if (body is ReturnStatement) { var rs = body as ReturnStatement; if (rs.Expression is UnaryExpression && rs.Expression.NodeType == AstNodeType.Convert) { var ux = (UnaryExpression)rs.Expression; var op = ux.Operand; if (op is VoidExpression) { return(Rewrite(op as VoidExpression)); } if (op is CommaExpression) { var ce = op as CommaExpression; var block = RewriteExpressions(ce.Expressions, x => Ast.Return(Ast.ConvertHelper(x, ux.Type))); return(Rewrite(Ast.Block(block))); } } if (rs.Expression is CommaExpression) { var ce = rs.Expression as CommaExpression; var le = ce.Expressions[ce.Expressions.Count - 1]; if (le is VoidExpression && ((VoidExpression)le).Statement is ContinueStatement) { var block = RewriteExpressions(ce.Expressions, x => Ast.Continue()); return(Rewrite(Ast.Block(block))); } else { var block = RewriteExpressions(ce.Expressions, Ast.Return); return(Rewrite(Ast.Block(block))); } } if (rs.Expression is VoidExpression) { var ve = rs.Expression as VoidExpression; return(Rewrite(ve)); } if (rs.Expression is MethodCallExpression) { var mce = rs.Expression as MethodCallExpression; var uce = Unwrap(mce.Instance); if (uce is CommaExpression) { var ce = uce as CommaExpression; var block = RewriteExpressions(ce.Expressions, x => { if (mce.Arguments.Count == 1 && mce.Arguments[0].Type == typeof(object[])) { var mc = Ast.SimpleCallHelper(x, mce.Method, mce.Arguments.ToArray()); mc.TailCall = mce.TailCall; return(Ast.Return(mc)); } else { //var args = Array.ConvertAll(mce.Arguments.ToArray(), y => Ast.ConvertHelper(y, typeof(object))); var mc = Ast.SimpleCallHelper(x, mce.Method, mce.Arguments.ToArray()); mc.TailCall = mce.TailCall; return(Ast.Return(mc)); } }); return(Rewrite(Ast.Block(block))); } } } if (body is ExpressionStatement) { var es = (ExpressionStatement)body; var trs = TryRewriteExpression(es.Expression); if (trs != null) { return(trs); } } if (body is IfStatement) { var ifs = (IfStatement)body; if (ifs.ElseStatement == null) { return(Ast.If(ifs.Tests[0].Test, Rewrite(ifs.Tests[0].Body)).ToStatement()); } else { return(Ast.If(ifs.Tests[0].Test, Rewrite(ifs.Tests[0].Body)).Else(Rewrite(ifs.ElseStatement))); } } return(body); }
internal override MSA.Expression /*!*/ Transform(AstGenerator /*!*/ gen) { MSA.Expression parentScope = gen.CurrentScopeVariable; ScopeBuilder scope = new ScopeBuilder(); // define hidden parameters and RHS-placeholders (#1..#n will be used as RHS of a parallel assignment): MSA.Expression blockParameter, selfParameter; MSA.ParameterExpression[] parameters = DefineParameters(out selfParameter, out blockParameter); MSA.Expression scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyBlockScope)); MSA.LabelTarget redoLabel = Ast.Label(); gen.EnterBlockDefinition( scope, blockParameter, selfParameter, scopeVariable, redoLabel ); if (_definedScope != null) { _definedScope.TransformLocals(scope); } MSA.Expression paramInit = MakeParametersInitialization(gen, parameters); MSA.ParameterExpression blockUnwinder = scope.DefineHiddenVariable("#unwinder", typeof(BlockUnwinder)); MSA.Expression loop = AstFactory.Infinite(null, redoLabel, AstUtils.Try( gen.TransformStatements(_body, ResultOperation.Return) ).Catch(blockUnwinder, // redo: AstUtils.IfThen(Ast.Field(blockUnwinder, BlockUnwinder.IsRedoField), Ast.Continue(redoLabel)), // next: gen.Return(Ast.Field(blockUnwinder, BlockUnwinder.ReturnValueField)) ) ); if (gen.TraceEnabled) { int firstStatementLine = _body.Count > 0 ? _body[0].Location.Start.Line : Location.End.Line; int lastStatementLine = _body.Count > 0 ? _body[_body.Count - 1].Location.End.Line : Location.End.Line; loop = Ast.TryFinally( Ast.Block( Methods.TraceBlockCall.OpCall(scopeVariable, blockParameter, Ast.Convert(Ast.Constant(gen.SourceUnit.Path), typeof(string)), Ast.Constant(firstStatementLine)), loop ), Methods.TraceBlockReturn.OpCall(scopeVariable, blockParameter, Ast.Convert(Ast.Constant(gen.SourceUnit.Path), typeof(string)), Ast.Constant(lastStatementLine)) ); } MSA.Expression body = Ast.Block( Ast.Assign(scopeVariable, Methods.CreateBlockScope.OpCall(scope.VisibleVariables(), parentScope, blockParameter, selfParameter) ), paramInit, loop, Ast.Empty() ); body = gen.AddReturnTarget(scope.CreateScope(body)); gen.LeaveBlockDefinition(); int parameterCount = _parameters.LeftValues.Count; var attributes = _parameters.GetBlockSignatureAttributes(); return(Methods.DefineBlock.OpCall( gen.CurrentScopeVariable, gen.CurrentRfcVariable, gen.CurrentSelfVariable, Ast.Lambda( BlockDispatcher.GetDelegateType(parameterCount, attributes), body, gen.EncodeMethodName(gen.CurrentMethod.MethodName, Location), new ReadOnlyCollection <MSA.ParameterExpression>(parameters) ), Ast.Constant(parameterCount), Ast.Constant(attributes) )); }