Skip to content

Commit

Permalink
merge cli into master
Browse files Browse the repository at this point in the history
  • Loading branch information
rbonifacio authored and rbonifacio committed Sep 13, 2019
2 parents 63b3ba0 + 9c0af14 commit 0c5d09c
Show file tree
Hide file tree
Showing 5 changed files with 633 additions and 82 deletions.
84 changes: 66 additions & 18 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,69 @@
# Semantic Merge Conflicts Soot Library

This project aims to implement a library of soot analysis to detect semantic merge conflicts.
Current supported algorithms:

* Intraprocedural use-def conflicts: This algorithm identifies conflicts
that occur when a contribution from "left" defines a variable
that a contribution of "right" uses. Its implementation is mostly based on
intraprocedural dataflow analysis.
* Reachability conflicts: This algorithm identifies conflicts that occur
when there is a interprocedural flow path from a left statement contribution from
to a right statement contribution.
# Semantic Merge Conflicts Soot Library

This project aims to implement a library of soot analysis to detect semantic merge conflicts.
Current supported algorithms:

* Intraprocedural use-def conflicts: This algorithm identifies conflicts
that occur when a contribution from "left" defines a variable
that a contribution of "right" uses. Its implementation is mostly based on
intraprocedural dataflow analysis.

* Reachability conflicts: This algorithm identifies conflicts that occur
when there is a interprocedural flow path from a left statement contribution from
to a right statement contribution



## TODO

* when computing the number of conflicts, consider that multiple JIMPLE statements might appear in the same line.
For this reason, we would rather use a data structure to avoid reporting multiple conflicts from the same pairs
of source-sink lines.
* when computing the number of conflicts, consider that multiple JIMPLE statements might appear in the same line
For this reason, we would rather use a data structure to avoid reporting multiple conflicts from the same pairs
of source-sink lines.

## Usage

### Build

To build the project, you will need Maven and Java 8 (or higher). Run the command below:

```SHELL
mvn clean install -DskipTests
```

### Execute

To execute, you need a Java project to analyze and a set of lines changed in this project.
This set could be in a .csv file, but if the project uses Git, you can use the DiffClass Analyzer project.
In this case, tyou need the hash of a merge commit to get the set of modified lines. See the CLI parameters below:


#### -cp
The path of the folder containing the .class files of the project to be analyzed.
Remember, you need to compile the project under analysis. This parameters is required.

#### -csv
The input csv files with the list of changes. You must provider either this parameter or the ```-commit``` parameter.

#### -commit
the merge commit hash. See the comment above regarding the -csv parameter.

#### -mode
Analysis mode: dataflow or reachability.

#### -repo
The folder path or URL of the git project under analysis.

Usage example of a Java project, with a .csv file.

```SHELL
mvn exec:java -Dexec.mainClass="br.unb.cic.analysis.Main" \
-Dexec.args="-csv /path/of/csv/file.csv -cp /path/of/class/files/folder"
```

Usage example with of a local Java git project, without a .csv file.

```SHELL
mvn exec:java -Dexec.mainClass="br.unb.cic.analysis.Main" \
-Dexec.args="-repo /path/of/project -commit <hash-of-merge-commit> -cp /path/of/class/files/folder"
```

24 changes: 21 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-core</artifactId>
<version>1.3.0</version>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
Expand All @@ -24,7 +24,12 @@
<dependency>
<groupId>ca.mcgill.sable</groupId>
<artifactId>soot</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.8.0.201706111038-r</version>
</dependency>
</dependencies>
<build>
Expand All @@ -38,6 +43,19 @@
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>br.unb.cic.analysis.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
</project>
179 changes: 119 additions & 60 deletions src/main/java/br/unb/cic/analysis/Main.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package br.unb.cic.analysis;

import br.unb.cic.analysis.df.DataFlowAnalysis;
import br.unb.cic.analysis.io.DefaultReader;
import br.unb.cic.analysis.io.MergeConflictReader;
import br.unb.cic.analysis.model.Statement;
import br.unb.cic.analysis.reachability.ReachabilityAnalysis;
import java.util.*;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.Map.Entry;

import org.apache.commons.cli.*;

