Fine Slicing for Advanced Method Extraction

4 downloads 1811 Views 122KB Size Report
Aug 27, 2009 - We have introduced fine slicing, a method for computing program slices that can .... that outputs the selected pictures to the HTML view. These.
Fine Slicing for Advanced Method Extraction Aharon Abadi

Ran Ettinger

Yishai A. Feldman

IBM Haifa Research Lab {aharona,rane,yishai}@il.ibm.com

Abstract The potential of the Extract Method refactoring for maintaining high-quality software is well-recognized. It can be useful by itself, or as a building block in larger refactoring tasks. Because it requires a deep analysis of data and control dependences, it is difficult to perform correctly without tools in complicated cases. Unfortunately, refactoring tools do not perform the required analysis, and will fail in or reject such cases. Some very useful cases, such as those involving non-contiguous code, cannot even be expressed using these tools’ interfaces. We present a new refactoring to untangle desired computations from their surrounding code, in preparation for Extract Method. It is based on the concept of fine slicing, which is a method for computing program slices that can be tailored by the removal of some data and control dependences. Fine slices are executable and extractable from their surrounding code. Fine slices can also be used to compute co-slices, the part of the program that should be left behind when extracting a fine slice. This is crucial for the ability to reuse the extracted slice from the original code.

1. Introduction Extract Method is the most celebrated refactoring in Fowler’s catalog (Fowler 2000), and is the basis for many other transformations. Many enterprise refactorings such as the separation of presentation code from business logic, separation of tiers, and service extraction can be automated to a large extent by combining elementary refactorings, with Extract Method playing a leading role. In a previous case study we found that 13 out of 36 steps required to transform a photoalbum servlet to the MVC pattern used Extract Method; another three used Replace Temp with Query, which is another form of method extraction. Only three out of these steps could be performed fully automatically (Abadi, Et-

[Copyright notice will appear here once ’preprint’ option is removed.]

Fine Slicing for Advanced Method Extraction

tinger, and Feldman 2008). The other 13 cases exhibited four different types of challenges: the extraction of multiple fragments; the extraction of partial fragments, with selected sub-expressions as parameters; loop duplication with potential data flow between the extracted part and the complement; and the extraction of code with conditional exits. Code to be extracted must be executable on its own, given suitable parameters. Program slicing (Weiser 1984) is a technique for computing executable subprograms. This technique recursively adds to the slice data and control dependents of computations already in the slice, until it is selfcontained. As a result, program slices are often too large for extraction, since they contain all the code relevant to the computation of a given value. Often, this includes code that should not be extracted; for example, some values should be passed as parameters instead of participating in the extracted method, and some control structures should remain surrounding the call instead of being included. We have introduced fine slicing, a method for computing program slices that can be tailored by the removal of some data and control dependences (Abadi, Ettinger, and Feldman 2009a). Fine slices are executable and extractable from their surrounding code. As such, they enable the precise and flexible extraction of reusable computations. We have implemented the fine-slicing algorithm as part of our effort to build a static-analysis infrastructure for program understanding and transformation. In order to extract a (fine) slice, it is also necessary to compute the part of the program that is left behind and uses the extracted code; this is called a co-slice (Ettinger 2006). Co-slicing is not trivial; for example, the extracted slice may contain part of the computation of a loop; the parts of the loop that remain in the co-slice may require a duplication of the loop control structure, which is also part of the extracted slice. It turns out that a co-slice is a special case of a fine slice. We introduce a new refactoring, called Extract Computation, which extracts a fine slice from its surrounding code and turns it into a contiguous part of the code, ready for method extraction. We have developed an algorithm for fineslice extraction, which is the basis for the automation of Extract Computation. We show in this paper how to use Extract Computation followed by Extract Method in order to auto-

1

2009/8/27

mate the problematic refactorings from our previous case study (Abadi et al. 2008). That case study was based on Alex Chaffee’s “Refactoring to Model-View-Controller” article1 showing how to convert a monolithic application to the MVC pattern. We did not follow Chaffee’s exact path, which uses several complicated transformations. Instead, we chose a path that makes maximal use of documented refactorings. As mentioned above, that revealed several types of weaknesses in current support for Extract Method. In the next section we demonstrate how fine slicing and the Extract Computation refactoring enable the automation of the four examples from that study, each of which exhibits one of the types of challenges we found.

