package com.mobilesorcery.sdk.html5.debug.rewrite; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Set; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.Block; import org.eclipse.wst.jsdt.core.dom.EmptyStatement; import org.eclipse.wst.jsdt.core.dom.Expression; import org.eclipse.wst.jsdt.core.dom.ExpressionStatement; import org.eclipse.wst.jsdt.core.dom.SimpleName; import org.eclipse.wst.jsdt.core.dom.TryStatement; import com.mobilesorcery.sdk.core.IFilter; import com.mobilesorcery.sdk.html5.debug.JSODDSupport; import com.mobilesorcery.sdk.html5.debug.LocalVariableScope; import com.mobilesorcery.sdk.html5.debug.Position; public class StatementRewrite extends NodeRewrite { private static final String NESTED_FUNCTION_BUG = "JSDT internals makes it impossible to add breakpoints to or hot replace this piece of code (Reason: 'nested function bug')"; private long fileId; private NavigableMap<Integer, LocalVariableScope> localVariables; private Set<ASTNode> blockifiables; private Map<Integer, NodeRewrite> instrumentedLines; private boolean forceInstrumentation; public StatementRewrite(ISourceSupport rewriter, ASTNode node, long fileId, NavigableMap<Integer, LocalVariableScope> localVariables, Set<ASTNode> blockifiables, Map<Integer, NodeRewrite> instrumentedLines, boolean forceInstrumentation) { super(rewriter, node); this.fileId = fileId; this.localVariables = localVariables; this.blockifiables = blockifiables; this.instrumentedLines = instrumentedLines; this.forceInstrumentation = forceInstrumentation; } @Override public void rewrite(IFilter<String> features, IRewrite rewrite) { ASTNode node = getNode(); Position start = getPosition(node, true); rewrite.seek(start); if (hasNestedFunctionBug()) { setBlacklisted(NESTED_FUNCTION_BUG); rewrite.insert("/*" + NESTED_FUNCTION_BUG + "*/"); return; } Position end = getPosition(node, false); int lineNo = start.getLine(); Entry<Integer, LocalVariableScope> scope = localVariables.floorEntry(start.getPosition()); String scopeDesc = ""; if (scope != null) { // Skip this in output //scopeDesc = "/*" + scope.getValue().getLocalVariables() // + "*/"; } boolean isInstrumentationSupported = supports(features, JSODDSupport.LINE_BREAKPOINTS); boolean isDebuggerStatement = false; if (node instanceof ExpressionStatement) { Expression expression = ((ExpressionStatement) node) .getExpression(); if (expression instanceof SimpleName) { String identifier = ((SimpleName) expression).getIdentifier(); isDebuggerStatement = "debugger" .equals(identifier); } } NodeRewrite instrumentor = instrumentedLines.get(lineNo); boolean canInstrumentThisLine = instrumentor == null || instrumentor == this; boolean doInstrument = forceInstrumentation || isDebuggerStatement || (isInstrumentationSupported && canInstrumentThisLine); if (doInstrument) { String statementPreamble = " MoSyncDebugProtocol.updatePosition(" + fileId + "," + lineNo + ",this," + isDebuggerStatement + "," + JSODDSupport.EVAL_FUNC_SNIPPET + ");"; // Max one instrumentation per line! Except for debugger statements. instrumentedLines.put(lineNo, this); String addThisBefore = scopeDesc + statementPreamble + "\n"; if (shouldBlockify()) { rewrite.insert("{"); } rewrite.insert(addThisBefore); } defaultRewrite(features, rewrite); rewrite.seek(end); if (doInstrument && shouldBlockify()) { rewrite.insert("}"); } } private boolean hasNestedFunctionBug() { // The allegedly fixed JSDT bug #227489? if (getNode() instanceof EmptyStatement) { // Heuristic to find out whether this // is in fact, NOT an empty statement. if (getSource(getNode()).contains("(")) { return true; } } return false; } private boolean shouldBlockify() { return blockifiables.contains(getNode()); } }