Example #1
0
 public override BoundNode VisitFixedStatement(BoundFixedStatement node)
 {
     AddAll(node.Locals);
     base.VisitFixedStatement(node);
     RemoveAll(node.Locals);
     return(null);
 }
        /// <summary>
        /// Basically, what we need to know is, if an exception occurred within the fixed statement, would
        /// additional code in the current method be executed before its stack frame was popped?
        /// </summary>
        private static bool IsInTryBlock(BoundFixedStatement boundFixed)
        {
            SyntaxNode node = boundFixed.Syntax.Parent;

            while (node != null)
            {
                switch (node.Kind())
                {
                case SyntaxKind.TryStatement:
                    // NOTE: if we started in the catch or finally of this try statement,
                    // we will have bypassed this node.
                    return(true);

                case SyntaxKind.UsingStatement:
                    // ACASEY: In treating using statements as try-finally's, we're following
                    // Dev11.  The practical explanation for Dev11's behavior is that using
                    // statements have already been lowered by the time the check is performed.
                    // A more thoughtful explanation is that user code could run between the
                    // raising of an exception and the unwinding of the stack (via Dispose())
                    // and that user code would likely appreciate the reduced memory pressure
                    // of having the fixed local unpinned.

                    // NOTE: As in Dev11, we're not emitting a try-finally if the fixed
                    // statement is nested within a lock statement.  Practically, dev11
                    // probably lowers locks after fixed statement, and so, does not see
                    // the try-finally.  More thoughtfully, no user code will run in the
                    // finally statement, so it's not necessary.

                    // BREAK: Takes into account whether an outer fixed statement will be
                    // lowered into a try-finally block and responds accordingly.  This is
                    // unnecessary since nothing will ever be allocated in the finally
                    // block of a lowered fixed statement, so memory pressure is not an
                    // issue.  Note that only nested fixed statements where the outer (but
                    // not the inner) fixed statement has an unmatched goto, but is not
                    // contained in a try-finally, will be affected.  e.g.
                    // fixed (...) {
                    //   fixed (...) { }
                    //   goto L1: ;
                    // }
                    return(true);

                case SyntaxKind.ForStatement:
                    // We're being conservative here - there's actually only
                    // a try block if the enumerator is disposable, but we
                    // can't tell that from the syntax.  Dev11 checks in the
                    // lowered tree, so it is more precise.
                    return(true);

                case SyntaxKind.SimpleLambdaExpression:
                case SyntaxKind.ParenthesizedLambdaExpression:
                case SyntaxKind.AnonymousMethodExpression:
                    // Stop looking.
                    return(false);

                case SyntaxKind.CatchClause:
                    // If we're in the catch of a try-catch-finally, then
                    // we're still in the scope of the try-finally handler.
                    if (((TryStatementSyntax)node.Parent).Finally != null)
                    {
                        return(true);
                    }
                    goto case SyntaxKind.FinallyClause;

                case SyntaxKind.FinallyClause:
                    // Skip past the enclosing try to avoid a false positive.
                    node = node.Parent;
                    Debug.Assert(node.Kind() == SyntaxKind.TryStatement);
                    node = node.Parent;
                    break;

                default:
                    if (node is MemberDeclarationSyntax)
                    {
                        // Stop looking.
                        return(false);
                    }
                    node = node.Parent;
                    break;
                }
            }

            return(false);
        }
        public override BoundNode VisitFixedStatement(BoundFixedStatement node)
        {
            BoundLocalDeclaration localDecl = node.Declaration;
            int numFixedLocals = 1;

            var localBuilder = ArrayBuilder <LocalSymbol> .GetInstance(node.Locals.Length);

            localBuilder.AddRange(node.Locals);

            var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(numFixedLocals + 1 + 1); //+1 for body, +1 for hidden seq point

            var cleanup = new BoundStatement[numFixedLocals];

            {
                LocalSymbol pinnedTemp;
                statementBuilder.Add(InitializeFixedStatementLocal(localDecl, _factory, out pinnedTemp));
                localBuilder.Add(pinnedTemp);

                // NOTE: Dev10 nulls out the locals in declaration order (as opposed to "popping" them in reverse order).
                if (pinnedTemp.RefKind == RefKind.None)
                {
                    // temp = null;
                    cleanup[0] = _factory.Assignment(_factory.Local(pinnedTemp), _factory.Null(pinnedTemp.Type.TypeSymbol));
                }
                else
                {
                    Debug.Assert(!pinnedTemp.Type.IsManagedType);

                    // temp = ref *default(T*);
                    cleanup[0] = _factory.Assignment(_factory.Local(pinnedTemp), new BoundPointerIndirectionOperator(
                                                         _factory.Syntax,
                                                         _factory.Default(new PointerTypeSymbol(pinnedTemp.Type)),
                                                         pinnedTemp.Type.TypeSymbol),
                                                     isRef: true);
                }
            }

            BoundStatement rewrittenBody = VisitStatement(node.Body);

            statementBuilder.Add(rewrittenBody);
            statementBuilder.Add(_factory.HiddenSequencePoint());

            Debug.Assert(statementBuilder.Count == numFixedLocals + 1 + 1);

            // In principle, the cleanup code (i.e. nulling out the pinned variables) is always
            // in a finally block.  However, we can optimize finally away (keeping the cleanup
            // code) in cases where both of the following are true:
            //   1) there are no branches out of the fixed statement; and
            //   2) the fixed statement is not in a try block (syntactic or synthesized).
            if (IsInTryBlock(node) || HasGotoOut(rewrittenBody))
            {
                return(_factory.Block(
                           localBuilder.ToImmutableAndFree(),
                           new BoundTryStatement(
                               _factory.Syntax,
                               _factory.Block(statementBuilder.ToImmutableAndFree()),
                               ImmutableArray <BoundCatchBlock> .Empty,
                               _factory.Block(cleanup))));
            }
            else
            {
                statementBuilder.AddRange(cleanup);
                return(_factory.Block(localBuilder.ToImmutableAndFree(), statementBuilder.ToImmutableAndFree()));
            }
        }