public static Expression Convert(Expression expression, Type type) { ContractUtils.RequiresNotNull(expression, "expression"); if (expression.Type == type) { return(expression); } if (expression.Type == typeof(void)) { return(Expression.Block(expression, Utils.Default(type))); } if (type == typeof(void)) { return(Void(expression)); } // TODO: this is not the right level for this to be at. It should // be pushed into languages if they really want this behavior. if (type == typeof(object)) { return(Box(expression)); } return(Expression.Convert(expression, type)); }
// Add a default return value if needed private Expression AddDefaultReturn(Expression body) { if (body.Type == typeof(void) && _returnType != typeof(void)) { body = Expression.Block(body, Utils.Default(_returnType)); } return(body); }
/// <summary> /// Takes current result and wraps it into try-filter(MethodUnwinder)-finally block that ensures correct "break" behavior for /// library method calls with block given in bfcVariable (BlockParam). /// </summary> public static void RuleControlFlowBuilder(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args) { if (metaBuilder.Error) { return; } var metaBlock = args.GetMetaBlock(); Debug.Assert(metaBlock != null, "RuleControlFlowBuilder should only be used if the signature has a block"); // We construct CF only for non-nil blocks thus we need a test for it: if (metaBlock.Value == null) { metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null))); return; } // don't need to test the exact type of the Proc since the code is subclass agnostic: metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null))); Expression bfcVariable = metaBuilder.BfcVariable; Debug.Assert(bfcVariable != null); // Method call with proc can invoke control flow that returns an arbitrary value from the call, so we need to type result to Object. // Otherwise, the result could only be result of targetExpression unless its return type is void. Expression resultVariable = metaBuilder.GetTemporary(typeof(object), "#result"); ParameterExpression unwinder; metaBuilder.Result = Ast.Block( Ast.Assign(bfcVariable, Methods.CreateBfcForLibraryMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))), AstUtils.Try( Ast.Assign(resultVariable, AstUtils.Convert(metaBuilder.Result, typeof(object))) ).Filter(unwinder = Ast.Parameter(typeof(MethodUnwinder), "#unwinder"), Methods.IsProcConverterTarget.OpCall(bfcVariable, unwinder), Ast.Assign(resultVariable, Ast.Field(unwinder, MethodUnwinder.ReturnValueField)), AstUtils.Default(typeof(object)) ).Finally( Methods.LeaveProcConverter.OpCall(bfcVariable) ), resultVariable ); }
protected override Expression VisitTry(TryExpression node) { // Visit finally/fault block first BlockInfo block = new BlockInfo { InFinally = true }; _blocks.Push(block); Expression @finally = Visit(node.Finally); Expression fault = Visit(node.Fault); block.InFinally = false; LabelTarget finallyEnd = block.FlowLabel; if (finallyEnd != null) { // Make a new target, which will be emitted after the try block.FlowLabel = Expression.Label(); } Expression @try = Visit(node.Body); IList <CatchBlock> handlers = Visit(node.Handlers, VisitCatchBlock); _blocks.Pop(); if (@try == node.Body && handlers == node.Handlers && @finally == node.Finally && fault == node.Fault) { return(node); } if (!block.HasFlow) { return(Expression.MakeTry(null, @try, @finally, fault, handlers)); } if (node.Type != typeof(void)) { // This is not hard to support in principle, but not needed by anyone yet. throw new NotSupportedException("FinallyFlowControlExpression does not support TryExpressions of non-void type."); } // If there is a control flow in finally, emit outer: // try { // // try block body and all catch handling // } catch (Exception all) { // saved = all; // } finally { // finally_body // if (saved != null) { // throw saved; // } // } // // If we have a fault handler we turn this into the better: // try { // // try block body and all catch handling // } catch (Exception all) { // fault_body // throw all // } if (handlers.Count > 0) { @try = Expression.MakeTry(null, @try, null, null, handlers); } var saved = Expression.Variable(typeof(Exception), "$exception"); var all = Expression.Variable(typeof(Exception), "e"); if (@finally != null) { handlers = new[] { Expression.Catch( all, Expression.Block( Expression.Assign(saved, all), Utils.Default(node.Type) ) ) }; @finally = Expression.Block( @finally, Expression.Condition( Expression.NotEqual(saved, AstUtils.Constant(null, saved.Type)), Expression.Throw(saved), Utils.Empty() ) ); if (finallyEnd != null) { @finally = Expression.Label(finallyEnd, @finally); } } else { Debug.Assert(fault != null); fault = Expression.Block(fault, Expression.Throw(all)); if (finallyEnd != null) { fault = Expression.Label(finallyEnd, fault); } handlers = new[] { Expression.Catch(all, fault) }; fault = null; } // Emit flow control return(Expression.Block( new[] { saved }, Expression.MakeTry(null, @try, @finally, fault, handlers), Expression.Label(block.FlowLabel), MakeFlowControlSwitch(block) )); }
public override MSAst.Expression Reduce() { // allocated all variables here so they won't be shared w/ other // locals allocated during the body or except blocks. MSAst.ParameterExpression lineUpdated = null; MSAst.ParameterExpression runElse = null; if (_else != null || (_handlers != null && _handlers.Length > 0)) { lineUpdated = Ast.Variable(typeof(bool), "$lineUpdated_try"); if (_else != null) { runElse = Ast.Variable(typeof(bool), "run_else"); } } // don't allocate locals below here... MSAst.Expression body = _body; MSAst.Expression @else = _else; MSAst.Expression @catch, result; MSAst.ParameterExpression exception; if (_handlers != null && _handlers.Length > 0) { exception = Ast.Variable(typeof(Exception), "$exception"); @catch = TransformHandlers(exception); } else { exception = null; @catch = null; } // We have else clause, must generate guard around it if (@else != null) { Debug.Assert(@catch != null); // run_else = true; // try { // try_body // } catch ( ... ) { // run_else = false; // catch_body // } // if (run_else) { // else_body // } result = Ast.Block( Ast.Assign(runElse, AstUtils.Constant(true)), // save existing line updated, we could choose to do this only for nested exception handlers. PushLineUpdated(false, lineUpdated), AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)), body ).Catch(exception, Ast.Assign(runElse, AstUtils.Constant(false)), @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Default(body.Type) ), AstUtils.IfThen(runElse, @else ), AstUtils.Empty() ); } else if (@catch != null) // no "else" clause // try { // <try body> // } catch (Exception e) { // ... catch handling ... // } // { result = AstUtils.Try( GlobalParent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)), // save existing line updated PushLineUpdated(false, lineUpdated), body ).Catch(exception, @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Call(AstMethods.ExceptionHandled, Parent.LocalContext), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Default(body.Type) ); } else { result = body; } return(Ast.Block( GetVariables(lineUpdated, runElse), AddFinally(result) )); }
public override MSAst.Expression Reduce() { // allocated all variables here so they won't be shared w/ other // locals allocated during the body or except blocks. MSAst.ParameterExpression?lineUpdated = null; MSAst.ParameterExpression?runElse = null; MSAst.ParameterExpression?previousExceptionContext = null; Debug.Assert(Else is null || _handlers.Length > 0); if (_handlers.Length > 0) { lineUpdated = Ast.Variable(typeof(bool), "$lineUpdated_try"); if (Else != null) { runElse = Ast.Variable(typeof(bool), "run_else"); } } // don't allocate locals below here... MSAst.Expression body = Body; MSAst.Expression? @else = Else; MSAst.Expression? @catch; MSAst.Expression result; MSAst.ParameterExpression?exception; if (_handlers.Length > 0) { previousExceptionContext = Ast.Variable(typeof(Exception), "$previousException"); exception = Ast.Variable(typeof(Exception), "$exception"); @catch = TransformHandlers(exception, previousExceptionContext); } else if (Finally != null) { exception = Ast.Variable(typeof(Exception), "$exception"); @catch = null; } else { exception = null; @catch = null; } // We have else clause, must generate guard around it if (@else != null) { Debug.Assert(runElse != null); Debug.Assert(previousExceptionContext != null && exception != null && @catch != null); // run_else = true; // try { // try_body // } catch ( ... ) { // run_else = false; // catch_body // } // if (run_else) { // else_body // } result = Ast.Block( Ast.Assign(runElse, AstUtils.Constant(true)), // save existing line updated, we could choose to do this only for nested exception handlers. PushLineUpdated(false, lineUpdated), LightExceptions.RewriteExternal( AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(HeaderIndex))), Ast.Assign(previousExceptionContext, Ast.Call(AstMethods.SaveCurrentException)), body, AstUtils.Constant(null) ).Catch(exception, Ast.Assign(runElse, AstUtils.Constant(false)), @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Constant(null) ) ), AstUtils.IfThen(runElse, @else ), AstUtils.Empty() ); } else if (@catch != null) // no "else" clause // try { // <try body> // } catch (Exception e) { // ... catch handling ... // } // { Debug.Assert(previousExceptionContext != null && exception != null); result = LightExceptions.RewriteExternal( AstUtils.Try( GlobalParent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(HeaderIndex))), // save existing line updated PushLineUpdated(false, lineUpdated), Ast.Assign(previousExceptionContext, Ast.Call(AstMethods.SaveCurrentException)), body, AstUtils.Constant(null) ).Catch(exception, @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Constant(null) ) ); } else { result = body; } return(Ast.Block( GetVariables(lineUpdated, runElse, previousExceptionContext), AddFinally(result), AstUtils.Default(typeof(void)) )); }