private Statement ParseUsing(TokenSet followers) //^ requires this.currentToken == Token.Using; //^ ensures followers[this.currentToken] || this.currentToken == Token.EndOfFile; { SourceLocationBuilder slb = new SourceLocationBuilder(this.scanner.SourceLocationOfLastScannedToken); this.GetNextToken(); if (Parser.IdentifierOrNonReservedKeyword[this.currentToken]) { this.HandleError(Error.SyntaxError, "("); this.GetNextToken(); if (this.currentToken == Token.Semicolon) this.GetNextToken(); slb.UpdateToSpan(this.scanner.SourceLocationOfLastScannedToken); EmptyStatement es = new EmptyStatement(false, slb); this.SkipTo(followers); return es; } this.Skip(Token.LeftParenthesis); Statement resourceAcquisition = this.ParseExpressionStatementOrDeclaration(false, false, followers|Token.RightParenthesis|Parser.StatementStart, false); //^ assert resourceAcquisition is ExpressionStatement || resourceAcquisition is LocalDeclarationsStatement; this.Skip(Token.RightParenthesis); Statement body = this.ParseStatement(followers); slb.UpdateToSpan(body.SourceLocation); Statement result = new ResourceUseStatement(resourceAcquisition, body, slb); //^ assume followers[this.currentToken] || this.currentToken == Token.EndOfFile; return result; }
/// <summary> /// Looking for the pattern: /// IDisposable<T> variable = e; /// try { /// using_body /// } finally { /// if (variable != null) { /// variable.Dispose(); /// } /// } /// The type of the variable is *not* actually IDisposable, but some /// type that implements IDisposable. /// The assignment might be a local-declaration statement or else it /// might be an assignment statement. /// </summary> public override void TraverseChildren(IBlockStatement block) { Contract.Assume(block is BlockStatement); var decompiledBlock = (BlockStatement)block; var statements = decompiledBlock.Statements; for (int i = 2; i < statements.Count; ++i) { var tryFinally = statements[i] as TryCatchFinallyStatement; if (tryFinally == null) { continue; } var finallyBody = tryFinally.FinallyBody; if (finallyBody == null) { continue; } var finallyStatements = finallyBody.Statements; if (tryFinally.CatchClauses.Any()) { continue; } ILocalDefinition variable; IStatement resourceAcquisition; var variableDeclaration = statements[i - 2] as ILocalDeclarationStatement; if (variableDeclaration == null) { var es = statements[i - 2] as IExpressionStatement; if (es == null) { continue; } var assign = es.Expression as IAssignment; if (assign == null) { continue; } if (assign.Target.Instance != null) { continue; } var loc = assign.Target.Definition as ILocalDefinition; if (loc == null) { continue; } variable = loc; resourceAcquisition = es; } else { variable = variableDeclaration.LocalVariable; resourceAcquisition = variableDeclaration; } // finally block either looks like: // variable.Dispose(); // or // if (variable != null) variable.Dispose(); // or // iDisposableLocalVar := variable as IDisposable; // if (iDisposableLocalVar != null) iDisposableLocalVar.Dispose(); var c = finallyStatements.Count(); if (c == 1) { var expression = finallyStatements.Single() as IExpressionStatement; var isDispose = this.MatchMethodCall(expression.Expression, variable, "Dispose"); if (!isDispose) { continue; } } else if (c == 3 || c == 4) { IBoundExpression be; ILocalDefinition iDisposableVariable = variable; var index = 0; if (c == 4) { var es = finallyStatements.ElementAt(index++) as IExpressionStatement; if (es == null) { continue; } var assignment = es.Expression as IAssignment; if (assignment == null) { continue; } var castIfPossible = assignment.Source as ICastIfPossible; if (castIfPossible == null) { continue; } if (!TypeHelper.TypesAreEquivalent(castIfPossible.TargetType, this.IDisposable)) { continue; } be = castIfPossible.ValueToCast as IBoundExpression; if (be == null) { continue; } if (be.Instance != null) { continue; } if (be.Definition != variable) { continue; } if (assignment.Target.Instance != null) { continue; } iDisposableVariable = assignment.Target.Definition as ILocalDefinition; if (iDisposableVariable == null) { continue; } } var conditional = finallyStatements.ElementAt(index++) as IConditionalStatement; if (conditional == null) { continue; } var expressionStatement = finallyStatements.ElementAt(index++) as IExpressionStatement; if (expressionStatement == null) { continue; } var lableledStatement = finallyStatements.ElementAt(index++) as ILabeledStatement; if (lableledStatement == null) { continue; } var equality = conditional.Condition as IEquality; if (equality == null) { continue; } be = equality.LeftOperand as IBoundExpression; if (be == null) { continue; } if (be.Instance != null) { continue; } if (be.Definition != iDisposableVariable) { continue; } if (!(conditional.FalseBranch is IEmptyStatement)) { continue; } var gotoStatement = conditional.TrueBranch as IGotoStatement; if (gotoStatement == null) { continue; } if (gotoStatement.TargetStatement != lableledStatement) { continue; } var methodCall = expressionStatement.Expression as IMethodCall; if (methodCall == null) { continue; } var ct = methodCall.MethodToCall.ContainingType; if (!TypeHelper.TypesAreEquivalent(ct, this.IDisposable)) { continue; } } else { continue; } var resourceUse = new ResourceUseStatement() { Locations = tryFinally.Locations, ResourceAcquisitions = resourceAcquisition, Body = tryFinally.TryBody }; statements[i] = resourceUse; statements.RemoveAt(i - 2); i--; } base.TraverseChildren(block); }