Ran Ettinger, IBM Research – Haifa Beijing, China 16 June 2012

Slides:



Advertisements
Similar presentations
IBM Haifa Research Lab © 2008 IBM Corporation Co-Slicing for Program Comprehension and Reuse March 2008 Ran Ettinger Software Asset Management Group In.
Advertisements

IBM Haifa Research Lab © 2007 IBM Corporation Program Sliding A Novel Approach for the Automation of Slicing-Based Refactoring June 2007 Ran Ettinger In.
Refactoring via Program Slicing and Sliding Ran Ettinger Programming Tools Group University of Oxford In Panayas Reading Group 2 November, 2006.
IBM Haifa Research Lab © 2007 IBM Corporation Refactoring via Program Slicing and Sliding June 2007 Ran Ettinger Software Asset Management Group In HRL.
CPSC 388 – Compiler Design and Construction
Synopsys University Courseware Copyright © 2012 Synopsys, Inc. All rights reserved. Compiler Optimization and Code Generation Lecture - 3 Developed By:
Context-Sensitive Interprocedural Points-to Analysis in the Presence of Function Pointers Presentation by Patrick Kaleem Justin.
Compilation 2011 Static Analysis Johnni Winther Michael I. Schwartzbach Aarhus University.
ECE 454 Computer Systems Programming Compiler and Optimization (I) Ding Yuan ECE Dept., University of Toronto
Control-Flow Graphs & Dataflow Analysis CS153: Compilers Greg Morrisett.
Composition CMSC 202. Code Reuse Effective software development relies on reusing existing code. Code reuse must be more than just copying code and changing.
Exploring Clear Clones Based on Splitting Iteration Structure and Selection Structure Chung Yung and Yen-Chang Lai Reporter: Qin-Xin Tu 1.
A survey of techniques for precise program slicing Komondoor V. Raghavan Indian Institute of Science, Bangalore.
Lecture 3: Topics If-then-else Operator precedence While loops Static methods Recursion.
CS 536 Spring Intermediate Code. Local Optimizations. Lecture 22.
Program Slicing for Refactoring Advanced SW Tools Seminar Jan 2005Yossi Peery.
1 Sharing Objects – Ch. 3 Visibility What is the source of the issue? Volatile Dekker’s algorithm Publication and Escape Thread Confinement Immutability.
Generative Programming. Generic vs Generative Generic Programming focuses on representing families of domain concepts Generic Programming focuses on representing.
Topic #10: Optimization EE 456 – Compiling Techniques Prof. Carl Sable Fall 2003.
Software Engineering Prof. Dr. Bertrand Meyer March 2007 – June 2007 Chair of Software Engineering Static program checking and verification Slides: Based.
IBM Haifa Research Lab © 2008 IBM Corporation Automating Big Refactorings for Componentization and the Move to SOA IBM Programming Languages and Development.
Mining and Analysis of Control Structure Variant Clones Guo Qiao.
Bug Localization with Machine Learning Techniques Wujie Zheng
Small changes to code to improve it. Refactoring Defined A change made to the internal structure of software to make it easier to understand and cheaper.
1 Minding the Gap Between Slicing and Refactoring (or “Fine Slicing: A new slicing technique to yield slices whose complements are slices too”) Ran Ettinger,
ADTs and C++ Classes Classes and Members Constructors The header file and the implementation file Classes and Parameters Operator Overloading.
1 Code optimization “Code optimization refers to the techniques used by the compiler to improve the execution efficiency of the generated object code”
The Functions and Purposes of Translators Syntax (& Semantic) Analysis.
Industrial Project (236504) Advanced programming tools for refactoring Java code in Eclipse Student: Alexander Libov Supervisor: Dr. Ran Ettinger, IBM.
Compiler Optimizations ECE 454 Computer Systems Programming Topics: The Role of the Compiler Common Compiler (Automatic) Code Optimizations Cristiana Amza.
CS 0401 Debugging Hints and Programming Quirks for Java by John C. Ramirez University of Pittsburgh.
Testing CSE 160 University of Washington 1. Testing Programming to analyze data is powerful It’s useless (or worse!) if the results are not correct Correctness.
CSSE 375 Organizing Data – Part 1 Shawn and Steve Q1.
Refactoring. DCS – SWC 2 Refactoring ”A change made to the internal structure of software to make it easier to understand and cheaper to modify without.
ECE 750 Topic 8 Meta-programming languages, systems, and applications Automatic Program Specialization for J ava – U. P. Schultz, J. L. Lawall, C. Consel.
Catalog of Refactoring (1) Composing Methods. Code Smells Long methods Dubious temporary variables Dubious methods.
Code Optimization Code produced by compilation algorithms can often be improved (ideally optimized) in terms of run-time speed and the amount of memory.
Code Optimization Overview and Examples
The need for Programming Languages
User-Written Functions
GC211Data Structure Lecture2 Sara Alhajjam.
Compositional Pointer and Escape Analysis for Java Programs
Testing and Debugging.
Testing UW CSE 160 Winter 2017.
Program Sliding Ran Ettinger, IBM Research – Haifa
Introduction to Algorithms
Rename Local Variable Refactoring Instances
Pointers and References
Advanced Programming Behnam Hatami Fall 2017.
Testing UW CSE 160 Spring 2018.
CS139 – Fall 2010 How far we have come
Program Slicing Baishakhi Ray University of Virginia
Testing UW CSE 160 Winter 2016.
Programming Funamental slides
Lecture 15 (Notes by P. N. Hilfinger and R. Bodik)
Group Status Project Status.
Programming Funamental slides
Mock Object Creation for Test Factoring
Generic programming in Java
Compiler Code Optimizations
Data Flow Analysis Compiler Design
자바 언어를 위한 정적 분석 (Static Analyses for Java) ‘99 한국정보과학회 가을학술발표회 튜토리얼
Operator Overloading Professor Hugh C. Lauer CS-2303, System Programming Concepts (Slides include materials from The C Programming Language, 2nd edition,
Review for Final Exam.
Intermediate Code Generation
CPS120: Introduction to Computer Science
Refactoring.
Java Coding 6 David Davenport Computer Eng. Dept.,
Chapter 6: Methods CS1: Java Programming Colorado State University
Presentation transcript:

Ran Ettinger, IBM Research – Haifa ECOOP @ Beijing, China 16 June 2012 Program Sliding Ran Ettinger, IBM Research – Haifa ECOOP @ Beijing, China 16 June 2012

for (int i = 1; i <= N; i++) { prod *= i; } print("Sum: " + sum); int prod = 1; for (int i = 1; i <= N; i++) { prod *= i; } print("Sum: " + sum); print("Product: " + prod); int sum = 0; for (int i = 1; i <= N; i++) { sum += i; } Programmers use slices when debugging They use slices when refactoring, too Automation can help But a slice captures a subset of functionality A complementary slice (or co-slice) will capture the remaining functionality Sliding is a recomposition transformation Replace a code fragment with “slice ; co-slice” Identify threats to behavior preservation

Slice of V={sum} int prod = 1; for (int i = 1; i <= N; i++) { Sample Run initial values N = 4 System.out.* = “” int prod = 1; for (int i = 1; i <= N; i++) { prod *= i; } print("Sum: " + sum); print("Product: " + prod); int sum = 0; for (int i = 1; i <= N; i++) { sum += i; } final values sum = 10 final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24” We know how to compute slices, automatically, but what how about the complement? Definition of a slice: The slice of a code fragment S for a set of variables V is a subset of the statements of S, say SV If S terminates, SV terminates with the same result in variables V when started on the same initial state Is it the slice of all non-V variables? void print(String message) { System.out.println(message); }

Slice of CoV={prod,System.out.*} initial values N = 4 System.out.* = “” int sum = 0; for (int i = 1; i <= N; i++) { sum += i; } int sum = 0; int prod = 1; for (int i = 1; i <= N; i++) { sum += i; prod *= i; } print("Sum: " + sum); print("Product: " + prod); The co-slice will follow the slice, so let’s (re)use its result final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24” Assume variables V hold the final value on entry to the co-slice (Incomplete) Definition of a Co-slice: The co-slice of a code fragment S for a set of variables V is a subset of the statements of S, say SCoV If S terminates, SCoV terminates with the same result in all variables outside the set V when started on the same initial state void print(String message) { System.out.println(message); }

Co-slice of V={sum} int sum = 0; for (int i = 1; i <= N; i++) { initial values N = 4 System.out.* = “” sum = 10 int sum = 0; for (int i = 1; i <= N; i++) { sum += i; } int prod = 1; for (int i = 1; i <= N; i++) { prod *= i; } print("Sum: " + sum); print("Product: " + prod); final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24” Definition of a Co-slice The co-slice of a code fragment S for a set of variables V is a subset of the statements of S, say SCoV If S terminates, SCoV terminates with the same result in all variables outside the set V when started on the same initial state in all variables except V, whose initial value is the result computed by S This kind of a slice (of final values of CoV) with reuse (of V) is a (simple) case of a “fine slice” void print(String message) { System.out.println(message); } See “Fine Slicing: Theory and Applications for Computation Extraction” [Abadi, Ettinger, and Feldman, FASE @ ETAPS 2012]

Sliding for V={sum} initial values final values of the slice System.out.* = “” final values of the slice sum = 10 N = 4 System.out.* = “” int sum = 0; for (int i = 1; i <= N; i++) { sum += i; } int prod = 1; for (int i = 1; i <= N; i++) { prod *= i; } print("Sum: " + sum); print("Product: " + prod); final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24” final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24” Sliding: A Slice-Motion Transformation Isolates the slice of S on V making it contiguous Identifies the co-slice of S on V Replaces S with a sequential composition of the slice of S on V and its co-slice Expected to preserve functionality for all variables When unable, identifies problematic variables Compensatory measures may be taken to resolve those issues

Sliding for V={prod} initial values final values of the slice System.out.* = “” final values of the slice prod = 24 N = 4 System.out.* = “” int prod = 1; for (int i = 1; i <= N; i++) { prod *= i; } int sum = 0; for (int i = 1; i <= N; i++) { sum += i; } print("Sum: " + sum); print("Product: " + prod); final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24” final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24”

Sliding for V={sum,prod} initial values N = 4 System.out.* = “” final values of the slice sum = 10 prod = 24 N = 4 System.out.* = “” int sum = 1; int prod = 1; for (int i = 1; i <= N; i++) { sum += i; prod *= i; } print("Sum: " + sum); print("Product: " + prod); final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24” final values sum = 10 prod = 24 System.out.* = “Sum: 10 Product: 24”

In the Paper… A practical sliding algorithm Suitable for (sequential) Java Building on slicing and dependence graphs Revised mechanics to refactorings Split Loop Replace Temp with Query Separate Query from Modifier Evaluation Manually refactored Eclipse’s Java compiler No detected regression

Replace Temp with Query

Replace Temp with Query Source: org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo String def = "FlowInfo<def:[" + this.definiteInits; //$NON-NLS-1$ String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ int i, ceil; for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { def += "," + this.extra[0][i]; //$NON-NLS-1$ pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { def += ",..."; //$NON-NLS-1$ pot += ",..."; //$NON-NLS-1$ return def + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ Source: org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo this fragment is a clone of another Extract Method – to make it reusable and reuse it - would fail …for two reasons: (1) ambiguous result, in variables def and pot

Replace Temp with Query Step 1: Sliding for V={def} String def = "FlowInfo<def:[" + this.definiteInits; //$NON-NLS-1$ String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ int i, ceil; for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { def += "," + this.extra[0][i]; //$NON-NLS-1$ pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { def += ",..."; //$NON-NLS-1$ pot += ",..."; //$NON-NLS-1$ return def + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$

Replace Temp with Query Step 1: Sliding for V={def} String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { pot += ",..."; //$NON-NLS-1$ return def + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ String def = "FlowInfo<def:[" + this.definiteInits; //$NON-NLS-1$ int i, ceil; for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { def += "," + this.extra[0][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { def += ",..."; //$NON-NLS-1$ Source: org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo.toString() tucking would do the same (if we focus on the tightest SESE, the one without the return statement) KH03 would keep the loop as is, and would move the last assignment to pot to the after part

Replace Temp with Query {... String def = "FlowInfo<def:[" + this.definiteInits; //$NON-NLS-1$ int i, ceil; for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { def += "," + this.extra[0][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { def += ",..."; //$NON-NLS-1$ String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ pot += "," + this.extra[1][i]; //$NON-NLS-1$ pot += ",..."; //$NON-NLS-1$ return def + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ ...} Step 2: Extract Method, on the slice of V={def}

Replace Temp with Query {... String def = def(); int i, ceil; String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { pot += ",..."; //$NON-NLS-1$ return def + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ ...} private int def() { String def = "FlowInfo<def:[" + this.definiteInits; //$NON-NLS-1$ def += "," + this.extra[0][i]; //$NON-NLS-1$ def += ",..."; //$NON-NLS-1$ return def; Step 2: Extract Method, on the slice of V={def}

Replace Temp with Query {... String def = def(); int i, ceil; String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { pot += ",..."; //$NON-NLS-1$ return def + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ ...} private int def() { String def = "FlowInfo<def:[" + this.definiteInits; //$NON-NLS-1$ def += "," + this.extra[0][i]; //$NON-NLS-1$ def += ",..."; //$NON-NLS-1$ return def; Step 3: Inline Temp

Replace Temp with Query {... int i, ceil; String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { pot += ",..."; //$NON-NLS-1$ return def() + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ ...} private int def() { String def = "FlowInfo<def:[" + this.definiteInits; //$NON-NLS-1$ def += "," + this.extra[0][i]; //$NON-NLS-1$ def += ",..."; //$NON-NLS-1$ return def; Step 3: Inline Temp

Replace Temp with Query {... int i, ceil; String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { pot += ",..."; //$NON-NLS-1$ return def() + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ ...} Next, the same refactoring for V={pot} requires no sliding

Replace Temp with Query {... return def() + pot() + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ ...} private String pot() { int i, ceil; String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { pot += ",..."; //$NON-NLS-1$ return pot; Next, the same refactoring for V={pot} requires no sliding

Replace Temp with Query {... return def() + pot() + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ ...} private String pot() { int i, ceil; String pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ for (i = 0, ceil = this.extra[0].length > 3 ? 3 : this.extra[0].length; i < ceil; i++) { pot += "," + this.extra[1][i]; //$NON-NLS-1$ } if (ceil < this.extra[0].length) { pot += ",..."; //$NON-NLS-1$ return pot; Finally, let’s replace the clone too

Replace Temp with Query ...for (...) { def += "," + this.extra[0][i]; //$NON-NLS-1$ pot += "," + this.extra[1][i]; //$NON-NLS-1$ nullS += "," + this.extra[2][i] //$NON-NLS-1$ + this.extra[3][i] + this.extra[4][i] + this.extra[5][i]; } if (ceil < this.extra[0].length) { def += ",..."; //$NON-NLS-1$ pot += ",..."; //$NON-NLS-1$ nullS += ",..."; //$NON-NLS-1$ return def + pot + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + nullS + "]>"; //$NON-NLS-1$ else { if (this.extra == null) { return ...; //$NON-NLS-1$ return def() + pot() + ", no null info>"; //$NON-NLS-1$ ...} Second reason Extract Method would have failed: this is not an exact clone Finally, let’s replace the clone too

Replace Temp with Query ...for (...) { nullS += "," + this.extra[2][i] //$NON-NLS-1$ + this.extra[3][i] + this.extra[4][i] + this.extra[5][i]; } if (ceil < this.extra[0].length) { nullS += ",..."; //$NON-NLS-1$ return def() + pot() + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + nullS + "]>"; //$NON-NLS-1$ else { if (this.extra == null) { return ...; //$NON-NLS-1$ + ", no null info>"; //$NON-NLS-1$ ...} The loop and conditional are duplicated now 3 times instead of 2 (so 6 statements instead of 4) The exclusive 3 statements in the computation of def are no longer duplicated (3 instead of 6) The exclusive 3 statements in the computation of pot are no longer duplicated (3 instead of 6) Total: added 2 statements and removed 6!

Separate Query from Modifier

Separate Query from Modifier Source: org.eclipse.jdt.internal.compiler.codegen.ConstantPool public int literalIndex(int key) { int index; if (this.intCache == null) { this.intCache = new IntegerCache(INT_INITIAL_SIZE); } if ((index = this.intCache.putIfAbsent(key, this.currentIndex)) < 0) { this.currentIndex++; if ((index = -index) > 0xFFFF){ someSideEffects(); // one (long) statement here someMoreSideEffects(index, key); // 11 statements here return index;

Separate Query from Modifier public int literalIndex(int key) { int index; if (this.intCache == null) { this.intCache = new IntegerCache(INT_INITIAL_SIZE); } index = this.intCache.getValue(key, this.currentIndex); this.intCache.putIfAbsent(key, this.currentIndex); if (index < 0) { this.currentIndex++; index = -index; if (index > 0xFFFF){ someSideEffects(); // one (long) statement here someMoreSideEffects(index, key); // 11 statements here return index; 2 manual updates: SQfM and an inlined equivalent

Separate Query from Modifier public int literalIndex(int key) { int index; if (this.intCache == null) { this.intCache = new IntegerCache(INT_INITIAL_SIZE); } index = this.intCache.getValue(key, this.currentIndex); this.intCache.putIfAbsent(key, this.currentIndex); if (index < 0) { this.currentIndex++; index = -index; if (index > 0xFFFF){ someSideEffects(); // one (long) statement here someMoreSideEffects(index, key); // 11 statements here return index; The return could be inside the scope for sliding, but I prefer to exclude it so the co-slice will be directly extracted as the modifier (without the return)

Separate Query from Modifier int index; if (this.intCache == null) { this.intCache = new IntegerCache(INT_INITIAL_SIZE); } index = this.intCache.getValue(key, this.currentIndex); if (index < 0) { index = -index; if (this.intCache == null) { this.intCache = new IntegerCache(INT_INITIAL_SIZE); } index = this.intCache.getValue(key, this.currentIndex); this.intCache.putIfAbsent(key, this.currentIndex); if (index < 0) { this.currentIndex++; if (index > 0xFFFF){ someSideEffects(); // one (long) statement here someMoreSideEffects(index, key); // 11 statements here Sample input: this.intCache = null key = 40 this.currentIndex = 6 Modified values after slice with no compensation: this.intCache = new object {(40,6)} index = 6 Modified or relevant values after co-slice with no compensation: this.currentIndex = 6 (instead of 7) index = 6 (luckily recomputed to the same value) Modified or relevant values after slice with compensation, renaming this.intCache in the slice, copying its initial value: localIntCache = a new object {(40,6)} Modified values after co-slice with compensation, localizing index in the co-slice: this.intCache = a 2nd new object {(40,6)} this.currentIndex = 7 localIndex = 6

int index; if ( == null) { = new IntegerCache(INT_INITIAL_SIZE); } index = .getValue(key, this.currentIndex); if (index < 0) { index = -index; IntegerCache intCache = this.intCache; intCache if (this.intCache == null) { this.intCache = new IntegerCache(INT_INITIAL_SIZE); } index = this.intCache.getValue(key, this.currentIndex); this.intCache.putIfAbsent(key, this.currentIndex); if (index < 0) { this.currentIndex++; if (index > 0xFFFF){ someSideEffects(); // one (long) statement here someMoreSideEffects(index, key); // 11 statements here

int index; if ( == null) { = new IntegerCache(INT_INITIAL_SIZE); } index = .getValue(key, this.currentIndex); if (index < 0) { index = -index; IntegerCache intCache = this.intCache; intCache int index1; index1 if (this.intCache == null) { this.intCache = new IntegerCache(INT_INITIAL_SIZE); } = this.intCache.getValue(key, this.currentIndex); this.intCache.putIfAbsent(key, this.currentIndex); if ( < 0) { this.currentIndex++; if (index > 0xFFFF){ someSideEffects(); // one (long) statement here someMoreSideEffects(index, key); // 11 statements here

Evaluation A manual experiment Sliding for Replace Temp with Query Refactoring the well-tested code of the open-source Java compiler in Eclipse Update the code as needed in preparation for the refactoring Sliding for Replace Temp with Query Loops with more than one result Sliding for Separate Query from Modifier Non-void methods with side effects (detected using WALA’s ModRef analysis) Update the code as needed in preparation for the refactoring Split statements that declare more than one variable Split called methods if the result is needed in the slice and the side effects in the co-slice Separate side effects from expressions in conditionals

Evaluation 55 refactorings 77 manual preparatory changes 23 successful cases of sliding for Replace Temp with Query 28 successful cases of sliding for Separate Query from Modifier A single problematic case returning the value a field (see paper) 3 remaining cases would work (depending on successful SQfM of a problematic polymorphic call) 77 manual preparatory changes

Evaluation Code size: 6 to 97 statements Average: 23.5 Slice size: 1 to 85 statements Average: 10.4 Co-slice size: 1 to 82 statements Average: 20 (duplicated 6.9)

Evaluation No compensation needed in 44% of the cases (7 RTwQ and 17 SQfM) In all remaining cases variable renaming and simple initial-value backup of up to 3 variables was sufficient Reuse of slice result 132 final-value uses 107 non-final uses 46 non-final uses remain (in 22 co-slices)

Related Work: Statements as Input Tucking [Lakhotia & Deprez, IST98] No reuse of slice results in the complement Rejected when a variable is modified on both sides Semantics preserving procedure extraction [Komondoor & Horwitz, POPL00] Rejected when any duplication is needed Effective, automatic procedure extraction [Komondoor & Horwitz, IWPC03] If a statements cannot be moved it is added to the extracted code Only predicates and jumps may be duplicated

Related Work: Variables as Input Block-based slice extraction [Maruyama, SSR01] Extract the slice of a single variable Reuse the slice result, but incorrectly for non-final uses too Identification of extract method refactoring opportunities [Tsantalis & Chatzigeorgiou, CSMR09,JSS11] Solved Maruyama’s correctness issue through rules for identifying and rejecting problematic cases No duplication of a statement that modifies the state of an object

A Limitation of Sliding for Variables No way to extract the code for “computing and printing sum” in the following: int prod = 1; for (int i = 1; i <= N; i++) { prod *= i; } print("Sum: " + sum); print("Product: " + prod); int sum = 0; for (int i = 1; i <= N; i++) { sum += i; } Need support for the extraction of intermediate values See “Fine Slicing” [Abadi et al., FASE12] for a general approach

Conclusion Sliding is a new way of recomposing programs automatically Focuses on final-value slices of sets of variables Useful in refactoring Testing, verification, and compiler optimizations too? Practical algorithm based on program dependence graphs Some topics for further work: interprocedural and amorphous sliding (avoid need for manual code changes, remove duplication) automation of the refactorings sliding for intermediate values too sliding for any selection of statements reverse sliding to merge matching loops accuracy vs. speed investigations (e.g. of pointer analyses)

Thanks! Join (or follow) our ongoing WALA-based refactoring tool implementation effort: https://wala.svn.sourceforge.net/svnroot/wala/incubator/com.ibm.wala.refactoring Past and present contributors: Alex Libov, Shay Menaia, Dima Rabkin, Vlad Shumlin, Eli Kfir, Daniel Lemel, Moshe Zemah Special thanks to Steve Fink