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 org.ntlab.deltaExtractor.Alias;
import org.ntlab.deltaExtractor.Alias.AliasType;
import org.ntlab.deltaExtractor.IAliasTracker;
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 Map<MethodExecution, Set<MethodExecution>> shrink() {
		List<Alias> oldAliasList = new ArrayList<>(aliasList);
		List<Alias> standardMethodInvocations = collectStandardMethodInvocations(aliasList);
		List<List<Alias>> invocationChains = collectInvocationChains(standardMethodInvocations);
		aliasList = replaceInvocationChains(aliasList, invocationChains);
		Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap = collectNewToOldMethodExecutionMap(oldAliasList, aliasList);
		aliasList = removeEnclosingInstanceAccessor(aliasList, newToOldMethodExecMap);
		aliasList = replaceStaticObjectIds(aliasList);

		// 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());
		}
		return newToOldMethodExecMap;
	}

	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
	 * @return
	 */
	private List<Alias> replaceStaticObjectIds(List<Alias> aliasList) {
		List<Alias> replacedAliasList = new ArrayList<>(aliasList);
		for (Alias alias: replacedAliasList) {
			if (alias.getObjectId().matches("0")) {
				alias.setObjectId(alias.getObjectId() + ":" + alias.getMethodExecution().getThisClassName());
			}
		}
		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;
		for (int i = 0; i < removedAliasList.size(); i++) {
			Alias a = removedAliasList.get(i);
			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 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);
					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);
				} 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);
					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);
				}
			}
		}
		return removedAliasList;
	}
	
	/**
	 * 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;
	}

}