Prerequisites
Make sure you downloaded the source code and made a successful build following the download and install guide. We assume you’re at the pasta
directory. The directory will look like this:
$ tree .
.
├── examples
│ ├── distiller.sh
│ ├── synthesizer.sh
...
│ ├── string2file
...
│ │ ├── old
│ │ │ └── Example.java
│ │ ├── new
│ │ │ └── Example.java
...
An Example Software Update
With a proof-of-concept Example
class in examples/string2file
:
// examples/string2file/old/Example.java
import java.io.File;
public class Example {
private String file;
public Example(String path) {
file = path;
}
public String getPath() {
if (file == null)
return null;
else
return file;
}
}
The developer may feel better storing a File
object in the Example
class, yielding the new-version code:
// examples/string2file/new/Example.java
public class Example {
private File file;
public Example(String path) {
if (path != null) {
file = new File(path);
}
}
public String getPath() {
if (file == null)
return null;
else
return file.getAbsolutePath();
}
}
The interface Example.getPath()
keeps unchanged across the version. However, the internal data representation changed: the field file
underwent a type change from String
to File
.
Object Transformation
If anyone would like to dynamically update (DSU) Example
from the old version to the new version, each live Example
object in the heap’s internal data representation must be transformed by an object transformer. PASTA automatically synthesizes object transformers like the following one:
class DSUHelper {
static void transform(ExampleNew object, ExampleOld stale) {
// object: new-version object
// stale: old-version object
if (stale.file != null) {
object.file = new File(stale.file);
}
}
}
DSUHelper
will be invoked by the DSU system over each stale object in the heap, to create a consistent heap for new-version code. PASTA separately synthesizes transformer for each changed field. Please refer to our paper for more information.
Object Transformer Synthesis
PASTA takes two versions of a program and a target field as input, and automatically produces potentially useful field transformers for the target field.
Preprocess the Source Code
PASTA works in two steps. First, the distiller
analyzes the program to extract gadgets (code snippets with variable holes). These gadgets are used as basic components of a transformer. Given an existing code snippet (either old/new version), variables and constants inside the code snippet may be replaced by placeholders. Later, gadgets will be (re)assembled with placeholders being filled, to yield object transformers.
An example of extracted gadget:
file = new File(path); -- distill -> [1] = new File([2]);
Suppose you’re now at the examples
directory. We prepared the scripts for passing correct arguments to the PASTA preprocessor:
$ bash distiller.sh string2file
All preprocessing results go to string2file/.pasta
. A concerned reader may particularly interested in the .pasta/gadget/
directory, which contains extracted gadgets as JSON files, on the per-class basis (changed classes will be gadget-extracted for both old/new versions).
Perform the Synthesis
Once the project is preprocessed, one can call the bash script to conduct the actual synthesis. Again suppose you’re at the examples
directory:
$ bash synthesizer.sh string2file
Generated transformers will be printed in terminal, ranked by their costs (lower the better). In our experiments, these outputs (field transformers) are redirected to the PASTA’s built-in verifier for unit-test and filtering out test-failing transformers.
Each produced field transformer looks like the following, which can be copy-pasted into DSUHelper
:
/* -------- Transformer #5 (cost = 9.0909) -------- */
Example _var0_ = _stale_;
java.io.File _object_ = null;
java.lang.String _var2_ = _var0_.file; /* g: String [] = [].file; */
if (!(_var0_.file == null)) { /* g: !([].file == null) */
_object_ = new java.io.File(_var2_); /* g: [] = new File([]); */
}
Variables both start and end with an underscore (_
) are transformer-specific.
Particularly, _stale_
is the stale object, _object_
is the new-version object, and others are temporary variables.
This transformer (#5) is a semantically correct field transformer for file
in DSU of Example
. We can rewrite it by assigning variables meaningful names and doing some simplifications:
class DSUHelper {
static void transform(ExampleNew object, ExampleOld stale) {
String file = stale.file;
if (stale.file != null) {
object.file = new File(file);
}
}
}
On the other hand, the highest ranked (Transformer #1) is not correct:
/* -------- Transformer #1 (cost = 2.3684) -------- */
Example _var0_ = _stale_;
java.io.File _object_ = null;
java.lang.String _var2_ = _var0_.file; /* g: String [] = [].file; */
_object_ = new java.io.File(_var2_); /* g: [] = new File([]); */
For most of the cases, this transformer works. However, if stale.file
is null
, this transformer will crash the system upon a NullPointerException
. Such kind of failure is expected to be captured by a test case, with this incorrect transformer being filtered out.
Notes
Exactly the same preprocess-synthesis procedure is done for all experimental subjects. Please refer to our reproduction docker image for more information.