250 likes | 386 Views
JPred-P 2 : Runtime Precondition and Postcondition Checking in Java. Josh Choi Mike Welch. Precondition. … a fact that must always be true just prior to the execution of some section of code, such as when entering a procedure Can be enforced with assert() statements at top of procedure body
E N D
JPred-P2: Runtime Precondition and Postcondition Checking in Java Josh Choi Mike Welch
Precondition • … a fact that must always be true just prior to the execution of some section of code, such as when entering a procedure • Can be enforced with assert() statements at top of procedure body • In software engineering, usually specified simply as comments around a procedure body
Postcondition • … a fact that must always be true just after the execution of some section of code, such as when leaving a procedure • Could enforce with assert() statements at bottom of procedure body • Usually exists in the form of programmer comments
Wouldn’t it be nice if… public int divide( int numr, int denr) { assert ( denr != 0 ); return numr / denr; }
Wouldn’t it be nice if… public int divide( int numr, int denr) { assert ( denr != 0 ); return numr / denr; }
Wouldn’t it be nice if… public int divide( int numr, int denr) requires denr != 0 { return numr / denr; }
How about… public void deposit( Account account, Money amount ) { secureDeposit( account, amount ); assert( account != null ); }
How about… public void deposit( Account account, Money amount ) { secureDeposit( account, amount ); assert( account != null ); }
How about… public void deposit( Account account, Money amount ) ensures account != null { secureDeposit( account, amount ); }
Get ready for this… public Person pointOfNoReturn( Person you ) requires you != null ensures ret$REF == null { System.out.println( you.getName() + “ goes away”); return null; }
JPred • Dynamic method dispatch based upon predicates public char whatGradeShouldIGive( int testScore ) when testScore >= 90 { return ‘A’; } Public char whatGradeShouldIGive( int testScore ) when testScore < 90 { return ‘F’; }
JPred + Pre/Postconditions public void doTermProject( Student s ) requires s.hasALife == false ensures s.avgHoursOfSleep < 4 && s.finishedProject == false when p@ComputerScienceGradStudent { /* do project */ } public void doTermProject( Student s ) { /* do project */ } = JPred-P2
Implementation - Tools Used • Polyglot (Cornell) • Compiler front end framework for building Java language extensions • Definition of new grammar • Custom abstract syntax tree (AST) nodes • Specialized AST traversal passes • CVC Lite (Stanford) • Theorem prover • Predicate validation • MultiJava (http://multijava.sourceforge.net) • JPred written in MultiJava
Implementation - Our Work • Added requires and ensures support on top of existing JPred Polyglot extension • Modified JPred’s grammar definition files to support the new optional keywords • Added AST nodes: reqNode and ensNode along with JPred’s whenNode (formerly called predNode)
Implementation (continued) • Modified JPred’s traversal passes • requires support • In the translator pass, add an if-else statement, with the requires predicate as its guard • Enclose the method body under the if’s body • else body outputs precondition failure message and terminates program
Implementation (continued) • ensures support • Few issues involved • There can be multiple method exit points: end of method body and arbitrary number of return statements • Postcondition must be checked immediately before each of these exit points • How to specify postconditions on return values? • Ex: int f() ensures <?> { …; return 1+2; }
Implementation (continued) • ensures support (continued) • Locate all return statements in a method body • Enclose a return statement in an if-else statement • Guards are the ensures predicate • else body outputs postcondition failure message and terminates program
Implementation (continued) • ensures support (continued) • Postcondition check on a return value requires a store • Ex: int f() ensures <?>{ … return 1+2;} int f() ensures ret$INT > 2{ …int ret$INT = 1+2;if ( ret$INT > 2 ) return ret$INT; else /* postcondition failed */}
Implementation (continued) • ensures support (continued) • JPred’s predicates can have three types of expressions: integer, boolean, and null • Which means postcondition check on a return value is limited to one of those three types • Define ret$INT, ret$BOOL, and ret$REF “keywords” • Ex: “Make sure g() returns a non-null reference” • Object g() ensures ret$REF != null {…}
Implementation (continued) • ensures support (continued) • Define a new pass to be executed early • Declare static ret$INT, ret$BOOL, and ret$REF variables in class body • When ensures predicate specifies any of those “keywords,” they are valid variables in the AST and so Polyglot does not complain
Example - Abstract Divide • int absDivide( int num, int denom ) returns the absolute value of the quotient of num divided by denom public static int absDivide( int num, int denom ) requires denom != 0 ensures ret$INT >= 0when denom >= 0 { int result; if ( num > 0 ) result = num / denom; else result = -num / denom; return result; }
Example (continued) public static int absDivide( int num, int denom ) requires denom != 0 ensures ret$INT >= 0 when denom < 0 { int result; if ( num > 0 ) result = -num / denom; else result = num / denom; return result; }
Example (continued) • Compiling the source code with JPred-P2 produces a standard Java translation public static int absDivide(int arg$0$Real, int arg$1$Real){ if (arg$1$Real < 0) return absDivide$body1(arg$0$Real, arg$1$Real); else return absDivide$body0(arg$0$Real, arg$1$Real);} Dispatch method
Example (continued) private static int absDivide$body0(int num, int denom) { if (denom != 0) { int result; if (num > 0) result = num / denom; else result = -num / denom;ret$INT = result; if (ret$INT >= 0) return ret$INT;else { System.out.println("postcondition failed: ret$INT >= 0"); System.exit(1); return ret$INT; } } else { System.out.println("precondition failed: denom != 0"); System.exit(1); return 0; } }
Conclusion • JPred-P2 makes it easy on the programmer to enforce precondition and postcondition checking • By defining these predicates in the method header, code becomes a lot more readable • Inheritance of requires and ensures clauses is an idea for future work