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; /** * CollaborationAliasCollector is IAliasCollector implementation class to merge aliasList in time stamp order. * * @author Nitta Lab. */ public class CollaborationAliasCollector implements IAliasCollector { // Execution order. private List<Alias> aliasList = new ArrayList<>(); public CollaborationAliasCollector(IAliasCollector dac) { aliasList.addAll(dac.getAliasList()); aliasList = sortAliasListByTimeStamp(aliasList); } /* * Don't write anything here. */ @Override public void addAlias(Alias alias) { } @Override public List<Alias> getAliasList() { return aliasList; } /** * 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, Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap) { List<Alias> 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 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<MethodExecution> 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()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp)); thisIdx++; } } aliasList.add(otherAlias); otherIdx++; thisIdx++; continue; } 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 { long otherAliasTs = otherAlias.getTimeStamp(); long thisAliasTs = thisAlias.getTimeStamp(); if (otherAliasTs < thisAliasTs) { if (extract != null && extract.isToConnect() && otherIdx == 0 && thisIdx > 0) { Alias thisPrevAlias = aliasList.get(thisIdx - 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 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<MethodExecution> 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()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp)); thisIdx++; } } aliasList.add(thisIdx, otherAlias); otherIdx++; thisIdx++; } else if (otherAliasTs > thisAliasTs) { if (extract != null && extract.isToConnect() && thisOrgIdx == 0 && otherIdx > 0) { Alias otherPrevAlias = otherAliasList.get(otherIdx - 1); 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 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<MethodExecution> 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()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp)); thisIdx++; } } thisIdx++; thisOrgIdx++; } else { if (aliasList.contains(otherAlias)) { otherIdx++; } else { // BUGがあるかもしれません(ACTUAL_ARGUMENTとRECEIVERの出現順) aliasList.add(thisIdx, otherAlias); otherIdx++; thisIdx++; } } } } if (extract != null && extract.isToConnect() && thisOrgIdx == 0 && otherIdx > 0 && thisIdx < aliasList.size()) { Alias thisAlias = aliasList.get(thisIdx); Alias otherPrevAlias = otherAliasList.get(otherIdx - 1); 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 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<MethodExecution> 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()); DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv); aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp)); } } } private Alias getUpdatedAlias(Alias alias, Map<MethodExecution, Set<MethodExecution>> 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. * @return Sorted AliasList. */ private List<Alias> sortAliasListByTimeStamp(List<Alias> aliasList) { List<Alias> cloneAliasList = new ArrayList<>(aliasList); List<Alias> sortedAliasList = cloneAliasList.stream().sorted(new Comparator<Alias>() { @Override public int compare(Alias alias1, Alias alias2) { if (alias1.getTimeStamp() > alias2.getTimeStamp()) return 1; else if (alias1.getTimeStamp() < alias2.getTimeStamp()) return -1; return 0; } } ).collect(Collectors.toList()); return sortedAliasList; } 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; } }