Open Collections

UBC Theses and Dissertations

UBC Theses Logo

UBC Theses and Dissertations

Tool support for understanding and diagnosing pointcut expressions Ye, Lingdong 2007

Your browser doesn't seem to have a PDF viewer, please download the PDF to view this item.

Item Metadata

Download

Media
831-ubc_2007-0646.pdf [ 2.84MB ]
Metadata
JSON: 831-1.0052055.json
JSON-LD: 831-1.0052055-ld.json
RDF/XML (Pretty): 831-1.0052055-rdf.xml
RDF/JSON: 831-1.0052055-rdf.json
Turtle: 831-1.0052055-turtle.txt
N-Triples: 831-1.0052055-rdf-ntriples.txt
Original Record: 831-1.0052055-source.json
Full Text
831-1.0052055-fulltext.txt
Citation
831-1.0052055.ris

Full Text

Tool Support For Understanding and Diagnosing Pointcut Expressions by Lingdong Ye B.Eng., Southwest University of Finance and Economics, 1998 B . S c , McMaster University, 2005 A THESIS S U B M I T T E D IN P A R T I A L F U L F I L M E N T O F T H E R E Q U I R E M E N T S F O R T H E D E G R E E OF Master of Science in The Faculty of Graduate Studies (Computer Science) The University Of British Columbia August 31, 2007 © Lingdong Ye 2007 Abstract Writing correct AspectJ pointcuts is hard. This is partly because of the complexity of the pointcut language and partly because it requires under-standing how a pointcut matches across the entire code base. In this thesis, we present algorithms that compute two kinds of useful information that can help AspectJ developers diagnose and fix potential problems with their pointcuts. First, we present an algorithm to compute almost matched join points. Second we present algorithms to compute ex-planations of why a pointcut does not match (or does match) a specific join point. We implemented two tools using these algorithms. The first is an offline tool that analyzes a code base and produces a comprehensive report. Using this tool, we were able to find several real problems in existing, medium-sized AspectJ code bases. The second tool is an Eclipse plugin called PointcutDoctor. Pointcut-Doctor is a natural extension of A J D T , the mainstream I D E for AspectJ. It provides developers easy access to the same information from within their already familiar development environment. Table of Contents A b s t r a c t i i Table of Contents i i i L i s t of Tables v L i s t of F igures v i Acknowledgments vii 1 In t roduc t ion 1 1.1 Thesis Statement 1 1.2 AspectJ and Aspect Oriented Programming 1 1.3 Existing IDE Support for Writing AspectJ Pointcuts . . . . 4 1.4 Motivating Examples 7 1.4.1 Example 1 7 1.4.2 Example 2 8 1.5 Algorithms to Compute Useful Information 9 1.6 Contributions 10 1.7 Overview of the Thesis 10 2 P o i n t c u t D o c t o r : A n Ex tens ion of A J D T 12 2.1 PointcutDoctor 12 2.1.1 Almost matched join point shadows 12 2.1.2 Pointcut explainer 14 2.2 A Use Case 14 2.3 Summary 15 3 C o m p u t i n g A l m o s t M a t c h e d J o i n Po in t s 16 3.1 Pointcut Relaxation 16 3.1.1 Step 1: Preprocessing 17 3.1.2 Step 2: Relaxing Conjunctions 17 i i i Table of Contents 3.2 Virtual Shadows ' . , • 19 3.3 Related Work 21 3.4 Summary 21 4 Po in t cu t E x p l a n a t i o n 22 4.1 Computing Color-coded Highlighting Explanation 22 4.1.1 Converting Pointcuts into Propositional Formulas . . 22 4.1.2 Key Definitions . 23 4.2 The Algorithm 24 4.3 Algorithm Variants 28 4.4 Computing Textual Explanation 29 4.5 Summary 31 5 E v a l u a t i o n 35 5.1 Case Study Procedures 35 5.2 Results •. . • ' 37 5.3 Performance Impact 40 5.4 Summary 40 6 L i m i t a t i o n s and Fu tu re W o r k 41 6.1 Limitation of Implementation 41 6.2 Limitation of Evaluation 41 6.3 Summary 42 7 Conc lus ion 43 B i b l i o g r a p h y 44 iv List of Tables 3.1 Relaxers 20 4.3 Explanation Messages 30 4.1 Not-match Explanation Heuristics 32 4.2 Match Explanation Heuristics 33 4.4 Conventions used in Table 4.1 and Table 4.2 34 5.1 Messages Issued and Number of Bugs Found in Case Study (Lldentif ied, C:Connrmed, R:Real bugs, V : "Virtual" bugs) . . 38 v List of Figures 1.1 A J D T ' s Cross References View showing a list of matches . . . 5 1.2 One of A J D T ' s warning messages if no match 6 2.1 Screenshot of PointcutDoctor 13 5.1 A Sample of the Report Used in Case Study 36 Acknowledgments I thank my supervisors Kris De Voider and Gregor Kiczales for their guid-ance, support and encouragement that make this work possible. Thanks to them for being supportive for me to work on the thesis in another city. Thanks to them for all the insightful discussions that will continue to guide my whole career life. I thank Gail Murphy for being my second reader and giving me invaluable comments. 1 Thanks to all the great friends I've met in Vancouver for making it such an amazing experience. vii Chapter 1 Introduction 1.1 Thesis Statement In this thesis, we present algorithms to compute two kinds of information about AspectJ [14] pointcuts: almost matched join points and explanation on why a pointcut does not match (or does match) a given join point. We claim that this information can help a developer find problems in existing pointcuts. We also claim that this information can be added to an existing integrated development environment (IDE) in a logical and clean way. In the following sections, we first introduce the background information about AspectJ, Aspect-Oriented Programming, pointcuts and current I D E support for writing pointcuts. We then illustrate that pointcuts are hard to write, and that current AspectJ IDE tools do not provide sufficient support for writing AspectJ pointcuts. Finally, we explain how we will prove our thesis and outline the key contributions of this thesis. 1.2 AspectJ and Aspect Oriented Programming AspectJ is an extension to the Java programming language that provides support for Aspect-Oriented Programming (AOP) in a Java-like syntax. Aspect-Oriented Programming [15] is a new programming paradigm that attempts to improve modularity of software systems. It provides support for modularizing crosscutting concerns using a new language construct, as-pect. Crosscutting concerns refer to concerns that cut across other concerns, and thus, conflict with them in terms of system decomposition. Crosscut-ting concerns cannot be well modularized using mainstream programming methodologies, such as procedural programming and object-oriented pro-gramming. Using mainstream programming methodologies, if we try to modularize crosscutting concerns, other concerns will be scattered across many modules. We next illustrate the concepts of crosscutting concern and pointcut using the code of a simple figure editor. In the following code, class Shape is 1 Chapter 1. Introduction the common super class for all shapes in the system. There are two kinds of shapes in the system, Point and Line. Class Display manages the shapes drawn on the screen. abstract class Shape { Color color; public void setColor(Color c) { this.color = c; } } class Point extends Shape { int x, y; public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } } class Line extends Shape { Point p i , p2; public void setPl(Point p) { th i s . p l = p; } public void se tP2(Point p) { this . p 2 = p; } } class Display { public s t a t i c void refreshO { } } 2 Chapter 1. Introduction Let's consider the implementation of a display-refresh concern: to refresh the display whenever any type of shape changes. Its implementation in Java requires adding a line D i s p l a y . r e f r e s h O in every method that renders a change of any type of shape. These methods include Shape. se tColor (Color) , P o i n t . s e t X ( i n t ) , P o i n t . s e t Y ( i n t ) , L i n e . s e t P l ( P o i n t ) and L i n e .setP2 ( P o i n t ) . We say this display-refresh concern is a crosscutting concern because it cuts across the existing system decomposition. As a result, its implementation in Java scatters across multiple modules, i.e. classes and methods. In AspectJ, we can modularize this concern using the following aspect without the need to modify any class above: 1. aspect DisplayRefreshing { 2. / / The Poin tcut 3. pointcut shapeChange(): execut ion(void Shape.se tColor(Color)) II 4. execut ion(void P o i n t . s e t X ( i n t ) ) | | 5. execut ion(void P o i n t . s e t Y ( i n t ) ) | | 6. execut ion(void L i n e . s e t P l ( P o i n t ) ) I I 7. execut ion(void L i n e .setP2 ( P o i n t ) ) ; 8. / / The Advice 9. after():shapeChange() { 10. D i sp l ay . r e f r e shO ; 11. } 12. } In the above code, line 3-7 declares a pointcut that captures execution of all methods that will change the state of a shape. Pointcut is an AspectJ language construct that is used to pick out dynamic join points, well-defined events in the execution of a program, such as method execution, object in-stantiation and field access. Every dynamic join point has a corresponding static shadow in the source code or byte code of the program, which is called join point shadow[13). For simplicity, in this thesis, when we say that a pointcut matches a join point shadow, we mean that the pointcut matches the join points that correspond to the join point shadow. Primi-tive pointcuts such as c a l l , execution and set specify the kind and other characteristics of the expected join points. Primitive pointcuts can be com-bined using logical operators including && (AND), I I (OR) and ! (NOT). The example pointcut above has the semantics "to pick up join points at the execution of method Shape . se tColor (Color) , or execution of method P o i n t . s e t X ( i n t ) or 3 Chapter 1. Introduction Line 9-11 is an advice that defines the behavior after the state of a shape is changed: to refresh the display. Advice'is an AspectJ language construct that defines the behavior of the aspect at the the join points specified by the pointcut. 1.3 Existing IDE Support for Writing AspectJ Pointcuts It is important to note that tool support is essential for effectively working with AspectJ code and pointcuts in particular. First, aspects potentially affect the entire code base. The AspectJ pointcut language is powerful and is designed to be capable of picking up join points arising from the entire code base. For instance, in the previous example we can write a pointcut execution(* * . s e t * ( . .)) and an advice that refreshes the display when every method whose name starts with string "set" gets executed, no matter in which class those methods are declared. Second, there is nothing in the source code of those affected methods that describes or refers to the pointcut or advice. This absence of information has been called obliviousness [11] [12] [16] [8] by the A O P community. The obliviousness characteristic of A O P is necessary for modularizing crosscutting concerns, because it allows crosscutting concerns to be expressed in separate modules without modifying the code that they crosscut with. But controversially, it also makes the code harder to read and maintain. Tools that explicitly reveal and display the correlation between aspects and the code affected by them help to improve the readability and maintainability of A O P code. AspectJ Development Tools (AJDT) [1], a plug-in of the Eclipse platform [3], is a mainstream IDE for AspectJ. A J D T provides support for writing pointcuts by showing a list of advisees for the selected advice (i.e. matches for the pointcut) in a Cross References View. A screenshot of A J D T with its Cross References View open is shown in Figure 1.1. A J D T also issues some compiler warning messages for several cases of suspicious code, for example, when the pointcut of an advice does not match any join points (as shown in Figure 1.2). In the next section, we are going to see why such information is insuffi-cient for a developer to ascertain the correctness of her pointcuts, as well as to diagnose the problems in her pointcuts. 4 W= Java - spacewar/src/spacewar/SoundEffi File Edit Source Refactor Navigate Search Project Run J r J ' e ! # ' Q-~ % ~.'% ~ -\ IB \B :__ Missile java Ci Debug.aj 'i SoundEffect.a] J J 1 j //' add sound e f f e c t , fox : // - f i r e , and the volume Help SimpIeErrors.aj 11 Sfc Java i CVS Reposi.. SoundEffectaj I the sound e f f e c t // w i l l be a c c o r d i n g to the i n i t i a l p o s i t i o n // o f the b u l l e t . // use: p l a y S o u n d ( S t r i n g type, double x, double y i p o i n t c u t f i r e (doable x, doable y > : c a l l (Bullet-f.new(..) ) Sfiwithincode ( v o i d S h i p . f i r e ( ) ) ££args(*,x,y,.. t ; a f t e r ( d o a b l e x, doable y ) r e t u r n i n g ( B u l l e t b u l l e t i : f i r e ( x , y ) { i f ( b u l l e t i n s t a n c e o f M i s s i l e ) playSound ( " i r a s s i l e F i r e d " , x, y) ; e l s e p l a y S o u n d ( " f c u l l e t F i r e d " , x, y ) ; Problem j Javadoc Declarat • Console C Progres | _, History Synchro I []•: Outline f Wi Cross R in!*] d —I 3 J " k afterReturning(double, double) Pj -<i> advises : {-} ship: constructor-call(void spacewar.Bullet.<init>(spacevvar.Game, double, double, double, double)} • (-> BigShip: constructor-call(void spacewar.Missile.<init>(spacewar.Game, double, double, double, double)) Writable Smart Insert i 24 : 43 Figure 1.1: A J D T ' s Cross References View showing a list of matches p o i n t c u t g e t S x z e ( ) : c a l l < * B u l l e t . g e t S x z e ( . . ) } ; b e f o r e f ) :ge t S i z e { ) { } j j ' lUttlll IL. ! 1 Problems 23 \Javadocj Declaration; C o n s o l e Progress History: Synchronize! Outflne] Cross References; 0 errors, 2 warnings, 0 infos [j Description * I t 3 !*: W a r n i n g s ( 2 i t e m s ) A does net match because declaring type is spacewar.SpaceObject, if match desired use tarqet(spacewar.Guilet) [XlintiunmatchedSuperTypelnCail] 1! A does not match because declaring type is spacewar.SpaceObject, if match desired use target(spacev.<ar.Butlet} tXlint:unmatchedSuperTypeBiCatl] Figure 1.2: One of A J D T ' s warning messages if no match Chapter 1. Introduction 1.4 Motivating Examples To write a correct pointcut, a developer needs to understand how the point-cut matches across her entire code base, as well as the subtleties of the pointcut language. In this section we provide two examples to illustrate these two requirements respectively. 1.4.1 Example 1 Ascertaining that a pointcut is correct in a given code base requires a global understanding of the code at a level of detail that is not easy to obtain or remember for developers. To assist developers, A J D T provides an explicit representation of the join points a pointcut matches in a given code base, i.e., a list of matches in its Cross References View. We believe that.this information is insufficient for a developer to determine a pointcut's correct-ness. We illustrate this problem with a concrete example which was taken from [17]: pointcut connectionCreation(String u r l , String username,String password) : c a l l ( p u b l i c s t a t i c Connection DriverManager.getConnection(String, String, String)) && args(url, username, password); This pointcut matches the calls to method getConnection of DriverManager that have three String parameters. The args pointcut binds the three pa-rameters of the method call to variables so that they can be used later. Assume that a developer formulated this pointcut to capture the creation of database connections by calling related methods in DriverManager class. How would the developer know that this pointcut is correct? Since the in-tention is to capture all creations, verifying correctness means ascertaining that there are no accidentally missed creation sites. A J D T ' s cross references view shows which join point shadows produce join points that are matched by the pointcut, but it does not show any explicit information about join points that are not being matched. Consequently the view does not help to discover unintended misses. Indeed, it is hard for a developer to scan a list of matches and realize something that should be there is not. However this kind of determination is often critical in understanding whether a pointcut is correct. For example, there are three getConnection methods declared in DriverManager that can be used to create database'connections: 7 Chapter 1. Introduction DriverManager.getConnection(String,String,String) DriverManager.getConnection(String, Properties) DriverManager.getConnection(String) Only calls to the first of these 3 methods are matched by the example point-cut. The pointcut is therefore incorrect, since it fails to match calls to the other two. Because A J D T ' s cross references view provides no explicit infor-mation about non-matched join points, it provides little help to discover this important fact. As a result the bug in the pointcut is likely to go unnoticed. A tool that shows explicitly information about unmatched join points could help developers avoid this kind of unintended misses. 1.4.2 Example 2 Another difficulty with regard to pointcut writing is the complexity of the pointcut language semantics itself. Consider for example the following point-cut: pointcut f i r e ( ) : c a l l ( B u l l e t . n e w ( . . ) ) && withincode(void S h i p . f i r e ( ) ) W i l l the following constructor calls be matched by this pointcut? class BigShip extends Ship { pub l i c void f i r e ( ) { B u l l e t b l = new B u l l e t ( n u l l , 0, 0); B u l l e t b3 = new M i s s i l e ( n u l l , 0, 0); } } Intuitively, this pointcut matches calls to the constructor of B u l l e t in the lexical scope of Ship.f i r e ( ) . However, the reality of how the pointcut actually matches in this eample might be surprising to some developers: the join point corresponding to (1) is matched, while the join point correspond-ing to (2) is not. First of all, both calls pass the test withincode (void S h i p . f i r e ( ) ) , though neither of them are in S h i p . f i r e . This is be-cause withincode not only matches join point shadows within the speci-fied method, but also implicitly matches join points shadows within all the methods that override it, so (1) is matched. Second, although M i s s i l e is a subclass of B u l l e t ) , (2) is not matched. This is because the c a l l pointcut does not match any calls to constructors for subtypes of the type specified in / / ( D / / ( 2 ) 8 Chapter 1. Introduction the pointcut. These and other complexities of the AspectJ pointcut language and the Java language make writing correct pointcuts difficult. A J D T only provides limited support for explaining a pointcut, e.g. the warning message shown in Figure 1.2. We believe that a tool that offers an explanation of why a certain join point is matched or not matched could help developers diagnose problems with their pointcuts. 1.5 Algorithms to Compute Useful Information The examples in the previous section motivate that it would be useful to show information about non-matched join points to developers as well as explanations about why a pointcut matches or does not match certain join points. This thesis contributes algorithms to compute such information. Our first algorithm computes almost matched join points. We use a technique we call pointcut relaxation that uses simple heuristics to provide a more nuanced notion of "almost matched" than existing algorithms. Our algorithm also computes virtual join point shadows to handle possible evolutions of the code base. We also contribute several algorithms that compute two kinds of expla-nations to help a developer understand and diagnose problems in her point-cuts. The first computes a color-coded highlighting of a pointcut expression that shows which parts of the pointcut expression are responsible for the non-match(or match). The second produces a text message explaining why a highlighted part matches (or does not match) the given join point. We claim that the information discussed above can help a developer find problems in her pointcuts. We also claim that this information can be added to existing I D E in a logical and clean way. To prove our first claim, we developed an offline tool that produces a comprehensive report about all the pointcuts in a given code base. We applied this tool to the code bases of two medium-sized AspectJ projects in different domains (AspectJ refactored jEdit [9] and ORBacus [18]). We found that even in a relatively stable code base developed by experienced programmers there are still problems in the pointcuts due to programmers' incomplete knowledge of the code base or confusion about the AspectJ point-cut language. This shows that the information computed by our algorithms can help a developer find problems in her pointcuts. To prove our second claim, we developed an Eclipse plugin called Point-9 Chapter 1. Introduction cutDoctor 1. PointcutDoctor extends A J D T seamlessly to provide developers easy access to the same information from within their familiar development environment. This shows that this information can be added to existing . I D E in a logical and clean way. 1.6 Contributions This thesis makes the following contributions: • We present our pointcut relaxation algorithm to compute almost matched join point shadows. Our algorithm is more sophisticated and provides a more nuanced notion of "almost matched" than the boundary join points technique previously proposed by Anbalagan and Xie [5]. We will discuss these differences in detail in Chapter 3. • We present algorithms that explain why a pointcut does not match (or does match) join points produced by a given join point shadow and provide improvement suggestions. • We developed a user interface that is a natural extension to A J D T on Eclipse and shows this information to developers from within their familiar development environment. • We show that the information on almost matched join points and the explanations computed by our algorithms can be used to find problems with pointcuts in real AspectJ code. 1.7 Overview of the Thesis The remainder of this thesis is organized as follows: Chapter 2 presents PointcutDoctor, our IDE tool implemented as an extension of A J D T . This shows how the information our algorithms compute can be made accessible from within the IDE, and thus supports our second claim. We choose to discuss about PointcutDoctor first because it will give readers a more intu-itive overview of the idea behind this thesis by seeing how the information our algorithms compute is used in a concrete IDE. Chapter 3 discusses our algorithm and heuristics to compute almost matched join point shadows. Chapter 4 gives the pointcut explanation algorithm as well as the heuristics for providing the detailed textual explanation. Chapter 5 presents our case 1 PointcutDoctor: http://pointcutdoctor.cs.ubc.ca 10 Chapter 1. Introduction studies on two medium sized code base. Chapter 6 presents some of the l imitat ions of our algorithms,. of their current implementat ion and o f the evaluation presented in this thesis, and some ideas on how these could lead to future work. Chapter 7 presents our conclusions. 11 Chapter 2 PointcutDoctor: An Extension of AJDT In this chapter, we demonstrate PointcutDoctor, an extension to A J D T that provides developers two kinds of information about AspectJ pointcuts: almost matched join points and an explanation on why a pointcut does not match (or does match) a given join point. We can see how PointcutDoctor adds the information to existing IDE in a logical and clean way. This proves our second claim of our thesis. We choose to discuss the IDE tool first because it provides an intuitive overview of the idea behind this thesis .by seeing how the information our algorithms compute is used in a concrete IDE. 2.1 PointcutDoctor The user interface of PointcutDoctor is designed to be non-disruptive to the users who are already familiar with A J D T . A l l features are integrated into the current A J D T user interface without introducing any new views. The existing A J D T interface is preserved and extended with some additional behavior in a clean and logical way. The main point where PointcutDoctor functionality has been added is in the cross references view. The two major features are discussed below. 2.1.1 Almost matched join point shadows First, as discussed in Chapter 1, the original A J D T cross references view only displays information about matched join point shadows (see Figure 1.1). As shown in Figure 2.1, PointcutDoctor extends this with an additional list of almost matches. Notice also that some of the almost matched shadows are marked as "virtual". Our algorithm produces virtual shadows for.calls to methods that have been declared but for which no actual calls exist within the code base. The rationale behind this is that even though such calls do not exist in the current code base it is possible, and perhaps likely, that they 12 M J 1 p o i n t c u t b u i i e t C r e a t i o n (Game game,- B u l l e t .bullet).:... . c a l l (Bui l e t .new (Game, double, double, double, double)) ccargs(game) Sttarcjet ^ u l l e t ) • Constructor calis don't have "target"..Use the advice after(...) returning] ^ , (...) to capture the object being created: '_• . •'-a f t e r (Game, game, B i i i x C i , . ^ i ^ i . w - - " « ^ ^ ^ " ^ ^ " t H " ^ i f - J ' « J - ' t ' - w - i - . i i f ( b u l l e t i nstaneeof H i s s i l e ) playSound("missileFired") ; e l s e p l a y S o u n d ( " t a u l l e t F i r e d " ) ; Problems,} Javadocj ^ d ^ £ j ^ ] | £ [ 0 9 r e s s Q K biJetCreatk)n(6arne, Bufet) B - «*> almost matched join points f I <-> KgShp: constructor-cal(void Missile.<init>(6ame,double,double/double,double)) | |.{-> • on t^Ji I- t< i r-4(y/< ,it n r M ( G i r i f d>i bl^: d 'Lj!~,d i '[•' /inLib'^Vj ) «- H (wtual) constructor-ca8(vojd Bullet.<init>(double,double,Game)) • ...-4> matched join points Figure 2.1: Screenshot of PointcutDoctor Chapter 2. PointcutDoctor: An Extension of AJDT will be added in future versions and a developer should take such potential join points into account when trying to write "robust" pointcuts. 2.1.2 Pointcut explainer The second extension is a pointcut explanation feature that is closely inte-grated into A J D T ' s cross references view and editor. As shown in Figure 2.1, when the developer clicks on a shadow in the cross references view Pointcut-Doctor provides an explanation by highlighting specific parts of the pointcut right in the editor. The highlighted parts are those that are responsible for matching or not matching this join point shadow. The highlighting uses a color-coding scheme based on the matching status of the pointcut fragment. Three highlighting colors are chosen for "Matching" (colored green), "Not matching" (colored red) and "Maybe" (colored yellow), where "Maybe" in-dicates that matching requires runtime determination. If the colored highlighting alone is not a sufficient explanation, the de-veloper may elicit more information by hovering over the highlighted part of the pointcut to request an explanation why this particular pointcut frag-ment is colored the way it is. The textual explanation elaborates the causes and tries to educate the user on the subtleties of AspectJ and provides sug-gestions for improving the pointcut. The explanations are tailored to the specific context of the user's code. 2.2 A Use Case Figure 2.1 shows the screenshot of PointcutDoctor in a typical scenario. A user writes a pointcut and finds an expected join point shadow appear in the list of almost matches. She clicks on that join point shadow in the cross refer-ences view of A J D T . The responsible fragments are highlighted in two differ-ent colors: args(game) and target (bu l l e t ) are in red indicating that they do not match the selected join point shadow, and withincode are in green indicating that it matches. Because she is not sure why target (bu l l e t ) does not match the join point shadow, she moves her mouse over that frag-ment. A tooltip shows up explaining why this fragment does not match and providing suggestion to include this join point shadow. 14 Chapter 2. PointcutDoctor: An Extension of AJDT 2.3 Summary In this chapter, we showed PointcutDoctor, an extension to A J D T that pro-vides developers two kinds of information about AspectJ pointcuts: almost matched join points and an explanation on why a pointcut does not match (or does match) a given join point. PointcutDoctor provides developers easy access to the information from within their already familiar develop-ment environment. This chapter proves the second claim of our thesis, the information computed by our algorithm can be added to existing I D E in a clean and logical way. 15 Chapter 3 Computing Almost Matched Join Points In this chapter we present our algorithm to compute almost matched join point shadows. We use a technique we call pointcut relaxation. We also propose virtual join point shadows to handle the possible evolution of the code base. Finally, we will discuss how our algorithms are different from some existing work. 3.1 Pointcut Relaxation Pointcut relaxation is the process of slightly modifying the original pointcut to produce relaxed pointcuts so that they match more join points than the original one. We can compute almost matched join point shadows from the difference of the sets of matches for the relaxed pointcuts and the original pointcut. As previously noted, pointcut relaxation is similar to Anbalagan and Xie's method of computing boundary join points [5]. However, pointcut relaxation provides a'more nuanced notion of "almost matched" and provides support for virtual join point shadows in potential future versions of the code. A more detailed comparison of the two techniques can be found in Section 3.3. A pointcut is relaxed in two steps—preprocessing and conjunction relax-ation. The overall process is as shown below: Set<Pointcut> re l a x ( P o i n t c u t pointcut) { /* Step 1: preprocessing */ Pointcut ptcDNF = convertToDNF( dropNegation(pointcut)); /* Step 2: r e l a x i n g conjunctions */ Set<Pointcut> relaxedPointcuts =• (/>; f o r each conjunction p i n ptcDNF 16 Chapter 3. Computing Almost Matched Join Points { Set<Pointcut> ptcs = re laxConjunct ion(p) ; re laxedPointcuts = re laxedPointcuts | J p t c s ; } re tu rn re laxedPoin tcu ts ; } 3.1.1 Step 1: Preprocessing In the preprocessing step, all negation pointcuts (pointcuts proceeded by a !) are dropped and the resulting pointcut is then converted into Disjunctive Normal Form ( D N F ) 2 . Dropping negations relaxes a pointcut allowing it to match join points excluded by the negated expression. Dropping nega-tions also has the advantage that it greatly reduces the complexity of the resulting D N F . Dropping negated parts of a pointcut is straightforward and conversion to D N F is a standard technique [7], so we.will not elaborate on the preprocessing step any further. Pointcut relaxation is simplified by this preprocessing step because we can just relax the conjunctions in the D N F separately in the following steps. 3.1.2 Step 2: Relaxing Conjunctions In the second step, each conjunction (primitive pointcuts connected by &&) of the resulting D N F will be relaxed separately, and the results are recombined. This step is driven by a set of heuristic rules. We used instructional books ([17], [10]), web resources ([4]), and the result of a study on the AspectJ-user mailing list [2], as guidelines to devise these heuristics. In our mailing list study, we identified and classified 67 discussion topics about pointcut writing in the topics posted from May 2005 to December 2006. Relaxers are defined for different parts of a pointcut. Specifically, for primitive pointcuts that have method/field signatures ( c a l l , execution, get, set, withincode, p r e / i n i t i a l i z a t i o n ) , a relaxer is assigned to each component of their signature patterns, such as the return type pattern, the declaring type pattern and the parameter list pattern. For other primitive pointcuts, a relaxer is assigned to the whole pointcut. Some relaxers are capable of applying different relaxation methods based on heuristics that analyze both the pointcut and the code base to which it is being matched. 2 D N F is a standard notion in logic as described in [7], e.g., (a A b A c) V (d A e) is in DNF, while (a V b) A c is not. 17 Chapter 3. Computing Almost Matched Join Points Table 3.1 elaborates the different relaxers used in PointcutDoctor and our reporting tool. The pseudo code for relaxing conjunctions is given below: Set<Pointcut> re laxConjunct ion(Pointcut ptcConjunction) { List<Relaxer> candidates = createRelaxers( p tcConjunct ion) ; sortRelaxersByPrecedence(candidates); List<Relaxer> selectedRelaxers = s e l e c t F i r s t N ( candidates, N); Set<Pointcut> re laxedPointcuts = </>; applyAHRelaxers (p tcConjunc t ion , 0, se lec tedRelaxers , r e l axedPo in tcu t s ) ; r e tu rn re laxedPoin tcu ts ; } v o i d app lyAHRelaxers (Po in tcu t po in t cu t , i n t index, List<Relaxer> se lec tedRelaxers , Set<Pointcut> re laxedPointcuts ) { i f ( index<se lec tedRelaxers . s izeO) { Set<Pointcut> relaxedByOne = se l ec t edRe laxe r s [ index ] . r e l ax (po in t cu t ) ; fo r each re laxedPointcut i n relaxedByOne applyAHRelaxers ( re laxedPointcut , index+1, se lec tedRelaxers , r e l axedPo in tcu t s ) ; } e lse re laxedPoin tcu t s . add(po in tcu t ) ; } First, a list of candidate relaxers are generated for the non-wildcard parts 3 in the original pointcut. For example, for pointcut c a l l ( * Foo . b a r ( i n t ) ) && target ( f ) , the candidate list consists of DeclaringTypeRelaxer, NameRelaxer, ParamsRelaxer and ThisOrTargetRelaxer. Next, the list of candidate re-laxers is sorted using a heuristic rule based on our intuition determining their precedence. The precedence of relaxers used in PointcutDoctor and our reporting tool is defined as follows: 3 A non-wildcard part refers to a pointcut part that is neither a * nor . . 18 Chapter 3. Computing Almost Matched Join Points ParamsRelaxer > ArgsRelaxer > AnnotationRelaxer > ModifierRelaxer > ThrowRelaxer > ReturnTypeRelaxer > HandlerRelaxer > DeclaringTypeRelaxer > ThisOrTargetRelaxer > WithinRelaxer > NameRelaxer The first N most significant relaxers will be selected for the next step, where N is a configurable parameter that can be changed to produce more or less almost matched join point shadows. In our current implementation, an experimental value of 6 is initially assigned, to N . In addition, we re-strict N to be less than the number of non-wildcard parts in the pointcut to avoid creating an overly-broad relaxed pointcut (one that matches every join point). The effect of this constraint on N is to relax specific pointcuts more than generic ones. This approach seems to work well in practice. Finally, the selected relaxers are executed to relax all applicable parts in the pointcut depending on the conditions defined in each relaxer. Note that it is possible that the conditions of multiple relaxation methods in a relaxer (e.g. DeclaringTypeRelaxer) are satisfied simultaneously. In such cases, multiple relaxed pointcuts will be created. We use the relaxed pointcuts and a modified AspectJ weaver to compute almost matched join point shadows. To compute almost matched join point shadows, we augmented the original AspectJ weaver to match the relaxed pointcuts in parallel with the original pointcut. 3.2 Virtual Shadows In some cases, even when a join point does not exist in the current code base, it is very likely these join points will occur in future versions of the code, e.g. if a method is declared but not used (this is often the case when using code libraries). Our tool is able to detect this case and identify these virtual join point shadows as matched/almost matched join point shadows. This technique is implemented by augmenting the weaving process of AspectJ compilation. The AspectJ compiler produces instances of different subclasses of its abstract Shadow class when visiting each join point shadow in the code base and matches these shadows against each applicable pointcut. We introduce a group of virtual shadow classes that extend the Shadow class and that are instantiated right after the related code element (e.g. a method declaration) is visited. These virtual shadows act almost the same as other shadows in the matching process, except that they are not matched against the standard shadow mungers, such as advices, declares etc. 19 Table 3.1: Relaxers Relaxer Condition Relax Operation Signature Relaxers AnnotationRelaxer change to * Modifier Relaxer change to * , Ret urnTy p eRelaxer if the return type pattern is primitive type ( int , v o i d , . . . ) change to * if the return type pattern can be resolved to a single type change to its super type if the return type pattern has no + add + DeclaringTypeRelaxer if the declaring type pattern has no + add + if the pointcut is not about constructors and can be resolved to a single type change to its super type if the declaring type does not match types in all packages add *. . at the beginning NameRelaxer if the name pattern does not start with * add * at the beginning if the name pattern does not end with * ' add * at the end ParamsRelaxer change to . . ThrowRelaxer change to * Pointcut Relaxers ArgsRelaxer drop Handler Relaxer if the exception type pattern has no + add + if the exception type pattern can be resolved to a single type change to its super type ThisOrTargetRelaxer drop WithinRelaxer if the type pattern has no + add + Chapter 3. Computing Almost Matched Join Points 3.3 Related Work We next discuss how our algorithm is different from some existing work. A n -balagan and Xie propose boundary join points [5] in the context of AspectJ pointcut testing. Boundary join points are join points that are not matched by the pointcut but have close textual representations to the matched ones. Our algorithm for computing almost matched join point shadows is more so-phisticated. Firstly, in contrast to boundary join points, pointcut relaxation, uses important structural information from the code base to inform relax-ation. For example we consider two classes as similar if they are related through inheritance whereas boundary join points only considers textual similarity. Secondly, pointcut relaxation attaches a different significance to different parts of a pointcut. For example a variation in the name of a method signature is considered more significant than a variation in the re-turn type. In contrast boundary join points treat all parts of a pointcut as having the.same significance. Thirdly, we propose the notion of virtual join point shadows which allows showing information about almost matched join points that do not exist, in the current code base, but are likely to occur in future versions of the code. Anbalagan and Xie also propose mutant pointcuts [6]. The way to gener-ate mutant pointcuts is similar to our pointcut relaxation in that the original pointcuts are slightly modified. However, this technique is used for verifying if a test suite is able to detect common errors in pointcuts, rather than com-puting almost matched join points information that helps developers write pointcuts. 3.4 Summary In this chapter, we presented our algorithm, a technique called pointcut re-laxation, to compute almost matched join point shadows. We also proposed virtual join point shadows to handle the possible evolution of the code base. Finally, we discussed how our algorithms are different from Anbalagan and Xie's work on boundary join point and mutant pointcuts. 21 Chapter 4 Pointcut Explanation In order to explain why a pointcut does not match (or does match) a join point, we present two kinds of explanations in PointcutDoctor. The first is a color-coded highlighting of a pointcut expression that shows which parts of the pointcut expression are responsible for the non-match(or match). The second is a text message explaining why a highlighted part matches (or does not match) the given join point. In the following two sections, we will discuss the algorithms that compute these two kinds of explanation. 4.1 Computing Color-coded Highlighting Explanation Intuitively, if a pointcut unintentionally does not match a join point, this is because there is something wrong with one or more parts of the pointcut, i.e. these parts do not match (or do match 4) the given join point. Point-cutDoctor highlights these responsible parts in the pointcut using a color indicating whether they match or not. In this section we present our algorithm to identify these responsible parts. First we show that any pointcut can be converted into a propositional formula, so that the following definitions and algorithms can be discussed using standard propositional logical notations. We then develop the key definitions and the algorithm to compute the explanation. Finally we discuss some variations of the given algorithm. 4.1.1 Converting Pointcuts into Propositional Formulas Any pointcut can be converted into a propositional formula. First, any primitive pointcut can be converted into a propositional formula that has a conjunction of variables. Each variable corresponds to a fragment of the 4When a negated pointcut matches a join point, it could cause the entire pointcut not to match. 22 Chapter 4. Pointcut Explanation pointcut (e.g. the declaring type) and its value is determined by the given join point and the pointcut. Secondly, since any compound pointcut consists of primitive pointcuts connected by A(AND), V(OR) and/or -^(NOT) oper-ators, we can recursively convert a compound pointcut into a propositional formula. For example, the pointcut c a l l (* Foo+. f 00 ( i n t , . . ) ) && target (SubFoo) can be converted into u\ A it 2 A 113 A U4 A 1x5. where the variable u\,u$ represent the matching status of different parts of the pointcut for a given join point: • u\\ does the return type in the signature of the join point match the pattern *? . • U2: does the declaring type in the signature of the join point match the pattern F 0 0 + ? • 113: does the name in the signature of the join point match the pattern foo? • U 4 : does the arguments of the given join point match the pattern (int,..)? • 115: does the target of the given join point match the pattern SubFoo? Notice that we convert each component of a pointcut into a unique vari-able. Each variable represents a fragment of the pointcut and is treated as an independent piece of the cause, even though intuitively these vari-ables might not be independent. For example, in c a l l ( * Foo. foo ( i n t ) ) && args.( int) , two distinct variables are introduced to represent the two occurrences of ( i n t ) in the pointcut. As a result when a join point does not have an ( in t ) parameter lists both occurrences of ( i n t ) in the pointcut are considered as independent causes for the non-match. This results in the desired behavior for generating coloring explanations, i.e. each occurrence of ( i n t ) in the pointcut will be independently marked as responsible for the non-match and colored accordingly. 4.1.2 K e y Definitions We can now present formal definitions to allow us to identify the responsible parts in a pointcut, that is, the parts of the pointcut that are responsible for a particular (non)match. We mainly use the terminologies and mathematical notations from [7]. 23 Chapter 4. Pointcut Explanation For the propositional formula L consisting of variables u\, ..., un and the logical operators A(AND),V(OR) and ->(NOT), we represent its inter-pretation / as a set of tuples: / = {(u\, z \ ) , ( u n , zn)}\ where Z{ € {T, F} and (ui,Zi) stands for assigning value Z{ to variable U{. L(I) denotes the truth value of L given the interpretation / . A subset of an interpretation is called a partial interpretation. We now define the following terms: Def in i t ion 4.1.1. [sufficient falsifying condition] The partial interpre-tation P is a sufficient falsifying condition for L if and only if V interpre-tation I D Ip, it follows that L(I) = False Def in i t ion 4.1.2. [minimal sufficient falsifying condition] The partial interpretation Ip is a minimal sufficient falsifying condition for L if and only if both of the following conditions are met: 1. Ip is a sufficient falsifying condition for L 2. VP' C Ip, 3 an interpretation I D P' such that L(I) = True Def in i t ion 4.1.3. [Lp] For the formula L and an interpretation I, we define Lp to be the set of all minimal sufficient falsifying conditions that are subsets of I. Similarly, we can define Lip to be the set of all minimal sufficient true conditions for L. Our tool uses the definitions of LF and LT to explain why a pointcut does not match (or does match) a given join point. They represent all the possible ways that cause a non-match (or match). We use the value in the assignment tuple to determine the color code for the part of the pointcut that corresponds to the respective variable. 4.2 The Algorithm We can use the definitions developed above to derive our explanation algo-rithm based on a recursive traversal of the given propositional formula. A l g o r i t h m 4.2.1. Let L denote a propositional formula. Let I an inter-pretation for that formula and V a boolean value. We inductively define 24 Chapter 4. Pointcut Explanation cause(L,I, V) by the-following equations: cause(M AN,I,T) =cause(M,I,T)(£)cause(N,I,T); (4.1) cause(M A N, I, F) =cause(M, I, F) [j cause(N, I,F); (4.2) cause(M V N, I, T) =cause(M, I, T) | J cause(N, I, T); (4.3) cause(M V N, I, F) =cause(M, I,F)(£) cause(N, I, F); (4.4) cause(^M, I, T) =cause(M, I, F); (4.5) cause(-^M, I, F) =cause(M, I,T); (4.6) Where M and N denote propositional formulae, u denotes a proposi-tional variable, and (^ ) is an operator similar to cartesian product such that for power sets A and B (set of sets), A^)B = {x[Jy \ x £ A,y £ B}, for example, {{a}, {b, c}} ®{{b}, {d}} = {{a, b}, {a, d}, {b, c}, {b, c, d}}. T h e o r e m 4.2.1. Let L be a propositional formula where every variable only occurs at most once. Let I be an interpretation for L. Algorithm 4-2.1 will compute Lp and L^, i.e. cause(L,I,True)=LIT and cause(L,I,False)=LF. Proof : By structural induction on the propositional formula. The ma-jority of the proof is straightforward. The only interesting part of the proof is that, in order to prove each element in the set cause(M A N, I, False) is a minimal falsifying condition for MAN, we need to assume that in the propositional formula L every variable only occurs once. As discussed in Section 4.1.1 we can assume that this is so because of the way the formula is derived. (Base Case) When L is a single variable, that is L = u, if I = {(u,T)}, then trivially {(u,T)} is the only minimal sufficient true condition for L, so we have ulT = {{(u,T)}} according to the definition of up, and up — (j) since there is no minimal sufficient falsifying condition exists for L. On the other hand, according to Algorithm 4.2.1, cause(u, I,T) = {{(u, T)}} and cause(u, I,F) — <f>. So we have cause(u, I,T) = u!T, and cause(u,I,F) = uF. Similarly, if / = { ( i t , F)}, we can prove cause(u, I, T) = u!T, and cause(u, I, 25 Chapter 4. Pointcut Explanation (Induction Step) Suppose MF,M-,NF,N- are min ima l sufficient fal-sifying/true conditions for Boolean formulas M and JV given the interpre-tat ion / , and the following hold: • cause(M, I, T) — M-• cause(M, I, F) = M £ • cause(N, I, T) = NT • cause{N, I,F) = NF . We need to prove: 1. cause(M A N, I, T) = (M A N)T 2. cause(M A N, J, F) = (M A N)F 3. cause{M V N, I, T) = (M V N)T 4. cause(M V N, I, F) = (M V N)F 5. cause(->M,I,T) = (-iM)!r 6. cause(-iM, I, F) = (->M)F Accord ing to A l g o r i t h m 4.2.1 and the induct ion hypothesis, the proof of the above is equivalent to the proof of: (M A N)*T =MlT ( g ) Ni (4.9) {M AN)F=MF\jNF (4.10) (MV NyT=M±\jN± (4.11) ( M V AT)p =Aff ( g ) N f (4.12) (-iM)r =Af£ (4.13) (-iM)f =Mf (4.14) To avoid redundancy, we only prove (4.10), (4.12) and (4.13). T h e other three equations can be proved in the same fashion. Le t Im and In be interpretations such that M(Im) — F and N(In) = F. Let Im and 1Z denote any element in MFm and N1^, that is, Im € M | , m and /£ 6 J V > . 26 Chapter 4. Pointcut Explanation From Definition 4.1.3 we know that: Im D l £ and InDl£ (4.15) V7 ' n C 3 interpretation lfn D 7^ such that M ( / £ ) = T (4.16) Let Vm and 7^ be any subset of 7^ and l£ respectively. From (4.16), we know that we can find interpretations 7,^  and 7^ such that 7^ D I'm and M(li)=T,Isn D / ; and iV(7,f) = T. P r o o f for (4.10) ( M A 7V)^ = M / , U NF: • (Sufficient) Since M ( 7 m ) = F, we have V7 D Im D , (M A N)(I) = M(7) A JV(J) = M ( 7 m ) A 7V(7) = F A iV(7) = F • (Minimal) We could select an 7 such that 7 D 7^ and N(I) =T. So we have J D / ^ D 7^ and ( M A JV)(7) = Af A JV(J) = T. That is, is a minimal sufficient falsifying condition for M AN. Similarly, we can prove l£ is also a minimal sufficient falsifying condition for M A N = F. Thus, ( M A N)F = MF{JNF. P r o o f for (4.12) ( M V N)F = MT ® • (Sufficient) From (4.15), we know that 7 m [ j 7 n D I^\Jl£- Since M ( 7 m ) = F and N(In) = F, we have V interpretation 7 D 7 m ( J7 n 3 / £ U , V AO (7) = M ( 7 m ) V N(In) = F. . (Minimal) Since M ( / £ ) = = T, let 7' = J L . U 4 , I = we have 7' c / £ (J l £ , 7 D 7' and ( M V N){I) = M ( /£ ) V N(I%) = T. That is, 7^ | J 7,f is a minimal sufficient falsifying condition for MvN. Since 7^ and l£ could be any element in MFl and A ^ ' 1 respectively, according to the definition of the operator (g) (See Section 4.2), ( M V N)F = Mp(g)NF. P r o o f for (4.13) (->M)f = M / - : • (Sufficient) Since M(Im) = F , we have ( - .M)(J m ) = ->M(7m) = T. • (Minimal) Since M ( j £ > = T, we have ( n M ) ( ^ ) = ( /£ ) = F So, 7,/^  is a minimal sufficient true condition for (->M). That is, ( - iM)j . = (End of proof) 27 Chapter 4. Pointcut Explanation 4.3 Algor i thm Variants Three-value Var ian t : In AspectJ, there is a notion of Maybe that rep-resents the result of matching in case runtime determination is required. If we want to explain why a pointcut matching is evaluated as Maybe, we can define the cause similar to previous definitions of LF and lJT. We can augment Algorithm 4.2.1 by adding: cause(M V N, I, Maybe) =(cause(M, I, Maybe) (^) cause(N, IJ,Maybe))\^ (cause(M, I, Maybe) (^) cause(N, I, True)) ( J (ca,use(M, I, True) (^) cause(N, I, Maybe)); cause(M V N, I, Maybe) =(cause(M, I, Maybe) (^) cause(N, I, Maybe)) [ J (cause(M, I, Maybe) cause(N, I, False)) [J (cause(M, I, False) (^) cause(N, I, Maybe)); cause(-iM, I, Maybe) —cause(M, I, Maybe); cause(u, I, Maybe) = ( M o ^ ) » ^ & ' y q> otherwise A p p r o x i m a t i o n Var i an t : The algorithm described in Theorem 4.2.1 not only finds the variables that are responsible for the non-match (or match), but also provides some structural information in the cause. For example, for L — u V (v A w) with the interpretation u — v — w — F, we have Lp = {{(u, F), (v, F)}, {(u, F), (w, F)}}. This result not only says uu,v,w are all responsible variables", but also indicates " i t and v (or u and w) alone can sufficiently make L = F". However, this information introduces exponential time and space complexity to the algorithm. The complexity comes from the cartesian product operation involved in (1) and (4). In practice, we believe the information of responsible variables alone is adequate for users to diagnose the problems in their pointcuts. In order to improve the performance of PointcutDoctor, we chose to discard this structural information in the cause by replacing (1) with: cause(L A N, I, T) = cause (L,I,T)\Jcause(N,I,T); 28 Chapter 4. Pointcut Explanation and replacing (4) wi th : cause(L V N, I, F) = cause (L,I,F)\Jcause(N,I,F); T h e modified algori thm has linear complexity in the number of variables in the formula, and s t i l l provides the responsible variables in the results. 4.4 Computing Textual Explanation T h e textual explanation aims to provide explanations in the context of the code base and educate users about language subtleties and best practices. Simi lar to our pointcut relaxation algori thm (See 3.1), we use the results of our study of the aspectj-users mai l ing list, instruct ional books [17][10], and other web resources [4] as general guidelines to create a catalog of ex-planat ion heuristics. T h e heuristic rules are categorized by the k ind of pointcut components and the type of jo in point. A heuristic rule consists of a group of conditions and a message to be shown for the user when these conditions are satisfied. The conditions describe the testing on the given jo in point and the patterns in the pointcut. The messages are customized by the information in the code base, e.g. the actual declaring type of the given jo in point. There are 35 rules implemented in Poin tcu tDoctor at the time of this wr i t ing . Table 4.1 elaborates the heuristic rules to be used when the pointcut does not. match the jo in point, and Table 4.2 presents the heuristic rules for matches. Table 4.4 explains the code used in Table 4.1 and Table 4.2. Table 4.3 lists the actual messages being presented to the user when the condit ion is satisfied. For example, let's consider pointcut c a l l (Foo .new(. . )) &&target(f oo) and jo in point shadow c o n s t r u c t o r - c a l l (Foo .new()). T h e target( foo) sub-expression causes the pointcut not to match the jo in point. Our tool w i l l produce the message MSGTarge tO: "Constructor calls do not have tar-get. Use the advice after(...) returning(...) to capture the object being created.". T h i s message is determined by looking up rows correspond-ing to target pointcut in Table 4.1, and then looking up jo in point k ind c o n s t r u c t o r - c a l l i n the found rows. 29 Chapter 4. Pointcut Explanation Table 4.3: Explanation Messages Code Message MSGAnnoO Annotations are not inherited by default, though the method [methodName] is overridden in [jp.dt] MSGRTO The return type [jp.rt] is not matched even though it's a subtype of [ptn.rt]. Add a "+" to include subtypes of [ptn.rt] in the return type. MSGDTO Calls to constructors of [ptn.dt]'s subtypes (e.g. [jp.dt]) won't be matched. Use [ptn.dt]+ to include calls to constructors of its subtypes. MSGDT1 Constructors of [ptn.dtj's subtypes (e.g. [jp.dt]) won't be matched. Use [ptn.dt]+ to include constructors of its subtypes. MSGDT2 The declaring type of [private/static] methods has to be matched exactly by the pattern "[ptn.dt]", i.e. methods with the same signatures in subtypes are not matched. MSGDT3 The method [methodSig] is not applicable to the type [ptn.dt], i.e. it is declared in [jp.dt] but not in [ptn.dt] or any of [ptn.dt]'s super types. Use [ptn.dt]+ to include all qualifying methods declared in its subtypes. MSGDT4 Call pointcut only matches against the static target type, but the static target type of this call ([jp.dt]) is neither [ptn.dt] nor sub-type of [ptn.dt]. Use [call(* set*(..))&&target(SubFoo)] to in-clude the join point with the runtime target type being [ptn.dt]. MSGDT5 [jp.dt] cannot be matched by pattern "[ptn.dt]" MSGDT6 A "[jp.dt]" is not a "[ptn.dt]". Use [execution(* set*(..))&&this(SubFoo)] to include the join point arising here with the runtime type being [ptn.dt]. MSGDT7 The declaring type is not matched because field [fieldName] is re-declared in class [jp.dt]. - Use [jp.dt] to pick this join point only, and [ptn.dt]+ to pick fields declared in [ptn.dtj's subtypes MSGArgsO [ptn.param] does not match join points with [param-num] argu-ments MSGArgsl [jp.param] cannot be matched by pattern [ptn.param]. MSGParamO Unlike args(...), the parameter pattern here only matches against the STATIC types of the join point's parameters, so the parameter types of this join point ([jp.param]) cannot be matched by the pattern [ptn.param] MSGThrowO The method does not declare "throws [ptn.throws]". By Java convention, a method does not need to explicitly declare unchecked exceptions, though any method could throw such ex-ceptions. MSGThisO There is no "this" in a static context MSGTargetO Constructor calls don't have "target". Use the advice after(...) returning(...) to capture the object being created. MSGTargetl Calls to static methods don't have "target". Continued on next page ) 30 Chapter 4. Pointcut Explanation Table 4.3 — continued from previous page Code Message MSGTarget2 The "target" could not be of type [ptn.type] MSGWithincodeO This is not matched because method [jp.enclosingMethod] is de-clared in [declaring type of jp.enclosingMethod] but not in its super type [ptn.dt] MSGWithinO This is not matched because within only cares about static lexi-cal scope. Use [ptn.type]+ to include join points within subtypes of Foo. MSGRefO [ptn.refName] does not match this join point, see the definition of [ptn.refName] for detailed reason. MSGHandlerO The handler pointcut does not implicitly match subtypes. Use handler([ptn.et]+) to include this join point. MSGO The [partType] of the given join point "[partValue]" cannot be matched by the pattern "[pattern]" IVLMSGDTO It is matched because: 1) The static target type of the call ([jp.dt]) is a subtype of [ptn.dt]; 2) method [methodSig] is [de-clared/inherited] in [ptn.dt] MJVISGParamO It is matched because: [paramT] is a subtype of [paramPattern] M.MSGWithincodeO The withincode pointcut matches method overrides, and [Sub-Foo.bar()] overrides method [Foo.bar()]. M.MSGArgsO [ptn.param] is a subtype of [jp.param]. Runtime test is needed because the runtime type of the argument could, but, not neces-sarily, be [ptn.param]. MJVISGArgsl [jp.param] is a subtype of [ptn.param], and the runtime type of the argument will always be of type [ptn.param]. MJVlSGHandlerO It is matched because [jp.param] is a subtype of [ptn.param] M.MSG0 4.5 Summary In this chapter, we discussed the algorithms that computes two kinds of explanations about why a pointcut does not match (or does match) a jo in point. T h e first is a color-coded highlighting of a pointcut expression that shows which parts of the pointcut expression are responsible for the non-match(or match). T h e second is a text message explaining why a highlighted part matches (or does not match) the given jo in point. In the next chapter, we w i l l present a case study as the evaluation of the u t i l i ty of the information computed by our algorithms in this chapter and Chapter 3. 31 Table 4 . 1 : Not-match Explanation Heuristics Pattern Condition Message Annotation Pattern [jp.dt] isSubTypeOf [ptn.dt] & [jp.method] declared in both [jp.dt] and [ptn.dt] & [ptn.dt].method has annotation [ptn.anno]& [jp.method] hasn't annotation [ptn. anno] MSGAnnoO Return type pattern [jp.rt] isSubTypeOf [ptn.rt] & [ptn.rt] does not have "+" MSGRTO Patterns • [jp.kind]=constructor-call & [jp.dt] isSubTypeOf [ptn.dt] MSGDTO in sig-nature [jp.kind]=constructor-execution/(pre)initialization & [jp.dt] isSub-TypeOf [ptn.dt] .. MSGDT1 pattern Declaring [jp .kind] =method-call [jp.method] is private or static & [jp.dt] isSub-TypeOf [ptn.dt] MSGDT2 type pattern & [jp.dt] isSubTypeOf [ptn.dt] & [jp.method] is de-clared in [jp.dt] but not in [ptn.dt] MSGDT3 [ptn.dt] isSubTypeOf [jp.dt] MSGDT4 otherwise MSGDT5 [jp.kind]=method-execution & [jp.method] is private or static & [jp.dt] isSub-TypeOf [ptn.dt] MSGDT2 [jp.dt] isSubTypeOf [ptn.dt] & [jp.method] de-clared in [jp.dt] but not in [ptn.dt] MSGDT3 [ptn.dt] isSubTypeOf [jp.dt] MSGDT6 otherwise MSGDT5 [jp.kind]=get/set & [jp.dt] isSubTypeOf [ptn.dt] & [jp.field] is declared in both [jp.dt] and [ptn.dt] MSGDT7 Parameter the number of [jp.args is not matched by [ptn.args] MSGArgsO pattern the number of [jp.args] is matched by [ptn.args] & one of [ptn.args] isSub-TypeOf one of [jp.args] MSGParamO Throws Pattern [jp.method] does not declare [ptn.et] & [ptn.et] is an unchecked Exception MSGThrowO args the number of [jp.args is not matched by [ptn.args] MSGArgsO . the number of [jp.args] is matched by [ptn.args] & [jp.argType] IsameOrSub-TypeOf [ptn.argType] & [ptn.argType] IsameOrSubTypeOf [jp.argType] MSGArgsl this the join point is in static context MSGThisO [jp. kind] =constructor-call MSGTargetO [jp.kind]=method-call & [jp.method] is static MSGTargetl target - [jp.kind]=field get/set & [jp.field] is static MSGTarget2 Primitive [jp.kind]=initialization/preinitialization MSGTarget3 Pointcuts [jp.kind] =exception-handler MSGTarget4 • withincode [jp.enclosingClass] isSubTypeOf [ptn.dt] & [jp.enclosingMethod] is not de-clared in [ptn.dt] & [ptn.dt] does not have a "+" MSGWithincodeO within [jp.enclosingClass] isSubTypeOf [ptn.type] MSGWithinO handler jjp.et] isSubTypeOf [ptn.et] MSGHandlerO otherwise MSGO Table 4.2: Match Explanation Heuristics Pattern Condition Message Patterns in sig-nature pattern Declaring type [jp.kind]=method-call & [jp.dt] isSubTypeOf [ptn.dt] & [jp.method] de-clared/inherited in [jp.dt] and [ptn.dt] MLMSGDTO pattern Parameter pattern [jp.kind]=method-execution & [jp.dt] isSubTypeOf [ptn.dt] & method declared in [ptn.dt] or its super type M.MSGDT0 [jp.param] isSubTypeOf [ptn.param] & [ptn.param] includeSubtypes M_MSGParamO Primitive pointcuts withincode [jp.enclosingMethod] overrides [ptn.method] M_MSGWithincodeO args [ptn.param] isSubTypeOf [jp.param] MJvISGArgsO [jp.param] isSubTypeOf [ptn.param] M_MSGArgsl otherwise M_MSG0 Chapter 4. Pointcut Explanation Table 4.4: Conventions used in Table 4.1 and Table 4.2 [jp.dt] The declaring type in the signature of the join point [ptn.dt] The declaring type pattern of. the signature in the pointcut [ptn. anno] The annotation pattern of the sig-nature in the pointcut [jp. method] The corresponding method at the join point (for method execution and method call join point only) [ptn.rt] The return type pattern of the sig-nature in the pointcut [j p. kind] The kind of the join point [ jp . f ie ld] The corresponding field at the join point (for field get/set join point only) [jp.args] The arguments at the join point [ptn.args] The argument patter in the point-cut [ptn.throws] The throws pattern in the pointcut [jp. enclosingClass] The enclosing class of the join point [ptn. type] The type in within pointcut [jp. et] The exception type in the exception handler join point [ptn.et] The exception type in the handler pointcut 34 Chapter 5 Evaluation We implemented an offline analysis tool that generates a comprehensive report about all pointcuts in a given code base. Using this tool, we were able to find several real problems in existing, medium sized AspectJ code base. This supports our first claim of our thesis that the information computed by our algorithm can help a developer find problems in her pointcuts. The report generated by our tool consists of almost matched and matched join point shadow information for all pointcuts in the code base, and the cor-responding textual explanation for each listed join point shadow. A sample of the report is shown in Figure 5.1. The code bases we used in our case study are Aspect-oriented ORBacus[18] and Aspect-oriented jEdit [9]. Aspect-oriented ORBacus is an AspectJ refac-toring of a C O R B A middleware 5 consisting of 310 pointcuts, 1297 classes, and 122,110 lines of code. Aspect-oriented jEdit is an AspectJ refactoring of a mature text editor 6 consisting of 354 pointcuts, 428 classes, and 35,890 lines of code. 5.1 Case Study Procedures Prior to our case studies, we had no knowledge or experience with the se-lected code bases. We wanted to determine whether it is possible to discover bugs (i.e. unintended misses) in pointcuts by looking through the list of al-most matched join point shadows and their corresponding explanations. We skipped finding bugs that result in unintended matches because A J D T al-ready provides a list of matches and evaluation of the utility of such list is outside the scope of this thesis. First, we ran our tool against both code bases to generate the reports. Second, we read, through the reports to try to identify unintended misses. Since we were not the authors of the code and we did not directly know the intention of these pointcuts, we made assumptions based on the reports and 5ORBacus: http://www.iona.com/ 6jEdit: http://www.jedit.org 35 Chapter 5. Evaluation after(com.ooc.OBCDRBA.0RB_impl orb_impl, com.ooc.OB.Properties p r o p e r t i e s , S t r i n g key) r e t u r n i n g ( S t r i n g value): c a l l ( * com.ooc.OB.Properties.getProperty(..)) && t h i s ( o r b _ i m p l ) && args(key) && t a r g e t ( p r o p e r t i e s ) ...\orbacus\src\aop\codeset\aspects\Codec.aj:42::1253 * almost matched (21): 1. ORBSupport: method-call(Java.lang.String J a v a . u t i l . P r o p e r t i e s . getProperty(j ava.lang.String)) Not matched because: - C a l l pointcut only matches against the s t a t i c target type, but the s t a t i c target type of t h i s c a l l ( J a v a . u t i l . P r o p e r t i e s ) i s n e i t h e r com.ooc.OB.Properties nor subtype of com.ooc.OB.Properties. Use c a l l ( * getProperty(..))&&target(com.ooc.OB.Properties) to include the j o i n point with i t s runtime target type com.ooc.OB.Properties. - The " t h i s " of the given j o i n point (ORBSupport or i t s subtype) cannot be matched by the patter n "this(com.ooc.OBCORBA.0RB_impl)" * matched (3): 1. 0RB_impl: method-call(Java.lang.String com.ooc.OB.Properties. getProperty(j ava.lang.String)) 2. 0RB_impl: method-call(Java.lang.String com.ooc.OB.Properties. getProperty(j ava.lang.String)) 3. ORB.impl: method-call(Java.lang.String com.ooc.OB.Properties. getProperty(j ava.lang.String)) Figure 5.1: A Sample of the Report Used in Case Study 36 Chapter 5. Evaluation quick investigations of the code base (e.g. we looked at whether a class is subclass of another, whether a method is overridden in a subclasses etc.) F ina l ly , we sent the list of possible bugs to the authors of both code bases seeking to determine the val idi ty of those bugs. Out of the 310 and 354 pointcuts in O R B a c u s and j E d i t code bases, there are respectively 56 and 71 pointcuts that have at least one almost matched jo in point shadows. It took us roughly 2 hours to go through the O R B a c u s report, and 1.5 hours to go through the j E d i t report. 5.2 Results Table 5.1 shows the numbers of potential bugs we identified , and those the original developers confirmed to be val id. In the table, the first two columns lists the type of the bug and the code of the explanatory message produced by our tool . A s for the bug numbers, column I refers to the numbers of bugs that were identified as potential bugs when we investigated the report; column R refers to the numbers of real bugs confirmed by the developers; column V refers to the number of "v i r tua l" bugs: the developers claimed that they were not bugs since the missed v i r tua l and potential jo in points does not affect the correctness of the current code base, however, they agreed including the missing potential jo in points in the pointcut would be better style. We next explain each bug type in detail: declaring-type-call this error is caused by misunderstanding of the static semantics of c a l l pointcut: the declaring type in c a l l pointcut only matches against the static type of the call target, not its runtime type 7 . However, developers often th ink of the runtime type when they write point-cuts. For example, in one of the code bases, c a l l ( * com.ooc.OB.Properties.getProperty (..)) is used intending to capture calls to getProperty method for instances of com. ooc . OB.Properties only. Here, com. ooc. OB. Properties is a sub-class of j a v a . u t i l . Properties. T h i s pointcut does not match the jo in point below (in the second statement): j a v a . u t i l . P r o p e r t i e s props = new com.ooc.OB.Properties(); S t r i n g p = props.getProperty("key"); 7This semantics applies to the declaring type of execution and withincode too. 37 Chapter 5. Evaluation O R B a c u s j E d i t B u g T y p e Message I C . I C R V R V declaring- M S G D T 4 2 2 3 3 type-call param- M S G A r g s O 13 2 3 1 1 num-not-match param- M S G P a r a m O -1 1 type-not-match subtype- M S G D T O 3 3 constructc r-call modifier- M S G O 2 2 not-match return- M S G O 1 1 type-not-match handler- M S G H a n d l e r O 1 1 subtype Total 18 7 3 9 3 6 Table 5.1: Messages Issued and Number of Bugs Found in Case Study (Lldentif ied, ,C:Confirmed, R : R e a l bugs, V : " V i r t u a l " bugs) 38 Chapter 5. Evaluation param-num-not-match The developer was unaware of some overloaded constructors/methods in the code base. Some expected join points are ruled out by either the parameter pattern or args pointcut because the number of parameters in the join point cannot be matched. Note that we found 13 potential bugs of this type but most of them are false positives. This is be-cause most overloaded methods call each other, so it's the expected behavior to only match one "root" method in the pointcut. The "virtual" bugs in this category are mostly misses of virtual calls to overloaded constructors. param-type-not-match The join point is not matched because the type of one of the parameters cannot be matched. The developer wrote c a l K v o i d add*Listener(EventListener)) intending to match method-call(void JButton.addActionListener(ActionListener)). But it cannot be matched even though ActionListener is a subtype of EventListener. subtype-constructor-call If "+" is not used in the declaring type of cal l pointcut, it will not match calls to the constructors of the declaring type's subtypes. A l l of the identified bugs are classified as virtual since the developer agreed including all subtypes might be better style, as we discussed earlier. modifler-not-match The developer was unaware of the different mod-ifiers for some of the expected join points. return-type-not-match Similar to "modifier-not-match", the devel-oper was not aware of the different return type. It seems that developers often ignore the difference in modifier and return type when they write pointcuts. handler-subtype Similar to the subtype-constructor-call case, handler pointcut does not match exception handlers with sub-exception types, if "+" is not used in the pointcut. The developer used handler(Throuable) hoping to capture all exception handlers. In summary, we believe that the result of this study is quite positive: Several real bugs and style problems were discovered in real code bases. Given the sizes of both code bases, our unfamiliarity with them, and the relatively short time we spent on interpreting the reports, we believe it demonstrates that the process of identifying problems is facilitated by the information available in the report. Anecdotally, even when an almost matched join point shadow is not intended to be matched, the information can still be useful, as one of the developers of the case study code bases said, "these warnings indeed make me think about whether it could be problematic for other subtypes.". 39 Chapter 5. Evaluation 5.3 Performance Impact In our development environment (Intel Core2 Duo Processor 2.33GHz C P U , 2G memory, Windows X P Professional), it took 50 seconds and 68 seconds for our tool to generate the reports for the Aspect-oriented jEdit and OR-Bacus code base, respectively. The memory usage of the javaw.exe process is 350M and 397M bytes for the two code bases. Compared to the num-bers with the unmodified AspectJ compiler(20 seconds/250M and 30 sec-onds/290M), these numbers indicate that our extension to AspectJ compiler introduces performance overhead. We believe the running time overhead is caused by the large number of relaxed pointcuts being tested in the matching process. When a compound pointcut with many primitive pointcuts con-nected by I | and kk gets relaxed, potentially a large number of duplication will be produced by the D N F conversion (see Section 3.1.1). The memory usage overhead is because we produce extra helper objects in the process of pointcut relaxation and explanation, compared to the unmodified AspectJ compiler. 5.4 Summary In this chapter, we presented the result of our case study in order to validate the usefulness of the information produced by the algorithms presented in this thesis. Based on this result, we believe that the information computed by our algorithms can help a developer find problems in her pointcuts. This supports our first claim in this thesis. We also evaluated and analyzed the performance impact of our algorithms. 40 Chapter 6 Limitations and Future Work In this chapter we discuss some of the limitations of our algorithms, of their current implementation and of the evaluation presented in this thesis. We also present some ideas on how these could lead to future work. 6.1 Limitat ion of Implementation A limitation of our current implementation of pointcut relaxation is its per-formance overhead compared to the standard AspectJ compiler. It should be noted that performance was not the focus of this thesis. We rather fo-cused on providing information that helps developers to diagnose and fix problems with their pointcuts. Having established that the information our algorithms compute is useful, future work could focus on more efficient im-plementations. For example, the run-time overhead could be reduced by optimizing the preprocessing and relaxation algorithms to produce fewer relaxed pointcuts. The memory usage would also be improved by this opti-mization because fewer helper objects would be generated. Another limitation of the current implementation is that it relies on a fixed set of heuristics. Our evaluation shows that the current set of heuristics provides useful results. However we believe that they can still be improved and extended. This is a possible topic for future work. A third limitation of the current implementation presented is that we have not directly considered cf low, cf lowbelow and i f pointcuts. However, a user can indeed write the enclosed pointcut in cf low or cf lowbelow as a named pointcut, and have it explained by our tool. The direct support for these three pointcuts is a possible topic for future work. 6.2 Limitat ion of Evaluation The evaluation presented in this thesis focused on determining whether infor-mation on almost matched join points and explanations of why they do or do not match is useful to find problems in pointcut expressions. We decided to 41 Chapter 6. Limitations and Future Work separate the question of whether this information is useful by itself from the question of how to present it in the IDE. Therefore, we used an offline report to perform our case-studies rather than the PointcutDoctor IDE. Because of the "raw" presentation format used, we believe the results of the case studies are relatively independent from presentation issues. Presently, we have not directly evaluated the design of the PointcutDoctor GUI . Nevertheless, we have faith in our GUI design because a) the presented information has been shown to be independently useful and b) our GUI design naturally extends the state-of-the-practice A J D T UI. Future work could focus independently on the GUI design and determine whether it is effective in making this useful information readily available to developers. Another limitation of the evaluation is that we did not separately eval-uate the utility of the explanations versus the lists of matches and almost matches. What our results show is that in combination this information can be useful to find problems with pointcuts. However, our results pro-vide no solid conclusions about the usefulness of these types of information considered in isolation. 6.3 Summary In this chapter, we discussed some of the limitations of our algorithms, of their current implementation and of the evaluation presented in this thesis. We also presented some ideas on how these could lead to future work. 42 Chapter 7 Conclusion In this thesis, we proposed to help AspectJ developers diagnose and fix po-tential problems in their pointcuts by extending existing IDE to provide two kinds of information: almost matched join point shadows and expla-nation, on why a pointcut matches (or does not match) a given join point. We presented our algorithms to compute this information. We elaborated the heuristics used in our algorithms extracted from our previous empirical study on Aspectj user mailing list and other resources. We make two claims in this thesis. First, we claim that the informa-tion computed by our algorithms can help a developer find problems in her pointcuts. Second, we claim that this information can be added into existing IDE tools in a clean and logical way. To prove our first claim, we evaluated the utility of the information pro-duced by our algorithm by conducting case studies on two existing medium-sized AspectJ code bases. B y going through the report generated by our tool, we were able to identify real problems in the code bases in a couple of hours, despite that we did not have any previous knowledge about the code bases before the study. Based on the results of the case study, we believe that the information computed by our algorithms can help a developer find problems in her pointcuts. To prove our second claim, we developed an Eclipse plugin called Point-cutDoctor. PointcutDoctor is a natural extension of A J D T that provides developers easy access to the information discussed above from within their familiar development environment. 43 Bibliography [1] Ajdt: Aspectj development tools, http://www.eclipse.org/ajdt. [2] Aspectj users mailing list archive, http://dev.eclipse.org/mhonarc/lists/aspectj-users/maillist.html. [3] Eclipse - an open development platform, http://www.eclipse.org. [4] Ibm developerworks: Aop@work. http://www-128.ibm.com/developerworks/views/java/libraryview.jsp ?search by=AOP@work. [5] Prasanth Anbalagan and Tao Xie. Apte: automated pointcut testing for aspectj programs. In WTAOP '06: Proceedings of the 2nd workshop on Testing aspect-oriented programs, pages 27-32, New York, N Y , USA, 2006. A C M Press. [6] Prasanth Anbalagan and Tao Xie. Efficient mutant generation for mu-tation testing of pointcuts in aspect-oriented programs. In Proceedings of the 2nd Workshop on Mutation Analysis (MUTATION 2006), pages 51-56, November 2006. [7] M . Ben-Ari. Mathematical logic for computer science. Prentice-Hall, . Inc., Upper Saddle River, N J , 1993. [8] C. Clifton and G. Leavens. Obliviousness, modular reasoning, and the behavioral subtyping analogy. In Iowa State University Technical Re- . . port, TR 03-15. [9] J . Collins-Unruh and G. Murphy. Aspect-oriented jedit. unpublished. [10] Adrian Colyer, Andy Clement, George Harley, and Matthew Webster. Eclipse AspectJ: Aspect-Oriented Programming with AspectJ and the . Eclipse AspectJ development tools. Addison-Wesley, 2004. [11] R. Firman and D. Friedman. Aspect-oriented programming is quan-tification and obliviousness. In Workshop on Advanced Separation of Concerns, OOPSLA 2000, October 2000. 44 Bibliography [12] Robert Filman. What is aspect-oriented programming, revisited. In Workshop on Advanced Separation of Concerns, 15th European Con-ference on Object-Oriented Programming, June 2001. [13] Erik Hilsdale and Jim Hugunin. Advice weaving in aspectj. In AOSD '04'- Proceedings of the 3rd international conference on A sped-oriented software development, pages 26-35, New York, N Y , USA, 2004. A C M Press. [14] Gregor Kiczales, Erik Hilsdale, Jim Hugunin, Mik Kersten, Jeffrey Palm, and Will iam G. Griswold. A n overview, of AspectJ. Lecture Notes in Computer Science, 2072:327-355, 2001. [15] Gregor Kiczales, John Lamping, Anurag Menhdhekar, Chris Maeda, Cristina Lopes, Jean-Marc Loingtier, and John Irwin. Aspect-oriented programming. In Mehmet Ak§it and Satoshi Matsuoka, editors, Pro-ceedings European Conference on Object-Oriented Programming, vol-ume 1241, pages 220-242. Springer-Verlag, Berlin, Heidelberg, and New York, 1997. [16] Gregor Kiczales and Mira Mezini. Aspect-oriented programming and modular reasoning. In ICSE '05: Proceedings of the 27th international conference on Software engineering, pages 49-58, New York, N Y , .USA, 2005. A C M Press. [17] Ramnivas Laddad. AspectJ in Action: Practical Aspect-Oriented Pro-gramming. Manning Publications Co., Greenwich, C T , USA, 2003. [18] Charles Zhang and Hans-Arno. Jacobsen. Quantifying aspects in mid-dleware platforms. In AOSD '03: Proceedings of the 2nd international conference on A sped-oriented software development, pages 130-139, New York, N Y , USA, 2003. A C M Press. 45 

Cite

Citation Scheme:

        

Citations by CSL (citeproc-js)

Usage Statistics

Share

Embed

Customize your widget with the following options, then copy and paste the code below into the HTML of your page to embed this item in your website.
                        
                            <div id="ubcOpenCollectionsWidgetDisplay">
                            <script id="ubcOpenCollectionsWidget"
                            src="{[{embed.src}]}"
                            data-item="{[{embed.item}]}"
                            data-collection="{[{embed.collection}]}"
                            data-metadata="{[{embed.showMetadata}]}"
                            data-width="{[{embed.width}]}"
                            async >
                            </script>
                            </div>
                        
                    
IIIF logo Our image viewer uses the IIIF 2.0 standard. To load this item in other compatible viewers, use this url:
http://iiif.library.ubc.ca/presentation/dsp.831.1-0052055/manifest

Comment

Related Items