package fr.moresmau.jp.dbc; import java.lang.reflect.Method; import org.aspectj.lang.reflect.MethodSignature; import bsh.EvalError; import bsh.Interpreter; /** * The aspect implementing the condition checking * @author JP Moresmau JP Moresmau (jp_at_moresmau_dot_fr) * You can use that code freely as long as you keep my name as the author */ public aspect ContractAspect { /** * type of contract expressions to evaluate */ private enum ContractType { PRE, POST } /** * should we use asserts or runtime exceptions? */ private static boolean useAsserts=false; /** * @return Returns the useAsserts. */ public static boolean isUseAsserts() { return useAsserts; } /** * @param useAsserts The useAsserts to set. */ public static void setUseAsserts(boolean useAsserts) { ContractAspect.useAsserts = useAsserts; } /** * default constuctors */ public ContractAspect() { super(); } /** * pointcut for methods with contract */ pointcut dbcMethod() :call(@fr.moresmau.jp.dbc.ContractMethodAnnotation * *..*.*(..)); /** * pointcut for classes with contract */ pointcut dbcClass() : call(public * (@fr.moresmau.jp.dbc.ContractClassAnnotation *..*).*(..)); /** * handling method contracts: check pre and post */ Object around(): dbcMethod() { Method m=((MethodSignature)thisJoinPoint.getSignature()).getMethod(); //Method m=mr.getMethod(); checkMethodContract(ContractType.PRE,m,thisJoinPoint.getTarget(),thisJoinPoint.getArgs(),null); Object o=proceed(); checkMethodContract(ContractType.POST,m,thisJoinPoint.getTarget(),thisJoinPoint.getArgs(),o); return o; } /** * handling class contracts: check invariants before and after */ Object around(): dbcClass() { //Method m=mr.getMethod(); checkInvariants(thisJoinPoint.getTarget()); Object o=proceed(); checkInvariants(thisJoinPoint.getTarget()); return o; } /** * get the annotation from any superclass/interface * @param c the class to examine */ private ContractClassAnnotation getContractClassAnnotation(Class c){ ContractClassAnnotation ann=c.getAnnotation(ContractClassAnnotation.class); if (ann!=null){ return ann; } Class[] interfaces=c.getInterfaces(); for (Class i : interfaces){ ann=getContractClassAnnotation(i); if (ann!=null){ return ann; } } Class sup=c.getSuperclass(); return getContractClassAnnotation(sup); } /** * check class invariant on given instance * @param instance the object instance */ public void checkInvariants(Object instance){ // BeanShell interpreter Interpreter i=new Interpreter(); // get annotation ContractClassAnnotation ann=getContractClassAnnotation(instance.getClass()); if (ann!=null){ // get conditions String[] invariants=ann.invariants(); try { // "this" object i.set("me",instance); // evaluate each condition for (String s : invariants) { try { // eval script, importing the this object Object ret=i.eval("importObject(me);"+s); // only boolean return if (ret instanceof Boolean){ Boolean b=(Boolean)ret; if (!b.booleanValue()){ // contract not respected String errorStr=instance.getClass().getName()+":"+s; if (isUseAsserts()){ // throw assert assert b : errorStr; } else { // throw runtime exception throw new IllegalStateException(errorStr); } } } } catch (EvalError ee){ ee.printStackTrace(); } } } catch (EvalError ee){ ee.printStackTrace(); } } } /** * check method contract * @param ct the contract type (pre or post) * @param m the method to check * @param instance the object instance * @param parameters the parameters to the method call * @param returnObject the object returned by the method (for POST contract type) */ public void checkMethodContract(ContractType ct, Method m,Object instance, Object[] params, Object returnObject){ ContractMethodAnnotation ann=m.getAnnotation(ContractMethodAnnotation.class); if (ann!=null){ // BeanShell interpreter Interpreter i=new Interpreter(); try { // "this" object i.set("me",instance); // get conditions String[] conditions=null; switch (ct){ case PRE: conditions=ann.pre(); break; case POST: conditions=ann.post(); break; } // set parameters if (params!=null){ for (int a=0;a