diff --git a/src/main/java/br/unb/cic/analysis/df/DataFlowAbstraction.java b/src/main/java/br/unb/cic/analysis/df/DataFlowAbstraction.java index 0b24dd12..c50c1cf3 100644 --- a/src/main/java/br/unb/cic/analysis/df/DataFlowAbstraction.java +++ b/src/main/java/br/unb/cic/analysis/df/DataFlowAbstraction.java @@ -4,9 +4,10 @@ import soot.Local; import soot.Value; import soot.jimple.InstanceFieldRef; +import soot.jimple.InvokeStmt; import soot.jimple.StaticFieldRef; -import java.util.Objects; +import java.util.*; /** * Information wee keep while traversing @@ -17,8 +18,14 @@ public class DataFlowAbstraction { private Local local; private InstanceFieldRef localField; private StaticFieldRef localStaticRef; - private Value value; + private InvokeStmt methodCall; private Statement stmt; + private Value value; + + public DataFlowAbstraction(InvokeStmt methodCall, Statement stmt){ + this.methodCall = methodCall; + this.stmt = stmt; + } public DataFlowAbstraction(Value value, Statement stmt) { this.value = value; @@ -40,6 +47,14 @@ public DataFlowAbstraction(StaticFieldRef localStaticRef, Statement stmt) { this.stmt = stmt; } + public InvokeStmt getMethodCall() { + return methodCall; + } + + public void setMethodCall(InvokeStmt methodCall) { + this.methodCall = methodCall; + } + public Local getLocal() { return local; } @@ -48,16 +63,23 @@ public StaticFieldRef getLocalStaticRef() { return localStaticRef; } + public Value getValue() { + return value; + } + + public void setValue(Value value) { + this.value = value; + } + public InstanceFieldRef getFieldRef() { return localField; } - public Statement getStmt() { return stmt; } public Boolean containsLeftStatement(){ - return getStmt().getType().equals(Statement.Type.SOURCE); + return getStmt().getType().equals(Statement.Type.SOURCE); } public Boolean containsRightStatement(){ @@ -77,12 +99,4 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(local, stmt); } - - public Value getValue() { - return this.value; - } - - public void setValue(Value value) { - this.value = value; - } } diff --git a/src/main/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysis.java b/src/main/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysis.java index 8529c2d9..77a1d310 100644 --- a/src/main/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysis.java +++ b/src/main/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysis.java @@ -4,9 +4,7 @@ import br.unb.cic.analysis.model.Conflict; import br.unb.cic.analysis.model.Statement; import soot.*; -import soot.jimple.ArrayRef; -import soot.jimple.InstanceFieldRef; -import soot.jimple.StaticFieldRef; +import soot.jimple.*; import soot.toolkits.scalar.ArraySparseSet; import soot.toolkits.scalar.FlowSet; @@ -78,14 +76,17 @@ protected FlowSet gen(Unit u, FlowSet if (isLeftStatement(u) || isRightStatement(u)) { Statement stmt = getStatementAssociatedWithUnit(u); + if (u instanceof InvokeStmt){ + res.add(new DataFlowAbstraction((InvokeStmt) u, stmt)); + } u.getDefBoxes().forEach(valueBox -> { - if (valueBox.getValue() instanceof Local) { + if (valueBox.getValue() instanceof Local){ res.add(new DataFlowAbstraction((Local) valueBox.getValue(), stmt)); - } else if (valueBox.getValue() instanceof ArrayRef) { + } else if (valueBox.getValue() instanceof ArrayRef){ res.add(new DataFlowAbstraction(getArrayName(valueBox), stmt)); - } else if (valueBox.getValue() instanceof StaticFieldRef) { + } else if (valueBox.getValue() instanceof StaticFieldRef) { res.add(new DataFlowAbstraction((StaticFieldRef) valueBox.getValue(), stmt)); - } else if (valueBox.getValue() instanceof InstanceFieldRef) { + } else if (valueBox.getValue() instanceof InstanceFieldRef){ res.add(new DataFlowAbstraction(getFieldName(valueBox), stmt)); } }); @@ -125,10 +126,18 @@ protected void detectConflict(FlowSet in, Unit u) { private void checkConflicts(Unit u, List potentialConflictingAssignments) { for (DataFlowAbstraction dataFlowAbstraction : potentialConflictingAssignments) { if (abstractionVariableIsInIUnitDefBoxes(dataFlowAbstraction, u)) { - Conflict c = new Conflict(dataFlowAbstraction.getStmt(), findStatement(u)); - Collector.instance().addConflict(c); + reportConflict(dataFlowAbstraction.getStmt(), u); } } + //Report conflict when changing the same return + if (u instanceof ReturnStmt && isLeftStatement(u) && isRightStatement(u)){ + reportConflict(findStatement(u), u); + } + } + + private void reportConflict(Statement left, Unit right){ + Conflict c = new Conflict(left, findStatement(right)); + Collector.instance().addConflict(c); } /* @@ -138,17 +147,58 @@ private void checkConflicts(Unit u, List potentialConflicti private boolean abstractionVariableIsInIUnitDefBoxes(DataFlowAbstraction dataFlowAbstraction, Unit u) { for (ValueBox valueBox : u.getDefBoxes()) { Object value = valueBox.getValue(); - if (value instanceof InstanceFieldRef && dataFlowAbstraction.getFieldRef() != null) { + if (value instanceof InstanceFieldRef && dataFlowAbstraction.getFieldRef()!=null) { return dataFlowAbstraction.getFieldRef().toString().equals(getFieldsChain(value.toString())); } else if (value instanceof ArrayRef && value.toString().contains("$stack")) { // If contain $stack, contain a array chain return getVarNameFromAbstraction(dataFlowAbstraction).equals(getArrayChain(valueBox)); - } else { + } else{ return getVarNameFromAbstraction(dataFlowAbstraction).equals(getVarNameFromValueBox(valueBox)); } } + if (u instanceof InvokeStmt){ + return compareMethodAndObjectName(dataFlowAbstraction, u); + } + return false; + } + + /* + * Compares whether left and right called the same object and method + */ + private boolean compareMethodAndObjectName(DataFlowAbstraction dataFlowAbstraction, Unit u){ + if ( !(dataFlowAbstraction.getStmt().getUnit() instanceof InvokeStmt)){ + return false; + } + String objectNameFromUnit = getObjectName(u); + String methodStatementFromUnit = getMethodName(u); + + String objectNameFromAbstraction = getObjectName(dataFlowAbstraction.getStmt().getUnit()); + String methodStatementFromAbstraction = getMethodName(dataFlowAbstraction.getStmt().getUnit()); + + String methodName = (((InvokeStmt) u).getInvokeExpr()).getMethodRef().getName(); + + if ((objectNameFromUnit+""+methodStatementFromUnit).equals(objectNameFromAbstraction+""+methodStatementFromAbstraction) && !methodName.startsWith("get")){ + return true; + } return false; } + /* + * Returns a String containing the name of the object given a Unit + */ + private String getObjectName(Unit u){ + if (u instanceof VirtualInvokeExpr){ + return ((VirtualInvokeExpr)((InvokeStmt) u).getInvokeExpr()).getBase().toString(); + } + return ""; + } + + /* + * Returns a String containing the name of the method called given a Unit + */ + private String getMethodName(Unit u){ + return (((InvokeStmt) u).getInvokeExpr()).getMethodRef().toString(); + } + /* * Returns a String containing the name of the variable given a DataFlowAbstraction */ @@ -212,11 +262,11 @@ private Value getArrayRefName(ArrayRef arrayRef) { /* * If it's a InstanceField, returns your complete name from the hashMapInstanceField */ - private InstanceFieldRef getFieldName(ValueBox valueBox) { + private InstanceFieldRef getFieldName(ValueBox valueBox){ InstanceFieldRef fieldValue = (InstanceFieldRef) valueBox.getValue(); if (fieldValue.toString().contains("$stack")) { String chainName = getFieldsChain(valueBox.getValue().toString()); //return the complete chain with the FieldRef - ((Local) fieldValue.getBase()).setName(chainName.replace("." + fieldValue.getField().toString(), "")); //remove the double FieldRef from jInstanceFieldValue + ((Local) fieldValue.getBase()).setName(chainName.replace("."+fieldValue.getField().toString(), "")); //remove the double FieldRef from jInstanceFieldValue } return fieldValue; } @@ -276,23 +326,19 @@ private String getArrayChain(ValueBox valueBox){ * Class object = new Class(); * object.b.a.a = object.b.b.a + 3; * Jimple: - * $stack2 = new Class; * object = $stack2; - * $stack3 = object.b; * $stack8 = $stack3.a; * $stack7 = $stack6 + 3; - * $stack8.a = $stack7; //$stack8.a is the nextKey of the call object.b.a.a - * The result is: * $stack8.a -> $stack8 = $stack3.a; -> $stack3 = object.b; -> object.b; (STOP) -> Return object chain * InitialKey($stack8.a) -> nextKey ($stack8) -> nextKey ($stack3) -> object.b has no nextKey (STOP) -> Return object chain -> Return: object.b.a.a */ private String getFieldsChain(String nextKey){ List> auxValuesHashMap = new ArrayList<>(); - auxValuesHashMap.addAll(getHashMapJInstanceField()); + auxValuesHashMap.addAll(getHashMapInstanceField()); //If nextKey not contain $stack is because simple key if (!(nextKey.contains("$stack"))){ @@ -345,7 +391,7 @@ public void generateFieldDictionary(Unit u){ for (ValueBox valueBox: u.getUseBoxes()) { if (valueBox.getValue() instanceof InstanceFieldRef) { generateClassFieldDictionary(getStatementAssociatedWithUnit(u)); - } else if (valueBox.getValue() instanceof StaticFieldRef) { + }else if (valueBox.getValue() instanceof StaticFieldRef) { generateStaticRefDictionary(u, valueBox); } } diff --git a/src/main/java/br/unb/cic/analysis/df/ReachDefinitionAnalysis.java b/src/main/java/br/unb/cic/analysis/df/ReachDefinitionAnalysis.java index 18e4dd39..cdc51223 100644 --- a/src/main/java/br/unb/cic/analysis/df/ReachDefinitionAnalysis.java +++ b/src/main/java/br/unb/cic/analysis/df/ReachDefinitionAnalysis.java @@ -11,6 +11,7 @@ import soot.jimple.ArrayRef; import soot.jimple.InstanceFieldRef; import soot.jimple.StaticFieldRef; +import soot.jimple.internal.JInstanceFieldRef; import soot.toolkits.graph.ExceptionalUnitGraph; import soot.toolkits.scalar.ArraySparseSet; import soot.toolkits.scalar.FlowSet; @@ -48,7 +49,7 @@ public ReachDefinitionAnalysis(Body methodBody, AbstractMergeConflictDefinition definition.loadSinkStatements(); doAnalysis(); } - + /** * Runs the algorithm analysis at a given statement (Unit d). Here we * manipulate and compute an out set from the income set in (foward analysis). @@ -175,28 +176,28 @@ protected boolean isSourceStatement(Unit d) { protected boolean isSinkStatement(Unit d) { return definition.getSinkStatements().stream().map(s -> s.getUnit()).collect(Collectors.toList()).contains(d); - } + } - public void clear() { - Collector.instance().clear(); - } + public void clear() { + Collector.instance().clear(); + } - public Set getConflicts() { - return Collector.instance().getConflicts(); - } + public Set getConflicts() { + return Collector.instance().getConflicts(); + } - public Set> getHashMapJInstanceField() { - return Collector.instance().getHashInstanceField(); - } + public Set> getHashMapInstanceField() { + return Collector.instance().getHashInstanceField(); + } - public Set> getHashMapStatic() { - return Collector.instance().getHashStaticField(); - } + public Set> getHashMapStatic() { + return Collector.instance().getHashStaticField(); + } - protected List getUseVariables(Unit u) { - return u.getUseBoxes().stream() - .map(box -> box.getValue()) - .filter(v -> v instanceof Local) + protected List getUseVariables(Unit u) { + return u.getUseBoxes().stream() + .map(box -> box.getValue()) + .filter(v -> v instanceof Local) .map(v -> (Local)v) .collect(Collectors.toList()); } @@ -204,15 +205,15 @@ protected List getUseVariables(Unit u) { protected List getDefVariables(Unit u) { List localDefs = new ArrayList<>(); for (ValueBox v : u.getDefBoxes()) { - if (v.getValue() instanceof Local) { - localDefs.add((Local) v.getValue()); - } else if (v.getValue() instanceof ArrayRef) { - ArrayRef ref = (ArrayRef) v.getValue(); - localDefs.add((Local) ref.getBaseBox().getValue()); - } else if (v.getValue() instanceof InstanceFieldRef) { - InstanceFieldRef ref = (InstanceFieldRef) v.getValue(); - localDefs.add((Local) ref.getBaseBox().getValue()); - } + if (v.getValue() instanceof Local) { + localDefs.add((Local) v.getValue()); + } else if (v.getValue() instanceof ArrayRef) { + ArrayRef ref = (ArrayRef) v.getValue(); + localDefs.add((Local) ref.getBaseBox().getValue()); + } else if (v.getValue() instanceof InstanceFieldRef) { + InstanceFieldRef ref = (InstanceFieldRef) v.getValue(); + localDefs.add((Local) ref.getBaseBox().getValue()); + } } return localDefs; } diff --git a/src/test/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysisMethodCallConflictTest.java b/src/test/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysisMethodCallConflictTest.java new file mode 100644 index 00000000..5d28c6d4 --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysisMethodCallConflictTest.java @@ -0,0 +1,63 @@ +package br.unb.cic.analysis.df; + +import br.unb.cic.analysis.AbstractMergeConflictDefinition; +import br.unb.cic.analysis.SootWrapper; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import soot.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OverridingAssignmentAnalysisMethodCallConflictTest { + + private OverridingAssignmentAnalysis analysis; + + @Before + public void configure() { + G.reset(); + Collector.instance().clear(); + + AbstractMergeConflictDefinition definition = new AbstractMergeConflictDefinition() { + @Override + protected Map> sourceDefinitions() { + Map> res = new HashMap<>(); + List lines = new ArrayList<>(); + lines.add(7); //left + lines.add(10); //left + res.put("br.unb.cic.analysis.samples.OverridingAssignmentMethodCallSample", lines); + return res; + } + + @Override + protected Map> sinkDefinitions() { + Map> res = new HashMap<>(); + List lines = new ArrayList<>(); + lines.add(9); //right + res.put("br.unb.cic.analysis.samples.OverridingAssignmentMethodCallSample", lines); + return res; + } + }; + + PackManager.v().getPack("jtp").add( + new Transform("jtp.oneConflict", new BodyTransformer() { + @Override + protected void internalTransform(Body body, String phaseName, Map options) { + analysis = new OverridingAssignmentAnalysis(body, definition); + } + })); + String cp = "target/test-classes"; + String targetClass = "br.unb.cic.analysis.samples.OverridingAssignmentMethodCallSample"; + PhaseOptions.v().setPhaseOption("jb", "use-original-names:true"); + + SootWrapper.builder().withClassPath(cp).addClass(targetClass).build().execute(); + } + + @Test + public void testDataFlowAnalysisExpectingOneConflict() { + Assert.assertEquals(1, analysis.getConflicts().size()); + } +} \ No newline at end of file diff --git a/src/test/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysisSameReturnConflictTest.java b/src/test/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysisSameReturnConflictTest.java new file mode 100644 index 00000000..794d0e5b --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/df/OverridingAssignmentAnalysisSameReturnConflictTest.java @@ -0,0 +1,62 @@ +package br.unb.cic.analysis.df; + +import br.unb.cic.analysis.AbstractMergeConflictDefinition; +import br.unb.cic.analysis.SootWrapper; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import soot.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OverridingAssignmentAnalysisSameReturnConflictTest { + + private OverridingAssignmentAnalysis analysis; + + @Before + public void configure() { + G.reset(); + Collector.instance().clear(); + + AbstractMergeConflictDefinition definition = new AbstractMergeConflictDefinition() { + @Override + protected Map> sourceDefinitions() { + Map> res = new HashMap<>(); + List lines = new ArrayList<>(); + lines.add(8); //left + res.put("br.unb.cic.analysis.samples.OverridingAssignmentSameReturnSample", lines); + return res; + } + + @Override + protected Map> sinkDefinitions() { + Map> res = new HashMap<>(); + List lines = new ArrayList<>(); + lines.add(8); //right + res.put("br.unb.cic.analysis.samples.OverridingAssignmentSameReturnSample", lines); + return res; + } + }; + + PackManager.v().getPack("jtp").add( + new Transform("jtp.oneConflict", new BodyTransformer() { + @Override + protected void internalTransform(Body body, String phaseName, Map options) { + analysis = new OverridingAssignmentAnalysis(body, definition); + } + })); + String cp = "target/test-classes"; + String targetClass = "br.unb.cic.analysis.samples.OverridingAssignmentSameReturnSample"; + PhaseOptions.v().setPhaseOption("jb", "use-original-names:true"); + + SootWrapper.builder().withClassPath(cp).addClass(targetClass).build().execute(); + } + + @Test + public void testDataFlowAnalysisExpectingOneConflict() { + Assert.assertEquals(1, analysis.getConflicts().size()); + } +} \ No newline at end of file diff --git a/src/test/java/br/unb/cic/analysis/samples/OverridingAssignmentMethodCallSample.java b/src/test/java/br/unb/cic/analysis/samples/OverridingAssignmentMethodCallSample.java new file mode 100644 index 00000000..2a5ba7b7 --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/samples/OverridingAssignmentMethodCallSample.java @@ -0,0 +1,16 @@ +package br.unb.cic.analysis.samples; + +public class OverridingAssignmentMethodCallSample { + private int x; + + public void method(OverridingAssignmentMethodCallSample obj) { + obj.m(3); //left + obj.m(43); + obj.m(7); //right + obj.m(87); //left + } + + public void m(int x){ + this.x = x; + } +} diff --git a/src/test/java/br/unb/cic/analysis/samples/OverridingAssignmentSameReturnSample.java b/src/test/java/br/unb/cic/analysis/samples/OverridingAssignmentSameReturnSample.java new file mode 100644 index 00000000..2b61eea6 --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/samples/OverridingAssignmentSameReturnSample.java @@ -0,0 +1,10 @@ +package br.unb.cic.analysis.samples; + +public class OverridingAssignmentSameReturnSample { + + public int m(){ + int x=3, y=7; + + return x+y; //left, right + } +} \ No newline at end of file