+ All Categories
Home > Documents > Untangling: A Slice Extraction...

Untangling: A Slice Extraction...

Date post: 07-Aug-2020
Category:
Upload: others
View: 1 times
Download: 0 times
Share this document with a friend
9
Untangling: A Slice Extraction Refactoring Ran Ettinger and Mathieu Verbaere Programming Tools Group Computing Laboratory University of Oxford ABSTRACT Separation of concerns in existing code can be achieved by specific refactoring techniques. Modern refactoring tools support a number of well-known refactoring transformations, including method extraction. In this paper, we examine how method extraction can be improved through program slic- ing. Furthermore, we show how a generalization of such slice extraction can be applied to untangle existing code by extracting aspects. 1. INTRODUCTION Refactoring [6] is the process of improving the design of existing software by performing source code transformations that preserve the original functionality. Modern software de- velopment tools, e.g. Eclipse [3], includes useful support for basic refactoring techniques such as renaming and moving pr°gram entities (e.g. variables, classes), method extraction, and more. Aspect-oriented programming is a way of separating cer- tain concerns, such as logging or tracing, from the base code of a program [10]. The most popular implementation of aspect-oriented programming is an extension of Java called AspectJ [2]. It is natural to ask whether it is possible to support the move from traditional Java code to AspectJ through refactoring, and this question was first addressed by Hanneman and Kiczales [9]. A refactoring technique called Replace Temp with Query was defined by Fowler [6] to turn the use of a temporary variable holding the value of an expression into a query, i.e. a method call, calling a new method that returns the value of the expression. The benefit is increased readability, and that the same computation can be reused in other places. This refactoring is indeed supported in Eclipse, as a special case of the Extract Method tool. A more complicated case of Replace Temp with Query appears when the temp is not assigned the result of an expression, but rather the result of a computation spanning several lines of code. If these lines are consecutive in code (i.e. a code fragment) they can be Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fec provided that copies are not made or distributed for profit or commercialadvantage and that copies bcar this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistributeto lists, requires prior specific permission and/or a fee. AOSD 04, March 2004. Lancaster UK. Copyright 2004 ACM 1-58113-842-3/03/0004 ...$5.00. selected by the user and again the Extract Method tool may handle them successfully. Many times this is not the case, and the code for computing a temp is tangled with code for other concerns. To untangle the desired statements from their context, one can employ program slicing [18]. A program slice singles out those statements that may have affected the value of a given variable, say v, at a given point in the program, say s. The pair < s, v > is called the 81icing criterion and is normally selected by the user as an input to a slicing tool. This slice can then be extracted into a new method. The idea of using slicing for refactoring is due to Maruyama [13]. In earlier work [5] we formalized the above idea of ex- tracting a slice into a method as a refactoring, Extract Slice as Method, in the spirit of [6] -- an example will be shown in Section 2. To allow automation, we have identified con- ditions for its correct application (i.e. refactoring precon- ditions [14]). We then identified some limitations of the approach, such as: the preconditions are too restrictive (e.g. no modifica- tion of global variables in the extracted code), leading to rejection of desirable untangling transformations. the refactored code is usually not as efficient as the original tangled code. As an example, think of a tan- gied algorithm that computes two independent results by traversing a (possibly large) data strucure. In the refactored version, the price of having two separate al- gorithms, each one independently reusable, is the run- time overhead of having two traversals. (many times, this was the reason for entangling the two computa- tions in the first place.) To overcome the above limitations of extracting a whole slice into a method, we suggest in this paper two alternative approaches for code untangling. These involve the extrac- tion of certain fragments of the slice into separate code enti- ties. In Section 3 each fragment is extracted as a method in a new class, whereas in Section 4, each fragment is extracted as an advice in a new aspect. As the content of this paper reflects work in progress, it is mostly example-driven. In Section 5 we report on a larger example in which we apply the suggested untangling approaches in refactoring the AspectJ compiler. We then briefly explain the automation of these techniques in Section 6, based on a program slicing tool developed by the second author. These ideas are being implemented through the Eclipse refactoring infrastructure. 93
Transcript
Page 1: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

Untangling: A Slice Extraction Refactoring

Ran Ettinger and Mathieu Verbaere Programming Tools Group

Computing Laboratory University of Oxford

ABSTRACT Separation of concerns in existing code can be achieved by specific refactoring techniques. Modern refactoring tools support a number of well-known refactoring transformations, including method extraction. In this paper, we examine how method extraction can be improved through program slic- ing. Furthermore, we show how a generalization of such slice extraction can be applied to untangle existing code by extract ing aspects.

1. INTRODUCTION Refactoring [6] is the process of improving the design of

existing software by performing source code transformations that preserve the original functionality. Modern software de- velopment tools, e.g. Eclipse [3], includes useful support for basic refactoring techniques such as renaming and moving pr°gram entities (e.g. variables, classes), method extraction, and more.

Aspect-oriented programming is a way of separat ing cer- tain concerns, such as logging or tracing, from the base code of a program [10]. The most popular implementat ion of aspect-oriented programming is an extension of Java called AspectJ [2]. I t is na tura l to ask whether it is possible to support the move from tradi t ional Java code to AspectJ through refactoring, and this question was first addressed by Hanneman and Kiczales [9].

