diff --git a/src/org/ntlab/deltaViewer/CollaborationAliasCollector.java b/src/org/ntlab/deltaViewer/CollaborationAliasCollector.java index c1be68f..dad7fd1 100644 --- a/src/org/ntlab/deltaViewer/CollaborationAliasCollector.java +++ b/src/org/ntlab/deltaViewer/CollaborationAliasCollector.java @@ -1,14 +1,22 @@ package org.ntlab.deltaViewer; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import org.ntlab.deltaExtractor.Alias; import org.ntlab.deltaExtractor.IAliasCollector; import org.ntlab.featureExtractor.Extract; import org.ntlab.trace.MethodExecution; +import org.ntlab.trace.MethodInvocation; +import org.ntlab.trace.Statement; import org.ntlab.trace.TracePoint; /** @@ -41,26 +49,51 @@ * Merge other into aliasList(this) in time stamp order. * @param other�@IAliasCollector to be merged into the aliasList. * @param extract + * @param newToOldMethodExecMap */ - public void merge(IAliasCollector other, Extract extract) { + public void merge(IAliasCollector other, Extract extract, Map> newToOldMethodExecMap) { List otherAliasList = other.getAliasList(); otherAliasList = sortAliasListByTimeStamp(otherAliasList); + int otherIdx = 0; // Index in otherAliasList int thisIdx = 0; // Index in thisAliasList int thisOrgIdx = 0; // Index in the original thisAliasList + MethodExecution newCallee = null; while(otherIdx < otherAliasList.size()) { Alias otherAlias = otherAliasList.get(otherIdx); + if (newCallee != null && newToOldMethodExecMap.get(newCallee).contains(otherAlias.getMethodExecution())) { + Statement st = otherAlias.getOccurrencePoint().getStatement(); + if (st instanceof MethodInvocation) { + // Caution!!: The internal structure of the trace is modified. + MethodExecution calleeCallee = ((MethodInvocation) st).getCalledMethodExecution(); + calleeCallee.setCaller(newCallee, calleeCallee.getCallerStatementExecution()); + } + DummyTracePoint dummyTp = new DummyTracePoint(otherAlias.getOccurrencePoint(), newCallee); + Alias newAlias = new Alias(otherAlias.getAliasType(), otherAlias.getIndex(), otherAlias.getObjectId(), dummyTp); + otherAliasList.set(otherIdx, newAlias); + } if (thisIdx >= aliasList.size()) { if (extract != null && extract.isToConnect() && otherIdx == 0) { Alias thisPrevAlias = aliasList.get(aliasList.size() - 1); if (!otherAlias.getMethodExecution().isStatic() && otherAlias.getMethodExecution().getCallerMethodExecution() != thisPrevAlias.getMethodExecution()) { // Add a dummy alias to connect disjunct call hierarchies. (thisPrevAlias -> otherAlias) MethodExecution caller = thisPrevAlias.getMethodExecution(); -// MethodExecution callee = new DummyMethodExecution(otherAlias.getMethodExecution()); // Currently does not work because this dummy and the original one will be mixed. - MethodExecution callee = otherAlias.getMethodExecution(); + MethodExecution oldCallee = otherAlias.getMethodExecution(); + MethodExecution callee = null; + if (oldCallee instanceof DummyMethodExecution) { + callee = oldCallee; + } else { + callee = getNewMethodExec(newToOldMethodExecMap, oldCallee); + if (callee == null) { + callee = new DummyMethodExecution(oldCallee); + Set oldExecs = new HashSet<>(); + oldExecs.add(oldCallee); + newToOldMethodExecMap.put(callee, oldExecs); + newCallee = callee; + } + } callee.setCaller(caller, caller.getStatements().indexOf(thisPrevAlias.getOccurrencePoint().getStatement())); DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, thisPrevAlias.getOccurrencePoint().getStatement().getThreadNo()); - dummyInv.setTimeStamp(callee.getEntryTime()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp)); thisIdx++; @@ -72,6 +105,11 @@ } Alias thisAlias = aliasList.get(thisIdx); + Alias newThisAlias = getUpdatedAlias(thisAlias, newToOldMethodExecMap); + if (newThisAlias != null) { + aliasList.set(thisIdx, newThisAlias); + thisAlias = newThisAlias; + } if (otherAlias.equals(thisAlias)) { otherIdx++; thisIdx++; thisOrgIdx++; } else { @@ -84,11 +122,22 @@ if (!otherAlias.getMethodExecution().isStatic() && otherAlias.getMethodExecution().getCallerMethodExecution() != thisPrevAlias.getMethodExecution()) { // Add a dummy alias to connect disjunct call hierarchies. (thisPrevAlias -> otherAlias) MethodExecution caller = thisPrevAlias.getMethodExecution(); -// MethodExecution callee = new DummyMethodExecution(otherAlias.getMethodExecution()); // Currently does not work because this dummy and the original one will be mixed. - MethodExecution callee = otherAlias.getMethodExecution(); + MethodExecution oldCallee = otherAlias.getMethodExecution(); + MethodExecution callee = null; + if (oldCallee instanceof DummyMethodExecution) { + callee = oldCallee; + } else { + callee = getNewMethodExec(newToOldMethodExecMap, oldCallee); + if (callee == null) { + callee = new DummyMethodExecution(oldCallee); + Set oldExecs = new HashSet<>(); + oldExecs.add(oldCallee); + newToOldMethodExecMap.put(callee, oldExecs); + newCallee = callee; + } + } callee.setCaller(caller, caller.getStatements().indexOf(thisPrevAlias.getOccurrencePoint().getStatement())); DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, thisPrevAlias.getOccurrencePoint().getStatement().getThreadNo()); - dummyInv.setTimeStamp(callee.getEntryTime()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp)); thisIdx++; @@ -102,11 +151,22 @@ if (!thisAlias.getMethodExecution().isStatic() && thisAlias.getMethodExecution().getCallerMethodExecution() != otherPrevAlias.getMethodExecution()) { // Add a dummy alias to connect disjunct call hierarchies. (otherPrevAlias -> thisAlias) MethodExecution caller = otherPrevAlias.getMethodExecution(); -// MethodExecution callee = new DummyMethodExecution(thisAlias.getMethodExecution()); // Currently does not work because this dummy and the original one will be mixed. - MethodExecution callee = thisAlias.getMethodExecution(); + MethodExecution oldCallee = thisAlias.getMethodExecution(); + MethodExecution callee = null; + if (oldCallee instanceof DummyMethodExecution) { + callee = oldCallee; + } else { + callee = getNewMethodExec(newToOldMethodExecMap, oldCallee); + if (callee == null) { + callee = new DummyMethodExecution(oldCallee); + Set oldExecs = new HashSet<>(); + oldExecs.add(oldCallee); + newToOldMethodExecMap.put(callee, oldExecs); + newCallee = callee; + } + } callee.setCaller(caller, caller.getStatements().indexOf(otherPrevAlias.getOccurrencePoint().getStatement())); DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, otherPrevAlias.getOccurrencePoint().getStatement().getThreadNo()); - dummyInv.setTimeStamp(callee.getEntryTime()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp)); thisIdx++; @@ -130,17 +190,61 @@ if (!thisAlias.getMethodExecution().isStatic() && thisAlias.getMethodExecution().getCallerMethodExecution() != otherPrevAlias.getMethodExecution()) { // Add a dummy alias to connect disjunct call hierarchies. (otherPrevAlias -> thisAlias) MethodExecution caller = otherPrevAlias.getMethodExecution(); -// MethodExecution callee = new DummyMethodExecution(thisAlias.getMethodExecution()); // Currently does not work because this dummy and the original one will be mixed. - MethodExecution callee = thisAlias.getMethodExecution(); + MethodExecution oldCallee = thisAlias.getMethodExecution(); + MethodExecution callee = null; + if (oldCallee instanceof DummyMethodExecution) { + callee = oldCallee; + } else { + callee = getNewMethodExec(newToOldMethodExecMap, oldCallee); + if (callee == null) { + callee = new DummyMethodExecution(oldCallee); + Set oldExecs = new HashSet<>(); + oldExecs.add(oldCallee); + newToOldMethodExecMap.put(callee, oldExecs); + newCallee = callee; + } + } callee.setCaller(caller, caller.getStatements().indexOf(otherPrevAlias.getOccurrencePoint().getStatement())); DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, otherPrevAlias.getOccurrencePoint().getStatement().getThreadNo()); - dummyInv.setTimeStamp(callee.getEntryTime()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp)); } } } + private Alias getUpdatedAlias(Alias alias, Map> newToOldMethodExecMap) { + Alias newAlias = null; + MethodExecution newExec = getNewMethodExec(newToOldMethodExecMap, alias.getMethodExecution()); + Statement st = alias.getOccurrencePoint().getStatement(); + DummyMethodInvocation dummyInvocation = null; + if (st instanceof MethodInvocation) { + MethodInvocation mi = ((MethodInvocation) st); + MethodExecution calledExec = mi.getCalledMethodExecution(); + MethodExecution newCalledExec = getNewMethodExec(newToOldMethodExecMap, calledExec); + if (newCalledExec != null) { + dummyInvocation = new DummyMethodInvocation(newCalledExec, mi.getThisClassName(), mi.getThisObjId(), 0, mi.getThreadNo()); + } + } + if (newExec != null || dummyInvocation != null) { + TracePoint dummyTp = null; + if (newExec == null) { + // Only the called method is to be replaced. + dummyTp = new DummyTracePoint(alias.getMethodExecution(), dummyInvocation); + } else { + if (dummyInvocation == null) { + // Only this method is to be replaced. + Statement originalSt = alias.getOccurrencePoint().getStatement(); + dummyTp = new DummyTracePoint(newExec, originalSt); + } else { + // Both the calling and the called methods are to be replaced. + dummyTp = new DummyTracePoint(newExec, dummyInvocation); + } + } + newAlias = new Alias(alias.getAliasType(), alias.getIndex(), alias.getObjectId(), dummyTp); + } + return newAlias; + } + /** * Sort aliasList in time stamp order. * @param aliasList AliasList to sort. @@ -159,4 +263,11 @@ ).collect(Collectors.toList()); return sortedAliasList; } + + private MethodExecution getNewMethodExec(Map> newToOldMethodExecMap, MethodExecution oldExec) { + for (MethodExecution newExec: newToOldMethodExecMap.keySet()) { + if (newToOldMethodExecMap.get(newExec).contains(oldExec)) return newExec; + } + return null; + } } diff --git a/src/org/ntlab/deltaViewer/CollaborationObjectCallGraph.java b/src/org/ntlab/deltaViewer/CollaborationObjectCallGraph.java index fff5242..84380b0 100644 --- a/src/org/ntlab/deltaViewer/CollaborationObjectCallGraph.java +++ b/src/org/ntlab/deltaViewer/CollaborationObjectCallGraph.java @@ -177,6 +177,7 @@ refs = replaceStaticObjectIds(refs); references = new HashSet<>(refs); // Convert to Set from List. relatedPoints = replaceRelatedPoints(relatedPoints, newToOldMethodExecutionMap); + startPoints = replaceStartPoints(startPoints, newToOldMethodExecutionMap); // For debug. System.out.println("collectionReferences: "); @@ -196,7 +197,7 @@ System.out.println("\t" + ref.getSrcClassName() + "(" + ref.getSrcObjectId() + ")" + " -> " + ref.getDstClassName() + "(" + ref.getDstObjectId() + "): " + ref.isCollection() + ", " + ref.isCreation()); } } - + private List collectCollectionReferences(List references) { // Collect references that are Collection. List collectionRefs = new ArrayList<>(); @@ -344,5 +345,19 @@ } return replacedRp; } - + + private List replaceStartPoints(List startPoints, Map> newToOldMethodExecutionMap) { + List replacedSp = new ArrayList<>(startPoints); + for (int i = 0; i < replacedSp.size(); i++) { + MethodExecution startPoint = replacedSp.get(i); + for (Entry> entry: newToOldMethodExecutionMap.entrySet()) { + MethodExecution newMethodExec = entry.getKey(); + Set oldMethodExecSet = entry.getValue(); + if (oldMethodExecSet.contains(startPoint)) { + replacedSp.set(i, newMethodExec); + } + } + } + return replacedSp; + } } diff --git a/src/org/ntlab/deltaViewer/CollaborationViewer.java b/src/org/ntlab/deltaViewer/CollaborationViewer.java index 9b9ef31..7bc86ef 100644 --- a/src/org/ntlab/deltaViewer/CollaborationViewer.java +++ b/src/org/ntlab/deltaViewer/CollaborationViewer.java @@ -48,7 +48,7 @@ public void init(IObjectCallGraph objectCallGraph, IAliasCollector aliasCollector, IObjectLayout layout) { this.objectCallGraph = objectCallGraph; this.aliasCollector = aliasCollector; - createObjectVertices(this.objectCallGraph); + createObjectVertices(this.objectCallGraph, this.aliasCollector); layout.execute(objectCallGraph, aliasCollector, objectToVertexMap); createEdgeToObject(this.objectCallGraph, this.aliasCollector); } @@ -345,8 +345,9 @@ * Create vertices(mxGraph) and OvjectVerticies in {@code objectToVertexMap}. Vertices(mxGraph) coordinate are appropriate. * * @param objectCallGraph + * @param aliasCollector */ - private void createObjectVertices(IObjectCallGraph objectCallGraph) { + private void createObjectVertices(IObjectCallGraph objectCallGraph, IAliasCollector aliasCollector) { //Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { @@ -408,6 +409,17 @@ } } } + for (Alias alias: aliasCollector.getAliasList()) { + if (alias.getAliasType() == Alias.AliasType.THIS) { + if (!objectToVertexMap.containsKey(alias.getObjectId())) { + // When both of the calling method and called method are static. + String thisObjId = alias.getObjectId(); + String thisClassName = alias.getMethodExecution().getThisClassName(); + mxICell vertex = (mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), thisObjId, thisClassName, 0, 0, objecVertexWidth, ObjectVertexHeight, "fillColor=white"); //creates a white vertex. + objectToVertexMap.put(thisObjId, new ObjectVertex(thisClassName, vertex, 0, 0)); + } + } + } } finally { mxgraph.getModel().endUpdate(); } diff --git a/src/org/ntlab/deltaViewer/DeltaAliasCollector.java b/src/org/ntlab/deltaViewer/DeltaAliasCollector.java index 61409b2..6dc62bc 100644 --- a/src/org/ntlab/deltaViewer/DeltaAliasCollector.java +++ b/src/org/ntlab/deltaViewer/DeltaAliasCollector.java @@ -6,10 +6,12 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Stack; import org.ntlab.deltaExtractor.Alias; import org.ntlab.deltaExtractor.Alias.AliasType; import org.ntlab.deltaExtractor.IAliasTracker; +import org.ntlab.trace.FieldAccess; import org.ntlab.trace.MethodExecution; import org.ntlab.trace.MethodInvocation; import org.ntlab.trace.Statement; @@ -83,14 +85,14 @@ } - public Map> shrink() { + public void shrink(Map> newToOldMethodExecMap) { List oldAliasList = new ArrayList<>(aliasList); List standardMethodInvocations = collectStandardMethodInvocations(aliasList); List> invocationChains = collectInvocationChains(standardMethodInvocations); aliasList = replaceInvocationChains(aliasList, invocationChains); - Map> newToOldMethodExecMap = collectNewToOldMethodExecutionMap(oldAliasList, aliasList); + newToOldMethodExecMap.putAll(collectNewToOldMethodExecutionMap(oldAliasList, aliasList)); aliasList = removeEnclosingInstanceAccessor(aliasList, newToOldMethodExecMap); - aliasList = replaceStaticObjectIds(aliasList); + aliasList = replaceStaticObjectIds(aliasList, newToOldMethodExecMap); // for debug. System.out.println("standardMethodInvocations: "); @@ -108,7 +110,6 @@ for (Alias alias: aliasList) { System.out.println(alias.getObjectId() + ": " + alias.getMethodSignature() + " l." + alias.getLineNo() + " :: " + alias.getAliasType().toString()); } - return newToOldMethodExecMap; } private List collectStandardMethodInvocations(List aliasList) { @@ -301,13 +302,113 @@ * Replace objectId of {@code Alias} of static object in aliasList. * * @param aliasList + * @param newToOldMethodExecMap * @return */ - private List replaceStaticObjectIds(List aliasList) { + private List replaceStaticObjectIds(List aliasList, Map> newToOldMethodExecMap) { List replacedAliasList = new ArrayList<>(aliasList); - for (Alias alias: replacedAliasList) { - if (alias.getObjectId().matches("0")) { - alias.setObjectId(alias.getObjectId() + ":" + alias.getMethodExecution().getThisClassName()); + for (int i = 0; i < replacedAliasList.size(); i++) { + Alias alias = replacedAliasList.get(i); + Statement st = alias.getOccurrencePoint().getStatement(); + if (st instanceof MethodInvocation && ((MethodInvocation) st).getCalledMethodExecution().isStatic()) { + // The called method is static. + MethodInvocation mi = (MethodInvocation) st; + MethodExecution oldCalledMethodExec = mi.getCalledMethodExecution(); + MethodExecution calledMethodExec = null; + if (!(oldCalledMethodExec instanceof DummyMethodExecution)) { + calledMethodExec = getNewMethodExec(newToOldMethodExecMap, oldCalledMethodExec); + if (calledMethodExec == null || !(calledMethodExec instanceof DummyMethodExecution)) { + calledMethodExec = new DummyMethodExecution(oldCalledMethodExec); + Set oldExecs = new HashSet<>(); + oldExecs.add(oldCalledMethodExec); + newToOldMethodExecMap.put(calledMethodExec, oldExecs); + } + mi = new DummyMethodInvocation(mi); + mi.setCalledMethodExecution(calledMethodExec); + String newObjId = calledMethodExec.getThisObjId(); + if (newObjId.matches("0")) newObjId += ":" + oldCalledMethodExec.getThisClassName(); // Since the called method is static. + calledMethodExec.setThisObjId(newObjId); + DummyTracePoint tp = new DummyTracePoint(alias.getMethodExecution(), mi); + if (alias.getObjectId().matches("0") && alias.getAliasType() == Alias.AliasType.RECEIVER) { + // Alias type is RECEIVER and the called method is static. + alias = new Alias(alias.getAliasType(), alias.getIndex(), newObjId, tp); + replacedAliasList.set(i, alias); + } else { + // Alias type is ACTUAL_ARGUMENT or METHOD_INVOCATION, and the called method is static. + alias = new Alias(alias.getAliasType(), alias.getIndex(), alias.getObjectId(), tp); + replacedAliasList.set(i, alias); + } + } + } + if (alias.getMethodExecution().isStatic()) { + // This method is static. + MethodExecution oldCallingMethodExec = alias.getMethodExecution(); + MethodExecution callingMethodExec = null; + ////////////////////////////////////////////////////////////////////// + // There are three types of goals below; + // 1) to share the same dummy method execution among distinct aliases, + // 2) to share the same dummy method execution and the same non-dummy statement among them, and + // 3) to share both the same dummy method and the same dummy statement among them. + // For example, distinct method invocations from the same static method execution corresponds to the case 1), + // a RECEIVER alias and an ACTUAL_ARGUMENT alias of the same method invocation from the same static method execution corresponds to the case 2), and + // a THIS alias and a CONSTRUCTOR_INVOCATION alias of the same constructor invocation from the same static method execution corresponds to the case 3). + if (oldCallingMethodExec instanceof DummyMethodExecution) { + callingMethodExec = oldCallingMethodExec; + } else { + callingMethodExec = getNewMethodExec(newToOldMethodExecMap, oldCallingMethodExec); + } + if (callingMethodExec == null || !(callingMethodExec instanceof DummyMethodExecution)) { + callingMethodExec = new DummyMethodExecution(oldCallingMethodExec); + Set oldExecs = new HashSet<>(); + oldExecs.add(oldCallingMethodExec); + newToOldMethodExecMap.put(callingMethodExec, oldExecs); + } + String newObjId = oldCallingMethodExec.getThisObjId(); + if (newObjId.matches("0")) newObjId += ":" + oldCallingMethodExec.getThisClassName(); + callingMethodExec.setThisObjId(newObjId); + DummyTracePoint tp = new DummyTracePoint(alias.getOccurrencePoint(), callingMethodExec); // Share the same dummy method execution. + if (alias.getObjectId().matches("0") && alias.getAliasType() == Alias.AliasType.THIS) { + // Alias type is THIS and the this method is static. + // Actually, we don't want to create a new dummy statement as below but to share the same dummy statement with other relevant aliases. + if (st instanceof FieldAccess && !(st instanceof DummyFieldAccess)) { + DummyFieldAccess dummyFld = new DummyFieldAccess((FieldAccess) st); + dummyFld.setThisObjId(newObjId); + if (dummyFld.getContainerObjId().matches("0")) dummyFld.setContainerObjId(newObjId); + tp = new DummyTracePoint(callingMethodExec, dummyFld); + } else if (st instanceof MethodInvocation && !(st instanceof DummyMethodInvocation)) { + // Constructor invocation + DummyMethodInvocation dummyInv = new DummyMethodInvocation((MethodInvocation) st); + dummyInv.setThisObjId(newObjId); + MethodExecution newCalledMethodExec = getNewMethodExec(newToOldMethodExecMap, dummyInv.getCalledMethodExecution()); + if (newCalledMethodExec != null) { + dummyInv.setCalledMethodExecution(newCalledMethodExec); + } + dummyInv.getCalledMethodExecution().setCaller(callingMethodExec, dummyInv.getCalledMethodExecution().getCallerStatementExecution()); // We don't want to modify the called method execution. + tp = new DummyTracePoint(callingMethodExec, dummyInv); + } + replacedAliasList.set(i, new Alias(alias.getAliasType(), alias.getIndex(), newObjId, tp)); + } else { + // Alias type is FORMAL_PARAMETER, FIELD, RECEIVER, ACTUAL_ARGUMENT, METHOD_INVOCATION , CONSTRUCTOR_INVOCATION or RETURN_VALUE, and this method is static. + // Actually, we don't want to create a new dummy statement as below but to share the same dummy statement with other relevant aliases. + if (st instanceof MethodInvocation) { + // Alias type is RECEIVER, ACTUAL_ARGUMENT, METHOD_INVOCATION or CONSTRUCTOR_INVOCATION, and this method is static. + DummyMethodInvocation dummyInv = new DummyMethodInvocation((MethodInvocation) st); + dummyInv.setThisObjId(newObjId); + MethodExecution newCalledMethodExec = getNewMethodExec(newToOldMethodExecMap, dummyInv.getCalledMethodExecution()); + if (newCalledMethodExec != null) { + dummyInv.setCalledMethodExecution(newCalledMethodExec); + } + dummyInv.getCalledMethodExecution().setCaller(callingMethodExec, dummyInv.getCalledMethodExecution().getCallerStatementExecution()); // We don't want to modify the called method execution. + tp = new DummyTracePoint(callingMethodExec, dummyInv); + } else if (st instanceof FieldAccess && !(st instanceof DummyFieldAccess)) { + // Alias type is FIELD and this method is static. + DummyFieldAccess dummyFld = new DummyFieldAccess((FieldAccess) st); + dummyFld.setThisObjId(newObjId); + if (dummyFld.getContainerObjId().matches("0")) dummyFld.setContainerObjId(newObjId); + tp = new DummyTracePoint(callingMethodExec, dummyFld); + } + replacedAliasList.set(i, new Alias(alias.getAliasType(), alias.getIndex(), alias.getObjectId(), tp)); + } } } return replacedAliasList; @@ -318,16 +419,28 @@ MethodExecution caller = null; int callerStatement = 0; MethodExecution accessor = null; + MethodExecution newCallee = null; + Stack thisIndices = new Stack<>();; for (int i = 0; i < removedAliasList.size(); i++) { Alias a = removedAliasList.get(i); - if (a.getAliasType() == Alias.AliasType.ACTUAL_ARGUMENT) { + if (a.getAliasType() == Alias.AliasType.THIS) { + // When a final local variable or an accessor to the enclosing instance is evaluated, some virtual aliases are inserted on existing statements. + Statement st = a.getOccurrencePoint().getStatement(); + if (st instanceof MethodInvocation) { + // If an existing statement is accidentally a method invocation. + if (Trace.getMethodName(((MethodInvocation) st).getCalledMethodExecution().getSignature()).startsWith("access$")) { + thisIndices.push(i); + continue; + } + } + } else if (a.getAliasType() == Alias.AliasType.ACTUAL_ARGUMENT) { Statement st = a.getOccurrencePoint().getStatement(); if (st instanceof MethodInvocation) { if (Trace.getMethodName(((MethodInvocation) st).getCalledMethodExecution().getSignature()).startsWith("access$")) { caller = a.getMethodExecution(); MethodInvocation mi = ((MethodInvocation) st); callerStatement = mi.getCalledMethodExecution().getCallerStatementExecution(); - accessor = mi.getCalledMethodExecution(); + accessor = mi.getCalledMethodExecution(); removedAliasList.remove(i); i--; continue; @@ -343,29 +456,88 @@ if (a.getMethodExecution() == accessor) { Statement st = a.getOccurrencePoint().getStatement(); if (a.getAliasType() == Alias.AliasType.ACTUAL_ARGUMENT) { -// MethodExecution callee = new DummyMethodExecution(((MethodInvocation) st).getCalledMethodExecution()); // Currently does not work because this dummy and the original one will be mixed. - MethodExecution callee = ((MethodInvocation) st).getCalledMethodExecution(); - callee.setCaller(caller, callerStatement); + MethodExecution oldCallee = ((MethodInvocation) st).getCalledMethodExecution(); + MethodExecution callee = null; + callee = getNewMethodExec(newToOldMethodExecMap, oldCallee); + if (callee == null) { + if (oldCallee.getCallerMethodExecution() == caller) { + callee = oldCallee; + } else { + callee = new DummyMethodExecution(oldCallee); + callee.setCaller(caller, callerStatement); + Set oldCallees = new HashSet<>(); + oldCallees.add(oldCallee); + oldCallees.add(accessor); + newToOldMethodExecMap.put(callee, oldCallees); + newCallee = callee; + } + } DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, st.getThreadNo()); - dummyInv.setTimeStamp(callee.getEntryTime()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); Alias newAlias = new Alias(Alias.AliasType.ACTUAL_ARGUMENT, a.getIndex(), a.getObjectId(), dummyTp); removedAliasList.set(i, newAlias); + while (thisIndices.size() > 0) { + int thisIdx = thisIndices.pop(); + Alias callerAlias = removedAliasList.get(thisIdx); + Statement callerSt = callerAlias.getOccurrencePoint().getStatement(); + DummyMethodInvocation dummyInv2 = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, callerSt.getThreadNo()); + DummyTracePoint dummyTp2 = new DummyTracePoint(caller, dummyInv2); + Alias newAlias2 = new Alias(Alias.AliasType.THIS, callerAlias.getIndex(), callee.getThisObjId(), dummyTp2); + removedAliasList.set(thisIdx, newAlias2); + } } else if (a.getAliasType() == Alias.AliasType.RECEIVER) { -// MethodExecution callee = new DummyMethodExecution(((MethodInvocation) st).getCalledMethodExecution()); // Currently does not work because this dummy and the original one will be mixed. - MethodExecution callee = ((MethodInvocation) st).getCalledMethodExecution(); - callee.setCaller(caller, callerStatement); + MethodExecution oldCallee = ((MethodInvocation) st).getCalledMethodExecution(); + MethodExecution callee = null; + callee = getNewMethodExec(newToOldMethodExecMap, oldCallee); + if (callee == null) { + if (oldCallee.getCallerMethodExecution() == caller) { + callee = oldCallee; + } else { + callee = new DummyMethodExecution(oldCallee); + callee.setCaller(caller, callerStatement); + Set oldCallees = new HashSet<>(); + oldCallees.add(oldCallee); + oldCallees.add(accessor); + newToOldMethodExecMap.put(callee, oldCallees); + newCallee = callee; + } + } DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, st.getThreadNo()); - dummyInv.setTimeStamp(callee.getEntryTime()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); Alias newAlias = new Alias(Alias.AliasType.RECEIVER, a.getIndex(), a.getObjectId(), dummyTp); removedAliasList.set(i, newAlias); + while (thisIndices.size() > 0) { + int thisIdx = thisIndices.pop(); + Alias callerAlias = removedAliasList.get(thisIdx); + Statement callerSt = callerAlias.getOccurrencePoint().getStatement(); + DummyMethodInvocation dummyInv2 = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, callerSt.getThreadNo()); + DummyTracePoint dummyTp2 = new DummyTracePoint(caller, dummyInv2); + Alias newAlias2 = new Alias(Alias.AliasType.THIS, callerAlias.getIndex(), callee.getThisObjId(), dummyTp2); + removedAliasList.set(thisIdx, newAlias2); + } } + } else if (newCallee != null && newToOldMethodExecMap.get(newCallee).contains(a.getMethodExecution())) { + Statement st = a.getOccurrencePoint().getStatement(); + if (st instanceof MethodInvocation) { + // Caution!!: The internal structure of the trace is modified. + MethodExecution calleeCallee = ((MethodInvocation) st).getCalledMethodExecution(); + calleeCallee.setCaller(newCallee, calleeCallee.getCallerStatementExecution()); + } + DummyTracePoint dummyTp = new DummyTracePoint(a.getOccurrencePoint(), newCallee); + Alias newAlias = new Alias(a.getAliasType(), a.getIndex(), a.getObjectId(), dummyTp); + removedAliasList.set(i, newAlias); } } return removedAliasList; } + private MethodExecution getNewMethodExec(Map> newToOldMethodExecMap, MethodExecution oldExec) { + for (MethodExecution newExec: newToOldMethodExecMap.keySet()) { + if (newToOldMethodExecMap.get(newExec).contains(oldExec)) return newExec; + } + return null; + } + /** * Collect methodExecutions for Alias that are not equal in oldAliasList and newAliasList. * @param oldAliasList diff --git a/src/org/ntlab/deltaViewer/DummyFieldAccess.java b/src/org/ntlab/deltaViewer/DummyFieldAccess.java new file mode 100644 index 0000000..bd61d84 --- /dev/null +++ b/src/org/ntlab/deltaViewer/DummyFieldAccess.java @@ -0,0 +1,31 @@ +package org.ntlab.deltaViewer; + +import org.ntlab.trace.FieldAccess; + +public class DummyFieldAccess extends FieldAccess { + private String containerObjId = null; + protected String thisObjId = null; + + public DummyFieldAccess(FieldAccess original) { + super(original.getFieldName(), original.getValueClassName(), original.getValueObjId(), original.getContainerClassName(), original.getContainerObjId(), + original.getThisClassName(), original.getThisObjId(), original.getLineNo(), original.getThreadNo(), original.getTimeStamp()); + } + + public void setContainerObjId(String containerObjId) { + this.containerObjId = containerObjId; + } + + public String getContainerObjId() { + if (containerObjId != null) return containerObjId; + return super.getContainerObjId(); + } + + public void setThisObjId(String thisObjId) { + this.thisObjId = thisObjId; + } + + public String getThisObjId() { + if (thisObjId != null) return thisObjId; + return super.getThisObjId(); + } +} diff --git a/src/org/ntlab/deltaViewer/DummyFieldUpdate.java b/src/org/ntlab/deltaViewer/DummyFieldUpdate.java new file mode 100644 index 0000000..5d0d2e0 --- /dev/null +++ b/src/org/ntlab/deltaViewer/DummyFieldUpdate.java @@ -0,0 +1,20 @@ +package org.ntlab.deltaViewer; + +import org.ntlab.trace.FieldUpdate; + +public class DummyFieldUpdate extends FieldUpdate { + private String containerObjId = null; + + public DummyFieldUpdate(DummyFieldUpdate original) { + super(original.getFieldName(), original.getValueClassName(), original.getValueObjId(), original.getContainerClassName(), original.getContainerObjId(), original.getLineNo(), original.getThreadNo(), original.getTimeStamp()); + } + + public void setContainerObjId(String containerObjId) { + this.containerObjId = containerObjId; + } + + public String getContainerObjId() { + if (containerObjId != null) return containerObjId; + return super.getContainerObjId(); + } +} diff --git a/src/org/ntlab/deltaViewer/DummyMethodExecution.java b/src/org/ntlab/deltaViewer/DummyMethodExecution.java index c699264..ae69353 100644 --- a/src/org/ntlab/deltaViewer/DummyMethodExecution.java +++ b/src/org/ntlab/deltaViewer/DummyMethodExecution.java @@ -4,15 +4,26 @@ import org.ntlab.trace.Statement; public class DummyMethodExecution extends MethodExecution { - public DummyMethodExecution(MethodExecution methodExec) { - super(methodExec.getSignature(), methodExec.getCallerSideSignature(), methodExec.getThisClassName(), methodExec.getThisObjId(), methodExec.isConstructor(), methodExec.isStatic(), methodExec.getEntryTime()); - setCollectionType(methodExec.isCollectionType()); - setArguments(methodExec.getArguments()); - setReturnValue(methodExec.getReturnValue()); - setCaller(methodExec.getCallerMethodExecution(), methodExec.getCallerStatementExecution()); - for (Statement st: methodExec.getStatements()) { + private String thisObjId = null; + + public DummyMethodExecution(MethodExecution original) { + super(original.getSignature(), original.getCallerSideSignature(), original.getThisClassName(), original.getThisObjId(), original.isConstructor(), original.isStatic(), original.getEntryTime()); + setCollectionType(original.isCollectionType()); + setArguments(original.getArguments()); + setReturnValue(original.getReturnValue()); + setCaller(original.getCallerMethodExecution(), original.getCallerStatementExecution()); + for (Statement st: original.getStatements()) { addStatement(st); } - setExitTime(methodExec.getExitTime()); + setExitTime(original.getExitTime()); + } + + public void setThisObjId(String thisObjId) { + this.thisObjId = thisObjId; + } + + public String getThisObjId() { + if (thisObjId == null) return super.getThisObjId(); + return thisObjId; } } diff --git a/src/org/ntlab/deltaViewer/DummyMethodInvocation.java b/src/org/ntlab/deltaViewer/DummyMethodInvocation.java index b0fca17..901677d 100644 --- a/src/org/ntlab/deltaViewer/DummyMethodInvocation.java +++ b/src/org/ntlab/deltaViewer/DummyMethodInvocation.java @@ -6,20 +6,23 @@ public class DummyMethodInvocation extends MethodInvocation { private long timeStamp; - public DummyMethodInvocation(MethodInvocation methodInvocation) { - super(methodInvocation.getCalledMethodExecution(), methodInvocation.getThisClassName(), methodInvocation.getThisObjId(), methodInvocation.getLineNo(), methodInvocation.getThreadNo()); + public DummyMethodInvocation(MethodInvocation original) { + super(original.getCallerSideMethodName(), original.getThisClassName(), original.getThisObjId(), original.getLineNo(), original.getThreadNo()); + super.setCalledMethodExecution(original.getCalledMethodExecution()); + timeStamp = original.getTimeStamp(); } public DummyMethodInvocation(MethodExecution methodExecution, String thisClassName, String thisObjId, int lineNo, String threadNo) { - super(methodExecution, thisClassName, thisObjId, lineNo, threadNo); - } - - public void setTimeStamp(long timeStamp) { - this.timeStamp = timeStamp; + super(methodExecution.getCallerSideSignature(), thisClassName, thisObjId, lineNo, threadNo); + super.setCalledMethodExecution(methodExecution); + timeStamp = methodExecution.getEntryTime(); } public long getTimeStamp() { return timeStamp; } + public void setThisObjId(String thisObjId) { + this.thisObjId = thisObjId; + } } diff --git a/src/org/ntlab/deltaViewer/DummyTracePoint.java b/src/org/ntlab/deltaViewer/DummyTracePoint.java index 82f6e85..96f921f 100644 --- a/src/org/ntlab/deltaViewer/DummyTracePoint.java +++ b/src/org/ntlab/deltaViewer/DummyTracePoint.java @@ -11,10 +11,11 @@ public DummyTracePoint(MethodExecution methodExecution, Statement dummyStatement) { super(methodExecution, 0); this.dummyStatement = dummyStatement; + this.dummyMethodExecution = methodExecution; } - public DummyTracePoint(TracePoint tracePoint, MethodExecution dummyMethodExecution) { - super(tracePoint.getMethodExecution(), tracePoint.getMethodExecution().getStatements().indexOf(tracePoint.getStatement())); + public DummyTracePoint(TracePoint originalPoint, MethodExecution dummyMethodExecution) { + super(originalPoint.getMethodExecution(), originalPoint.getMethodExecution().getStatements().indexOf(originalPoint.getStatement())); this.dummyMethodExecution = dummyMethodExecution; } diff --git a/src/org/ntlab/deltaViewer/MagnetRONFrame.java b/src/org/ntlab/deltaViewer/MagnetRONFrame.java index 3601a8f..f6cbb0d 100644 --- a/src/org/ntlab/deltaViewer/MagnetRONFrame.java +++ b/src/org/ntlab/deltaViewer/MagnetRONFrame.java @@ -13,6 +13,7 @@ import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -209,11 +210,11 @@ } IAliasCollector ac = extracted.getValue(); DeltaAliasCollector dac = (DeltaAliasCollector) ac; - newToOldMethodExecMap.putAll(dac.shrink()); + dac.shrink(newToOldMethodExecMap); if (cac == null) { cac = new CollaborationAliasCollector(dac); } else { - cac.merge(dac, extract); + cac.merge(dac, extract, newToOldMethodExecMap); } } cocg.shrinkAll(newToOldMethodExecMap); @@ -251,11 +252,11 @@ } IAliasCollector ac = extracted.getValue(); DeltaAliasCollector dac = (DeltaAliasCollector) ac; - newToOldMethodExecMap.putAll(dac.shrink()); + dac.shrink(newToOldMethodExecMap); if (cac == null) { cac = new CollaborationAliasCollector(dac); } else { - cac.merge(dac, null); + cac.merge(dac, null, newToOldMethodExecMap); } } cocg.shrinkAll(newToOldMethodExecMap); diff --git a/src/org/ntlab/trace/MethodExecution.java b/src/org/ntlab/trace/MethodExecution.java index 9553e15..2b4fe4c 100644 --- a/src/org/ntlab/trace/MethodExecution.java +++ b/src/org/ntlab/trace/MethodExecution.java @@ -42,7 +42,7 @@ this.arguments = arguments; } - public void setThisObjeId(String thisObjId) { + public void setThisObjId(String thisObjId) { this.thisObjId = thisObjId; } diff --git a/src/org/ntlab/trace/ThreadInstance.java b/src/org/ntlab/trace/ThreadInstance.java index 03f0341..6cf0a9a 100644 --- a/src/org/ntlab/trace/ThreadInstance.java +++ b/src/org/ntlab/trace/ThreadInstance.java @@ -63,7 +63,7 @@ if (curMethodExecution == null) return; curMethodExecution.setReturnValue(returnValue); if (curMethodExecution.getThisObjId().equals("0")) { - curMethodExecution.setThisObjeId(thisObjId); + curMethodExecution.setThisObjId(thisObjId); } curMethodExecution.setCollectionType(isCollectionType); curMethodExecution = curMethodExecution.getParent(); diff --git a/src/tests/CollaborationAliasCollectorTest.java b/src/tests/CollaborationAliasCollectorTest.java index f1eb166..baf6704 100644 --- a/src/tests/CollaborationAliasCollectorTest.java +++ b/src/tests/CollaborationAliasCollectorTest.java @@ -3,14 +3,17 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; import org.ntlab.deltaExtractor.Alias; import org.ntlab.deltaExtractor.ExtractedStructure; import org.ntlab.deltaExtractor.IAliasCollector; import org.ntlab.deltaViewer.CollaborationAliasCollector; +import org.ntlab.trace.MethodExecution; class CollaborationAliasCollectorTest { @@ -26,11 +29,12 @@ List eList = new ArrayList<>(extractedMultipleDeltas.keySet()); List dacList = new ArrayList<>(extractedMultipleDeltas.values()); CollaborationAliasCollector cac = null; + Map> newToOldMethodExecMap = new HashMap<>(); for (IAliasCollector dac: dacList) { if (cac == null) { cac = new CollaborationAliasCollector(dac); } else { - cac.merge(dac, null); + cac.merge(dac, null, newToOldMethodExecMap); } } System.out.println("mergedAliasList: "); diff --git a/src/tests/CollaborationObjectCallGraphTest.java b/src/tests/CollaborationObjectCallGraphTest.java index d6a8454..35c7cd0 100644 --- a/src/tests/CollaborationObjectCallGraphTest.java +++ b/src/tests/CollaborationObjectCallGraphTest.java @@ -42,11 +42,11 @@ for (IAliasCollector ac: dacList) { if (ac instanceof DeltaAliasCollector) { DeltaAliasCollector dac = (DeltaAliasCollector) ac; - newToOldMethodExecMap.putAll(dac.shrink()); + dac.shrink(newToOldMethodExecMap); if (cac == null) { cac = new CollaborationAliasCollector(dac); } else { - cac.merge(dac, null); + cac.merge(dac, null, newToOldMethodExecMap); } } } diff --git a/src/tests/DeltaAliasCollectorTest.java b/src/tests/DeltaAliasCollectorTest.java index 9ba5459..c08e570 100644 --- a/src/tests/DeltaAliasCollectorTest.java +++ b/src/tests/DeltaAliasCollectorTest.java @@ -3,14 +3,17 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; import org.ntlab.deltaExtractor.ExtractedStructure; import org.ntlab.deltaExtractor.IAliasCollector; import org.ntlab.deltaViewer.CollaborationAliasCollector; import org.ntlab.deltaViewer.DeltaAliasCollector; +import org.ntlab.trace.MethodExecution; class DeltaAliasCollectorTest { @@ -25,7 +28,7 @@ for (IAliasCollector dac: dacList) { if (dac instanceof DeltaAliasCollector) { DeltaAliasCollector deltaAliasCollector = (DeltaAliasCollector)dac; - deltaAliasCollector.shrink(); + deltaAliasCollector.shrink(new HashMap>()); assertNotNull(deltaAliasCollector); } } diff --git a/src/tests/MagnetRONFrameTest.java b/src/tests/MagnetRONFrameTest.java index 4e92d54..d1f54e9 100644 --- a/src/tests/MagnetRONFrameTest.java +++ b/src/tests/MagnetRONFrameTest.java @@ -85,7 +85,7 @@ if (cac == null) { cac = new CollaborationAliasCollector(dac); } else { - cac.merge(dac, null); + cac.merge(dac, null, null); } } // new Thread() {