1 / 40

Aspectual Collaborations

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

azura
Télécharger la présentation

Aspectual Collaborations

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Aspectual Collaborations Flexible Modules for Generic Object Oriented Programming

  2. Host Class Graph • Consists of Classes written by programmer connected by interrelationships • Has-A parts • Is-A edges • Just plain ol’ java.

  3. Collaboration • Like a class graph, but • Roles instead of Classes • Roles may have missing behavior (methods or fields) • Closed • No new roles can be added to the collaboration

  4. Roles • Differ from classes in that they may have missing methods or fields • Different from abstract • Abstract behavior must be method • Abstract classes can never be instantiated • Roles will be completed in-situ at a later date.

  5. Adapter • Connects host class graph and collaboration • Classes and Roles equated pointwise • Subject to semantic constraints • Collaboration is stretched to fit • Provides expected parts by exports from host • Exports some parts of collaboration to outside world, encapsulates rest

  6. Example: Sort Host package 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 this.name.lexicographicallybefore(that.name); } boolean uidbefore(User u) { return this.uid < that.uid; } }

  7. Example: Sort Collaboration collaboration sort; role ArrayHolder { expected Item[] arr; void insertionsort() { for (int i=0; i<arr.length; i++) for (int j=i; j<arr.length; j++) if (arr[j].before(arr[i])) swap(i,j); int m=0.0; for (int i=0; i<arr.length; i++) m += arr[i].moved*arr[i].moved; System.out.println("geom. average number of moves: "+(m/arr.length)); } private void swap(int i, int j) { arr[i].moved++; arr[j].moved++; Item o = arr[i]; arr[i]=arr[j]; arr[j]=o; } } role Item { expect boolean before(Item other); int moved = 0; }

  8. Example: Sort Adapter adapter 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); } }

  9. Discussion • Expected 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.

  10. Different class graphs • How much may the collaboration’s role graph and host class graph differ? • Must maintain sub/super class links • Expected types after mapping must match types provided by host: Item must be User since arr is users

  11. Composition • Why 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

  12. A Tale of Two Implementations

  13. Dynamic / Proxy • Layering behavior like a cake • Host remains unchanged • Two simultaneous different types, but no object equality between them. • Behavior can be removed • No permanent memory overhead • Hard to compose • Object mapping hard (vectors are nigh impossible) • Unclear whether we can be type safe and pre-compiled at the same time

  14. ‘tis a far far better thing • Combine bytecode to make new class • Already front-end processed • Type checked, easily parsable • More SOP than layering • Determine types by adapter • Permanent • Can be precompiled type safely (each object has only one type)

  15. Format of Class Files • The secret to working with class files are their convenient layout • All references are stored in one local pool, the class pool. Parts are stored in area, and class attributes in the last. ClassPool Info Fields Methods Attributes

  16. The Constant Pool • All identifiers and references stored there • MethodRef, FieldRef, InterfaceMethodRef • External Method and Type Refs • same for fields and methods! • Constant: Strings, Ints, Doubles, Classes, … • Raw Strings

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

  18. Dealing with Expected Parts • Preprocessor 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.

  19. How to store annotations Constant pool Void foo() • Expected methods need to be marked as such, so that our implementation knows it’s ok to remove their existing body. • Class files allow additional attributes to be stored on fields and methods. Byte code is one OtherClass::int bar 1234.6 Method entry in class Info_index ATTR_expected ATTR_deprecated ATTR_Code

  20. Alpha Conversion vs Export • Of course, we don’t 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 ($).

  21. Implementation Subconclusion • Class files are easy and flexible to work with • Pre-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.

  22. Composition, Cont • Under insertive approach, the types are completely decided by adapter: we are creating new classes • Must make sure no expected parts are unexported – they become unprovidable

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

  24. Precise Aspects • Precise 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

  25. Example: No Duplicates, please collaboration nodups; role ElemHolder { expected Elem[] elems; expected void addElem(Elem e); void insertMaybe(Elem e) { for (int i=0; i<elems.length; i++) if (elems[i].equals(e)) return; addElem(e); } } role Elem { expect int id; public equals(Object o) { return ((Elem)o).id == id; } }

  26. Example: No Duplicates, cont adapter 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); } }

  27. What happens • When 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.

  28. Generic Aspects • Precise aspects are nice, but very limited • How often can do we know the interface precisely • Approach: • Precompile method to known simple signature • Wrap with generated code at adaptation time to convert arguments and return values of wrapped host method to the known signature

  29. Example: Keep it Sorted collaboration 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); } }

  30. Discussion: Generated CodeFrom Collaboration package 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(); }

  31. Discussion: Generated CodeFrom Adapter package 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); } }

  32. The wrapping approach • Handles return values of any type • Exceptions in a similar manner • Since 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

  33. The dollar signs • The 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.

  34. Causing a casting error collaboration 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 }

  35. Existential Types • ReturnVal 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 that’s java.

  36. Aspectual Subconclusion • Small 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.

  37. Scoping Collaborations • Objects have variables scoped per instance and per class. • Collaborations have another dimension: • Per Attachment • A counter for method invocation is per method. The default. • Per Host • A counter for method invocation is shared for methods of class. Expected parts have this property. • Per Application • All the methods of the application share the same counter. Can be simulated a-la object oriented globals.

  38. A Scoping example collaboration 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; }

  39. Discussion: Scoping • Composition 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.

  40. Conclusion • Very flexible system • Our mechanisms expressive enough to express many kinds of aspectual behavior • Few unclear issues • What is added once from composed collabs, what is duplicated. • Wrapping expected methods with ascpectual methods.

More Related