A refactoring technique called Replace Temp with Query was defined by Fowler [6] to turn the use of a t empora ry variable holding the value of an expression into a query, i.e. a method call, calling a new method tha t returns the value of the expression. The benefit is increased readability, and tha t the same computa t ion can be reused in other places. This refactoring is indeed supported in Eclipse, as a special case of the Extract Method tool. A more complicated case of Replace Temp with Query appears when the temp is not assigned the result of an expression, but ra ther the result of a computat ion spanning several lines of code. If these lines are consecutive in code (i.e. a code fragment) they can be

Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fec provided that copies are not made or distributed for profit or commercial advantage and that copies bcar this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. AOSD 04, March 2004. Lancaster UK. Copyright 2004 ACM 1-58113-842-3/03/0004 ...$5.00.

selected by the user and again the Extract Method tool may handle them successfully. Many t imes this is not the case, and the code for computing a t emp is tangled with code for other concerns.

To untangle the desired s ta tements from their context, one can employ program slicing [18]. A program slice singles out those s ta tements tha t may have affected the value of a given variable, say v, at a given point in the program, say s. The pair < s, v > is called the 81icing criterion and is normally selected by the user as an input to a slicing tool. This slice can then be extracted into a new method. The idea of using slicing for refactoring is due to Maruyama [13].

In earlier work [5] we formalized the above idea of ex- t ract ing a slice into a method as a refactoring, Extract Slice as Method, in the spiri t of [6] - - an example will be shown in Section 2. To allow automation, we have identified con- ditions for i ts correct applicat ion (i.e. refactoring precon- ditions [14]). We then identified some l imitat ions of the approach, such as:

the preconditions are too restr ict ive (e.g. no modifica- tion of global variables in the ext rac ted code), leading to rejection of desirable untangling transformations.

the refactored code is usually not as efficient as the original tangled code. As an example, think of a tan- gied algorithm tha t computes two independent results by traversing a (possibly large) d a t a strucure. In the refactored version, the price of having two separate al- gorithms, each one independent ly reusable, is the run- t ime overhead of having two traversals. (many times, this was the reason for entangling the two computa- tions in the first place.)

To overcome the above l imitat ions of extract ing a whole slice into a method, we suggest in this paper two al ternat ive approaches for code untangling. These involve the extrac- t ion of certain fragments of the slice into separate code enti- ties. In Section 3 each fragment is ex t rac ted as a method in a new class, whereas in Section 4, each fragment is extracted as an advice in a new aspect.

As the content of this paper reflects work in progress, it is mostly example-driven. In Section 5 we report on a larger example in which we apply the suggested untangling approaches in refactoring the Aspec tJ compiler. We then briefly explain the automation of these techniques in Section 6, based on a program slicing tool developed by the second author. These ideas are being implemented through the Eclipse refactoring infrastructure.

93

Page 2: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

