| /******************************************************************************* |
| * Copyright (c) 2009 IBM Corporation and others All rights reserved. This |
| * program and the accompanying materials are made available under the terms of |
| * the Eclipse Public License v1.0 which accompanies this distribution, and is |
| * available at http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.e4.languages.javascript.debug.rhino; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.e4.languages.javascript.debug.DebugRuntime; |
| import org.eclipse.e4.languages.javascript.debug.connect.DisconnectException; |
| import org.eclipse.e4.languages.javascript.debug.connect.EventPacket; |
| import org.eclipse.e4.languages.javascript.debug.connect.Request; |
| import org.eclipse.e4.languages.javascript.debug.connect.Response; |
| import org.eclipse.e4.languages.javascript.debug.connect.TimeoutException; |
| import org.mozilla.javascript.Context; |
| import org.mozilla.javascript.ContextFactory; |
| import org.mozilla.javascript.debug.DebugFrame; |
| import org.mozilla.javascript.debug.DebuggableScript; |
| import org.mozilla.javascript.debug.Debugger; |
| |
| public class RhinoDebugger implements Debugger, ContextFactory.Listener, Runnable { |
| private final DebugRuntime runtime; |
| private final RequestHandler requestHandler; |
| private final Thread requestHandlerThread; |
| private volatile boolean shutdown = false; |
| |
| private final Map threadToThreadId = new HashMap(); |
| private final Map threadIdToData = new HashMap(); |
| private final Map scripts = new HashMap(); |
| private final Map debuggableScripts = new HashMap(); |
| private final Map breakpoints = new HashMap(); |
| |
| private long currentThreadId = 0L; |
| private long currentBreakpointId = 0L; |
| private long currentScriptId = 0L; |
| |
| public RhinoDebugger(DebugRuntime runtime) { |
| this.runtime = runtime; |
| this.requestHandler = new RequestHandler(this); |
| this.requestHandlerThread = new Thread(this, "RhinoDebugger - Request Handler"); |
| } |
| |
| public void start() { |
| requestHandlerThread.start(); |
| } |
| |
| public void stop() { |
| shutdown = true; |
| try { |
| requestHandlerThread.interrupt(); |
| requestHandlerThread.join(); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public void run() { |
| while (!shutdown) { |
| try { |
| Request request = runtime.receiveRequest(1000); |
| Response response = requestHandler.handleRequest(request); |
| runtime.sendResponse(response); |
| } catch (TimeoutException e) { |
| // ignore |
| } catch (DisconnectException e) { |
| break; |
| } |
| } |
| } |
| |
| public DebugFrame getFrame(Context context, DebuggableScript debuggableScript) { |
| ScriptImpl script = getScript(debuggableScript); |
| ContextData contextData = (ContextData) context.getDebuggerContextData(); |
| ThreadData thread = (ThreadData) threadIdToData.get(contextData.getThreadId()); |
| return thread.getFrame(context, debuggableScript, script); |
| } |
| |
| private ScriptImpl getScript(DebuggableScript debuggableScript) { |
| while (!debuggableScript.isTopLevel()) { |
| debuggableScript = debuggableScript.getParent(); |
| } |
| return (ScriptImpl) debuggableScripts.get(debuggableScript); |
| } |
| |
| public void handleCompilationDone(Context context, DebuggableScript debuggableScript, String source) { |
| if (!debuggableScript.isTopLevel()) |
| return; |
| Long scriptId = nextScriptId(); |
| ScriptImpl script = new ScriptImpl(scriptId, debuggableScript, source); |
| scripts.put(scriptId, script); |
| debuggableScripts.put(debuggableScript, script); |
| |
| ContextData contextData = (ContextData) context.getDebuggerContextData(); |
| contextData.scriptLoaded(script); |
| } |
| |
| private synchronized Long nextScriptId() { |
| return new Long(currentScriptId++); |
| } |
| |
| public synchronized void contextCreated(Context context) { |
| Thread thread = Thread.currentThread(); |
| Long threadId = (Long) threadToThreadId.get(thread); |
| if (threadId == null) { |
| threadId = new Long(currentThreadId++); |
| threadToThreadId.put(thread, threadId); |
| } |
| ThreadData threadData = (ThreadData) threadIdToData.get(threadId); |
| if (threadData == null) { |
| threadData = new ThreadData(threadId, this); |
| threadIdToData.put(threadId, threadData); |
| } |
| threadData.contextCreated(context); |
| } |
| |
| public synchronized void contextReleased(Context context) { |
| Thread thread = Thread.currentThread(); |
| Long threadId = (Long) threadToThreadId.get(thread); |
| if (threadId == null) |
| return; |
| |
| ThreadData threadData = (ThreadData) threadIdToData.get(threadId); |
| threadData.contextReleased(context); |
| if (!threadData.hasContext()) { |
| threadToThreadId.remove(thread); |
| threadIdToData.remove(threadId); |
| } |
| } |
| |
| public synchronized void resume(Long threadId, String stepType) { |
| ThreadData threadData = (ThreadData) threadIdToData.get(threadId); |
| threadData.resume(stepType); |
| } |
| |
| public synchronized void resumeAll() { |
| for (Iterator it = threadIdToData.keySet().iterator(); it.hasNext();) { |
| Long threadId = (Long) it.next(); |
| resume(threadId, null); |
| } |
| } |
| |
| public synchronized void suspend(Long threadId) { |
| ThreadData threadData = (ThreadData) threadIdToData.get(threadId); |
| threadData.suspend(); |
| } |
| |
| public synchronized void suspendAll() { |
| for (Iterator it = threadIdToData.keySet().iterator(); it.hasNext();) { |
| Long threadId = (Long) it.next(); |
| suspend(threadId); |
| } |
| } |
| |
| public void disconnect() { |
| } |
| |
| public synchronized List getFrameIds(Long threadId) { |
| ThreadData threadData = (ThreadData) threadIdToData.get(threadId); |
| return threadData.getFrameIds(); |
| } |
| |
| public synchronized DebugFrameImpl getFrame(Long threadId, Long frameId) { |
| ThreadData threadData = (ThreadData) threadIdToData.get(threadId); |
| return threadData.getFrame(frameId); |
| } |
| |
| public synchronized List getScriptIds() { |
| return new ArrayList(scripts.keySet()); |
| } |
| |
| public synchronized ScriptImpl getScript(Long scriptId) { |
| return (ScriptImpl) scripts.get(scriptId); |
| } |
| |
| public synchronized Collection getBreakpoints() { |
| return new ArrayList(breakpoints.keySet()); |
| } |
| |
| public synchronized BreakpointImpl setBreakpoint(Long scriptId, Integer lineNumber, String functionName, String condition, Long threadId) { |
| ScriptImpl script = (ScriptImpl) scripts.get(scriptId); |
| if (!script.isValid(lineNumber, functionName)) |
| return null; |
| |
| BreakpointImpl breakpoint = new BreakpointImpl(nextBreakpointId(), script, lineNumber, functionName, condition, threadId); |
| breakpoints.put(breakpoint.getId(), breakpoint); |
| script.addBreakpoint(breakpoint); |
| return breakpoint; |
| } |
| |
| private synchronized Long nextBreakpointId() { |
| return new Long(currentBreakpointId++); |
| } |
| |
| public synchronized BreakpointImpl clearBreakpoint(Long breakpointId) { |
| BreakpointImpl breakpoint = (BreakpointImpl) breakpoints.remove(breakpointId); |
| if (breakpoint != null) { |
| ScriptImpl script = breakpoint.getScript(); |
| script.removeBreakpoint(breakpoint); |
| } |
| return breakpoint; |
| } |
| |
| public void sendEvent(EventPacket event) { |
| try { |
| runtime.sendEvent(event); |
| } catch (DisconnectException e) { |
| // ignore |
| e.printStackTrace(); // for now for debugging purposes |
| } |
| } |
| |
| public BreakpointImpl getBreakpoint(Long breakpointId) { |
| return (BreakpointImpl) breakpoints.get(breakpointId); |
| } |
| |
| public ThreadData getThreadData(Long threadId) { |
| return (ThreadData) threadIdToData.get(threadId); |
| } |
| |
| public List getThreadIds() { |
| return new ArrayList(threadIdToData.keySet()); |
| } |
| |
| } |