Newer
Older
MagnetRON / src / org / ntlab / deltaViewer / DeltaAliasCollector.java
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;
	}

}