Let v~ a eetAa~ s[o

Figure 1: A sample cus tomer s t a tement .

H 0i

p u b 1 ~ String statement() { ~ot~le totalI~ount = O~ i ~ t f requentRenterPoints ffi O~ Iterato~ ~entals = rentals,iterator() ; string result = "ReFuel record for " +

• (r~h~Is.hasNext{)) { Rental s~ch = (:Rental) rentals.next() // show figures for this rental reSUlt += "\t" + each. ~etMovie ( ). getTitle (:) ; result += "\t" + each.getChargel) + "\n"; freq~entRenterPoints += each. getFR~ ( ) ; totalAmoUa~t += 'each.get,barge () ;

) //add fOOteE lihes

totalAmount + "\n"~ resul~ += "You earned " +

fre~uen~Rente r~ofnts + " frequent renter points";,

~.tuzn resu!t~ }

Figure 2: A tangled statement method .

2. EXTRACT SLICE AS M E T H O D The following example is adapted from Martin Fowler's

refactoring book [6], where all refactorings are performed manually. The example concerns a piece of software for run- ning a video store, focusing on the implementation of one feature: composing and printing a customer rental record statement. The statement includes information on each of the rentals made by a customer, and summary information; a sample statement is shown in Figure 1.

In the original implementation, the preparation of the text to be printed and the computation of the summary informa- tion are tangled inside a single method (see Figure 2). In fact, Fowler starts off with a much longer statement method containing all the logic for determining the amount to be charged and the number of frequent renter points earned per movie rental. These results depend on the type of the rented movie (regular , children's or nero release) and the number of days it was rented for.

Fowler then gradually improves the design by factoring out that rental-speciflc logic (into the Rental class, that is not shown here). The suggested refactoring steps are moti- vated by the introduction of a new requirement, namely the ability to print an htral version of the statement. A quick- and-dirty approach would be to copy the body of the state- ment method, paste it into a new htmlStatement method and replace the text-based layout control strings with corre- sponding html tags. This leads to duplication of the code to

~i - ~' ~iil ~ !!i!!!!!~i ¸ ....................

~i~' p~1~ String statement{) {

~16! Iterator rentals= rentals.iterato~() ~i string result = ~e~al re6o~d for +

getName() + "\n"; while (rentals.ha:sNext()) {

Kenta~ each = (R~ntal) rentals.next(); // shQw figures for this ~ental result +ffi "\t" + each.getMovie().getTitle(); result += "<,t" + ~ach.getCharge() + "\n";

} //add foot.eL" lines result += "Amount ~¢ed is " + getTotalchargeO ÷ "\n"~

result += "YhU earned " + ~tTotalFAP () +

" frequent renter points,,;

Figure 3: The statement m e t h o d after ex t rac t ing the computa t ions of the total charge and frequent renter points.

{

I ~ r e n t a l s -ite rator ( ),

~40~ ~nta~Ch ~Rental) rentals, next.() ; ~IE re~lt +~ ea~.getCharge()p

43 ! ~u~ result;

Figure 4: The ex t r ac t ed total charge computa t ion .

~4~ I. ~t ~e~ult o, = ~481 Ite~ator rentals :.rentals.iterator() ;

~501 Re,tel efach ffi (Rental) rentals.next() ; ~5!I result + each,getFRP ( )F

~53 zests result; ~s4, )

Figure 5: The ex t rac ted frequent renter points com- puta t ion.

94

Page 3: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

compute the temporary totalAmount and frequentRenter- Points variables.

For brevity, we join the refactoring session at the stage Fowler calls "Removing Temps" (page 26 in [6]). At this stage the computat ions of totaIAmount and frequentRenter- Points are factored out (see Figures 3-5 for the result of those two steps). Fowler describes the pa th by which this was achieved as "not the simplest case of Replace Temp with Query, totalAmount was assigned to within the loop, so I have to copy the loop into the query method." Indeed, Eclipse's refactoring tool was not designed to support such cases.

Here is an outline of the mechanical steps tha t needs to be performed by a programmer, in the absence of tool support , for extracting the total charge computationX:

• In the statement method of Figure 2, look for the tem- porary variable tha t is assigned the result of the total charge computat ion. This is totaIAmount which is de- clared to be of type double in line 14. Its final value is added to the customer s ta tement in line 29.

• Create a new method, and name it after the intention of the computat ion: getTotalCharge. Declare it to return the type of the extracted variable: double. See line 36 in Figure 4.

• Identify all the s ta tements tha t contribute to the com- putat ion of totalAmount In this case these are the statements in lines {14, 16, 19, 20, 25, 26]. .

• Copy the identified s tatements to the new method. See lines 37 to 42 in Figure 4.

• Scan the ext rac ted code for references to any variables that are parameters to the statement method. These should be parameters to getTotaICharge as well. In this case, the paramete r list is empty.

• Look to see which of the extracted s tatements are no longer needed in the statement method and delete those. In this case, the while loop is still relevant, and therefore the s ta tements in lines {16, 19, 20, 26} cannot be deleted. Instead, we say tha t these s tate- ments are duplicated. Lines {14, 25} are needed only in the extracted code and are therefore deleted. These statements are said to be fully extracted. In Figure 3 they are shown as blank lines, for clarity.

• Rename the ext rac ted variable, totalAmount, in the extracted method, getTotaICharge, to result, and add a return s ta tement at the end of that method (see line 43 in Figure 4).

• Replace the reference to the result of the extracted computat ion with a call to the target getTotalCharge method (line 29 in Figure 3).

• Compile and test .

3. EXTRACT SLICE AS OBJECT Looking back to the refactored version of Section 2 above

(Figures 3-5), we may conclude tha t it is much improved in

aSee [5] for a detai led description of the mechanics

terms of reusabili ty and readability. Nevertheless, the Ex- tract Slice as Method refactoring often fails to apply. By extract ing the slice into a method, we potential ly reorder the execution of some of the statements, and duplicate oth- ers (e.g. the while loop). Such reordering often changes the behaviour of the program, for instance when an input s ta tement is duplicated or if two input s tatements are exe- cuted in a reverse order. In such cases the refactoring must be rejected, and unfortunately this happens frequently. In- s tead of rejecting the refactoring, we propose here a second approach for code untangling.

The key is to avoid s ta tement reordering and duplica- tion. In order to avoid duplication, we must look to ex- t rac t only the s ta tements tha t were fully extracted in the first approach, i.e. both extracted and deleted from the original. Since only ext rac ted s ta tements may have been deleted in tha t first approach, we are only interested in the set of deleted statements. Now, since we must avoid reorder- ing, we may wish to ext rac t each s ta tement into a separate method. Even better , consecutive s tatements may be ex- t rac ted together. We can conclude tha t each fragment of the exclusively relevant s ta tements in the slice (i.e. each deleted fragment) should be ext rac ted separately as a method, us- ing the Extract Method refactoring. This will be rejected in cases where more than one local variable is modified in one fragment. To overcome this we may think of turning locals into fields (instance variables) but this will fail in case of recursive calls.

The solution we propose is a variation on Fowler's refac- tot ing of Replace Method with Method Object: "You have a long method tha t uses local variables in such a way t ha t you cannot apply Extract Method. Turn the method into i ts own object so tha t all the local variables become fields on tha t object. You can then decompose the method into other methods on the same object." In our case, we first use Extract Slice as Method, but without the insertion of a call to tha t new method. We go on to replace tha t new method with a method object. Next we break each exclu- sive fragment into a method using Extract Method. Finally, back in the original method, we create a local instance of the ext rac ted object, and place calls to the extracted methods, in place of the corresponding deleted statements. Figures 6-8 demonstra te the effect of our approach. In line 14 of Figure 6, instead of initializing the totaIAmount variable as before, we create a new instance, totalCharge, of the cor- responding new class, Charge, which in turn initializes i ts result field (Figure 7, line 2). In Figure 6 line 25, ins tead of upda t ing the to~alAmount variable, we call the add method of totalCharge, which in turn updates the relevant result field.

Finally, when the result of totalAmount should be added to the s ta tement (Figure 6, line 29), the result field is ac- cessed through its getter method (Figure ?, lines 8-10). Sim- ilarly, the frequent renter points computat ion was ex t rac ted to a separate class, leading to an improved separat ion of concerns. A main method for each new method objeet's class could have been included. That method would have invoked the extracted methods such tha t the untangled fea- ture would be then reusable outside its original location, if tha t is desired. Instead, in this case, the extracted methods are directly invoked in the original (Figure 6, lines 24, 25, 29 and 31).

In comparison to the Extract Slice as Method refactor-

95

Page 4: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

i13i public Stri,g statement() { ~l~i Charge totalCha~e = new Charge () ; 1 ~ FRD total¥~P = now FKP ( ) I~ Iterator rentals = [email protected] () ; i171 String result = "Rental record for " + ;~18 i getName{) + "\n"; ~19 Wl~% 1 ° (rentais.hasNext ()) ~!20! Rental each = (Rental) rentals.next(); 21 I~ // show f~gure~ for this rental

I!122~ result +ffi "\'t" + each.getMovie().getTitle(), '~231 result += "\t" ÷ each.getcharge() + "\n"; 24i totalFKP.add(each.get~J2~() ),

i~is ! . . . . . . . . . . .

::~2 ~ totalCharge.add (each.getcharge ( ) ) ;

~271 //add footer lines ~28 result += "~mo~nt owed is " ÷ ~291 totalCharge .igetResult () + "in" [.~ result +ffi' "YOU earned " + [~31~ totalFRP.:getBssult [:) + 321 " ~requent renter points";

L341 l

F i g u r e 6: T h e statement m e t h o d a f t e r factoring Charge and F R P o u t a s o b j e c t s . See F i g u r e 2 fo r t h e before v e r s i o n .

~%ic elan Charge { ~zlvaze d~u~le result : 0:

pu~ll~ vo£d add{d~u~l° charge) i{ resULt +: charg~

)

pu~1~c dou~l° .getResult.() { zotuzn result;

.)

F i g u r e 7: T h e e x t r a c t e d c lass c o m p u t i n g total charge.

l~u~llu ~lass FKP { 2| pzlvata iff~ result = 0~

4! p u b l i c ~O~.~ add(:hnt freq~entRenterPoints) { result += frequentRenterPoints;

i:? ~ pu~1$~ze~:uznlnZresult,getResult() {

~ll}

F i g u r e 8: T h e e x t r a c t e d c lass c o m p u t i n g frequent renter points.

131 pu~1~ String statement() { 14i totalAmount = 0; I~ frequentRenter~oints = O; 16~ Iterator rentals ,ffi _rentals.it°rater() ; 17i String result = "Rental record for " + 18~ getName() + "\n"; 19 i while (rentals.hasNext()) ~{

~ ~ Rental each ffi (Rental) rentals.next(), ~i result ÷= rehtalState~ent {each); ~22i } ~I12 ~ result += statementFoDter(); "~''~241 zetuzm result; ~25~ ) 2~ pzlvate String rentaistateme~t(Rental each) { 27, I // ghow figures for £hls rental 2~ Shrinq result = "";

•1 29i result +e "\t" + each.getMovie().getTitle{)I

result ÷= "\t" + each.ge~Charge() + "in", ~10i frequentRenter~oints +ffi each.getFB~(); 32} tOtalAmount +ffi each.g~tChar~e { ) ; ~4~! } mstUZm re.sult;

3~ ~i~to string s~e~t~ooter() 3~ ,string result = ""I

• 371 result += "Amount OWed is" " + 38] tQtaiAmount + "\n" ;

result "Ybu earned " + L439}

• 4@ f reque ntRente rPoint s: + 41i " frequent renter pc~ints"; 4~i zetuzm result,

~4~i )

F i g u r e 9: T h e s t a t e m e n t m e t h o d s ( t h e one m e t h o d o f F i g u r e 2 was r e f a c t o r e d i n t o t h r e e ) b e f o r e refac- toring t o a s p e c t s .

ing, this approach does not affect the ei~ciency of the code (because it does not dupl icate the loop) and it can be al- ways applied. On the other hand, Extract Slice a~ Method produces more compact and readable code. Therefore, this approach is applied whenever the first approach is rejected, or when the user explicit ly asks to use it (e.g. when per- formance is critical). Finally, this approach is somewhat cumbersome to apply, as the user must invent names for each of the extracted methods.

4. EXTRACT SLICE AS ASPECT Aspect-Oriented programming [10] is a software develop-

ment paradigm that promotes clean modularization of cross- cut t ing concerns in software. The Eztract Slice refactoring can be applied to introduce new aspects into existing code. We believe that it may thus serve as a tool to help facili- ta te an easy transit ion to aspect-orientat ion with an exist- ing code base. For brevity, we assume tha t the reader has a passing acquaintance with AspectJ .

The following example demonstra tes this third approach to code untangling through slice extraction (Figures 9-12). Here each reusable feature is extracted into an aspect. This approach is quite similar to the earlier idea of extracting fragments of a slice into a separate class (Section 3 above). The difference is tha t here each fragment is extracted into a piece of advice. In contrast to a method, such advice is an anonymous piece of code. Furthermore, instead of explicitly invoking an advice from its original position in code, the advice is coupled with a pointcut designator. Such

96

Page 5: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

113i p ~ s~ring statemen~i~ {

~ Iterator rentals = re ntals.iterator() 171 String result = "Ke~tal record for " + ',181 ge£Name O + "\n"; ~I w~ile (rentals.hasNext()) { 20 i Rental each = (Rental) rentals.next [) ; 21i result +=' =e~talStatement (ea'ch) ;

} result += statementFooter () z~tuzn result;

) p¢i~te String rentalStatement (Rental each) .{

// ShoW Zigures' for this rental string result = ""~ resnlt += "\t" + each.getMovie () .getTitle() ; result += "it" ÷ each.getCharge() + "\n";

~tux~1 result ~ ) ~a~e .string state~entFooter() {

String result = "";

4, I ~ t u ~ n re~u~;

4 }

Figure 10: T h e s t a t emen t methods a f te r fac tor ing Charge and FRP out as aspects .

b1~ a~pect ChargeAspect { ~l~le CuStoraer.'total~mount = O; aEt~=(Customer c, Rer, tal each) :

At(string nentalstatement (..)) && az~s(each)~ && rUt(c) {

c,totaiAmount += each.getCharge(); } Strung a~(Customer C) :

m~11-(s%ring statementFooter (,.) ). &~ taz~et (C) {

stying result ffi pz~(c)~

C.~otaiAmount ÷ ~\n"; }.

Figure 11: T h e total charge aspect .

a designator is a specification of where and when the advice should apply.

To facilitate this refactoring, we first refactored the origi- nal statement method (Figure 9) by extracting the body of the while loop into a method of its own (lines 26--34), and the statement footer (lines 35-42). The reason for introducing those methods as a first step is that it is easier in AspectJ to apply advice to a method call than to some statement in the middle of a method. The transformation extracted lines 14, 32 and 37-38 of Figure 9 to lines 2, 6 and 12-13 of the Charge Aspect in Figure 11, respectively. Similarly, lines 15, 31 and 39-41 of Figure 9 were extracted to lines 2, 6 and 12-14 of the FRPAspect in Figure 12. Again, Figure 10 shows the extracted statements as blank lines.

The advantage of the aspect approach over the method and object ones is that here each extracted feature is com- pletely removed from the original class. In Figure 10 there is no trace whatsoever of the total charge printing or the frequent renter points. Instead, we can see little arrows to the left of lines 21 and 23 of Figure 10, reminding us that an advice may apply here. This way, it is possible to build a system with or without the extracted aspects simply by changing a configuration fde.

5 . A L A R G E R E X A M P L E

We now turn to demonstrate our approaches for concern untangling on a larger program: the AspectJ compiler [2]. The version we are refactoring here a is written in Java and is built as a compiler for Java programs, with an extension

2From January 2003

~ ip,za.a ,~ .mt z-~spect ~ _ = . ~ ~nt Customer.. ~equentRenterPoin~s u

4} ~l[$t~ing rentalSt~tement(.. )) ~&

B I St~ing ~Oum~.(CUStc~e:r c) : ~ 9~ aall ( string statementFoote ri( • • ) ) & &

~ 0 i ta~(C) { s t r ing r e su l t = ~ I c ) 3 ' zeCurn result + "You earned " +

I ~ C. frequentRenterl~Dint% + ~ I " frequent r en t e r pP~'.~,tS"~

} }

Figure 12: T h e total frequent renter points aspect .

97

Page 6: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

54! pu~li~ Vo~I walkFlow(FiOwCheckerPass w) { il 55~ if (getBody() !~ n u l l ) { I 5~ setupFlowWalke r (w) !! 57~ w.setLive (%a%t~) ;

581 w.process (getBody ()) ;

i i£ ( ! getResultType ( ). isvoid ( ) & & w. isLive ( ) ) 61! showErrorf"missing return =tatement");, 62 i } 63~ t

F i g u r e 13: M i s s i n g r e t u r n d e t e c t i o n .

for compiling and weaving aspects. The following scenario was performed manually. In Section 6 we will follow up by reporting on the design and current implementation sta- tus of a tool specifically designed to support the suggested refactorings.

5.1 Missing return statements In Java [7], the execution of a non-void method must com-

plete abruptly. This can be done either by throwing an ex- ception or with a return statement. Therefore, one feature expected of a Java compiler is a mechanism to find the meth- ods that are missing a return statement, and report those errors.

Imagine a specification change to that language feature. For example, in the exceptions mechanisms, or even in the return statement itself, alowing, say, an abrupt return, to skip finally blocks. The programmer responsible for main- taining the compiler will need to locate the relevant code, and possibly analyze it, prior to making the change. Fur- thermore, the progrsmmer may need to work with that code for other reasons, such as for fixing a bug, if one was re- ported, or for trying to reuse that feature in another con- text.

5.2 Identifying the Relevant Code One way to locate the code for missin9 return detection

is to look for the place in code where tha t error is reported. In the AspectJ compiler, tha t was accomplished by a simple text search. We first tes ted the compiler, and found tha t the error reported in such cases reads: missing return state- ments. We went on to search for that string in all source files. It was found (see Figure 13, line 61) in CodcDec.]ava, inside a method walkFlow(FlowCheckerPassw).

