Vous êtes sur la page 1sur 10

Tutorial on JIR

Preliminary Draft

Jooyong Lee , Robby and Patrice Chalin

Kansas State University, Manhattan, Kansas, USA Concordia University, Montr eal, Qu ebec, Canada

January 25, 2010

Introduction

Despite the widespread uses of JML [2], one of practical problems of JML has been that its tool support has not been able to catch up with Java updates because of the lack of maintainability and extensibility of the existing tools. We propose JIR, i.e., JML Intermediate Representation [3], to address this problem. JIR sits between JML front-ends and back-ends; front-ends construct JML-enhanced Java ASTs and transform these ASTs into JIR, which then are stored in Java bytecode; later on, back-end tools that need to process JML can then read the embedded JIR and reconstruct the ASTs1 (with symbol and type information) by using the provided JIR infrastructure, and make use of these ASTs for their own purposes. Currently, we provide a front-end-side infrastructure built on top of OpenJML [1], and a back-end-side infrastructure that transforms embedded JIR into JIR ASTs where Java expressions and types are expressed with JDTs public ASTs often called DOM ASTs. As a result of the introduction of this intermediate representation, a front-end and a back-end are decoupled each other. Both front-end and back-end developers benet from this decoupling. Front-end developers only need to generate JIR correctly,2 not having to worry about back-end tasks, which helps with improving maintainability at the end. Back-end developers can also improve the maintainability and robustness of their tools because JIR and DOM AST are considerably less subject to change than front-end ASTs as Java and JML evolve. In addition, back-end developers can experiment with new JML extensions more easily because it is easier to extend JIR and its accompanying infrastructure than to extend a front-end. The reader is referred to [3] for more in-depth discussion about JIR. This tutorial is targeted for back-end developers. We provide an infrastructure that hides the details of JIR and make it easy for back-end developers to retrieve JML specications represented in JIR. Using this infrastructure, developers can obtain JIR information which is essentially a blueprint for a class that informs about its type and accompanying type specication, and its members (i.e., methods and elds) and specications for them. The rest of tutorial is organized as follows: Section 2 explains how one can embed JIR information in class les both through command line and programmatic way. Section 3 explains how one can extract JIR information
1 These 2 As

ASTs, while comprehensive, are not necessarily the same as the front-end ASTs. part of the JIR infrastructure, we provide a tool to check well-formedness of JIR.

from a class le, and Section 4 exhibits how one can easily process extracted JIR information by making use of abstract visitor classes that we provide.

JIR embedding

We expect JIR information about a class to be embedded in its corresponding class le. Users can meet this expectation by using a tool we provide: jire.3 This tool is developed on top of OpenJML which should be run in OpenJDK runtime environment. Users are expected to install OpenJDK before using our JIR embedding tool.4 The following is a usage message of jire:
OpenJirEmbedder vM.N.YYYYMMDD HHMM ( c ) SAnToS Laboratory , Kansas S t a t e U n i v e r s i t y ( c ) DSRG, Concordia U n i v e r s i t y Parameters : [ o p t i o n s ] < c l a s s name [ c l a s s name ] . . . > where p o s s i b l e o p t i o n s i n c l u d e : c l a s s p a t h <paths > S p e c i f y where t o f i n d c l a s s f i l e s ( d e f a u l t : . , a l i a s : cp ) s o u r c e p a t h <paths > S p e c i f y where t o f i n d s o u r c e f i l e s ( d e f a u l t : . , a l i a s : sp ) o u t d i r <paths > S p e c i f y where t o p l a c e JIRembedded c l a s s ( d e f a u l t : . , a l i a s : d )

files