2. Extract Computation The example of Figure 1(a) demonstrates the extraction of multiple fragments. In this example, we want to separate the preparation of the output from the collection of the information from the input form (shown in italics). A full slice from the last line of Figure 1(a) needs to include lines 1, 2, and 4, because these determine whether the rest of the code will be executed; in other words, the rest of the code has a control dependence on the test. However, we can compute a fine slice that ignores this control dependence; that will give us exactly the fragment we want: lines 6, 7, and 10. Equivalently, it is possible to choose these lines and compute a fine slice that ignores all dependences they have on other parts of the code; the result would be the same. (Even when choosing a set of statements and ignoring all dependences they have on other code, a fine slice may still contain statements that were not chosen, in order to be executable. For example, if the chosen statements include code from both sides of a conditional, the test of the conditional must also be in the slice.) An important property of fine slices is that they can be collected into a contiguous set of statements and placed in one location in the program; our fine-slice extraction algorithm characterizes the possible locations. Figure 1(b) shows the result of collecting the fine slice into a contiguous set of statements, and extracting it into a separate method startWritingPage, shown in Figure 1(c). In this example, it would also have been possible to put this call after the definition of albumName. The location chosen in this example is more convenient for the next refactoring step in the scenario. In this case, it would have been possible to achieve the same result by manually moving lines 8 and 9 outside the range to be extracted. Such a transformation is easy to do, but might not preserve behavior if there is a dependence between exchanged statements. The fine-slicing algorithm tracks these dependences, and will not allow non-behaviorpreserving transformations. In addition, the Extract Computation refactoring can be applied to any number of code fragments. 1 http://www.purpletech.com/articles/mvc/

refactoring-to-mvc.html.

Fine Slicing for Advanced Method Extraction

1 2 3 4 5 6 7 8 9 10

1 2 3 4 5 6 7 8

User user = getCurrentUser(request); if (user == null) { response.sendRedirect(LOGIN_PAGE_URL); return; } response.setContentType("text/html"); disableCache(response); String albumName = request.getParameter("album"); PrintWriter out = response.getWriter(); (a) User user = getCurrentUser(request); if (user == null) { response.sendRedirect(LOGIN_PAGE_URL); return; } PrintWriter out = startWritingPage(response); String albumName = request.getParameter("album"); (b)

1 2 3 4 5 6 7 8

PrintWriter startWritingPage (HttpServletResponse response) throws IOException { response.setContentType("text/html"); disableCache(response); PrintWriter out = response.getWriter(); return out; } (c)

Figure 1. Extract Computation, case 1: (a) original code; (b) refactored code; (c) extracted method.

In the example of Figure 2(a), we want to extract the code that outputs an error page, leaving the actual error text (italicized) as a parameter, since there is an almost-clone of this code that differs just in that text. In this case, we can compute a fine slice from the last line, ignoring the data dependence on the parameter in lines 7–8. In this case, when the fine slice is extracted into a separate method (Figure 2(c)), the call to the new method replaces all the code except for the computation of the parameter (Figure 2(b)). In the example of Figure 3(a), we want to extract the code that decides which pictures to present from the code that outputs the selected pictures to the HTML view. These are strongly entangled here, since the same loop retrieves the picture from the album (line 7) and prints it (line 8). In order to separate these, it will be necessary to duplicate the loop structure, and remember in a single data structure all the pictures that are to be printed. Fine slices are partial programs that may require certain inputs that are not part of the slice. As a result, they cannot be executed on their own. We defined a new oracle-based semantics for fine slices, and proved the equivalence of the 2

2009/8/27

1 2 3 4 5 6 7 8 9 10

1 2

out.println(DOCTYPE_HTML); out.println(""); out.println(""); out.println("Error"); out.println(""); out.print("

"); out.print("Could not load album ’" + albumName + "’"); out.println("

"); out.println(""); (a) printError(out, "Could not load album ’" + albumName + "’"); (b)

1 2 3 4 5 6 7 8 9 10 11 12

void printError(PrintWriter out, String message) { out.println(DOCTYPE_HTML); out.println(""); out.println(""); out.println("Error"); out.println(""); out.print("

"); out.print(message); out.println("

"); out.println(""); } (c)

Figure 2. Extract Computation, case 2: (a) original code; (b) refactored code; (c) extracted method. computation of the fine slice with a suitable oracle to the computation of the original program (Abadi et al. 2009a). The oracle specifies which values from the original program that are not in the slice need to be preserved and passed to the slice. This information is used by the fine-slice extraction algorithm to create the required values, including the creation of a list to hold a sequence of values if necessary. Figure 3(b) shows the result of the Extract Computation refactoring (without a subsequent Extract Method). Line 8 of the original program has been replaced by line 9, which adds the picture to the list pictures. Line 12 creates the iterator that yields the elements of the list to be used in the printing loop. The retrieval of the pictures has now been separated from the code that displays them. The fine-slice extraction algorithm guarantees that the lists it creates will always yield the correct values when accessed in the place determined by the algorithm. It is therefore not necessary to check whether the iterator has enough elements on line 14 in Figure 3(b). In this case, a more natural Java idiom would have been to replace the loop on i in line 13 by a loop on the list: for (Picture picture : pictures) printPicture(out, picture); Fine Slicing for Advanced Method Extraction

1 2 3 4 5 6 7 8 9 10

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

out.println(""); int start = page * 20; int end = start + 20; end = Math.min(end, album.getPictures().size()); for (int i=start; i