object reclassification and evolution
DESCRIPTION
Object reclassification and Evolution. Object Evolution - Tal Cohen, Yossi Gil Fickle (ECOOP 2001) Sophia Drossopoulou, Ferruccio Damiani, Mariangiola Dezani-Ciancaglini, Paola Giannini. Presented by: Oren D. Rubin. Agenda. Motivation – current trends Multiple inheritance Mixin - PowerPoint PPT PresentationTRANSCRIPT
Object reclassification Object reclassification and Evolutionand Evolution
Object Evolution - Object Evolution - Tal Cohen, Yossi GilTal Cohen, Yossi Gil
Fickle (ECOOP 2001) Fickle (ECOOP 2001) Sophia Drossopoulou, Sophia Drossopoulou, Ferruccio Damiani, Mariangiola Dezani-Ciancaglini, Ferruccio Damiani, Mariangiola Dezani-Ciancaglini, Paola GianniniPaola Giannini
Presented by: Oren D. Rubin
AgendaAgenda
Motivation – current trendsMotivation – current trendsMultiple inheritanceMultiple inheritanceMixinMixin
Object EvolutionObject EvolutionInheritanceInheritanceMixinMixinShakeinsShakeins
FickleFickle
MotivationMotivation
The quest for better reuse mechanismsThe quest for better reuse mechanisms Common forms of inheritanceCommon forms of inheritance
– SingleSingle– MultipleMultiple– ‘‘Mixin’Mixin’
MotivationMotivation
The challenge:Your manager asks you to extend two classes (classes A and B), which both have some read\write Operations.He wants you to code new classes which support same operations but these operations are now synchronized using a lock (classes SyncA and SyncB).
Sync these classes
NOW!!
Solution 1: Solution 1: Use C++’s multiple Use C++’s multiple inheritance, inheritance, base class with abstract methodsbase class with abstract methods
class SyncReadWrite { public: virtual int read() { acquireLock(); result = directRead(); releaseLock(); return result; } virtual void write(int n) { acquireLock(); directWrite(n); releaseLock(); }
protected:protected: virtual virtual void acquireLock() void acquireLock() { { // acquire lock// acquire lock } }
virtual virtual void releaseLock() void releaseLock() { { // release lock// release lock } }
virtualvirtual int directRead() = 0; int directRead() = 0; virtualvirtual void directWrite(int n) void directWrite(int n)
= 0;= 0;
}; }; // end of class SyncReadWrite// end of class SyncReadWrite
Solution 1: Solution 1: Use C++’s multiple Use C++’s multiple inheritance, inheritance, base class with abstract methodsbase class with abstract methods
class SyncA : public A, SyncReadWrite {
public: virtual int read() { return SyncReadWrite::read(); }
virtual void write(int n) { SyncReadWrite::write(n); }
protected: virtual int directRead() { return A::read(); }
virtual void directWrite(int n) { A::write(n); }
}; // end of class SyncA
class SyncB : public B, SyncReadWrite { public: virtual int read() { return SyncReadWrite::read(); }
virtual void write(int n) { SyncReadWrite::write(n); }
protected: virtual int directRead() { return B::read(); }
virtual void directWrite(int n) { B::write(n); }
}; // end of class SyncB
Code duplication
Let’s mix itLet’s mix it!!
• A A mixinmixin is an (abstract subclass) is an (abstract subclass) specificationspecification– may be applied to various classes to extend may be applied to various classes to extend
them with the same set of featuresthem with the same set of features
• Key idea: create components designed for Key idea: create components designed for reuse rather than for instantiationreuse rather than for instantiation
• Could be layered one on top of the anotherCould be layered one on top of the another– A* a = new Cache< A* a = new Cache< Sync<A>Sync<A> >; >;– Is CacheIs Cache<Sync<A><Sync<A>> same as > same as
Sync<Sync<Cache<A>Cache<A>>?!?>?!?
Solution 1: Solution 1: Use Use C++’s Mixins C++’s Mixins (no need for multiple inheritance)(no need for multiple inheritance)
template <class template <class Super>Super>classclass SyncSync : : public Superpublic Super { {public:public: virtual virtual int read()int read() {{ acquireLock();acquireLock(); result = result = Super::read();Super::read(); releaseLock();releaseLock(); return return result;result; }} virtual virtual void write(int n) void write(int n) { { acquireLock();acquireLock(); Super::write(n);Super::write(n); releaseLock();releaseLock(); }}
protected:protected: virtual virtual void acquireLock() void acquireLock() { { // acquire lock// acquire lock } }
virtual virtual void releaseLock() void releaseLock() { { // release lock// release lock } }
}; }; // end of MSyncReadWrite// end of MSyncReadWrite
// used either// used eitherA* a = new Sync<A>();B* b = new Sync<B>();// orclass SyncA : public Sync<A> {};class SyncB : public Sync<B> {};
Object EvolutionObject Evolution
Tal Cohen Dr. Yossi Gil
•Reclassification (evolution) changes at run-time the class membership of an object while retaining its identity.
I-EvolutionI-EvolutionBased on standard inheritanceBased on standard inheritanceEvolution is irreversibleEvolution is irreversibleInstance of a class can ‘evolve’ to subclassesInstance of a class can ‘evolve’ to subclasses
Example: Example:
V v = new V();V v = new V();
V v2 = v; //Transparent to aliasing V v2 = v; //Transparent to aliasing
..
vvC(…); // evolves object referenced by variable C(…); // evolves object referenced by variable v v
C must be a subclass of v’s
type
I-EvolutionI-Evolution -- Implementing The Implementing The State Design PatternState Design Pattern
TCPConnection_______________________ TCPState state _______________________Open() {state.Open}Close() {state.Close}Acknowledge(){…}
TCPState _______________________ Open()Close()Acknowledge()
TCPEsbablished____________Open() Close()Acknowledge()
TCPClosed ____________Open() Close()Acknowledge()
TCPListen ____________Open() Close()Acknowledge()
I-EvolutionI-Evolution
With Object Evolution:With Object Evolution:Only the 3 state (concrete) classes are Only the 3 state (concrete) classes are
need – no need for abstract state class and need – no need for abstract state class and the wrapperthe wrapper
Much more elegant (and shorter)Much more elegant (and shorter)TCPClosed.open(){TCPClosed.open(){
// open connection// open connectionthisthisTCPListen(); TCPListen();
}}More efficient, no need to delegate More efficient, no need to delegate
messages any moremessages any more
I-Evolution - I-Evolution - Lazy Data StructureLazy Data StructureLazy Data Structure
<html><html><head><head>
<title> My document <title><title> My document <title></head></head><body><body>
<h1> Header </h1><h1> Header </h1><p> Paragraph </p><p> Paragraph </p>
</body></body></html></html>
I-EvolutionI-Evolution -- Implementing DOM Implementing DOM treestrees
Element _______________________ getName()getAttribute()
Paragraph ____________________
….
Document ____________________
….
Head ____________________
….
Body ____________________
….
Node _______________________ appendChild()getChildNodes()
Text _______________________ getText()setText()
I-EvolutionI-Evolution -- Implementing DOM Implementing DOM treestreesNodes in the DOM tree are only created when they Nodes in the DOM tree are only created when they
are accessedare accessedThe initial call to getDocument() will return a DOM The initial call to getDocument() will return a DOM
tree that consists only of the Document nodetree that consists only of the Document node All other nodes exist unexpanded (virtual) All other nodes exist unexpanded (virtual)
All of the immediate children of a Node are created All of the immediate children of a Node are created when any of that Node's children are accessedwhen any of that Node's children are accessed A node can even exist without its parent being expanded A node can even exist without its parent being expanded
(for some implementations)(for some implementations)This shortens the time it takes to parse an XML file This shortens the time it takes to parse an XML file
and create a DOM tree at the expense of requiring and create a DOM tree at the expense of requiring more memory during parsing and traversing the more memory during parsing and traversing the documentdocument
I-EvolutionI-Evolution
Node n = new Node();Node n = new Node();
Node alias = n;Node alias = n;
nnHead ();Head ();
n.getAttributes();n.getAttributes();
The evolution is done on the object, not the reference (variable)
n’s static type is now Head until end of block
I-Evolution - examI-Evolution - exam
Node n = new Node(..);Node n = new Node(..);Node alias = n;Node alias = n;
If( someCondition ){If( someCondition ){n.getAttributes(); n.getAttributes(); nnHead();Head();n.getAttributes(); n.getAttributes(); alias.getAttributes(); alias.getAttributes(); (Head)alias.getAttributes();(Head)alias.getAttributes();
}}
n.getAttributes();n.getAttributes();(Head)alias.getAttributes();(Head)alias.getAttributes();
I-EvolutionI-Evolution
Node n = new Node(..);Node n = new Node(..);Node alias = n;Node alias = n;
If( someCondition ){If( someCondition ){n.getAttributes(); // Compile time errorn.getAttributes(); // Compile time errornnHead();Head();n.getAttributes(); // successn.getAttributes(); // successalias.getAttributes(); // Compile time erroralias.getAttributes(); // Compile time error(Head)alias.getAttributes();(Head)alias.getAttributes();
}}
n.getAttributes(); // Compile time errorn.getAttributes(); // Compile time error(Head)alias.getAttributes(); // Downcast might fail at runtime(Head)alias.getAttributes(); // Downcast might fail at runtime
I-Evolution - EvolversI-Evolution - Evolvers
Constructor-likeConstructor-likeSyntax Syntax
C(…)C(…)
No return typeNo return typeDefault EvolversDefault EvolversMonotonicMonotonicDeclare throwsDeclare throws
Not constructor-likeNot constructor-likeno need for super, why?no need for super, why?
I-Evolution - When this I-Evolution - When this evolvesevolvespublic class A {public class A {
public void foo() {public void foo() {println(“A.foo – before”);println(“A.foo – before”);thisthisB();B();println(“A.foo – after”);println(“A.foo – after”);bar();bar();baz();baz();
}}
public void bar(){public void bar(){println(“A.bar”);println(“A.bar”);
}}
private void baz(){private void baz(){println(“A.baz”);println(“A.baz”);
}}{{
public class B {public class B {public void foo() {public void foo() {
println(“B.foo”);println(“B.foo”);}}
public void bar(){public void bar(){println(“B.bar”);println(“B.bar”);
}}
private void baz(){private void baz(){println(“B.baz”);println(“B.baz”);
}}{{
Console
A.foo – beforeA.foo – before
B.bar
A.foo – afterA.foo – after
A.baz
I-Evolution – I-Evolution – Evolution FailuresEvolution Failures
NullPointerException - when vNullPointerException - when vC()C() v is null v is null
RunTimeException – when vRunTimeException – when vC()C() v’s dynamic type is not a subtype of Cv’s dynamic type is not a subtype of C
The Evolver throws an exceptionThe Evolver throws an exception Direct or indirectDirect or indirect What happened is irreversibleWhat happened is irreversible
Recall class Head which extended Entity which extended NodeRecall class Head which extended Entity which extended NodeNode n = new Node();Node n = new Node();Try{Try{
nnHead(); // Entity’s Evolver succeeds, Head’s Evolver throws Head(); // Entity’s Evolver succeeds, Head’s Evolver throws exceptionexception
}}Catch{Catch{
n.doSomething(); // object referenced by n is now of type Entityn.doSomething(); // object referenced by n is now of type Entity}}
M-Evolution - MixinM-Evolution - Mixin
To make the next code possibleTo make the next code possibleList myList = new ArrayList();List myList = new ArrayList();myList.add(..) // add numerous data itemsmyList.add(..) // add numerous data itemsmyList->Blocked<Vector>(); myList->Blocked<Vector>();
We needWe needMixin Blocked {Mixin Blocked {
inherited public void add(Object o);inherited public void add(Object o);inherited public void remove(int index);inherited public void remove(int index);
public final void add(Object o){public final void add(Object o){throw new IllegalOperationException();throw new IllegalOperationException();
}}
public final void remove(int index){public final void remove(int index){throw new IllegalOperationException();throw new IllegalOperationException();
}}}}
M-EvolutionM-Evolution
Very similar to I-EvolutionVery similar to I-Evolution Same as vSame as vM<V>() M<V>()
If v’s dynamic type is VIf v’s dynamic type is V But no need forBut no need for
Public void blockParam(List list){Public void blockParam(List list){
if (list instanceOf ArrayList)if (list instanceOf ArrayList)
listlistBlocked<ArrayList>();Blocked<ArrayList>();
else if (list instanceOf Vector)else if (list instanceOf Vector)
listlistBlocked<Vector>();Blocked<Vector>();
elseelse
throw new RunTimeException(“unknown list implementation”);throw new RunTimeException(“unknown list implementation”);}}
M-EvolutionM-Evolution
Solution – The Mixin receives a Solution – The Mixin receives a variable as parameter, not a Typevariable as parameter, not a Type
listlistBlocked<lst>();Blocked<lst>();
No evolution dead-end No evolution dead-end Never fails?!Never fails?!
M-Evolution - IdempotentM-Evolution - Idempotent
Example of an IdempotentExample of an Idempotent
@Idempotent public mixin Undo{@Idempotent public mixin Undo{
inherited public String getText();inherited public String getText();inherited public void setText(String s);inherited public void setText(String s);
private String lastString;private String lastString;
public void setText(String s){public void setText(String s){lastText = getText();lastText = getText();super.setText(s);super.setText(s);
}}
public void undo(){public void undo(){setText(lastText);setText(lastText);
}}}}
M-EvolutionM-Evolution
IdempotentIdempotentGenerates M<T> although asked for Generates M<T> although asked for
M< M< M<T>M<T> > >Mixin is idempotent ifMixin is idempotent if
It is annotated using @Idempotet It is annotated using @Idempotet Meets two criteriaMeets two criteria
Introduces no new members (except for Introduces no new members (except for private)private)
Any overridden method replaces (doesn’t Any overridden method replaces (doesn’t refine) its inherited versionrefine) its inherited version
M-Evolution - Mixin failuresM-Evolution - Mixin failures
Two esoteric casesTwo esoteric casesFailure by final declarations of a class Failure by final declarations of a class
class is declared as finalclass is declared as finalAccidental overriding Accidental overriding
Mixin attempts to introduce new method, only to find Mixin attempts to introduce new method, only to find a method with this signature already existsa method with this signature already exists
public class UndoableJButton extends JButton {public class UndoableJButton extends JButton {public void undo(){public void undo(){
// does something// does something}}
S-Evolution – Evolving with ShakeinsS-Evolution – Evolving with Shakeins
Shakeins – Programming construct that Shakeins – Programming construct that generates a new class from a given class generates a new class from a given class parameterparameter
As opposite to mixinAs opposite to mixinGenerates new class, not new typeGenerates new class, not new type
Can use pointcut expressions and advice Can use pointcut expressions and advice to selectiverly generate new to selectiverly generate new implementations of methods in the original implementations of methods in the original classclass
S-Evolution – Shakein ExampleS-Evolution – Shakein Example
@Idempotent public shakein Transactional{@Idempotent public shakein Transactional{
pointcut publicMethod:= public *(*);pointcut publicMethod:= public *(*);
around: publicMethod {around: publicMethod {Transaction tx = new Session.getTransaction();Transaction tx = new Session.getTransaction();try{try{ tx.begin();tx.begin();
result = proceed(); // invoke original methodresult = proceed(); // invoke original method tx.commit();tx.commit();}}catch(Exception e){catch(Exception e){
tx.rollback();tx.rollback();}}
}}}}
S-Evolution – example of class hierarchyS-Evolution – example of class hierarchy
S<C4> is subtype of S<C4> is subtype of
S’<C2>S’<C2>C4 S<C4>
S’’<C4>
S’<C4>
Type C4
C2 S<C2>
S’’<C2>
S’<C2>
Type C2
C3 S<C3>
S’’<C3>
S’<C3>
Type C3
C1 S<C1>
S’’<C1>
S’<C1>
Type C1C2, C3 extends C1C2, C3 extends C1
C4 extends C2C4 extends C2
S’ and S’’ are S’ and S’’ are
implemented using Simplemented using S
S-Evolution – Shakein State-GroupS-Evolution – Shakein State-Group
Set of Shakeins that share the @StateGroup annotation (with Set of Shakeins that share the @StateGroup annotation (with the same String parameter)the same String parameter)
Shakeins in the same state-group are mutually exclusiveShakeins in the same state-group are mutually exclusivefor arbitrary class C, S1 and S2 are in the same state-groupfor arbitrary class C, S1 and S2 are in the same state-group Applying S2 on S1<C> results in S2<C> Applying S2 on S1<C> results in S2<C>
Overcomes the inability of the I-Evolution-based solution to Overcomes the inability of the I-Evolution-based solution to retract itselfretract itself
Transitions within a state-group are type safeTransitions within a state-group are type safe The compiler enforces the limitation that all shakeins in a given The compiler enforces the limitation that all shakeins in a given
state-group define the same set of private class membersstate-group define the same set of private class members State-group are only for shakeins and not for mixinsState-group are only for shakeins and not for mixins
Established e = new Established<TCPConnection>();Established e = new Established<TCPConnection>(); Some message could change its state from Established to ClosedSome message could change its state from Established to Closed
Perfect for our ‘TCPConnection classes’ …Perfect for our ‘TCPConnection classes’ …
S-EvolutionS-Evolution
@StateGroup(“Connection”) public shakein Listen {@StateGroup(“Connection”) public shakein Listen {public void open(){… thispublic void open(){… thisEstablished<this>(); }Established<this>(); }public void close(){… thispublic void close(){… thisClose<this>(); }Close<this>(); }public void acknowledge(){… }public void acknowledge(){… }
}}
@StateGroup(“Connection”) public shakein Established {@StateGroup(“Connection”) public shakein Established {public void open(){ /* ignore*/ }public void open(){ /* ignore*/ }public void close(){… thispublic void close(){… thisClose<this>(); }Close<this>(); }public void acknowledge(){… }public void acknowledge(){… }
}}
@StateGroup(“Connection”) public shakein Closed {@StateGroup(“Connection”) public shakein Closed {public void open(){ throw new IllegalStateException(); }public void open(){ throw new IllegalStateException(); }public void close() { throw new IllegalStateException(); }public void close() { throw new IllegalStateException(); }public void acknowledge(){… }public void acknowledge(){… }
}}
S-EvolutionS-Evolution – – shakeins as dynamic shakeins as dynamic aspectsaspectsClass definition Class definition
@stateGroup(“Log”) public shakein Log[String filename] {@stateGroup(“Log”) public shakein Log[String filename] {
pointcut publicMethod = public (*);pointcut publicMethod = public (*);
before: publicMethod {before: publicMethod {FileOutputStream fos = new FileOutputStream(FileOutputStream fos = new FileOutputStream(filenamefilename););// log the operation to the file// log the operation to the fileproceed();proceed();
}}}}
@stateGroup(“Log”) public shakein NoLog {@stateGroup(“Log”) public shakein NoLog {// No changes to the class// No changes to the class
}}
UsageUsage
LstLstLog[system.log]<lst>();Log[system.log]<lst>();LstLstNoLog<lst>();NoLog<lst>();
S-EvolutionS-Evolution
Only fails on final classesOnly fails on final classes
Can be marked as idempotentCan be marked as idempotentNo accidental overrideNo accidental override
Classes can have multiple statesClasses can have multiple states
S-Evolution – ImplementationS-Evolution – Implementation
JVMJVM must generate class at run-timemust generate class at run-timeE.g. J2EE application servers use thisE.g. J2EE application servers use this
Object’s behavior is changed at run-timeObject’s behavior is changed at run-timemold is unchanged – change the VMTmold is unchanged – change the VMTmold is changed (several options)mold is changed (several options)
Run over all referencesRun over all referencesUse Object handler – using full garbage collectionUse Object handler – using full garbage collection
FickleFickleFrogs and princes play. When woken up, frogs
blow their pouches and princes swing their swords.
class Frog extends Player {
Vocal pouch;void wake() { pouch.blow(); }
}
class Player {int age;void wake() {}void kiss() {}
}
class Prince extends Player{
Weapon sword;
void wake() {
sword.swing();
}
}
root
state state
void kiss(){Prince}{ this⇓Prince }
{Prince}
FickleFickle
Reclassification is transparent to aliasing
Player p1, p2;p1 := new Frog;p2 := p1;p2.wake(); // blow pouchp1.kiss(); // reclassify bothp2.wake(); // swing sword
FickleFickleVariables are only root classes:
Player charming;Frog kermit; // illegalKermit = new Frog();Charming = kermitcharming.kiss() // turns to princeKermit.vocal // field doesn’t exist error
FickleFickle
More general than evolution Allow non-monotonic reclassification
Only instances of root can reclassify
Programmer must track the effect of each method Compiler verifies
Changes are also to the static type Methods might disappear
Fickle IIFickle IIint play(Frog f1, Frog f2) {
f1.pouch; // O.K.f2⇓Prince; // may change also f1f1.pouch; // could cause ERRORf1.age; // O.K.
}
Type of this and variablesmay change through aliasingFrog f;f:= new Frog;play(f,f);
Fickle III - Fickle III - MT
No more root and state classes
Re-classification are traced also by effectsMethods declare pairs of effects
E.g. myMethod {Prince⇓Frog, …}
Fickle-MT added multithreading
THE ENDTHE END