Example #1
0
            /// <summary>
            /// Visit block expressions for scope tracking and closure emitting purposes.
            /// </summary>
            /// <param name="node">The block expression to rewrite.</param>
            /// <returns>The rewritten expression.</returns>
            protected override Expression VisitBlock(BlockExpression node)
            {
                //
                // Introduce a new scope and keep track of the original scope in order to restore
                // it after visiting child expressions.
                //
                var currentScope = _scope;

                try
                {
                    var scope = _analysis[node];
                    _scope = new CompilerScope(currentScope, scope);

                    if (!scope.HasHoistedLocals)
                    {
                        //
                        // In case there are no hoisted locals, we don't need to use a builder to
                        // instantiate a closure. Simply visit the child expressions and update.
                        //
                        var expressions = Visit(node.Expressions);

                        return(node.Update(node.Variables, expressions));
                    }
                    else
                    {
                        //
                        // In case there are hoisted locals, enter the scope to obtain a builder,
                        // append the visited child expressions, and return a new block. This will
                        // add the statements needed to instantiate the closure and link it to its
                        // parent (if any).
                        //
                        var expressions = node.Expressions;
                        var n           = expressions.Count;

                        //
                        // Note we don't copy hoisted locals during Enter because the storage
                        // location of these is superseded by the storage field in the closure.
                        //
                        var builder = _scope.Enter(count: n, copyLocals: false);

                        for (var i = 0; i < n; i++)
                        {
                            builder.Append(Visit(expressions[i]));
                        }

                        //
                        // Note that we don't update the original block here;  the builder will
                        // create a new block anyway, and we can simply use that as our substitute,
                        // provided the original (non-hoisted) locals are declared.
                        //
                        return(builder.Finish(declareLocals: true));
                    }
                }
                finally
                {
                    _scope = currentScope;
                }
            }
Example #2
0
            /// <summary>
            /// Visit lambda expression for scope tracking and closure emitting purposes.
            /// </summary>
            /// <typeparam name="T">The type of the delegate represented by the lambda expression.</typeparam>
            /// <param name="node">The lambda expression to rewrite.</param>
            /// <returns>The rewritten lambda expression.</returns>
            protected override Expression VisitLambda <T>(Expression <T> node)
            {
                //
                // Allocate a slot for information about the rewritten lambda expression and thunk
                // type. We add a temporary empty value to the dictionary slot here in order to
                // boost the count of entries. This causes the index to be logically numbered in
                // the order the lambda expressions were visited.
                //
                var index = _lambdas.Count;

                _lambdas[index] = default;

                //
                // Introduce a new scope and keep track of the original scope in order to restore
                // it after visiting child expressions.
                //
                var currentScope = _scope;

                try
                {
                    var scope = _analysis[node];
                    _scope = new CompilerScope(currentScope, scope);

                    var body = default(Expression);

                    if (!scope.HasHoistedLocals && !scope.NeedsClosure)
                    {
                        body = Visit(node.Body);
                    }
                    else
                    {
                        //
                        // Visit the body and append it to the builder. Note that hoisted locals
                        // (i.e. parameters) are copied into the closure.
                        //
                        var builder = _scope.Enter(count: 1, copyLocals: true);
                        builder.Append(Visit(node.Body));
                        body = builder.Finish(declareLocals: false);
                    }

                    //
                    // Use the closure parameter of the original scope (i.e. the parent to the
                    // scope representing the lambda) to derive information about the thunk type.
                    //
                    var closureParam      = currentScope.Closure;
                    var thunkType         = _thunkFactory.GetThunkType(typeof(T), closureParam.Type);
                    var innerDelegateType = GetInnerDelegateType(thunkType);

                    //
                    // Construct a lambda that's parameterized on the closure that's passed in,
                    // using the inner delegate type that the thunk expects. This becomes the
                    // rewritten lambda expression, which will be passed to the constructor of
                    // the thunk type in GetMethodTable.
                    //
                    var parameters = new ParameterExpression[node.Parameters.Count + 1];
                    parameters[0] = closureParam;
                    node.Parameters.CopyTo(parameters, index: 1);

                    var lambda = Expression.Lambda(innerDelegateType, body, parameters);
                    _lambdas[index] = new LambdaInfo(lambda, thunkType);

                    //
                    // Inside the expression tree, replace the lambda by a call to CreateDelegate
                    // on the thunk. The thunk is retrieved from the method table that's passed to
                    // the top-level lambda by indexing into the Thunks array.
                    //
                    var createDelegate = thunkType.GetMethod(nameof(ActionThunk <object> .CreateDelegate));
                    var methodTable    = currentScope.Bind(_methodTable);

                    var createDelegateCall =
                        Expression.Call(
                            Expression.Convert(
                                Expression.ArrayIndex(
                                    Expression.Field(
                                        methodTable,
                                        nameof(MethodTable.Thunks)
                                        ),
                                    Expression.Constant(index)
                                    ),
                                thunkType
                                ),
                            createDelegate,
                            closureParam
                            );

                    return(createDelegateCall);
                }
                finally
                {
                    _scope = currentScope;
                }
            }
Example #3
0
            /// <summary>
            /// Visit catch blocks for scope tracking and closure emitting purposes.
            /// </summary>
            /// <param name="node">The catch block to rewrite.</param>
            /// <returns>The rewritten catch block.</returns>
            protected override CatchBlock VisitCatchBlock(CatchBlock node)
            {
                //
                // Store the filter and body in two locals. These will get overwritten by the
                // result of recursive visits and/or rewrites using the builder (see below), prior
                // to their usage to update the original expression.
                //
                var filter = node.Filter;
                var body   = node.Body;

                //
                // Introduce a new scope and keep track of the original scope in order to restore
                // it after visiting child expressions.
                //
                var currentScope = _scope;

                try
                {
                    var scope = _analysis[node];
                    _scope = new CompilerScope(currentScope, scope);

                    if (!scope.HasHoistedLocals)
                    {
                        //
                        // In case there is no hoisted local, we don't need to use a builder to
                        // instantiate a closure. Simply visit the child expressions.
                        //
                        filter = Visit(filter);
                        body   = Visit(body);
                    }
                    else
                    {
                        //
                        // CONSIDER: Refine the analysis step in order to keep track of the child
                        //           expressions where the variable needs to be hoisted into a
                        //           closure. Right now, we may use an unnecessary closure.
                        //

                        //
                        // In case there is a hoisted local, enter the scope to obtain a builder,
                        // append the visited child expressions, and return a new block. This will
                        // add the statements needed to instantiate the closure and link it to its
                        // parent (if any).
                        //
                        // Note that the original variable holding the caught exception instance is
                        // retained, so:
                        //
                        // * if hoisted, we copy it to the closure in Enter -and-
                        // * we don't re-declare it in Finish.
                        //
                        if (filter != null)
                        {
                            var builder = _scope.Enter(count: 1, copyLocals: true);
                            builder.Append(Visit(filter));
                            filter = builder.Finish(declareLocals: false);
                        }

                        if (body != null)
                        {
                            var builder = _scope.Enter(count: 1, copyLocals: true);
                            builder.Append(Visit(body));
                            body = builder.Finish(declareLocals: false);
                        }
                    }
                }
                finally
                {
                    _scope = currentScope;
                }

                //
                // Simply update the expression using the rewritten child expressions. Note that
                // the variable remains unchanged; its contents could be copied by the rewritten
                // child expressions in order to hoist it into a closure.
                //
                return(node.Update(node.Variable, filter, body));
            }