200 likes | 217 Views
Utilize AspectJ adaptive features for Law of Demeter checking. Capture all calls and avoid checking Java libraries. AspectJ program instrumentation with detailed explanation.
E N D
LoD in AspectJ Karl Lieberherr
Checking LoD in AspectJ • Show the idea, not the details. • How can we precisely express it in a programming language?
An adaptive aspect:Law of Demeter Checker (Object Form) aspect Check { … after(): MethodCallSite{ // call (* *(..)); // check whether // thisJoinPoint.getTarget() // is a preferred supplier // object }
How can we capture all calls? pointcut MethodCallSite(): scope() && call(**(..)); BUT: how to avoid checking calls in Java libraries?
Avoiding the Java libraries pointcut IgnoreCalls(): call(* java..*.*(..)); pointcut MethodCallSite(): scope() && call(**(..)) && !IgnoreCalls();
J DEMETER DHMHTRA AspectJ from PARC AJ (Demeter AspectJ) EMETERJ
Observation • Many AspectJ programs are adaptive (designed for a family of Java programs) • Context: Java program or its execution tree (lexical joinpoints or dynamic join points) • Features enabling adaptiveness: • *, .. (wildcards) • cflow, + (graph transitivity) • this(s), target(s), args(a), call (…), … • inheritance as wild card • pc(Object s, Object t): this(s) && target(t) && call(… f …)
AspectJ crosscutting used in Law of Demeter checker Dynamic call graph cflow(…) target(Object) Connected join points Isolated join points
Aspects and lexical join points • Going to the roots of the Northeastern branch of AOP: Law of Demeter. • Closing the circle: Write an ultimately adaptive program in AspectJ: • Works with all Java programs • Checks the object-form of the Law of Demeter: “talk only to your friends”
Instrumentation of Java programs with Aspects Aspect framework Aspect Diagram Supplier ImmediatePartBin TargetBinStack ArgumentBin Checker LocallyConstructedBin uses pointcuts ReturnValueBin Requirements: Statistics GlobalPreferredBin Good Separation of Concerns in Law of Demeter Checker
Explanation • The *bin* aspects collect potential preferred supplier objects that represent good coupling in the context of a method body. • The Checker aspect checks each method call whether the receiver is a preferred supplier object. • The Statistics aspect counts events generated by the Checker aspect.
AspectJ code • In AOSD 2003 paper with David Lorenz and Pengcheng Wu • AspectJ works well. Program uses most adaptive ingredients of AspectJ: *, cflow, this, target, etc.
package lawOfDemeter; public abstract class Any { public pointcut scope(): !within(lawOfDemeter..*) && !cflow(withincode(* lawOfDemeter..*(..))); public pointcut StaticInitialization(): scope() && staticinitialization(*); public pointcut MethodCallSite(): scope() && call(**(..)); public pointcut ConstructorCall(): scope() && call(*.new (..)); public pointcut MethodExecution(): scope() && execution(**(..)); public pointcut ConstructorExecution(): scope() && execution(*.new (..)); public pointcut Execution(): ConstructorExecution() || MethodExecution(); public pointcut MethodCall(Object thiz, Object target): MethodCallSite() && this(thiz) && target(target);
Class Any continued public pointcut SelfCall(Object thiz, Object target): MethodCall(thiz, target) && if(thiz == target); public pointcut StaticCall(): scope() && call(static * *(..)); public pointcut Set(Object value): scope() && set(* *.*) && args(value); public pointcut Initialization(): scope() && initialization(*.new(..)); }
package lawOfDemeter.objectform; import java.util.*; abstract class ObjectSupplier { protected boolean containsValue(Object supplier){ return targets.containsValue(supplier); } protected void add(Object key,Object value){ targets.put(key,value); } protected void addValue(Object supplier) { add(supplier,supplier); } protected void addAll(Object[] suppliers) { for(int i=0; i< suppliers.length; i++) addValue(suppliers[i]); } private IdentityHashMap targets = new IdentityHashMap(); }
package lawOfDemeter.objectform; public aspect Pertarget extends ObjectSupplier pertarget(Any.Initialization()) { before(Object value): Any.Set(value) { add(fieldIdentity(thisJoinPointStaticPart), value); } public boolean contains(Object target) { return super.containsValue(target) || Percflow.aspectOf().containsValue(target); } private String fieldIdentity(JoinPoint.StaticPart sp) { … } private static HashMap fieldNames = new HashMap(); }
package lawOfDemeter.objectform; aspect Check { private pointcut IgnoreCalls(): call(* java..*.*(..)); private pointcut IgnoreTargets(): get(static * java..*.*); after() returning(Object o):IgnoreTargets() { ignoredTargets.put(o,o); } after(Object thiz,Object target): Any.MethodCall(thiz, target) && !IgnoreCalls() { if (!ignoredTargets.containsKey(target) && !Pertarget.aspectOf(thiz).contains(target)) System.out.println( " !! LoD Object Violation !! " + thisJoinPointStaticPart/*[*/ + at(thisJoinPointStaticPart)/*]*/); } private IdentityHashMap ignoredTargets = new IdentityHashMap();}
package lawOfDemeter.objectform; aspect Percflow extends ObjectSupplier percflow(Any.Execution() || Any.Initialization()){ before(): Any.Execution() { addValue(thisJoinPoint.getThis()); addAll(thisJoinPoint.getArgs()); } after() returning (Object result): Any.SelfCall(Object,Object) || Any.StaticCall() || Any.ConstructorCall() { addValue(result); } }
Conclusions • Aspects and adaptiveness must work closely together to achieve best results. Crosscutting is closely linked to adaptiveness. • AP is a specialization of AOP and AOP is a specialization of AP. It goes both ways. • AspectJ is a really useful language but we are a little concerned about how difficult it was to debug the Law of Demeter checkers.