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())); } }