Looking into the source of the Flo~oCheckerPass class, that was sent as a pa ramete r to the found method, we observed a leading comment (see Figure 14) specifying its goals.

Essentially, FlowCheckerPas~ is a class that implements six separate features. Fur ther examining this class reveals that other than its own 625 lines of code (46 members), it delegates to a method called walkFlow tha t visits the ab- stract syntax tree representat ion of the input program (the one being compiled). Tha t AST is a da ta structure connect- ing node elements. Each such node denotes some construct of the input program (e.g. an assignment statement) , and is represented with a dedicated class (a subclass of a gen- eral ASTNode class). In total there are 181 subclasses to ASTNode, leading to a potential of 182 different versions of the walkFlow method. In practice, there are 36 different versions of walkFlow. These include a total of 645 non- commented and non-blank lines of code.

32/~ Thin p . ~ S signal~ errors for 33

3~ <li>~se~.before-a~sig~e~ vats and blank final field~</li~ 3~ <ii>a~iq/ie~-t~'ice blanc Zilial~'</.',.i>

3~ .:I l),unreachabl~ stmts</li> 39 ! <li>mi~sing ra~n ~tg~ts<)'li> 40 i cli:,va~'ious ill~gal try/catch :talts,<;'li>

i 43 ~ '.:p> It ~a~ eg~Ct[y One ride-effect: In anonymc,uf classes, it 441 SStS Up the t2~tco~s CI~us~ tO inclu~ any thr~n checked 4S exceptions, as required ~ JL~ ,15.9.5.1. <,'~>

