LoDinAspectJ

Download Report

Transcript LoDinAspectJ

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();
AspectJ from PARC
J
AJ
EMETERJ
(Demeter AspectJ)
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)
Isolated join points
Connected 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
Requirements:
uses pointcuts
ReturnValueBin
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.