package com.smtscript.debug; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import org.mozilla.javascript.Callable; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Undefined; import org.mozilla.javascript.debug.Debugger; import org.mozilla.javascript.tools.debugger.Dim; import org.mozilla.javascript.tools.debugger.GuiCallback; import org.mozilla.javascript.tools.debugger.Dim.ContextData; import org.mozilla.javascript.tools.debugger.Dim.SourceInfo; import org.mozilla.javascript.tools.debugger.Dim.StackFrame; import org.mozilla.javascript.tools.debugger.ScopeProvider; import com.smtscript.run.ScriptRunScope; import com.smtscript.run.ScriptRuntime; import com.smtscript.utils.Json; import com.smtscript.utils.JsonWriter; import com.smtscript.utils.SMTStatic; public class ScriptRuntimeDebugger extends ScriptRuntime implements GuiCallback,ScopeProvider { private static Map _mapWatchEvent2Method; static { _mapWatchEvent2Method = SMTStatic.newEventMethodMap("WATCH_", ScriptRuntimeDebugger.class, new Class[]{String.class, Json.class}, "expr_list", "EXPR_LIST", "expr_add", "EXPR_ADD", "expr_expend", "EXPR_EXPEND", "set_breakpoint", "SET_BREAKPOINT", "clear_breakpoint", "CLEAR_BREAKPOINT" ); } private Dim _dim; private ScriptDebugSocketClient _socket; //private EventQueue awtEventQueue; public ScriptRuntimeDebugger(ContextFactory factory, ScriptRunScope scope, ScriptDebugSocketClient socket) { super(factory, scope); _dim = new Dim(); _dim.attachTo(_contextFactory); _dim.setBreak(); _dim.setScopeProvider(this); _dim.setGuiCallback(this); _dim.setBreakOnEnter(false); _dim.setBreakOnReturn(false); _dim.setBreakOnExceptions(false); _socket = socket; } @Override public void updateSourceText(SourceInfo sourceInfo) { try { _socket.onUpdateSourceText(sourceInfo); } catch (Exception e) { e.printStackTrace(); } } @Override public void enterInterrupt(StackFrame lastFrame, String threadTitle, String alertMessage) { try { _socket.onEnterInterrupt(lastFrame, threadTitle, alertMessage); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean isGuiEventThread() { //return SwingUtilities.isEventDispatchThread(); return true; } @Override public void dispatchNextGuiEvent() throws InterruptedException { // 获取查看指令 Object oRecvCmd = null; while((oRecvCmd = _socket.queryReceiveCmd()) != null) { if(oRecvCmd instanceof Json) { Json jsonWatchCmd = (Json)oRecvCmd; String eventName = jsonWatchCmd.getJson("event").asString().toUpperCase(); Method method = _mapWatchEvent2Method.get(eventName); try { method.invoke(this, eventName, jsonWatchCmd); } catch (Exception e) { e.printStackTrace(); } } else if(oRecvCmd instanceof String) { // 获取运行指令 String curRun = (String)oRecvCmd; if("STEP_INTO".equals(curRun)) { _dim.setReturnValue(Dim.STEP_INTO); } else if("STEP_OVER".equals(curRun)) { _dim.setReturnValue(Dim.STEP_OVER); } else if("STEP_OUT".equals(curRun)) { _dim.setReturnValue(Dim.STEP_OUT); } else if("GO".equals(curRun)) { _dim.setReturnValue(Dim.GO); } else if("BREAK".equals(curRun)) { _dim.setReturnValue(Dim.BREAK); } else if("EXIT".equals(curRun)) { _dim.setReturnValue(Dim.EXIT); } break; } } } @Override public Scriptable getScope() { return this._globalScope; } public Object eval(String expr, int frameIndex) { Object result = Undefined.instance; if (expr == null) { return result; } ContextData contextData = _dim.currentContextData(); if (contextData == null) { return result; } StackFrame frame = contextData.getFrame(frameIndex); Context cx = Context.getCurrentContext(); result = do_eval(cx, frame, expr); return result; } private static Object do_eval(Context cx, StackFrame frame, String expr) { Debugger saved_debugger = cx.getDebugger(); Object saved_data = cx.getDebuggerContextData(); int saved_level = cx.getOptimizationLevel(); cx.setDebugger(null, null); cx.setOptimizationLevel(-1); cx.setGeneratingDebug(false); try { Callable script = (Callable)cx.compileString(expr, "", 0, null); Object result = script.call(cx, (Scriptable) frame.scope(), (Scriptable)frame.thisObj(), org.mozilla.javascript.ScriptRuntime.emptyArgs); return result; } catch (Exception exc) { return exc; } finally { cx.setGeneratingDebug(true); cx.setOptimizationLevel(saved_level); cx.setDebugger(saved_debugger, saved_data); } } // {"expr":"a","cur_frame":0,"event":"expr_add"} public void WATCH_EXPR_ADD(String eventName, Json jsonRecv) throws Exception { int frameIndex = jsonRecv.getJson("cur_frame").asInteger(); JsonWriter jsonWr = new JsonWriter(false); jsonWr.addKeyValue("event", "expr_add"); addExprValueToJson("expr", jsonRecv, frameIndex, jsonWr); if(jsonRecv.has("id")) { jsonWr.beginMap("expr"); jsonWr.addKeyValue("id", jsonRecv.getJson("id").asString()); jsonWr.endMap(); } _socket.sendSockRequest(jsonWr.getRootJson()); } //{"line":3,"event":"set_breakpoint","url":"d:/tmp/a.jjs"} public void WATCH_SET_BREAKPOINT(String eventName, Json jsonRecv) throws Exception { String url = jsonRecv.getJson("url").asString(); int line = jsonRecv.getJson("line").asInteger(); boolean value = jsonRecv.getJson("value").asBoolean(); SourceInfo si = _dim.sourceInfo(url); if(si == null) return; si.breakpoint(line, value); } public void WATCH_CLEAR_BREAKPOINT(String eventName, Json jsonRecv) throws Exception { String url = jsonRecv.getJson("url").asString(); SourceInfo si = _dim.sourceInfo(url); if(si == null) return; si.removeAllBreakpoints(); } //{"expr_list":[{"expr":"a"}],"cur_frame":0,"event":"expr_list"} public void WATCH_EXPR_LIST(String eventName, Json jsonRecv) throws Exception { int frameIndex = jsonRecv.getJson("cur_frame").asInteger(); JsonWriter jsonWr = new JsonWriter(false); jsonWr.addKeyValue("event", "expr_list"); jsonWr.beginArray("expr_list"); for(Json jsonExpr : jsonRecv.getJson("expr_list").asJsonList()) { addExprValueToJson(null, jsonExpr, frameIndex, jsonWr); } jsonWr.endArray(); _socket.sendSockRequest(jsonWr.getRootJson()); } public void WATCH_EXPR_EXPEND(String eventName, Json jsonRecv) throws Exception { int frameIndex = jsonRecv.getJson("cur_frame").asInteger(); JsonWriter jsonWr = new JsonWriter(false); jsonWr.addKeyValue("event", "expr_expend"); jsonWr.beginMap("expend"); { Json jsonTree = jsonRecv.getJson("expend_tree"); jsonWr.addKeyRaw("expend_tree", jsonTree); jsonWr.addKeyValue("expr_id", jsonRecv.getJson("expr_id").asString()); List list = jsonTree.asJsonList(); // 获取表达式的值 String exprRoot = list.get(list.size() - 1).asString(); Object value = eval(exprRoot, frameIndex); // 推算到最后一级子属性 for(int i = list.size() - 2; i >= 0; i --) { Object id = list.get(i).getValue(); try { value = _dim.getObjectProperty(value, id); } catch(Exception ex) { value = ex; } } // 将数据打包 addPropValueToJson("value", null, value, true, null, jsonWr); } jsonWr.endMap(); _socket.sendSockRequest(jsonWr.getRootJson()); } private void addExprValueToJson(String keyName, Json jsonExpr, int frameIndex, JsonWriter jsonWr) { String expr = jsonExpr.getJson("expr").asString(); Object value = eval(expr, frameIndex); addPropValueToJson( keyName, expr, value, jsonExpr.safeGetBoolean("expend", false), jsonExpr.safeGetJson("children"), jsonWr); } private void addPropValueToJson(String keyName, Object expr, Object value, boolean expend, Json jsonProps, JsonWriter jsonWr) { jsonWr.beginMap(keyName); { if(expr != null) jsonWr.addKeyValue("expr", expr); String valueStr; try { valueStr = _dim.objectToString(value); } catch(Exception ex) { valueStr = "Exception : " + ex.getMessage(); } jsonWr.addKeyValue("value", valueStr); jsonWr.addKeyValue("type", value.getClass().getSimpleName()); Object[] ids = _dim.getObjectIds(value); jsonWr.addKeyValue("children_count", ids.length); if(ids.length > 0 && expend) { Map mapProps = new HashMap(); if(jsonProps != null) { for(Json jsonProp : jsonProps.asJsonList()) { Object key = jsonProp.getJson("id").getValue(); Json child= jsonProp.getJson("children"); mapProps.put(key, child); } } jsonWr.addKeyValue("expend", true); jsonWr.beginArray("children"); for(int i = 0; i < ids.length; i ++) { Object childValue = null; try { Object idKey = ids[i]; childValue = _dim.getObjectProperty(value, idKey); } catch(Exception ex) { childValue = ex; } if(childValue == null) childValue = Undefined.instance; addPropValueToJson( null, ids[i], childValue, mapProps.containsKey(ids[i]), mapProps.get(ids[i]), jsonWr); } jsonWr.endArray(); } } jsonWr.endMap(); } @Override public void closeScriptRuntime() throws Exception { super.closeScriptRuntime(); _socket.closeSocket(); } }