Users need to provide a classpath, a sourcepath, and a output directory where JIR-embedded class les are stored. A user can also embed JIR information in a programmatic way as follows:
import o r g . j m l s p e c s . j i r . openjml . OpenJirEmbedder ; ... String [ ] args = . . . ; i f ( ! new OpenJirEmbedder ( ) . run ( a r g s ) ) { // embedding f a i l u r e }

In the above, the same command line arguments as used for jire are assigned to args.

3 We 4 In

also provide another tool jirv by which users can verify the well-formedness of the embedded JIR. the future, jire will be integrated as part of the OpenJDK compiler, thus, it does not have to be run separately.

JIR extraction

As described in the previous section, JIR information is included in each class le once JIR embedding is performed successfully. To retrieve the specications, back-end tools need to extract this JIR information. This can be done by the following three lines of code when is refers to an instance of type java.io.InputStream for a class le of interest:
1 2 3 4 5 6 7 8 9 10 import o r g . j m l s p e c s . j i r . a s t . j d t . dom . J d t J i r B i n d i n g M a n a g e r ; import o r g . j m l s p e c s . j i r . i n f o . e x t e r n a l . J i r B y t e c o d e P r o c e s s o r ; import o r g . j m l s p e c s . j i r . u t i l . D o m J i r B y t e c o d e P r o c e s s o r ; import o r g . j m l s p e c s . j i r . i n f o . J i r T y p e I n f o ; import o r g . e c l i p s e . j d t . c o r e . dom . E x p r e s s i o n ; import o r g . e c l i p s e . j d t . c o r e . dom . Type ; ... J d t J i r B i n d i n g M a n a g e r bm = new J d t J i r B i n d i n g M a n a g e r ( ) ; J i r B y t e c o d e P r o c e s s o r <E x p r e s s i o n , Type> p = new D o m J i r B y t e c o d e P r o c e s s o r (bm ) ; J i r T y p e I n f o <E x p r e s s i o n , Type> i n f o = p . e x t r a c t ( i s ) ;

The extracted JIR information is encapsulated in info at line 8 of type JirTypeInfo; it contains specication information about the type of the class of interest, and its methods and elds. JML expressions used in these specications are represented as instances of org.eclipse.jdt.core.dom.Expression in the above because the JIR information is extracted using DomJirBytecodeProcessor, the ready-made extractor we provide. Though it is also possible for a user to easily implement his or her own extractor (e.g., to use OpenJMLs expression AST type), this will not be covered in this tutorial. The aforementioned processor also associates the type of Type with org.eclipse.jdt.core.dom.Type similarly. We provide a mechanism to extract the type of an expression using a binding manager like bm in the above as will be explained later.

JIR manipulation

Although users can process extracted JIR information for their own purposes from scratch, we make it even easier by providing several handy visitors. Users can extend these visitors and add appropriate actions. In this section, we exhibit by example how this can be done. Readers are referred to the accompanying javadoc documents for the complete information about the visitors. The top-level visitor is JirVisitor; it provides the visitJirTypeInfo API, and by calling it, sub-nodes of an instance of JirTypeInfo are visited recursively. Typical usage of the visitJirTypeInfo API is as follows, where bm and info denote an a binding manager and extracted JIR information, respectively.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import o r g . j m l s p e c s . j i r . J i r E x p r e s s i o n T y p e V i s i t o r ; import o r g . j m l s p e c s . j i r . J i r V i s i t o r ; import o r g . e c l i p s e . j d t . c o r e . dom . E x p r e s s i o n ; import o r g . e c l i p s e . j d t . c o r e . dom . Type ; // u s e d l a t e r import o r g . j m l s p e c s . j i r . a s t . j d t . dom . u t i l . T r e e U t i l ; import o r g . j m l s p e c s . j i r . a s t . j d t . dom . J d t J i r B i n d i n g M a n a g e r ; import o r g . j m l s p e c s . j i r . b i n d i n g . JirNaryOpBinding ; import o r g . j m l s p e c s . j i r . b i n d i n g . JirElementTypeOpBinding ; import o r g . j m l s p e c s . j i r . b i n d i n g . JirNameBinding ; import o r g . j m l s p e c s . j i r . b i n d i n g . JirNaryOpBinding ; import o r g . j m l s p e c s . j i r . b i n d i n g . J i r T y p e B i n d i n g ; import o r g . j m l s p e c s . j i r . b i n d i n g . JirTypeOfOpBinding ; import o r g . j m l s p e c s . j i r . b i n d i n g . JirTypeOpBinding ; import o r g . e c l i p s e . j d t . c o r e . dom . SimpleName ; import o r g . e c l i p s e . j d t . c o r e . dom . AST ; import o r g . e c l i p s e . j d t . c o r e . dom . MethodInvocation ; import o r g . e c l i p s e . j d t . c o r e . dom . T y p e L i t e r a l ; ... J i r E x p r e s s i o n T y p e V i s i t o r expTypeVis = new J i r E x p r e s s i o n T y p e V i s i t o r (bm) { . . . } ; J i r V i s i t o r <E x p r e s s i o n , Type> v = new J i r V i s i t o r <E x p r e s s i o n , Type >(expTypeVis ) ; v . visitJirTypeInfo ( info );

As shown at line 21, JirVisitor instance creation requires another visitor of type JirExpressionTypeVisitor.5 The ellipsis at line 20 means that JirExpressionTypeVisitor is extended. We show how JIR information can be manipulated by extending JirExpressionTypeVisitor below. Let us suppose that we want to translate a JML expression, \typeof(e)<:\type(A), into a Java expression, A.class.isAssignableFrom(e.getClass()). This transformation may be necessary for, e.g., implementing runtime assertion checking. To do so, we override the visitSubtype method of JirExpressionTypeVisitor as follows:
5 The

formal parameter type is IExpressionTypeVisitor that is a super interface of JirExpressionTypeVisitor.

1 2 3 4 5 6 7 8 9 10 11

@Override public boolean v i s i t S u b t y p e ( f i n a l JirNaryOpBinding <E x p r e s s i o n > op , f i n a l E x p r e s s i o n e1 , f i n a l E x p r e s s i o n e2 ) { ... MethodInvocation javaExp = T r e e U t i l . newMethodInvocation ( this . ast , selector , name , new E x p r e s s i o n [ ] { param } ) ; ... }

In the above, the op parameter represents the JIR subtype operator (and associated information), and e1 and e2 represent the operators two operand JIR expressions. While visit methods are dened in accordance with JIR expression denitions as in the example of visitSubtype, users do not have to know these denitions; e.g., users can simply view e1 and e2 in the above as two sub-expressions (or sub-ASTs) of the JML subtype expression. Method invocation of newMethodInvocation at line 5, one of utility methods we provide, creates a Java expression, selector . name ( param ) (assuming a proper interpretation function ). Field ast at line 6 is an instance of org.eclipse.jdt.core.dom.AST, and is necessary to manipulate DOM AST nodes such as when creating a new Java expression. We will see later how one can get this value. In order to generate the Java expression we want, i.e., A.class.isAssignableFrom(e.getClass()), we need to assign appropriate values to selector, name, and param in the rst ellipsis. First, the name value can be easily obtained by calling another utility method as follows:
1 SimpleName name = T r e e U t i l . newSimpleName ( t h i s . a s t , i s A s s i g n a b l e F r o m ) ;

On the other hand, the values of selector and param can be obtained through e1 and e2. A JML subtype expression consists of two sub-expressions of type \TYPE, and JML expressions of this type are \type, \typeof, and \elemtype. Therefore, we can get appropriate values of selector and param by overriding corresponding visit methods, i.e., visit(JirTypeOpBinding), visit(JirTypeOfOpBinding), and visit(JirElementTypeOpBinding). A small problem is that the default visitor does not provide a mechanism to push these values up to the visitSubtype scope. In order to address this problem, we use an extension of JirExpressionTypeVisitor as follows:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

abstract c l a s s J i r E x p T r a n s l a t o r extends J i r E x p r e s s i o n T y p e V i s i t o r { Expression r e s u l t ; public J i r E x p T r a n s l a t o r ( f i n a l J d t J i r B i n d i n g M a n a g e r bindingManager ) { super ( bindingManager ) ; } public E x p r e s s i o n g e t R e s u l t ( ) { return t h i s . r e s u l t ; } public void s e t R e s u l t ( f i n a l E x p r e s s i o n r e s u l t ) { this . r e s u l t = r e s u l t ; } }; J i r E x p T r a n s l a t o r t r a n s = new J i r E x p T r a n s l a t o r (bm) { . . . } ; t r a n s . v i s i t E x p r e s s i o n ( e1 ) ; E x p r e s s i o n param = t r a n s . g e t R e s u l t ( ) ; t r a n s . v i s i t E x p r e s s i o n ( e2 ) ; Expression s e l e c t o r = trans . getResult ( ) ;

JirExpTranslator, an extension of JirExpressionTypeVisitor, stores a translated expression in the result eld. We use JirExpTranslator to visit e1 and e2 at line 19 and 22, and access translated expressions at line 20 and 23. We are now ready to override visit methods of type-related expressions we mentioned before in the ellipsis at line 18. First, we translate a JML expression, \typeof(e), to a Java expression, e.getClass() by the following visit method overriding: 1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public boolean v i s i t ( f i n a l JirTypeOfOpBinding <E x p r e s s i o n , Type> op ) { f i n a l E x p r e s s i o n s e l e c t o r = op . getArgument ( ) ; selector . delete (); f i n a l SimpleName name = T r e e U t i l . newSimpleName ( a s t , g e t C l a s s ) ; f i n a l MethodInvocation mi = T r e e U t i l . newMethodInvocation ( ast , selector , name , new E x p r e s s i o n [ ] { } ) ; s e t R e s u l t ( mi ) ; return f a l s e ; }

In the above, op represents \typeof operator, and thus its argument obtained by op.getArgument() at line 3 represents the operators sole operand expression, which is used as a selector of the method name getClass. Since no argument is necessary for getClass, we pass empty array at line 10. Notice that selector.delete() comes 6

rst at line 4 before selector is used to create a Java expression at line 8. This is done because a DOM AST node cannot be shared between more than one parent; selector.delete() deletes the existing parent of selector. Once a desired Java expression is obtained, we store it by calling setResult as shown at line 11. The above visit method returns false, meaning that sub-ASTs will not be visited, which is appropriate in the case above. Note that the original visit methods return true so that all sub-ASTs can be visited. Continuously, we translate a JML expression, \type(A), to a Java expression, A.class by the following visit method overriding:
1 2 3 4 5 6 7 8 9 10 @Override public boolean v i s i t ( f i n a l JirTypeOpBinding <Type> op ) { JirTypeBinding <Type> tb = op . getType ( ) ; JirNameBinding tnb = tb . getTypeName ( ) ; S t r i n g typeName = tnb . g e t O p t i o n a l S o u r c e F u l l y Q u a l i f i e d N a m e ( ) ; i n t dim = tb . getNumOfDimensions ( ) ; T y p e L i t e r a l t y p e L i t e r a l = T r e e U t i l . n e w T y p e L i t e r a l ( a s t , type , dim ) ; setResult ( typeLiteral ); return f a l s e ; }

Similarly to before, op represents \type operator, and op.getType() returns the type binding information of the corresponding JML type expression. The returned type binding information contains a name binding information and a dimension; name binding information contains both source and binary names of the corresponding type, and the dimension is greater than zero if the corresponding type is of array type. We use the source name6 and the dimension to create a type literal at line 7. As before, we store the translated expression, and return false to stop visiting sub-expressions. Lastly, we translate a JML expression, \elemtype(\type(A[])), to a Java expression, A.class by the following visit method overriding quite similarly to the previous case:
1 2 3 4 5 6 7 8 9 10 11 12 @Override public boolean v i s i t ( f i n a l JirElementTypeOpBinding <E x p r e s s i o n , Type> op ) { JirTypeBinding <Type> tb = op . getArrayType ( ) ; JirNameBinding tnb = tb . getTypeName ( ) ; S t r i n g typeName = tnb . g e t O p t i o n a l S o u r c e F u l l y Q u a l i f i e d N a m e ( ) ; i n t dim = tb . getNumOfDimensions ( ) ; Type type = T r e e U t i l . newType ( a s t , typeName , dim 1 ) ; T y p e L i t e r a l t y p e L i t e r a l = T r e e U t i l . n e w T y p e L i t e r a l ( a s t , type ) ; setResult ( typeLiteral ); return f a l s e ; }

There are only two dierences from the previous case. First, in order to obtain the type binding information,
getArrayType() is called instead of getType() (line 4). Second, the dimension value is decreased by one to reect

the element type (line 8). We did not explain yet how the this.ast value can be obtained. We obtain that value by overriding visitExpression(Expression) at the same level as the visitSubtype method we showed before as follows:
6 Source

names in JIR are optional because they do not exist for anonymous types.

1 2 3 4 5

@Override public void v i s i t E x p r e s s i o n ( f i n a l E x p r e s s i o n e ) { t h i s . a s t = e . getAST ( ) ; e . accept ( this ) ; }

We do not show in this tutorial how to replace a JML subtype expression contained in a JirTypeInfo instance with a translated Java expression. This can be done by extending visitors; and we leave it out as an exercise to the reader.

JIR Binding Information

The above walkthrough gives an example on how to access JIR binding API. In general, JIR provides API in its binding manager (an instance of org.jmlspecs.jir.binding.IBindingManager such as org.jmlspecs.jir.ast.jdt.dom.JdtJirBindingManager) to retrieve information about Java and JML expressions and types (the following methods return null if there is no information that can be retrieved): JirArrayBinding<Type> getArrayBinding(Expression e): can be used to retrieve array information associated with an expression (e.g., for an array access). JirMethodBinding getMethodBinding(Expression e): can be used to retrieve method information associated with an expression (e.g., for an invoke expression). JirPositionInfo getPositionInfo(Expression e): can be used to retrieve positional information (start/end osets, lines, and columns) of an expression in the source code.7 JirTypeBinding<Type> getTypeBinding(Expression e): can be used to retrieve type information of an expression.8 JirVariableBinding<Type> getVariableBinding(Expression e): can be used to retrieve variable information (elds, parameters, locals, and quantied variables) associated with an expression.

References
[1] OpenJML wiki page. http://sourceforge.net/apps/trac/jmlspecs/wiki/OpenJml. [2] Lilian Burdy, Yoonsik Cheon, David R. Cok, Michael D. Ernst, Joseph R. Kiniry, Gary T. Leavens, K. Rustan M. Leino, and Erik Poll. An overview of JML tools and applications. International Journal on Software Tools for Technology Transfer (STTT), 7(3):212232, 2005. [3] Patrice Chalin, Robby, Perry R. James, Jooyong Lee, and George Karabotsos. Towards an industrial grade IVE for Java and next generation research platform for JML. Technical Report SAnToS-TR2009-10-01, Kansas State University, 2009.
7 Currently, 8 Currently,

position information may not be complete (invalid positional values are assigned 0). the JIR Embedder does not generate type information for all expressions.

Type Hierarchies

JirInfoNode

JirElementInfo binarySignature : String

JirInfoMap typeInfos : HashMap<String, JirTypeInfo<Expression, Type>>

JirTypeInfo JirMethodInfo methodSpec : org.jmlspecs.jir.ast.JirMethodSpec<Expression, Type> typeSpec : org.jmlspecs.jir.ast.JirTypeSpec<Expression> isInterface : boolean fields : HashMap<String, org.jmlspecs.jir.ast.JirFieldSpec<Expression>> methods : HashMap<String, JirMethodInfo<Expression, Type>>

Figure 1: The type hierarchy of JirInfoNode. A separate le, JirInfoNode.pdf, is also available as an accompanying document.

JirAstNode info : JirNodeInfo

JirLocalDeclaration JirClauseOrBlock JirCompilationUnitDeclaration JirExpression expression : Expression JirFieldSpec inDataGroupClauses : ArrayList<JirInDataGroupClause<Expression>> kind : JirVariableDeclKind typeReference : JirTypeReference<Type> names : ArrayList<String> initializations : ArrayList<JirExpression<Expression>>

JirMethodSpec isExtending : boolean specCases : ArrayList<JirSpecCase<Expression, Type>> impliedSpecCases : ArrayList<JirSpecCase<Expression, Type>> examples : ArrayList<JirMethodSpecCaseExample> JirMethodSpecCaseExample

JirSpecCase visibility : JirVisibility isCodeSpecCase : boolean kind : JirSpecCaseKind body : JirSpecCaseBody<Expression, Type>

JirSpecCaseBody requiresClauses : ArrayList<JirRequiresClause<Expression>> rest : ArrayList<JirClauseOrBlock> forallVars : ArrayList<JirLocalDeclaration<Expression, Type>> oldVars : ArrayList<JirLocalDeclaration<Expression, Type>> JirType type : Type

JirTypeSpec JirTypeReference dims : int name : String type : JirType<Type> invariantClauses : ArrayList<JirInvariantForType<Expression>> constraintClauses : ArrayList<JirConstraintClause<Expression>> initiallyClauses : ArrayList<JirInitiallyClause<Expression>> representsClauses : ArrayList<JirRepresentsClause<Expression>> axiomClauses : ArrayList<JirAxiomClause<Expression>>

JirClause isRedundant : boolean expr : JirExpression<Expression>

JirSpecCaseBlock bodies : ArrayList<JirSpecCaseBody<Expression, Type>>

JirAccessibleClause

JirAssignableClause

JirCapturesClause

JirConditionedClause predicate : JirExpression<Expression>

JirDivergesClause

JirEnsuresClause

JirInDataGroupClause groupNames : ArrayList<JirExpression<Expression>>

JirLoopInvariant

JirLoopVariant

JirMapsIntoClause groupNames : ArrayList<JirExpression<Expression>>

JirRequiresClause

JirSignalsClause arg : JirLocalDeclaration<Expression, Type>

JirSignalsOnlyClause typeRefs : ArrayList<JirTypeReference<Type>>

JirTypeBodyDeclaration isStatic : boolean visibility : JirVisibility

JirWhenClause

JirDurationClause

JirMeasuredByClause

JirWorkingSpaceClause

JirAxiomClause

JirConstraintClause

JirInitiallyClause

JirInvariantForType

JirRepresentsClause storeRef : JirExpression<Expression> isRelational : boolean

Figure 2: The type hierarchy of JirAstNode. A separate le, JirAstNode.pdf, is also available as an accompanying document.

JirBinding

JirPositionInfo JirMethodBinding JirArrayBinding arrayType : JirTypeBinding<Type> name : String signature : String declaringTypeName : JirNameBinding isStatic : boolean JirNameBinding binaryName : String optionalSourceFullyQualifiedName : String JirOpBinding startPosition : int endPosition : int beginLine : int endLine : int beginColumn : int endColumn : int JirTypeBinding optionalType : Type numOfDimensions : int typeName : JirNameBinding JirVarDeclaration name : String type : JirTypeBinding<Type> JirVariableBinding type : JirTypeBinding<Type>

JirElementTypeOpBinding arrayType : JirTypeBinding<Type>

JirGenericTypeConversionOpBinding argument : Expression type : JirTypeBinding<Type>

JirInvariantForOpBinding expr : Expression typeName : JirNameBinding

JirLabelOpBinding JirIsInitializedOpBinding typeName : JirNameBinding argument : Expression labelId : String isPositive : boolean

JirMethodOpBinding op : JirMethodOp methodBinding : JirMethodBinding arguments : ArrayList<Expression>

JirQuantificationOpBinding JirNaryOpBinding op : JirNaryOp optionalArguments : ArrayList<Expression> JirOnlyCalledOpBinding methodBindings : ArrayList<JirMethodBinding> rangeExpr : Expression predicateExpr : Expression quantifier : JirQuantifier varDeclarations : ArrayList<JirVarDeclaration<Type>> JirSetComprehensionOpBinding JirSpaceOpBinding typeName : JirNameBinding JirTypeConversionOpBinding argument : Expression typeFullyQualifiedName : String JirTypeOfOpBinding argument : Expression type : JirTypeBinding<Type> JirTypeOpBinding type : JirTypeBinding<Type>

JirFieldBinding name : String declaringTypeName : JirNameBinding isStatic : boolean

JirLocalBinding slotIndex : int isParameter : boolean name : JirNameBinding JirQuantifiedVariableBinding name : String

JirInstanceMethodOpBinding receiver : Expression

JirStaticMethodOpBinding

Figure 3: The type hierarchy of JirBinding. A separate le, JirBinding.pdf, is also available as an accompanying document.

Pending Features
Readable if clauses Writable if clauses Monitors for clauses Callable clauses Model programs \only called expression store refs of the forms, e1[e2..e3] and e[]

The following list of JML features are not supported yet.9

Also, redundancy is not recognized in the current release.

9 Some

of pending features are propagated from OpenJML.

10