import soot.Body;
import soot.BodyTransformer;
import soot.PackManager;
import soot.Transform;
import soot.toolkits.graph.ExceptionalUnitGraph;

import java.util.*;
import java.util.stream.Collectors;
import br.unb.cic.analysis.df.DataFlowAnalysis;
import br.unb.cic.analysis.io.DefaultReader;
import br.unb.cic.analysis.io.MergeConflictReader;
import br.unb.cic.analysis.model.Statement;
import br.unb.cic.analysis.reachability.ReachabilityAnalysis;
import br.unb.cic.diffclass.DiffClass;

public class Main {

Expand All @@ -30,13 +35,21 @@ public static void main(String args[]) {

CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(m.options, args);

String mode = "data-flow";

String mode = "dataflow";

if (cmd.hasOption("mode")) {
mode = cmd.getOptionValue("mode");
}
m.loadDefinition(cmd.getOptionValue("csv"));
m.runAnalysis(cmd.getOptionValue("cp"), mode);
if (cmd.hasOption("repo") && cmd.hasOption("commit")) {
DiffClass module = new DiffClass();
module.getGitRepository(cmd.getOptionValue("repo"));
module.diffAnalysis(cmd.getOptionValue("commit"));
m.loadDefinitionFromDiffAnalysis(module);
} else {
m.loadDefinition(cmd.getOptionValue("csv"));
}
m.runAnalysis(mode, cmd.getOptionValue("cp"), m.conflicts);
m.conflicts.stream().forEach(c -> System.out.println(c));
}
catch(ParseException e) {
Expand All @@ -49,73 +62,80 @@ public static void main(String args[]) {
}
}

private void runAnalysis(String classPath, String mode) {
switch (mode) {
case "data-flow": {
runDataflowAnalyis(classPath);
break;
}
case "reachability" : {
runReachabilityAnalysis(classPath);
break;
}
default: {
System.err.println("Invalid option for the analysis mode.");
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "java Main", options );
System.exit(-1);
}
}
}

/*
* run the analysis in the dataflow mode
*/
private void runDataflowAnalyis(String classPath) {
PackManager.v().getPack("jtp").add(
new Transform("jtp.df", new BodyTransformer() {
@Override
protected void internalTransform(Body body, String phaseName, Map<String, String> options) {
analysis = new DataFlowAnalysis(new ExceptionalUnitGraph(body), definition);
}
}));
soot.Main.main(new String[]{"-w", "-allow-phantom-refs", "-f", "J", "-keep-line-number", "-cp",
classPath, targetClasses.stream().collect(Collectors.joining(" "))});
conflicts.addAll(analysis.getConflicts().stream().map(c -> c.toString()).collect(Collectors.toList()));
}

/*
* run the analysis in the reachability mode.
*/
private void runReachabilityAnalysis(String classPath) {
ReachabilityAnalysis analysis = new ReachabilityAnalysis(definition);
PackManager.v().getPack("wjtp").add(new Transform("wjtp.analysis", analysis));
soot.options.Options.v().setPhaseOption("cg.spark", "on");
soot.options.Options.v().setPhaseOption("cg.spark", "verbose:true");
soot.Main.main(new String[]{"-w", "-allow-phantom-refs", "-f", "J", "-keep-line-number", "-cp",
classPath, targetClasses.stream().collect(Collectors.joining(" "))});
}


private void createOptions() {
options = new Options();
Option classPathOption = Option.builder("cp").argName("class-path")
.required().hasArg().desc("the classpath used in the analysis")
.build();

Option inputFileOption = Option.builder("csv").argName("csv")
.required().hasArg().desc("the input csv files with the list of changes")
.hasArg().desc("the input csv files with the list of changes")
.build();

Option analysisOption = Option.builder("mode").argName("mode")
.hasArg().desc("analysis mode [data-flow, reachability]")
.build();

Option repoOption = Option.builder("repo").argName("repo")
.hasArg().desc("the path or url of git repository")
.build();

Option commitOption = Option.builder("commit").argName("commit")
.hasArg().desc("the commit merge to analysis")
.build();

options.addOption(classPathOption);
options.addOption(inputFileOption);
options.addOption(analysisOption);
options.addOption(repoOption);
options.addOption(commitOption);
}


