Checking LoD in AspectJ • Show the idea, not the details.

Download Report

Transcript Checking LoD in AspectJ • Show the idea, not the details.

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)
Measuring Performance
• Traversals implemented in DemeterJ, DJ,
DAJ
• Vary the size of the Object Graph
• Measure the elapsed time between start and
finish
• SunBlade 100 running SunOS 5.8
Elapse Time (ms)
Performance Comparison
20000
15000
DemeterJ
DJ
10000
DAJ
5000
0
5000
10000
15000
Object Graph Size
20000
Elapsed Time (ms)
DemeterJ and
AJ Performance
80
60
DemeterJ
40
DAJ
20
0
5000
10000
15000
Object Graph Size
20000
Performance Comparison
OG Size DemeterJ DJ DAJ
5000
23 3577 32
10000
40 7908 48
15000
55 11863 62
20000
60 14905 70
Complex Request Interfaces
• Benefits
– Decoupling of details
– Optimization, conseq of decoupling
– Remote invocation
Complex Request Interfaces
• We distinguish between two kinds of
complex request interfaces
– Aspectual -> advice: They define code that will
be invoked implicitly. Visitor.
– Normal: They define code that will be invoked
explicitly. Example: SQL, Traversal strategy.
– Pc cri
Complex Request Interfaces
• We distinguish between two kinds of complex
request interfaces
– Normal: They define code that will be invoked
explicitly. Example: SQL, Traversal strategy.
– Aspectual
• Advice: They define code that will be invoked implicitly.
Demeter C++ wrapper bodies, COOL coordinators.
• Connection: They define when implicit invocation will take
place. Demeter/C++ wrapper signatures (e.g., wrapper ->
*,*,B /* all edges of a traversal entering
B */), AspectJ Pointcuts
Selectors
• Better name for
– Pointcut designators: select nodes of dynamic
call tree
– Traversal strategies:
• select nodes and edges of object graph or class
graph
• Select nodes of dynamic call tree
Mitch’s view
• Pcd is a predicate on join points; join point
may contain entire stack
• For one execution
• Does not select trees
• Demeter style selector expressions are
useful for dynamic call stacks
• Stacks, not trees
• Large step semantics of lambda calculus:
lambda and application
• A join point is a tree
Predefined join points
• Spread of a selector: number of different
tree nodes it selects.
• Form of a selector
• Granularity
• Selectors
– Selects set of nodes all getting the same advice
– Selects set of node tuples each tuple element
getting element dependent advice
– Selects set of nodes each node getting node
dependent advice
• graphs
• trees
• Graph where node is
Examples of CR
S
SS1
Normal: CR
Invoked explicitely
pcd
Cool, Error
Connection: CR for
Implicit Invocation
Advice
SS2
CRI 1 and 3 are of the same kind
Usage of CRI
S
SS1
Normal :CRI to send
request to S
pcd
Cool, errors
Connection: CRI for
Advice: CRI to send Implicit Invocation
request to SS2
SS2
LoD paper
Karl’s viewgraphs
From a talk with a different title
Some adjustment is needed
An adaptive aspect:
Law of Demeter Checker
(Object Form)
aspect Check { …
after(): Any.MethodCall{
// call (* *(..));
// check whether
// thisJoinPoint.getTarget()
// is a preferred supplier
// object
}
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.
Law of Demeter
(Join Point Form)
JPT(ID) =
[<target> ID]
<args> List(ID)
<children> List(JPT)
[<ret> ID].
List(S) ~ {S}.
JPT(ID) =
[<target> ID]
<args> List(ID)
<children> List(JPT)
[<ret> ID].
target t2
ret r1
List(S) ~ {S}.
E
target t2
args {a1,a2}
J
r1.foo1()
target null
a1.bar()
ret r3
t2.foo2()
r3.foo2()
Generic Law of Demeter
(Join Point Form)
Definition 1: The LoD_JPF requires that for
each join point J, target(J) is a potential
preferred supplier of J.
Definition 2: The set of potential preferred
suppliers to a join point J, child to the
enclosing join point E, is the union of the
objects in the following sets:
Generic Law of Demeter
(Join Point Form)
• Argument rule: the args of the enclosing
join point E, including the target
• Associated rule: the associated values of E:
the ret values of the children of E before J
whose target is the target of E or whose
target is null.
aspect LoD extends Violation {
pointcut LoD_JPF(): //LoD definition
ArgumentRule()
|| AssociatedRule();
pointcut ArgumentRule():
if(thisEnclosingJoinPoint.getArgs()
.contains(thisJoinPoint.getTarget());
pointcut AssociatedRule():
if(thisEnclosingJoinPoint
.hasSelfishChild(thisJoinPoint
.getTarget()));
}
Pseudo Aspect
• LoD is a ``pseudo'' aspect because it cannot
run in the current implementation of
AspectJ, which doesn't allow declare
warning to be defined on any pointcut with
an if expression.
Join Point Form
• The pointcuts ArgumentRule and
AssociatedRule select the ``good'' join
points.
• ArgumentRule selects those join points
whose target is one of the arguments of the
enclosing join point;
Join Point Form
• AssociatedRule selects those join points
whose target is in the set of locally returned
ID's, and the ID's created in the siblings of
the current node.
Map Dynamic Object Form
(DOF) to LoD_JPF
• We use LoD_JPF pointcut to check DOF:
– Dynamic join point model is mapped to JPT.
• Object is mapped to ID.
• Method invocations are mapped to JPF join points.
The enclosing join point is the parent in the control
flow.
Map Lexical Class Form (LCF)
to LoD_JPF
• We use LoD_JPF to check LCF as follows.
– Lexical join point model is mapped to JPT. Lexical join points
are nodes in the abstract syntax tree
– Class is mapped to ID.
– Join points are signatures of call sites. The enclosing join point
is the signature of the method in which the call site resides. To
run the aspect, a suitable ordering has to be given to the
elements of children:
• all constructor calls, followed by local method calls, followed by the
other join points.
AspectJ code
• In AOSD 2003 paper with David Lorenz
and Pengcheng Wu
• DOF: AspectJ works well. Program uses
most adaptive ingredients of AspectJ: *,
cflow, this, target, etc.
• LCF: AspectJ cannot do it. We sketch how
to add statically executable advice to
AspectJ.
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.