Beispiel #1
0
 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)));
 }
Beispiel #7
0
        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 }
                       ));
        }
Beispiel #8
0
        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));
        }
Beispiel #9
0
        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)
                       ));
        }
Beispiel #11
0
 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)
                       ));
        }