private void runAnalysis(String mode, String classpath, List<String> conflicts) {
switch(mode) {
case "dataflow": runDataFlowAnalysis(classpath, conflicts);
case "reachability": runReachabilityAnalysis(classpath, conflicts);
default: {
System.out.println("Error: " + "invalid mode " + mode);
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "java Main", options );
System.exit(-1);
}
}
}

private void runDataFlowAnalysis(String classpath, List<String> conflicts) {
PackManager.v().getPack("jtp").add(
new Transform("jtp.df", new BodyTransformer() {
@Override
protected void internalTransform(Body body, String phaseName, Map<String, String> options) {
analysis = new DataFlowAnalysis(new ExceptionalUnitGraph(body), definition);
}
}));
soot.Main.main(new String[] {"-w", "-allow-phantom-refs", "-f", "J", "-keep-line-number", "-cp"
, classpath, targetClasses.stream().collect(Collectors.joining(" "))});
conflicts.addAll(analysis.getConflicts().stream().map(c -> c.toString()).collect(Collectors.toList()));
}

/*
* TODO: run some test cases regarding the reachability
* mode.
*/
private void runReachabilityAnalysis(String classpath, List<String> conflicts) {
ReachabilityAnalysis analysis = new ReachabilityAnalysis(definition);

PackManager.v().getPack("wjtp").add(new Transform("wjtp.analysis", analysis));
soot.options.Options.v().setPhaseOption("cg.spark", "on");
soot.options.Options.v().setPhaseOption("cg.spark", "verbose:true");
soot.Main.main(new String[]{"-w", "-allow-phantom-refs", "-f", "J", "-keep-line-number", "-cp",
classpath, targetClasses.stream().collect(Collectors.joining(" "))});

conflicts.addAll(analysis.getConflicts().stream().map(c -> c.toString()).collect(Collectors.toList()));

}


private void loadDefinition(String filePath) throws Exception {
MergeConflictReader reader = new DefaultReader(filePath);
List<ClassChangeDefinition> changes = reader.read();
Expand Down Expand Up @@ -154,4 +174,43 @@ private void addChange(Map<String, List<Integer>> map, ClassChangeDefinition cha
map.put(change.getClassName(), lines);
}
}

private void loadDefinitionFromDiffAnalysis(DiffClass module) throws Exception {
ArrayList<Entry<String, Integer>> sourceClasses = module.getSourceModifiedClasses();
ArrayList<Entry<String, Integer>> sinkClasses = module.getSinkModifiedClasses();
Map<String, List<Integer>> sourceDefs = new HashMap<>();
Map<String, List<Integer>> sinkDefs = new HashMap<>();
targetClasses = new HashSet<>();
for (Entry<String, Integer> change : sourceClasses) {
addChangeFromDiffAnalysis(sourceDefs, change);
targetClasses.add(change.getKey());
}
for (Entry<String, Integer> change : sinkClasses) {
addChangeFromDiffAnalysis(sinkDefs, change);
targetClasses.add(change.getKey());
}

definition = new AbstractMergeConflictDefinition() {
@Override
protected Map<String, List<Integer>> sourceDefinitions() {
return sourceDefs;
}

@Override
protected Map<String, List<Integer>> sinkDefinitions() {
return sinkDefs;
}
};
}

private void addChangeFromDiffAnalysis(Map<String, List<Integer>> map, Entry<String, Integer> change) {
if(map.containsKey(change.getKey())) {
map.get(change.getKey()).add(change.getValue());
}
else {
List<Integer> lines = new ArrayList<>();
lines.add(change.getValue());
map.put(change.getKey(), lines);
}
}
}
1 change: 0 additions & 1 deletion src/main/java/br/unb/cic/analysis/io/DefaultReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import br.unb.cic.analysis.ClassChangeDefinition;
import br.unb.cic.analysis.model.Statement;
import com.sun.scenario.effect.Merge;

import java.nio.file.Files;
import java.nio.file.Paths;
Expand Down
Loading

0 comments on commit 0c5d09c

Please sign in to comment.