package org.ntlab.deltaViewer; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; 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; import org.ntlab.trace.Trace; import org.ntlab.trace.TracePoint; /** * Collect delta alias for MagnetRON.(Derived from DeltaAliasTracker.) * * @author Nitta Lab. */ public class DeltaAliasCollector implements IAliasTracker { // Reverse execution order. private List<Alias> aliasList = new ArrayList<>(); public DeltaAliasCollector() { } @Override public void addAlias(Alias alias) { switch(alias.getAliasType()) { case FORMAL_PARAMETER: aliasList.add(0, alias); break; case THIS: aliasList.add(0, alias); break; case METHOD_INVOCATION: aliasList.add(0, alias); break; case CONSTRACTOR_INVOCATION: aliasList.add(0, alias); break; case FIELD: aliasList.add(0, alias); break; case ARRAY_ELEMENT: aliasList.add(0, alias); break; case ARRAY: aliasList.add(0, alias); break; case ACTUAL_ARGUMENT: aliasList.add(0, alias); break; case RECEIVER: aliasList.add(0, alias); if (alias.getOccurrencePoint().getStatement() instanceof MethodInvocation) { MethodExecution me = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution(); } break; case RETURN_VALUE: aliasList.add(0, alias); break; default: break; } System.out.println(alias.getObjectId() + ", " + alias.getMethodSignature() + " l." + alias.getLineNo() + " : " + alias.getAliasType().toString()); } @Override public List<Alias> getAliasList() { return this.aliasList; } /* * Don't write anything here. */ @Override public void changeTrackingObject(String from, String to, boolean isSrcSide) { } public void shrink(Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap) { List<Alias> oldAliasList = new ArrayList<>(aliasList); List<Alias> standardMethodInvocations = collectStandardMethodInvocations(aliasList); List<List<Alias>> invocationChains = collectInvocationChains(standardMethodInvocations); aliasList = replaceInvocationChains(aliasList, invocationChains); newToOldMethodExecMap.putAll(collectNewToOldMethodExecutionMap(oldAliasList, aliasList)); aliasList = removeEnclosingInstanceAccessor(aliasList, newToOldMethodExecMap); aliasList = replaceStaticObjectIds(aliasList, newToOldMethodExecMap); // for debug. System.out.println("standardMethodInvocations: "); for (Alias alias: standardMethodInvocations) { System.out.println(alias.getAliasType() + ":: " + alias.getObjectId() + ": " + alias.getMethodSignature()); } System.out.println("invocationChains: "); for (int i = 0; i < invocationChains.size(); i++) { System.out.println("i = " + i); for (Alias alias: invocationChains.get(i)) { System.out.println("\t" + alias.getAliasType() + ":: " + alias.getObjectId() + ": " + alias.getMethodSignature()); } } System.out.println("replaceInvocationChains: "); for (Alias alias: aliasList) { System.out.println(alias.getObjectId() + ": " + alias.getMethodSignature() + " l." + alias.getLineNo() + " :: " + alias.getAliasType().toString()); } } private List<Alias> collectStandardMethodInvocations(List<Alias> aliasList) { List<Alias> standardMethodInvocations = new ArrayList<>(); List<Integer> standardMethodInvsIdx = new ArrayList<>(); // Collect 1 set of RECEIVER, THIS RETURN_VALUE, METHOD_INVOCATION and it is CollectionType. for (int i = 0; i < aliasList.size(); i++) { Alias alias = aliasList.get(i); if (alias.getAliasType() == AliasType.RECEIVER) { Statement st = alias.getOccurrencePoint().getStatement(); MethodInvocation methodInvocation = (MethodInvocation)st; if (methodInvocation.getCalledMethodExecution().isCollectionType()) { if (standardMethodInvsIdx.size() != 0) standardMethodInvsIdx.clear(); standardMethodInvsIdx.add(i); } } else if (alias.getAliasType() == AliasType.THIS) { if (alias.getMethodExecution().isCollectionType() && standardMethodInvsIdx.size() == 1) { standardMethodInvsIdx.add(i); } } else if (alias.getAliasType() == AliasType.RETURN_VALUE) { if (alias.getMethodExecution().isCollectionType() && standardMethodInvsIdx.size() == 2) { standardMethodInvsIdx.add(i); } } else if (alias.getAliasType() == AliasType.METHOD_INVOCATION) { Statement st = alias.getOccurrencePoint().getStatement(); MethodInvocation methodInvocation = (MethodInvocation)st; if (methodInvocation.getCalledMethodExecution().isCollectionType() && standardMethodInvsIdx.size() == 3) { standardMethodInvsIdx.add(i); for (int index: standardMethodInvsIdx) { standardMethodInvocations.add(aliasList.get(index)); } standardMethodInvsIdx.clear(); } } } return standardMethodInvocations; } private List<List<Alias>> collectInvocationChains(List<Alias> standardMethodInvocations) { List<List<Alias>> invocationChains = new ArrayList<>(); if (standardMethodInvocations.isEmpty()) return invocationChains; List<Integer> invChainsIdx = new ArrayList<>(); // Compare whether same callerMethodExecution. MethodExecution compareMethodExec = null; for (int i = 0; i < standardMethodInvocations.size(); i++) { Alias standardMethodInv = standardMethodInvocations.get(i); MethodExecution methodExec = null; if (standardMethodInv.getAliasType() == AliasType.RECEIVER && invChainsIdx.size() == 0) { methodExec = standardMethodInv.getMethodExecution(); } else if (standardMethodInv.getAliasType() == AliasType.THIS && invChainsIdx.size() == 1) { methodExec = standardMethodInv.getMethodExecution().getCallerMethodExecution(); } else if (standardMethodInv.getAliasType() == AliasType.RETURN_VALUE && invChainsIdx.size() == 2) { methodExec = standardMethodInv.getMethodExecution().getCallerMethodExecution(); } else if (standardMethodInv.getAliasType() == AliasType.METHOD_INVOCATION && invChainsIdx.size() == 3) { methodExec = standardMethodInv.getMethodExecution(); } else { invChainsIdx.clear(); continue; } if (compareMethodExec == null) { compareMethodExec = methodExec; invocationChains.add(new ArrayList<>()); } else { if (compareMethodExec != methodExec) { compareMethodExec = methodExec; invocationChains.add(new ArrayList<>()); } } invChainsIdx.add(i); if (invChainsIdx.size() == 4) { for (int index: invChainsIdx) { invocationChains.get(invocationChains.size() - 1).add(standardMethodInvocations.get(index)); } invChainsIdx.clear(); } } // Compare whether same objectId from RETURN_VALUE to THIS. int i = 0; while (i < invocationChains.size()) { List<Alias> invChain = invocationChains.get(i); if (invChain.size() > 4) { int j = 2; String compareObjId = null; while (j < invChain.size() - (1 + 2)) { Alias pRetauVal = invChain.get(j); Alias pMethodInv = invChain.get(j + 1); Alias nReceiver = invChain.get(j + 2); Alias nThis = invChain.get(j + 3); compareObjId = pRetauVal.getObjectId(); if (compareObjId.equals(pMethodInv.getObjectId()) && compareObjId.equals(nReceiver.getObjectId()) && compareObjId.equals(nThis.getObjectId())) { j += 4; } else { // Remove 1 set of from RECEIVER to METHOD_INVOCATION. for (int k = i - 2; k < i + 2; k++) { invChain.remove(k); } if (invChain.size() <= 4) { invocationChains.remove(i); i--; } } } i++; } else { invocationChains.remove(i); } } return invocationChains; } private List<Alias> replaceInvocationChains(List<Alias> aliasList, List<List<Alias>> invocationChains) { List<Alias> replacedAliasList = new ArrayList<>(aliasList); if (invocationChains.isEmpty()) return replacedAliasList; for (List<Alias> invChain: invocationChains) { int firstIdx = replacedAliasList.indexOf(invChain.get(0)); // RECEIVER int secondIdx = replacedAliasList.indexOf(invChain.get(1)); // THIS int thirdIdx = replacedAliasList.indexOf(invChain.get(invChain.size() - 2)); // RETURN_VALUE int lastIdx = replacedAliasList.indexOf(invChain.get(invChain.size() - 1)); // METHOD_INVOCATION if(firstIdx != -1 && secondIdx != -1 && thirdIdx != -1 && lastIdx != -1) { Alias receiverAlias = replacedAliasList.get(firstIdx); Alias oldThisAlias = replacedAliasList.get(secondIdx); Alias oldReturnValAlias = replacedAliasList.get(thirdIdx); // Collect signature chains. StringBuilder sb = new StringBuilder(); for (int i = 1; i < invChain.size(); i+=4) { if (i == 1) { sb.append(invChain.get(i).getMethodSignature()); } else { String[] splitMethodSig = invChain.get(i).getMethodSignature().split("\\."); sb.append("."); sb.append(splitMethodSig[splitMethodSig.length - 1]); } } String signatureChains = sb.toString(); String callerSideSignature = signatureChains; String thisClassName = oldThisAlias.getMethodExecution().getThisClassName(); long enterTime = oldThisAlias.getOccurrencePoint().getMethodExecution().getEntryTime(); long exitTime = oldReturnValAlias.getOccurrencePoint().getMethodExecution().getExitTime(); // Create new alias for THIS. String thisObjId = oldThisAlias.getObjectId(); MethodExecution newCalledMethodExec = new MethodExecution(signatureChains, callerSideSignature, thisClassName, thisObjId, false, false, enterTime); newCalledMethodExec.setCollectionType(true); newCalledMethodExec.setExitTime(exitTime); newCalledMethodExec.setReturnValue(oldReturnValAlias.getOccurrencePoint().getMethodExecution().getReturnValue()); TracePoint newThisTp = new TracePoint(newCalledMethodExec, -1); Alias newThisAlias = new Alias(AliasType.THIS, 0, thisObjId, newThisTp.duplicate()); // Change called method execution of RECEIVER alias. // TODO Caution: The trace will be changed by the following code! TracePoint receiverTp = receiverAlias.getOccurrencePoint(); Statement st = receiverTp.getStatement(); MethodInvocation methodInvocation = (MethodInvocation)st; methodInvocation.setCalledMethodExecution(newCalledMethodExec); newCalledMethodExec.setCaller(receiverTp.getMethodExecution(), receiverTp.getMethodExecution().getStatements().indexOf(st)); // Create new alias for RETURN_VALUE. String returnValObjId = oldReturnValAlias.getObjectId(); TracePoint newReturnValTp = new TracePoint(newCalledMethodExec, -1); Alias newReturnValAlias = new Alias(AliasType.RETURN_VALUE, 0, returnValObjId, newReturnValTp.duplicate()); // Create new alias for METHOD_INVOCATION. Alias newMethodInvAlias = new Alias(AliasType.METHOD_INVOCATION, 0, returnValObjId, receiverTp.duplicate()); /* Replace InvocationChains */ // Remove invocationChains from aliasList. for (int i = 1; i < invChain.size(); i++) { // Except first alias of THIS. Alias invAlias = invChain.get(i); int invAliasIdx = replacedAliasList.indexOf(invAlias); // Get index of invAlias in aliasList. if (invAliasIdx != - 1) replacedAliasList.remove(invAliasIdx); else System.out.println("Failed to remove invAlias in aliasList..."); } // Add new Alias for THIS and RETURN_VALUE, METHOD_INVOCATION. replacedAliasList.add(secondIdx, newMethodInvAlias); replacedAliasList.add(secondIdx, newReturnValAlias); replacedAliasList.add(secondIdx, newThisAlias); } else { System.out.println("Failed to shrink aliasList..."); } } return replacedAliasList; } /** * Replace objectId of {@code Alias} of static object in aliasList. * * @param aliasList * @param newToOldMethodExecMap * @return */ private List<Alias> replaceStaticObjectIds(List<Alias> aliasList, Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap) { List<Alias> replacedAliasList = new ArrayList<>(aliasList); 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<MethodExecution> 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<MethodExecution> 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; } private List<Alias> removeEnclosingInstanceAccessor(List<Alias> aliasList, Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap) { List<Alias> removedAliasList = new ArrayList<>(aliasList); MethodExecution caller = null; int callerStatement = 0; MethodExecution accessor = null; MethodExecution newCallee = null; Stack<Integer> thisIndices = new Stack<>();; for (int i = 0; i < removedAliasList.size(); i++) { Alias a = removedAliasList.get(i); 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(); removedAliasList.remove(i); i--; continue; } } } else if (a.getAliasType() == Alias.AliasType.FORMAL_PARAMETER) { if (Trace.getMethodName(a.getMethodSignature()).startsWith("access$")) { removedAliasList.remove(i); i--; continue; } } if (a.getMethodExecution() == accessor) { Statement st = a.getOccurrencePoint().getStatement(); if (a.getAliasType() == Alias.AliasType.ACTUAL_ARGUMENT) { 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<MethodExecution> 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()); 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 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<MethodExecution> 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()); 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<MethodExecution, Set<MethodExecution>> 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 * @param newAliasList * @return One-to-many, Key is new methodExecution in newAliasList, Values are set of old methodExecutions in oldAlias replaced by newAlias. */ private Map<MethodExecution, Set<MethodExecution>> collectNewToOldMethodExecutionMap(List<Alias> oldAliasList, List<Alias> newAliasList) { Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap = new HashMap<>(); int oldIdx = 0, newIdx = 0; Alias lastMatchedNextAlias = newAliasList.get(newIdx); for (oldIdx = 0; oldIdx < oldAliasList.size(); oldIdx++) { Alias oldAlias = oldAliasList.get(oldIdx); Alias newAlias = newAliasList.get(newIdx); if (oldAlias.equals(newAlias)) { if (newIdx + 1 < newAliasList.size()) newIdx++; lastMatchedNextAlias = newAliasList.get(newIdx); } else { MethodExecution oldMethodExec = oldAlias.getMethodExecution(); MethodExecution newMethodExec = lastMatchedNextAlias.getMethodExecution(); if (!newToOldMethodExecMap.containsKey(newMethodExec)) { newToOldMethodExecMap.put(newMethodExec, new HashSet<>()); newToOldMethodExecMap.get(newMethodExec).add(oldMethodExec); } else { newToOldMethodExecMap.get(newMethodExec).add(oldMethodExec); } if (!oldAliasList.contains(newAlias) && newIdx + 1 < newAliasList.size()) newIdx++; } } return newToOldMethodExecMap; } }