aspectual collaborations

of 40/40
Aspectual Collaborations Flexible Modules for Generic Object Oriented Programming

Post on 05-Jan-2016




0 download

Embed Size (px)


Aspectual Collaborations. Flexible Modules for Generic Object Oriented Programming. Host Class Graph. Consists of Classes written by programmer connected by interrelationships Has-A parts Is-A edges Just plain ol’ java. Collaboration. Like a class graph, but Roles instead of Classes - PowerPoint PPT Presentation


  • Aspectual CollaborationsFlexible Modules forGeneric Object OrientedProgramming

  • Host Class GraphConsists of Classes written by programmer connected by interrelationshipsHas-A partsIs-A edgesJust plain ol java.

  • CollaborationLike a class graph, butRoles instead of ClassesRoles may have missing behavior (methods or fields)ClosedNo new roles can be added to the collaboration

  • RolesDiffer from classes in that they may have missing methods or fieldsDifferent from abstractAbstract behavior must be methodAbstract classes can never be instantiatedRoles will be completed in-situ at a later date.

  • AdapterConnects host class graph and collaborationClasses and Roles equated pointwiseSubject to semantic constraintsCollaboration is stretched to fitProvides expected parts by exports from hostExports some parts of collaboration to outside world, encapsulates rest

  • Example: Sort Hostpackage host; class HasUsers { User[] users; void initUsers() { ... } void addUser(User u) { ... } void sortusers() { System.err.println("No sorting. Yet."); } User firstuser() { sortusers(); return users[0]; }}class User { String name; int uid; boolean alphabeticallybefore(User that) { return; } boolean uidbefore(User u) { return this.uid < that.uid; }}

  • Example: Sort Collaborationcollaboration sort;role ArrayHolder { expected Item[] arr; void insertionsort() { for (int i=0; i
  • Example: Sort Adapteradapter sortedhost;combine {sort,host} { host.HasUsers += sort.ArrayHolder { sort::Item[] arr provided-by host::User[] users; host::void sortusers() provided-by sort::void insertionsort(); } host.User += sort.Item { sort::boolean before(Item) provided-by host::boolean uidbefore(User); }}

  • DiscussionExpected parts are what give a collaboration a tight yet generic coupling to the host application.Allow generic behavior to have complex interactions with host behavior.

  • Different class graphsHow much may the collaborations role graph and host class graph differ?Must maintain sub/super class linksExpected types after mapping must match types provided by host: Item must be User since arr is users

  • CompositionWhy does the host need to be a complete class graph? Why not a collaboration too?Provide behavior needed by one from the other.Some behavior remains expected.But, What is the return type? come back next week for exciting conclusion! Same bat time, same bat channel

  • A Tale of Two Implementations

  • Dynamic / ProxyLayering behavior like a cakeHost remains unchangedTwo simultaneous different types, but no object equality between them.Behavior can be removedNo permanent memory overheadHard to composeObject mapping hard (vectors are nigh impossible)Unclear whether we can be type safe and pre-compiled at the same time

  • tis a far far better thingCombine bytecode to make new classAlready front-end processedType checked, easily parsableMore SOP than layeringDetermine types by adapterPermanentCan be precompiled type safely (each object has only one type)

  • Format of Class FilesThe secret to working with class files are their convenient layoutAll references are stored in one local pool, the class pool. Parts are stored in area, and class attributes in the last.ClassPoolAttributesInfoFieldsMethods

  • The Constant PoolAll identifiers and references stored thereMethodRef, FieldRef, InterfaceMethodRefExternal Method and Type Refs same for fields and methods!Constant: Strings, Ints, Doubles, Classes, Raw Strings

  • No need to inspect method bodyTo move into a new class, we just do a recursive copy into new class, chasing all index pointers into constant pool.

  • Dealing with Expected PartsPreprocessor adds stub body to all expected method.Postprocessor annotates expected parts as such, so that stub may be thrown away.For unit testing purposes, bodies may not be stubs, but user-written test stubs that simulate expected behavior. Allows collaborations to be tested without application.Can be simulated by linking to simplistic app.

  • How to store annotationsExpected methods need to be marked as such, so that our implementation knows its ok to remove their existing body.Class files allow additional attributes to be stored on fields and methods. Byte code is oneVoid foo()ATTR_expectedATTR_deprecatedInfo_indexOtherClass::int barATTR_Code1234.6Method entry in classConstant pool

  • Alpha Conversion vs ExportOf course, we dont want to pollute name space of resulting class file, so we systematically rename all intra-collaboration references.Names exported are renamed to resulting name, while non-exported names are renamed to unpronouncable names ($).

  • Implementation SubconclusionClass files are easy and flexible to work withPre-parsed semantics make our life easier, while compiled status means we can take well-formedness for granted.This implementation is statically linked (we build up new collaborations from old statically). Dynamic linking is an option, but hard.

  • Composition, ContUnder insertive approach, the types are completely decided by adapter: we are creating new classesMust make sure no expected parts are unexported they become unprovidable

  • AspectsWeve been promising aspectual programming. Now we get some.Aspects are defined as systematic behaviors that are localised to issue, but distributed over multiple classesAdditionally, the rest of the program can be oblivious to aspects existence (Filman&Friedman).

  • Precise AspectsPrecise aspects we almost have already. All we require is to not throw away old method body when replacing a method stub. (memo to self- what happens when we wrap an expected method?)Allows us to play with arguments of wrapped method

  • Example: No Duplicates, pleasecollaboration nodups;role ElemHolder { expected Elem[] elems; expected void addElem(Elem e); void insertMaybe(Elem e) { for (int i=0; i
  • Example: No Duplicates, contadapter sortednoduphost;combine {nodups,sortedhost} { sortedhost.HasUsers += nudups.ElemHolder { nodups::Elem[] elems provided-by sortedhost::User[] users; sortedhost::void addUser(User) replaced-by nodups::void insertMaybe(Elem) provides nodups::void addElem(Elem); } sortedhost.User += nodups.Elem { export nodups::boolean equals(Object); }}

  • What happensWhen the addUser method is invoked, the collaboration addElem is invoked instead, and eventually invokes the expected old method.Notice that the aspect magic is from the adapter, the collaboration sees it as a normal method.

  • Generic AspectsPrecise aspects are nice, but very limitedHow often can do we know the interface preciselyApproach:Precompile method to known simple signatureWrap with generated code at adaptation time to convert arguments and return values of wrapped host method to the known signature

  • Example: Keep it Sortedcollaboration keepsorted;role Thing { expected void sort(); aspectual ReturnVal sortafter(ExpectedMethod e) { ReturnVal r = e.invoke(); sort(); return r; } }

    adapter keepsortedhost;combine {keepsorted,sortedhost} { sortedhost.HasUsers += keepsorted.Thing { keepsorted::void sort() provided-by sortedhost::sortusers(); keepsorted::sortafter wraps sortedhost::void initUsers(); } } and { sortedhost.HasUsers += keepsorted.Thing { keepsorted::void sort() provided-by sortedhost::sortusers(); keepsorted::sortafter wraps sortedhost::void addUser(User); }}

  • Discussion: Generated CodeFrom Collaborationpackage keepsorted;class Thing { void sort() { /*expected*/ }; ReturnVal$A sortafter(ExpectedMethod$A e) { /*aspectual*/ ReturnVal$A r = e.invoke(); sort(); return r; } }abstract class ReturnVal$A { }abstract class ExpectedMethod$A { abstract ReturnVal$A invoke(); }

  • Discussion: Generated CodeFrom Adapterpackage keepsortedhost;class HasUsers { ... void sortusers() { ... }; ReturnVal$A sortafter$addUser(ExpectedMethod$A e) { /*aspectual*/ ReturnVal$A r = e.invoke(); sortusers(); return r; } void addUser$A(Elem e) { /* original method body, untouched */ } void addUser(Elem e) { } ExpectedMethod$A ex = new ExpectedMethod$A$addUser(this,e); ReturnVal$A$addUser r = (ReturnVal$A$addUser) sortafter$addUser(ex); return; }}class ReturnVal$A$addUser extends ReturnVal$A { }class ExpectedMethod$A$addUser extends ExpectedMethod$A { User arg1; HasUsers self; ExpectedMethod$A$addUser(HasUsers s, User u) { self=s; arg1=u; } ReturnVal$A invoke() { ReturnVal$A$addUser r = new ReturnVal$A$addUser(); self.addUser$A(arg1); }}

  • The wrapping approachHandles return values of any typeExceptions in a similar mannerSince each signature will generate different extracting code (exceptions must be rethrown, for example) we have to have subclasses of ReturnVal for each signature. We then downcast to the known type to extract info

  • The dollar signsThe needed downcast is in the generated body for addUser. We must make sure this cast succeeds, as it is not in user code, and they will not know what caused it.

  • Causing a casting errorcollaboration fail; role Foo { RV old; aspectual RV meth(EM e) { ReturnVal r = e.invoke(); ReturnVal o = old; old = r; return (o!=null):o?r; }}

    package host;class A { A get_a() { return new A(); }}class B { void print() { System.err.println("hi"); }}

    adapter whatever;combine {fail,host} { host.A += fail.Foo { export fail::RV old fail::meth wraps host::A get_a(); }} and { host.B += fail.Foo { export fail::RV old fail::meth wraps host::void print(); }}void failit() { A anA = new A(); B aB = new B(); aB.print(); // now aB.old has void ReturnVal aB.old = anA.old; // so does anA! A otherA = anA.get_a(); // tries to unwrap void to A}

  • Existential TypesReturnVal and ExpectedMethod are local to an attachment of the collaboration; we cannot export them.We assure this by adding the unpronounceable $ to their types; thus they cannot be mentioned in an adapter, and cannot be exported.Casting is still an issue, but thats java.

  • Aspectual SubconclusionSmall addition to collaborations make them intuitive fits for aspectual programming.We are able to offer type-safe separate compilation of aspects that can be used with a wide variety of class graphs.

  • Scoping CollaborationsObjects have variables scoped per instance and per class. Collaborations have another dimension:Per AttachmentA counter for method invocation is per method. The default.Per HostA counter for method invocation is shared for methods of class. Expected parts have this property.Per ApplicationAll the methods of the application share the same counter. Can be simulated a-la object oriented globals.

  • A Scoping examplecollaboration c_attachment; role Counter { int att=0; static int st_att=0; expected int sha; expected int st_sha; aspectual ReturnVal meth(ExpectedMethod exmeth) { att++; st_att++; sh++; st_sh++; System.err.println("this wrapped method on this object: "+att+ " and for all objects of this class: "+st_att); System.err.println("all wrapped methods of this object: "+sha+ " and for all objects of this class: "+st_sha); return exmeth.invoke(); }}

    adapter counting;combine {c_shared,c_attachment} { Counted = {c_shared.CountHolder,c_attachment.Counter} { c_attachment::int sha provided-by c_shared::int shared; c_attachment::static int st_sha provided-by c_shared::static int st_shared; export c_attachment::aspectual meth; }} collaboration c_shared;role CountHolder { int shared=0; static int st_shared=0;}

  • Discussion: ScopingComposition is used to add the expected parts to the host.It is still unclear how we specify that one sub-collaboration is only added once, the other possibly multiple times.

  • ConclusionVery flexible systemOur mechanisms expressive enough to express many kinds of aspectual behaviorFew unclear issuesWhat is added once from composed collabs, what is duplicated.Wrapping expected methods with ascpectual methods.