public override Expression asCLRSetValueExpression(Expression newValue) { return(Expression.Condition( isMutable, Expression.Assign(Expression.ArrayAccess(namedSlots, slotIndex), newValue), Expression.Block( TypeGuru.objectType, Expression.Throw(Expression.Constant(new ImmutableObjectException())), newValue))); }
public TryStatementBuilder Catch(ParameterExpression holder, Expression expr0, Expression expr1, Expression expr2, Expression expr3) { return(Catch(holder, Expression.Block(expr0, expr1, expr2, expr3))); }
public TryStatementBuilder Catch(Type type, params Expression[] body) { return(Catch(type, Expression.Block(body))); }
public TryStatementBuilder Catch(Type type, Expression expr0, Expression expr1, Expression expr2, Expression expr3) { return(Catch(type, Expression.Block(expr0, expr1, expr2, expr3))); }
public static TryStatementBuilder Try(params Expression[] body) { return(new TryStatementBuilder(Expression.Block(body))); }
public static TryStatementBuilder Try(Expression expr0, Expression expr1, Expression expr2, Expression expr3) { return(new TryStatementBuilder(Expression.Block(expr0, expr1, expr2, expr3))); }
internal Expression Reduce() { // Visit body Expression body = Visit(_generator.Body); Debug.Assert(_returnLabels.Count == 1); // Add the switch statement to the body int count = _yields.Count; var cases = new SwitchCase[count + 1]; for (int i = 0; i < count; i++) { cases[i] = Expression.SwitchCase(Expression.Goto(_yields[i].Label), AstUtils.Constant(_yields[i].State)); } cases[count] = Expression.SwitchCase(Expression.Goto(_returnLabels.Peek()), AstUtils.Constant(Finished)); Type generatorNextOfT = typeof(GeneratorNext <>).MakeGenericType(_generator.Target.Type); // Create the lambda for the GeneratorNext<T>, hoisting variables // into a scope outside the lambda var allVars = new List <ParameterExpression>(_vars); allVars.AddRange(_temps); // Collect temps that don't have to be closed over var innerTemps = new ReadOnlyCollectionBuilder <ParameterExpression>(1 + (_labelTemps != null ? _labelTemps.Count : 0)); innerTemps.Add(_gotoRouter); if (_labelTemps != null) { foreach (LabelInfo info in _labelTemps.Values) { innerTemps.Add(info.Temp); } } body = Expression.Block( allVars, Expression.Lambda( generatorNextOfT, Expression.Block( innerTemps, Expression.Switch(Expression.Assign(_gotoRouter, _state), cases), body, Expression.Assign(_state, AstUtils.Constant(Finished)), Expression.Label(_returnLabels.Peek()) ), _generator.Name, new ParameterExpression[] { _state, _current } ) ); // Enumerable factory takes Func<GeneratorNext<T>> instead of GeneratorNext<T> if (_generator.IsEnumerable) { body = Expression.Lambda(body); } // We can't create a ConstantExpression of _debugCookies array here because we walk the tree // after constants have already been rewritten. Instead we create a NewArrayExpression node // which initializes the array with contents from _debugCookies Expression debugCookiesArray = null; if (_debugCookies != null) { Expression[] debugCookies = new Expression[_debugCookies.Count]; for (int i = 0; i < _debugCookies.Count; i++) { debugCookies[i] = AstUtils.Constant(_debugCookies[i]); } debugCookiesArray = Expression.NewArrayInit( typeof(int), debugCookies); } // Generate a call to ScriptingRuntimeHelpers.MakeGenerator<T>(args) return(Expression.Call( typeof(ScriptingRuntimeHelpers), "MakeGenerator", new[] { _generator.Target.Type }, (debugCookiesArray != null) ? new[] { body, debugCookiesArray } : new[] { body } )); }
private Expression VisitAssign(BinaryExpression node) { int yields = _yields.Count; Expression left = Visit(node.Left); Expression right = Visit(node.Right); if (left == node.Left && right == node.Right) { return(node); } if (yields == _yields.Count) { return(Expression.Assign(left, right)); } var block = new ReadOnlyCollectionBuilder <Expression>(); if (_generator.RewriteAssignments) { // We need to make sure that LHS is evaluated before RHS. For example, // // {expr0}[{expr1},..,{exprN}] = {rhs} // -> // { l0 = {expr0}; l1 = {expr1}; ..; lN = {exprN}; r = {rhs}; l0[l1,..,lN] = r } // if (left == node.Left) { switch (left.NodeType) { case ExpressionType.MemberAccess: var member = (MemberExpression)node.Left; if (member.Expression != null) { left = member.Update(ToTemp(block, member.Expression)); } break; case ExpressionType.Index: var index = (IndexExpression)node.Left; left = index.Update((index.Object != null ? ToTemp(block, index.Object) : null), ToTemp(block, index.Arguments)); break; case ExpressionType.Parameter: // no action needed break; default: // Extension should've been reduced by Visit above, // and returned a different node throw Assert.Unreachable; } } else { // Get the last expression of the rewritten left side var leftBlock = (BlockExpression)left; block.AddRange(leftBlock.Expressions); block.RemoveAt(block.Count - 1); left = leftBlock.Expressions[leftBlock.Expressions.Count - 1]; } } if (right != node.Right) { right = ToTemp(block, right); } block.Add(Expression.Assign(left, right)); return(Expression.Block(block)); }
protected override Expression VisitTry(TryExpression node) { int startYields = _yields.Count; bool savedInTryWithFinally = _inTryWithFinally; if (node.Finally != null || node.Fault != null) { _inTryWithFinally = true; } Expression @try = Visit(node.Body); int tryYields = _yields.Count; IList <CatchBlock> handlers = Visit(node.Handlers, VisitCatchBlock); int catchYields = _yields.Count; // push a new return label in case the finally block yields _returnLabels.Push(Expression.Label()); // only one of these can be non-null Expression @finally = Visit(node.Finally); Expression fault = Visit(node.Fault); LabelTarget finallyReturn = _returnLabels.Pop(); int finallyYields = _yields.Count; _inTryWithFinally = savedInTryWithFinally; if (@try == node.Body && handlers == node.Handlers && @finally == node.Finally && fault == node.Fault) { return(node); } // No yields, just return if (startYields == _yields.Count) { return(Expression.MakeTry(null, @try, @finally, fault, handlers)); } if (fault != null && finallyYields != catchYields) { // No one needs this yet, and it's not clear how we should get back to // the fault throw new NotSupportedException("yield in fault block is not supported"); } // If try has yields, we need to build a new try body that // dispatches to the yield labels var tryStart = Expression.Label(); if (tryYields != startYields) { @try = Expression.Block(MakeYieldRouter(node.Body.Type, startYields, tryYields, tryStart), @try); } // Transform catches with yield to deferred handlers if (catchYields != tryYields) { var block = new List <Expression>(); block.Add(MakeYieldRouter(node.Body.Type, tryYields, catchYields, tryStart)); block.Add(null); // empty slot to fill in later for (int i = 0, n = handlers.Count; i < n; i++) { CatchBlock c = handlers[i]; if (c == node.Handlers[i]) { continue; } if (handlers.IsReadOnly) { handlers = handlers.ToArray(); } // the variable that will be scoped to the catch block var exceptionVar = Expression.Variable(c.Test, null); // the variable that the catch block body will use to // access the exception. We reuse the original variable if // the catch block had one. It needs to be hoisted because // the catch might contain yields. var deferredVar = c.Variable ?? Expression.Variable(c.Test, null); _vars.Add(deferredVar); // We need to ensure that filters can access the exception // variable Expression filter = c.Filter; if (filter != null && c.Variable != null) { filter = Expression.Block(new[] { c.Variable }, Expression.Assign(c.Variable, exceptionVar), filter); } // catch (ExceptionType exceptionVar) { // deferredVar = exceptionVar; // } handlers[i] = Expression.Catch( exceptionVar, Expression.Block( Expression.Assign(deferredVar, exceptionVar), Expression.Default(node.Body.Type) ), filter ); // We need to rewrite rethrows into "throw deferredVar" var catchBody = new RethrowRewriter { Exception = deferredVar }.Visit(c.Body); // if (deferredVar != null) { // ... catch body ... // } block.Add( Expression.Condition( Expression.NotEqual(deferredVar, AstUtils.Constant(null, deferredVar.Type)), catchBody, Expression.Default(node.Body.Type) ) ); } block[1] = Expression.MakeTry(null, @try, null, null, new ReadOnlyCollection <CatchBlock>(handlers)); @try = Expression.Block(block); handlers = new CatchBlock[0]; // so we don't reuse these } if (finallyYields != catchYields) { // We need to add a catch block to save the exception, so we // can rethrow in case there is a yield in the finally. Also, // add logic for returning. It looks like this: // // try { ... } catch (Exception all) { saved = all; } // finally { // if (_finallyReturnVar) goto finallyReturn; // ... // if (saved != null) throw saved; // finallyReturn: // } // if (_finallyReturnVar) goto _return; // We need to add a catch(Exception), so if we have catches, // wrap them in a try if (handlers.Count > 0) { @try = Expression.MakeTry(null, @try, null, null, handlers); handlers = new CatchBlock[0]; } // NOTE: the order of these routers is important // The first call changes the labels to all point at "tryEnd", // so the second router will jump to "tryEnd" var tryEnd = Expression.Label(); Expression inFinallyRouter = MakeYieldRouter(node.Body.Type, catchYields, finallyYields, tryEnd); Expression inTryRouter = MakeYieldRouter(node.Body.Type, catchYields, finallyYields, tryStart); var all = Expression.Variable(typeof(Exception), "e"); var saved = Expression.Variable(typeof(Exception), "$saved$" + _temps.Count); _temps.Add(saved); @try = Expression.Block( Expression.TryCatchFinally( Expression.Block( inTryRouter, @try, Expression.Assign(saved, AstUtils.Constant(null, saved.Type)), Expression.Label(tryEnd) ), Expression.Block( MakeSkipFinallyBlock(finallyReturn), inFinallyRouter, @finally, Expression.Condition( Expression.NotEqual(saved, AstUtils.Constant(null, saved.Type)), Expression.Throw(saved), Utils.Empty() ), Expression.Label(finallyReturn) ), Expression.Catch(all, Utils.Void(Expression.Assign(saved, all))) ), Expression.Condition( Expression.Equal(_gotoRouter, AstUtils.Constant(GotoRouterYielding)), Expression.Goto(_returnLabels.Peek()), Utils.Empty() ) ); @finally = null; } else if (@finally != null) { // try or catch had a yield, modify finally so we can skip over it @finally = Expression.Block( MakeSkipFinallyBlock(finallyReturn), @finally, Expression.Label(finallyReturn) ); } // Make the outer try, if needed if (handlers.Count > 0 || @finally != null || fault != null) { @try = Expression.MakeTry(null, @try, @finally, fault, handlers); } return(Expression.Block(Expression.Label(tryStart), @try)); }
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 Expression Reduce() { return(Expression.Block(typeof(object), _lastValueParamArray, new ReadOnlyCollectionBuilder <Expression> { Expression.Label(_returnLabel, _body) })); }
/// <summary> /// Fixes up lambda body and parameters to match the signature of the given delegate if needed. /// </summary> /// <param name="delegateType"></param> private void EnsureSignature(Type delegateType) { System.Diagnostics.Debug.Assert(_params != null, "must have parameter list here"); //paramMapping is the dictionary where we record how we want to map parameters //the key is the parameter, the value is the expression it should be redirected to //so far the parameter can only be redirected to itself (means no change needed) or to //a synthetic variable that is added to the lambda when the original parameter has no direct //parameter backing in the delegate signature // Example: // delegate siganture del(x, params y[]) // lambda signature lambda(a, b, param n[]) // // for the situation above the mapping will be <a, x>, <b, V1>, <n, V2> // where V1 and V2 are synthetic variables and initialized as follows - V1 = y[0] , V2 = {y[1], y[2],... y[n]} ParameterInfo[] delegateParams = delegateType.GetMethod("Invoke").GetParameters(); bool delegateHasParamarray = delegateParams.Length > 0 && delegateParams[delegateParams.Length - 1].IsDefined(typeof(ParamArrayAttribute), false); bool lambdaHasParamarray = ParamsArray != null; if (lambdaHasParamarray && !delegateHasParamarray) { throw new ArgumentException("paramarray lambdas must have paramarray delegate type"); } int copy = delegateHasParamarray ? delegateParams.Length - 1 : delegateParams.Length; int unwrap = _params.Count - copy; if (lambdaHasParamarray) { unwrap--; } // Lambda must have at least as many parameters as the delegate, not counting the paramarray if (unwrap < 0) { throw new ArgumentException("lambda does not have enough parameters"); } // shortcircuit if no rewrite is needed. if (!delegateHasParamarray) { bool needRewrite = false; for (int i = 0; i < copy; i++) { if (_params[i].Type != delegateParams[i].ParameterType) { needRewrite = true; } } if (!needRewrite) { return; } } List <ParameterExpression> newParams = new List <ParameterExpression>(delegateParams.Length); List <ParameterExpression> backingVars = new List <ParameterExpression>(); List <Expression> preambuleExpressions = new List <Expression>(); Dictionary <ParameterExpression, ParameterExpression> paramMapping = new Dictionary <ParameterExpression, ParameterExpression>(); for (int i = 0; i < copy; i++) { // map to a converted variable if (_params[i].Type != delegateParams[i].ParameterType) { ParameterExpression newParameter = Expression.Parameter(delegateParams[i].ParameterType, delegateParams[i].Name); ParameterExpression mappedParameter = _params[i]; ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name); newParams.Add(newParameter); backingVars.Add(backingVariable); paramMapping.Add(mappedParameter, backingVariable); preambuleExpressions.Add( Expression.Assign( backingVariable, Expression.Convert( newParameter, mappedParameter.Type ) ) ); } else { //use the same parameter expression newParams.Add(_params[i]); paramMapping.Add(_params[i], _params[i]); } } if (delegateHasParamarray) { ParameterInfo delegateParamarrayPi = delegateParams[delegateParams.Length - 1]; ParameterExpression delegateParamarray = Expression.Parameter(delegateParamarrayPi.ParameterType, delegateParamarrayPi.Name); newParams.Add(delegateParamarray); //unwarap delegate paramarray into variables and map parameters to the variables for (int i = 0; i < unwrap; i++) { ParameterExpression mappedParameter = _params[copy + i]; ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name); backingVars.Add(backingVariable); paramMapping.Add(mappedParameter, backingVariable); preambuleExpressions.Add( Expression.Assign( backingVariable, AstUtils.Convert( Expression.ArrayAccess( delegateParamarray, AstUtils.Constant(i) ), mappedParameter.Type ) ) ); } //lambda's paramarray should get elements from the delegate paramarray after skipping those that we unwrapped. if (lambdaHasParamarray) { ParameterExpression mappedParameter = _paramsArray; ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name); backingVars.Add(backingVariable); paramMapping.Add(mappedParameter, backingVariable); // Call the helper MethodInfo shifter = typeof(RuntimeHelpers).GetMethod("ShiftParamsArray"); shifter = shifter.MakeGenericMethod(delegateParamarrayPi.ParameterType.GetElementType()); preambuleExpressions.Add( Expression.Assign( backingVariable, AstUtils.Convert( Expression.Call( shifter, delegateParamarray, AstUtils.Constant(unwrap) ), mappedParameter.Type ) ) ); } } Expression newBody = new LambdaParameterRewriter(paramMapping).Visit(_body); preambuleExpressions.Add(newBody); _body = Expression.Block(preambuleExpressions); _paramsArray = null; _locals.AddRange(backingVars); _params = newParams; for (int i = 0; i < _visibleVars.Count; i++) { ParameterExpression p = _visibleVars[i].Key as ParameterExpression; ParameterExpression v; if (p != null && paramMapping.TryGetValue(p, out v)) { _visibleVars[i] = new KeyValuePair <ParameterExpression, bool>(v, _visibleVars[i].Value); } } }
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) { if (@finally != null) { @finally = Expression.Label(finallyEnd, @finally); } if (fault != null) { fault = Expression.Label(finallyEnd, fault); } // 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(@try, @finally, fault, handlers)); } // 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) { // saved = all; // fault_body // throw saved // } if (handlers.Count > 0) { @try = Expression.MakeTry(@try, null, null, handlers); } var all = Expression.Variable(typeof(Exception), "$exception"); if (@finally != null) { handlers = new[] { Expression.Catch(all.Type, Expression.Empty()) }; @finally = Expression.Block( @finally, Expression.Condition( Expression.NotEqual(all, Expression.Constant(null, all.Type)), Expression.Throw(all), Expression.Empty() ) ); } else { handlers = new[] { Expression.Catch( all.Type, Expression.Block(fault, Expression.Throw(all)) ) }; fault = null; } // Emit flow control return(Expression.Block( new ParameterExpression[] { all }, Expression.MakeTry(@try, @finally, fault, handlers), Expression.Label(block.FlowLabel), MakeFlowControlSwitch(block) )); }