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;
}
}