package com.mobilesorcery.sdk.html5.debug.hotreplace; import java.util.List; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.Block; import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; import org.eclipse.wst.jsdt.debug.core.jsdi.StackFrame; import com.mobilesorcery.sdk.core.IProvider; import com.mobilesorcery.sdk.html5.Html5Plugin; import com.mobilesorcery.sdk.html5.debug.IRedefinable; import com.mobilesorcery.sdk.html5.debug.IRedefiner; import com.mobilesorcery.sdk.html5.debug.JSODDSupport; import com.mobilesorcery.sdk.html5.debug.RedefineException; import com.mobilesorcery.sdk.html5.debug.RedefinitionResult; import com.mobilesorcery.sdk.html5.debug.ReloadVirtualMachine; import com.mobilesorcery.sdk.html5.debug.jsdt.ReloadStackFrame; import com.mobilesorcery.sdk.html5.debug.jsdt.ReloadThreadReference; import com.mobilesorcery.sdk.html5.debug.rewrite.ISourceSupport; import com.mobilesorcery.sdk.html5.debug.rewrite.NodeRewrite; public class FunctionRedefinable extends ASTRedefinable { private Boolean isAnonymous; private String subkey; private String instrumented; public FunctionRedefinable(IRedefinable parent, ISourceSupport source, ASTNode node) { super(parent, source, node); this.isAnonymous = isAnonymous(); } private int[] countAnonymousFunctions(List<IRedefinable> children) { int[] result = new int[2]; int count = 0; for (IRedefinable child : children) { if (child instanceof FunctionRedefinable && ((FunctionRedefinable) child).isAnonymous()) { count++; } if (child == this) { result[1] = count; } } result[0] = count; return result; } private boolean isAnonymous() { if (isAnonymous == null) { isAnonymous = getFunctionDeclaration().getName() == null; } return isAnonymous; } private FunctionDeclaration getFunctionDeclaration() { return (FunctionDeclaration) getNode(); } public RedefinitionResult canRedefine(FunctionRedefinable replacement) { if (!replacement.getFunctionSource(false).equals(getFunctionSource(false))) { if (isAnonymous()) { // Is this the only anonymous function? That's a nice heurisitic for assuming it's the 'same' function. List<IRedefinable> childrenToBeRedefined = replacement.getParent().getChildren(); List<IRedefinable> siblings = getParent().getChildren(); if (countAnonymousFunctions(childrenToBeRedefined)[0] > 1 || countAnonymousFunctions(siblings)[0] > 1) { return RedefinitionResult.fail("Cannot replace anonymous functions if there is more than one anonymous function in the same scope."); } } } return RedefinitionResult.ok(); } public String getFunctionSource(boolean instrumented) { if (instrumented) { if (this.instrumented == null) { this.instrumented = getInstrumentedSource(NodeRewrite.include(JSODDSupport.LINE_BREAKPOINTS), getFunctionDeclaration()); } return this.instrumented; } else { return getSourceRange(getFunctionDeclaration().getStartPosition(), getFunctionDeclaration().getStartPosition() + getFunctionDeclaration().getLength()); } } @Override public String key() { if (subkey == null) { if (isAnonymous()) { int index = countAnonymousFunctions(getParent().getChildren())[1]; subkey = "<" + index + ">"; } else { subkey = getFunctionName(); } } return constructKey(subkey); } public String getFunctionName() { return isAnonymous() ? Html5Plugin.ANONYMOUS_FUNCTION : getFunctionDeclaration().getName().getIdentifier(); } public String toString() { return getFunctionName(); } }