package com.mobilesorcery.sdk.html5.debug.jsdt; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.concurrent.TimeoutException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Path; import org.eclipse.wst.jsdt.debug.core.jsdi.Location; import org.eclipse.wst.jsdt.debug.core.jsdi.StackFrame; import org.eclipse.wst.jsdt.debug.core.jsdi.Value; import org.eclipse.wst.jsdt.debug.core.jsdi.Variable; import org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import com.mobilesorcery.sdk.core.Util; import com.mobilesorcery.sdk.html5.Html5Plugin; import com.mobilesorcery.sdk.html5.debug.LocalVariableScope; import com.mobilesorcery.sdk.html5.debug.ReloadVirtualMachine; public class ReloadStackFrame implements StackFrame { private final static JSONParser PARSER = new JSONParser(); private Variable thisVar; private SimpleLocation location; private Number catchLine; private boolean inited; private ArrayList<ReloadVariable> localVars; private final int stackDepth; private boolean isTop; private ReloadThreadReference thread; private ReloadVirtualMachine vm; public ReloadStackFrame(ReloadVirtualMachine vm, ReloadThreadReference thread, JSONObject suspendCommand, int ix) { this.vm = vm; this.thread = thread; this.stackDepth = ix; init(suspendCommand, ix); } public int getStackDepth() { return stackDepth; } private void init(JSONObject suspended, int ix) { Object lineObj = suspended.get("line"); int line = lineObj instanceof Number ? ((Number) lineObj).intValue() : 1; JSONArray stack = (JSONArray) suspended.get("stack"); JSONArray frame = ix >= 0 ? (JSONArray) stack.get(ix) : null; String functionName = frame == null ? "<unknown>" : (String) frame.get(0); String file = frame == null ? (String) suspended.get("file") : (String) frame.get(1); // For the non-top stack frame, the line has been stored elsewhere. isTop = ix == stack.size() - 1; if (!isTop) { JSONArray nextFrame = (JSONArray) stack.get(ix + 1); line = ((Number) nextFrame.get(3)).intValue(); } IFile resource = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(file)); location = new SimpleLocation(vm, resource, line); location.setFunctionName(functionName); // For exceptions; where was it caught!? this.catchLine = (Number) suspended.get("catchLine"); } @Override public VirtualMachine virtualMachine() { return thread.virtualMachine(); } @Override public Variable thisObject() { initVars(); return thisVar; } private void initVars() { if (!inited) { inited = true; thisVar = new ReloadVariable(vm, this, "this"); localVars = new ArrayList<ReloadVariable>(); int scopeLine = catchLine == null ? location.lineNumber() : catchLine.intValue(); LocalVariableScope scope = vm.getLocalVariableScope(location.scriptReference(), scopeLine); if (scope != null) { for (String localVar : scope.getLocalVariables()) { localVars.add(new ReloadVariable(vm, this, localVar)); } } } } @Override public List variables() { initVars(); ArrayList result = new ArrayList(); result.addAll(localVars); return result; } @Override public Location location() { return location; } @Override public Value evaluate(String expression) { return parse(internalEvaluate(expression, stackDepth)); } private String internalEvaluate(String expression, int stackDepth) { Integer stackDepthToSend = isTop ? null : stackDepth + 1; String valueStr; try { valueStr = "" + thread.evaluate(expression, stackDepthToSend); } catch (Exception e) { valueStr = null; } return valueStr; } private Value parse(String valueStr) { return vm.mirrorOf(valueStr); } public static String createDropToFrameExpression(int frameToDropTo, String expression) { if (Util.isEmpty(expression)) { // Noop. expression = "{}"; } return MessageFormat.format("MoSyncDebugProtocol.doDropToFrame({0}, {1});", Integer.toString(frameToDropTo), expression); } public Value getValue(ReloadProperty property) { String symbolToEvaluate = property.getSymbolToEvaluate(); String name = property.name(); String metaFn = "this".equals(symbolToEvaluate) ? "evalThis" : "evalVar"; String metaExpr = String.format("MoSyncDebugProtocol.%s(%s);", metaFn, symbolToEvaluate); String metaEvaluation = internalEvaluate(metaExpr, stackDepth); try { if (metaEvaluation != null) { JSONObject metaObject = (JSONObject) PARSER.parse(metaEvaluation); String type = (String) metaObject.get("type"); String repr = "" + metaObject.get("repr"); if ("object".equals(type) || "function".equals(type)) { Number oid = (Number) metaObject.get("oid"); String className = (String) metaObject.get("clazz"); JSONArray properties = (JSONArray) metaObject.get("properties"); if (properties != null) { properties.addAll(property.getIntrinsicProperties()); } ArrayList<ReloadProperty> generatedProperties = new ArrayList<ReloadProperty>(); boolean hasArrayProperty = false; boolean hasLengthProperty = false; HashSet<String> unDuplicateSet = new HashSet<String>(); for (int i = 0; properties != null && i < properties.size(); i++) { String propertyName = (String) properties.get(i); hasLengthProperty |= "length".equals(propertyName); boolean isArrayProperty = Character.isDigit(propertyName.charAt(0)); hasArrayProperty |= isArrayProperty; boolean isInternalProperty = "____oid".equals(propertyName); if (!isInternalProperty && !unDuplicateSet.contains(propertyName)) { unDuplicateSet.add(propertyName); generatedProperties.add(isArrayProperty ? new ReloadArrayProperty(vm, this, property, propertyName) : new ReloadProperty(vm, this, property, propertyName)); } } boolean isArray = hasArrayProperty; //|| hasLengthProperty; ReloadObjectReference ref = isArray ? new ReloadArrayReference(vm, repr, oid) : new ReloadObjectReference(vm, repr, className, oid); for (ReloadProperty generatedProperty : generatedProperties) { ref.addProperty(generatedProperty); } return ref; } else if ("null".equals(type)) { return vm.mirrorOfNull(); } else if ("string".equals(type)) { return vm.mirrorOf(repr); } else if ("number".equals(type)) { Number reprNum = Double.parseDouble(repr); return vm.mirrorOf(reprNum); } else if ("boolean".equals(type)) { Boolean reprBool = Boolean.parseBoolean(repr); return vm.mirrorOf(reprBool); } else { return vm.mirrorOfUndefined(); } } } catch (Exception e) { e.printStackTrace(); // IGNORE. } return vm.mirrorOfUndefined(); } public ReloadThreadReference thread() { return thread; } public String toString() { return "[" + Util.join(variables().toArray(), ", ") + "]"; } }