S0~t~Zi~ £i~/ m1~ ~l~,Chec.kerPass ~ ~alkerPass .1

F i g u r e 14: T h e e n t a n g l i n g s p e c i f i c a t i o n o f FlowCheckerPass. W e focus on u n t a n g l i n g t h e con - c e r n o f Hne 39.

z ~ , m ~ srg.aspect J. compiler, t~ase ~ 2i , 3 impost ovg ~ ~spect J. ccmpiler, base. ast. 41~zt org. aspect j. ccg~pile r. cro~s cuts • ast • * ;

6~ulaliv asp~t FlowcheckevAspeCt { 7

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

L0i 11 pu~ void And~ndOpExpr.wal~xlow(FlowCheckerPass w) { 12i // W. ~tArgs (~. getArgs ( ) ) 1~3i w. pro, t ee s ~ l e a n ( g e t a a n d l ( ) ) ; ~4 i J ' l o ~ k e r P a s s . v a r s pl = w.qetVar~(}

w, proceee~oolean (getz~an02 0 )~

2120 ) w. setXa~s (p2. getTpae ( ), pZ. getXal~e ( ). ~pin (p2, getYa~ve ( ] ))

F i g u r e 15: T h e FlowChecherAspect. T h e file g o e s o n t o d e f i n e 35 m o r e s u c h introduction m e t h o d s .

The 36 versions of the walkFlo~ method are scat tered in 36 different source files, making it harder to locate and follow the relevant code. Furthermore, in those 36 methods, the relevant code is tangled with code implementing the other five kinds of error checks mentioned above.

5.3 Un-scattering We went on to tackle the problem of scat tered implemen-

tation. AspectJ allows the introduction of members, in as- pect code, to other classes. In order to gather the 36 scat- tered versions of the walkFlow visiting method into one location, we created a new aspect, FlowCheckerAspect, to which we moved those methods (see Figure 15) 3.

At the end of this stage all the relevant code is co-located, making it easier to follow 4. Nevertheless, it is still tangled.

3In fairness to the AspectJ compiler developers, we must say tha t they used Java and not AspectJ (boots t rap prob- lem?). Furthermore, each one of the scat tered methods was actually preceded by a comment reading: / / I N T R O from FlowCheckerPass 4Still, each method visits a different class, and uses i ts spe-

98

Page 7: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

5.4 Untangling Our next s tep was to untangle the missing re turn detec-

tion code. We s tar ted by trying to use the Extract Slice as Method approach (presented above in Section 2). In this case, the scope of computation, from which we wish to un- tangle a single feature, is not clear. One extreme is to ex- tract only the error reporting statement (Figure 13, Line 61). In which case the extracted method cannot be reusable other than within its original method. The other extreme is to extract the relevant code as a completely separate pass of the AST. In tha t case, the scope for extract ion is both the FlowCheckerPass class and the 36 walkFlow methods, now in FlowCheckerAspect . For this, we need a general- ization of the Extract Slice as Method refactoring, in which we extract one method from any relevant method in scope, possibly moving i t into a new dedicated class.

We experimented with the la t ter extreme, manually, by first copying the FlowCheckerPass class into a new class, Miss ingRe turnPass , and by copying the 36 walkFlow vis- i t ing methods. Then, we gradually examined the whole code, t rying to figure out which parts are contr ibut ing to the missing return detection.

We then identified the place where the FlowCheckerPass is added to the compiler, and added, immediate ly after it, o u r new Miss ingRe turnPass .

The change d id work, but it was problematic in some ways. The order of applying the checks was modified. This means tha t if an input program had two different flow prob- lems, the order in which they are reported may alter. In many cases this is not critical. Ideally, a tool assisting in such a process should warn the user of such implications, allowing the user to take the final decision.

The other problem we observed was tha t a large body of code was duplicated. This is both the code for controlling the tree traversal, and the code for keeping note of whether the current posit ion in the tree is alive (i.e. completes nor- mally). The reason this code was duplicated is tha t it is needed for some of the other tangled features, e.g. for find- ing unreachable statements. Ideally, a tool can observe tha t the only piece of code tha t is exclusively related to the miss- ing return s ta tement feature is the error report ing s ta tement itself, and its enclosing i f s tatement (Figure 13, Lines 60- 62).

In such a case, a be t te r approach is to apply either of our new Extract Slice as Object (Section 3 above) or Extract Slice as Aspect (Section 4 above) refactorings. The la t ter result is shown in Figure 16. The advantage of the la t te r is tha t now we can easily exclude that feature from a future build if we wish to.

6. NATE: A REFACTORING TOOL We now proceed to introduce our own refactoring tool,

which is desigued to automate the application of the above refactoring techniques.

6.1 Overview Nate is a refactoring tool, designed to support interac-

tive code untangling, in part icular slicing-based refactoring techniques for the Java programming language [7]. The fol- lowing scenario demonstrates the application of the Extract

cific members, which makes reading it with isolation from the class itself non-ideal.

~ CVml~le ~ t u ~ e ~ ~ t . Ccode~3ec ~

I ~ ItpQ, ot,, M~S i~ReturnAs~ct {

!.i 71 ~ . ~ , ~ codeDecwallcFlow{C~x~c node, Flo~CheckerPass W): 81 ~L l { ' v~J .d COd~De¢.walkFlow(Ylc~CheckerPas,)) a&

loj ~ ~w)

• ZZ~ .~12[ af1~x{CodeDec node, XlowChec~rPa~s w) : codeoecW~ikF1ow[node, w) (

~ i , (~oct..ger.~odyf, lffi n~l) {

I~ ~. shovEr~or ("~sslng ~tff~n st~te~nt." ) }

] lpel ~igi}

F i g u r e 16: T h e u n t a n g l e d Miss ingReturnAspec t .

Slice as Method refactoring on the video store example from Section 2 above. The programmer s tar ts by selecting a slic- ing criterion, e.g. < 29, to talAmount >, and a name for the new extracted method. The tool responds by first comput- ing the relevant slice, then checking the refactoring precon- ditions 5 to verify the slice can indeed be ext rac ted without altering the behaviour of the original program. If the pre- conditions are not met, the programmer is informed, and the refactofing is rejected. Otherwise, the tool performs the transformation by extract ing the slice into a new method, replacing it with a method call. Finally, for each ext rac ted statement, the tool determines whether to delete it from the original method or to leave it there because it is still relevant.

6.2 Design A refactoring environment can be thought of as a smar t

source code editor, equipped with au tomated refactoring tools. That environment should be aware of the whole cur- rently developed program and be equipped with a set of operations to transform the source code. Furthermore, it should be aware of the progr~mmlng language, i ts syntax, and its semantics. For such an environment to be effec- tive, basic features such as project management , source code browsing, refactoring invocation and source code saving are indispensable. Other features such as compiling, source code editing or configuration management can be considered as desirable but less essential.

Integrating a refactoring tool with a development environ- ment is obviously desirable for i ts acceptance [4]. We have chosen to implement Nate as an extension to Eclipse's J D T refactoring tools. The immediate benefit was having all the features outl ined above (and many more) a l ready present. This way, only the specific par t s of the slicing-based refac- toring techniques needed to be implemented before the tool could be tested and applied.

6.3 Implementation The currently implemented pro to type of Nate suppor ts

stat ic slicing of a small subset of Java. Nevertheless, i t was designed with slicing for the whole language in mind. The slicing tool employs a demand-driven interprocedural slicing algorithm, reminiscent of tha t proposed by Reps in [16]. The demand-driven nature of the algori thm is impor tan t because

5See [5] for E ~ r a c t Slice as Method's preconditions.

99

Page 8: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

complete construction of the program dependence graph [15] would be too time consuming in an interactive environment. For each language construct, we define an inference rule that captures its slicing behaviour, thus making it easy to extend the tool to cover further language constructs. Furthermore, interprocedural rules to reflect method calls are built on demand and cached for possible reuse. Details of the slicing algorithm can be found in [17].

To implement the Extract Slice as Method refactoring, we employed a variation of a method extraction approach by Lakhotia and Deprez [12] ~. Briefly, starting with a slicing criterion < s, v > we compute five sets of statements:

scope :----all statements 7 that may be executed before s; extracted := slice(< s, v >); notExtracted := scope/extracted; kept := slice(notExtracted); deleted := scope/kept;

The idea is that every statement that was not extracted must be kept in the original method. Every statement that contributes to the computation of a kept statement must be kept as well. This is the reason for using slicing again s in the computation of kept. The rest of the statements (scope/kept) are not contributing to any statement that was not extracted, and thus may be deleted from the original method (from Figure 2 above) as they are no longer rele- vant there. To illustrate, extracting < 29, totaIAmount > yields:

scope := {14..26} extracted := {14, 16, 19, 20, 25, 26} notExtracted := {15, 17, 18, 21..24} kept := {15..24, 26} deleted := {14, 25}

The tool extracts all the lines in extracted, renames the slicing criterion's variable to result, and adds a return state- ment at the end (see Figure 4, line 43).

Automating the Extract Slice as Object approach would be straightforward in the presence of support for the Replace Method with Method Object refactoring. Unfortunately, this is not yet the case in Eclipse. Instead, support is achieved through a composition of the available refactorings Extract Method, Move Instance Method and Inline Method.

AspectJ is supported within Eclipse through AJDT [1] which is at the time of writing still under development. It already allows development of AspectJ projects in Eclipse. Different configurations can be developed simultaneously, leveraging the main power of aspects. Extension of the JDT refactoring tools to behave correctly in the presence of as- pects is planned. Similarly, full integration of the refactoring infra-structure and the AspectJ AST will allow implemen- tation of refactorings such as Extract Advice, Move Field to Aspect, Move Method to Aspect and finally Extract Aspect. With these in hand, supporting the Extract Slice as Aspect approach, proposed here, will be straightforward. Our slic-

6Where correctness of the approach is demonstrated. t in the current method. In future releases, this may include statements from other methods as well. Our slicing algo- rithm supports interprocedural slicing, but the refactoring tool does not yet take full advantage of this. Sin this case the criterion is a set of statements and the result is the union of results of slicing each statement for all the variables used in it.

ing algorithm can be extended to aspect-oriented programs along the lines of [19].

7. RELATED W O R K An interactive process for behaviour-preserving method

extraction was first proposed by Opdyke [14] and by Gris- wold and Notkin [8]. This was restricted to the selection of a consecutive sequence of statements.

Interactive slice extraction (as a method) in a behaviour- preserving way was proposed by Lakhotia [12] and later, explicitly in the context of refactoring, by Maruyama [13]. The former is a transformation, called Tuck, from which we adopted the idea of using slicing to determine which state- ments should be deleted from the original method, after the extraction. The latter supports a subset of Java, as our prototype does, but inherently falls to support statements like break and continue. Our main advantage is the integra- tion with Eclipse, whereas they both report on a text-based user interface prototypes and plans for a future graphical interaction. Algorithmically, both of them are based on traditional slicing methods that involve the computation- ally expensive construction of the system dependence graph [15]. Our demand driven algorithm is more suitable in the context of refactoring, where the analyzed program is con- stantly changing, and slicing is likely to be computed a small number of times between source modifications.

Komondoor and Horwitz proposed an automatic method extraction algorithm [11] out of a selected set of statements. This algorithm is not generally suitable for untangling as they do not allow loop duplication. (Instead, they allow a loop to be either extracted completely of not at all.) Nev- ertheless, their algorithm may be combined with our frag- ments extraction (into object or aspect) approaches as an optimizing phase to reduce the number of extracted frag- ments, leading to a more readable code.

Acknowledgements The authors would like to thank Stephen Drape and Yorck Hunke for t l~:r crucial help in important phases of the prepa- ration of this paper.

We thank Mike Spivey and Laurie Hendren for useful com- ments and suggestions, as well as Oege de Moor and the members of the Programming Tools Group at Oxford for many illuminating discussions on the topic of this paper.

We are also thankful to Adrian Colyer and the AOSD Team members at IBM Hursley for their invaluable feedback during mutual visits. The first author is partially supported by an Eclipse innovation grant from IBM.

8. REFERENCES [1] Ajdt web site. http://www.eclipse.org/ajdt/ . [2] Aspectj web site. http://www.eclipse.org/aspectj/. [3] Eclipse web site. http://www.eclipse.org/. [4] J. Brant D. Roberts and R. Johnson. A refactoring

tool for smalltalk. Theory and Practice of Object Systems, 3(4), 1997.

[5] R. Ettinger. Automated tools for refactoring. http: / /web.comlab.ox.ac.uk / oucl /research/ areas/ progtools/projects/nate/doc/TransferThesis.pdf, Transfer thesis, University of Oxford, 2003.

[6] M. Fowler. Refactoring: Improving the Design of Existing Code. Addison Wesley, 2000.

100

Page 9: Untangling: A Slice Extraction Refactoringfaculty.salisbury.edu/~xswang/Research/papers/serelated/refactor/p93-ettinger.pdfRefactoring [6] is the process of improving the design of

[7] James Gosling, Bill Joy, Guy Steele, and Gilad Bracha. The Java Language Specificz~tion Second Edition. Addison-Wesley, Boston, Mass., 2000.

[8] W.G. Griswold and D. Notkin. Automated assistance for program restructuring. A CM Transactions on Software Engineering, 2(3):228-269, July 1993.

[9] J. Hannemann and G. Kiczales. Overcoming the prevalent decomposition in legacy code. In Proceedings of Advanced Separation of Concerns Workshop, held at the International Conference on Software Engineering (ICSE), 2001.

[10] 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, Proceedings European Conference on Object-Oriented Programming, volume 1241, pages 220-242. Springer-Verlag, Berlin, Heidelberg, and New York, 1997.

[11] R. Komondoor and S. Horwitz. Effective automatic procedure extraction. In Proceedings of the 11th IEEE International Workshop on Program Comprehension, 2003.

[12] Arun Lakhotia and Jean-Christophe Deprez. Restructuring programs by tucking statements into functions. In/ormation and Software Technology, 40(11-12):677-690, 1998.

[13] Katsuhisa Maruyama. Automated method-extraction refactoring by using block-based slicing, pages 31-40. ACM Press, 2001.

[14] William F. Opdyke. Re/aetoring Object-Oriented tarameworks. PhD thesis, Urbana~Champaign, IL, USA, 1992.

[15] K.J. Ottenstein and L.M. Ottenstein. The program dependence graph in a software development environment. Proc. of the ACM SIGSOFT/SIGPLAN Soft~oare Engineering Symposium on Practical Software Development Environments, pages 177-184, 1984.

[16] Thomas W. Reps. Demand interprocedural program analysis using logic databases. In Workshop on Programming with Logic Databases (Book), ILPS, pages 163-196, 1993.

[17] M. Verbaere. Program slicing for refactoring. http:/ /web.comlab.ox.ac.uk/oucl/research/areas/ progtools/projects/nate/ doc/MScThesis.pdf, MSc thesis, University of Oxford, 2003.

[18] Mark Weiser. Program slicing. IEEE Transactions on Software Engineering, 10(4):352-357, 1984.

[19] Jianjun Zhao. Slicing aspect-oriented software. In Proceedings o/ the lOth IEEE International Workshop on Programming Comprehension, pages 251-260, June 2002.

101


Recommended