Slicer-Examples is a project with examples to show usage of Slicer project.
Slice arbitary java bytecodes given one or multiple indexes which should be kept in the slice.
- Generates executable java slices
- Plotting graphs using dot (see below)
- Graph generation using JGraphT
- Control Flow
- Control Dependency
- Block Dependency
- Data Dependency
- Argument Dependency
- Class Object Dependency
- Dominance (Post, Strict, Strict-Post, Immediate, Immediate-Post)
- Requires at least java language level 11
- Gradle build system
- WALA + WALA Shrike library with support for double-sized stack element processing (see WALA #753 here)
- Graphviz dot installed for potting graphs
To slice a method, the packaged jar has to be accessible. Let's slice the method ClientSidePreparedStatement.getMetaData()
from mariadb java connector (Version 2.7.2 at the time of writing). The interested slice criterion is for example line 4 here (the first if-statement).
Decompiled ClientSidePreparedStatement.getMetaData()
source:
(1) public ResultSetMetaData getMetaData() throws SQLException {
(2) this.checkClose();
(3) ResultSet rs = this.getResultSet();
(4) if (rs != null) {
(5) return rs.getMetaData();
(6) } else {
(7) if (this.resultSetMetaData == null) {
(8) this.loadParametersData();
(9) }
(10) return this.resultSetMetaData;
(11) }
(12) }
The resulting control flow graph is shown below. More graphs can be found in GraphExample.java
Lets slice the instruction with index 7 in the control flow graph (the if-condition at line 4 in the java source above). The SliceResult can be obtained using:
// Create a Slicer object with jar file and signature
Slicer slicer = new Slicer();
slicer.setInputJar("path/to/mariadb-java-client-2.7.2.jar");
slicer.setMethodSignature("org.mariadb.jdbc.ClientSidePreparedStatement.getMetaData()Ljava/sql/ResultSetMetaData;");
// Set one or more slicing criterions
slicer.setInstructionIndexes(Set.of(7));
System.out.println(slicer.getSliceResult());
The output is:
org.mariadb.jdbc.ClientSidePreparedStatement.getMetaData()Ljava/sql/ResultSetMetaData;
InstructionIndexes: [7]
InstructionIndexesToKeep: [17, 2, 18, 3, 19, 4, 5, 6, 7, 8, 9, 10]
instructionIndexesToIgnore: [11]
instructionPopMap: {}
VarIndexesToRenumber: {}
=== Slice ===
0: LocalLoad(Ljava/lang/Object;,0)
1: Invoke(VIRTUAL,Lorg/mariadb/jdbc/ClientSidePreparedStatement;,getResultSet,()Ljava/sql/ResultSet;)
2: LocalStore(Ljava/lang/Object;,1)
3: LocalLoad(Ljava/lang/Object;,1)
4: Constant(L;,null)
5: ConditionalBranch(Ljava/lang/Object;,eq,11)
6: LocalLoad(Ljava/lang/Object;,1)
7: Invoke(INTERFACE,Ljava/sql/ResultSet;,getMetaData,()Ljava/sql/ResultSetMetaData;)
8: Return(Ljava/lang/Object;)
9: Invoke(STATIC,Lde/uniks/vs/methodresourceprediction/slicer/export/Nothing;,doNothing,()V)
10: LocalLoad(Ljava/lang/Object;,0)
11: Get(Ljava/sql/ResultSetMetaData;,NONSTATIC,Lorg/mariadb/jdbc/ClientSidePreparedStatement;,resultSetMetaData)
12: Return(Ljava/lang/Object;)
Finally, the decompiled bytecode is:
public ResultSetMetaData getMetaData() throws SQLException {
ResultSet rs = this.getResultSet();
return rs != null ? rs.getMetaData() : this.resultSetMetaData;
}
The control flow graph is as follows:
- Bytecode instructions 0 and 1 were sliced out since there is no dependency to instruction 7 and the method return value
- Instruction 2 to 7 are copied unchanged (slice indexes 0 to 5) due to dependencies
- The instructions 8 to 10 are kept because there is a return statement at index 10 (slice indexes 6 to 8). Hence, the positive if-case is kept.
- Instructions between index 11 and 16 are sliced out (no dependency)
- The return statement at the end of the method (index 19) results in keeping instruction 10 to 12
Wait, what about the slice instruction at index 9? Where does it come from?
=> This instruction is added to deal as the jump target for the if-condition at index 5. The condition target (index 11) is not updated right here but during creation of the executable slice which is 9 afterwards.
See ExecutableSliceExample.java
The project uses a changed version of WALA-Shrike (see libs/com.ibm.wala.shrike-X.Y.Z-SNAPSHOT.jar) which supports processing double-sized stack elements (see WALA #753 and WALA-PR #775). If you do not need the support for double-sized stack processing, the unmodified WALA-Shrike can be used. You just have to rebuild the dependent projects (slicer, slicer-export, utils, ...).
Building on top of original WALA-Shrike currently means no support for primitive datatypes double
and long
! Please keep that in mind if your experience stack over-/underflow errors.