org.chromium.debug.core/src/org/chromium/debug/core/model/DebugTargetImpl.java
changeset 52 f577ea64429e
parent 2 e4420d2515f1
child 115 d367ed1b2991
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/DebugTargetImpl.java	Wed Jan 27 10:42:14 2010 -0800
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/DebugTargetImpl.java	Wed Jan 27 15:45:27 2010 -0800
@@ -4,11 +4,7 @@
 
 package org.chromium.debug.core.model;
 
-import java.util.Collection;
-
 import org.chromium.debug.core.ChromiumDebugPlugin;
-import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
-import org.chromium.sdk.Breakpoint;
 import org.chromium.sdk.CallFrame;
 import org.chromium.sdk.DebugContext;
 import org.chromium.sdk.DebugEventListener;
@@ -17,11 +13,8 @@
 import org.chromium.sdk.Script;
 import org.chromium.sdk.DebugContext.State;
 import org.chromium.sdk.DebugContext.StepAction;
-import org.chromium.sdk.JavascriptVm.BreakpointCallback;
-import org.chromium.sdk.JavascriptVm.ScriptsCallback;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarkerDelta;
-import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
@@ -30,47 +23,43 @@
 import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugException;
 import org.eclipse.debug.core.DebugPlugin;
-import org.eclipse.debug.core.IBreakpointManager;
 import org.eclipse.debug.core.ILaunch;
 import org.eclipse.debug.core.ILaunchListener;
 import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IDebugTarget;
 import org.eclipse.debug.core.model.IMemoryBlock;
 import org.eclipse.debug.core.model.IProcess;
-import org.eclipse.debug.core.model.ISourceLocator;
-import org.eclipse.debug.core.model.IStackFrame;
 import org.eclipse.debug.core.model.IThread;
 
 /**
  * An IDebugTarget implementation for remote JavaScript debugging.
  * Can debug any target that supports the ChromeDevTools protocol.
  */
-public class DebugTargetImpl extends DebugElementImpl implements IChromiumDebugTarget {
+public class DebugTargetImpl extends DebugElementImpl implements IDebugTarget {
 
   private static final IThread[] EMPTY_THREADS = new IThread[0];
 
-  private static final long OPERATION_TIMEOUT_MS = 15000L;
-
   private final ILaunch launch;
 
   private final JavascriptThread[] threads;
 
   private JavascriptVmEmbedder vmEmbedder = STUB_VM_EMBEDDER;
 
-  private ResourceManager resourceManager;
-
-  private BreakpointRegistry breakpointRegistry;
-
-  private IProject debugProject = null;
-
-  private DebugContext debugContext;
+  private volatile DebugContext debugContext;
 
   private boolean isSuspended = false;
 
   private boolean isDisconnected = false;
 
+  private final WorkspaceBridge.Factory workspaceBridgeFactory;
 
-  public DebugTargetImpl(ILaunch launch) {
+  private WorkspaceBridge workspaceRelations = null;
+
+  private final ListenerBlock listenerBlock = new ListenerBlock();
+
+  public DebugTargetImpl(ILaunch launch, WorkspaceBridge.Factory workspaceBridgeFactory) {
     super(null);
+    this.workspaceBridgeFactory = workspaceBridgeFactory;
     this.launch = launch;
     this.threads = new JavascriptThread[] { new JavascriptThread(this) };
   }
@@ -80,28 +69,23 @@
    * Loads browser tabs, consults the {@code selector} which of the tabs to
    * attach to, and if any has been selected, requests an attachment to the tab.
    *
-   * @param projectNameBase to create for the browser scripts
    * @param remoteServer embedding application we are connected with
-   * @param attachCallback to invoke on successful attachment
+   * @param attachCallback to invoke on successful attachment, can fail to be called
    * @param monitor to report the progress to
    * @return whether the target has attached to a tab
    * @throws CoreException
    */
-  public boolean attach(String projectNameBase,
-      JavascriptVmEmbedder.ConnectionToRemote remoteServer, DestructingGuard destructingGuard,
-      Runnable attachCallback, IProgressMonitor monitor) throws CoreException {
+  public boolean attach(JavascriptVmEmbedder.ConnectionToRemote remoteServer,
+      DestructingGuard destructingGuard, Runnable attachCallback,
+      IProgressMonitor monitor) throws CoreException {
     monitor.beginTask("", 2); //$NON-NLS-1$
     JavascriptVmEmbedder.VmConnector connector = remoteServer.selectVm();
     if (connector == null) {
       return false;
     }
     monitor.worked(1);
-    return performAttach(projectNameBase, connector, destructingGuard, attachCallback);
-  }
-
-  private boolean performAttach(String projectNameBase, JavascriptVmEmbedder.VmConnector connector,
-      DestructingGuard destructingGuard, Runnable attachCallback) throws CoreException {
     final JavascriptVmEmbedder embedder = connector.attach(embedderListener, debugEventListener);
+    // From this moment V8 may call our listeners. We block them by listenerBlock for a while.
 
     Destructable embedderDestructor = new Destructable() {
       public void destruct() {
@@ -111,88 +95,44 @@
 
     destructingGuard.addValue(embedderDestructor);
 
-    vmEmbedder = embedder;
+    this.vmEmbedder = embedder;
 
-    // We might want to add some url-specific suffix here
-    String projectName = projectNameBase;
     // We'd like to know when launch is removed to remove our project.
     DebugPlugin.getDefault().getLaunchManager().addLaunchListener(launchListener);
-    this.debugProject = ChromiumDebugPluginUtil.createEmptyProject(projectName);
-    this.breakpointRegistry = new BreakpointRegistry();
-    this.resourceManager = new ResourceManager(debugProject, breakpointRegistry);
-    onAttach(projectName, attachCallback);
-    return true;
-  }
 
-  private void onAttach(String projectName, Runnable attachCallback) {
+    this.workspaceRelations = workspaceBridgeFactory.attachedToVm(this,
+        vmEmbedder.getJavascriptVm());
+    listenerBlock.unblock();
+
     DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
     reloadScriptsAndPossiblyResume(attachCallback);
+
+    return true;
   }
 
   private void reloadScriptsAndPossiblyResume(final Runnable attachCallback) {
-    reloadScripts(true, new Runnable() {
-      public void run() {
-        try {
-          if (attachCallback != null) {
-            attachCallback.run();
-          }
-        } finally {
-          fireCreationEvent();
-        }
-        Job job = new Job("Update debugger state") {
-          @Override
-          protected IStatus run(IProgressMonitor monitor) {
-            debugEventListener.resumedByDefault();
-            return Status.OK_STATUS;
-          }
-        };
-        job.schedule();
-      }
-    });
-  }
+    workspaceRelations.reloadScriptsAtStart();
 
-  private void reloadScripts(boolean isSync, final Runnable runnable) {
-    Runnable command = new Runnable() {
-      public void run() {
-        vmEmbedder.getJavascriptVm().getScripts(new ScriptsCallback() {
-          public void failure(String errorMessage) {
-            ChromiumDebugPlugin.logError(errorMessage);
-          }
+    try {
+      if (attachCallback != null) {
+        attachCallback.run();
+      }
+    } finally {
+      fireCreationEvent();
+    }
 
-          public void success(Collection<Script> scripts) {
-            if (!vmEmbedder.getJavascriptVm().isAttached()) {
-              return;
-            }
-            for (Script script : scripts) {
-              getResourceManager().addScript(script);
-            }
-            if (runnable != null) {
-              runnable.run();
-            }
-          }
-
-        });
+    Job job = new Job("Update debugger state") {
+      @Override
+      protected IStatus run(IProgressMonitor monitor) {
+        debugEventListener.resumedByDefault();
+        return Status.OK_STATUS;
       }
     };
-    if (isSync) {
-      command.run();
-      return;
-    }
-    Thread t = new Thread(command);
-    t.setDaemon(true);
-    t.start();
-    try {
-      t.join(OPERATION_TIMEOUT_MS);
-    } catch (InterruptedException e) {
-      ChromiumDebugPlugin.log(e);
-    }
+    job.schedule();
   }
 
   public String getName() throws DebugException {
-    if (vmEmbedder == null) {
-      return ""; //$NON-NLS-1$
-    }
-    return vmEmbedder.getTargetName();
+    return workspaceBridgeFactory.getLabelProvider().getTargetLabel(this);
   }
 
   public IProcess getProcess() {
@@ -214,8 +154,7 @@
   }
 
   public boolean supportsBreakpoint(IBreakpoint breakpoint) {
-    return ChromiumDebugPlugin.DEBUG_MODEL_ID.equals(breakpoint.getModelIdentifier()) &&
-        !isDisconnected();
+    return workspaceRelations.getBreakpointHandler().supportsBreakpoint(breakpoint);
   }
 
   @Override
@@ -228,9 +167,8 @@
     return launch;
   }
 
-  @Override
-  public String getModelIdentifier() {
-    return ChromiumDebugPlugin.DEBUG_MODEL_ID;
+  public String getChromiumModelIdentifier() {
+    return workspaceBridgeFactory.getDebugModelIdentifier();
   }
 
   public boolean canTerminate() {
@@ -270,6 +208,7 @@
   }
 
   public void resumed(int detail) {
+    debugContext = null;
     fireResumeEvent(detail);
   }
 
@@ -289,7 +228,7 @@
     if (!canDisconnect()) {
       return;
     }
-    removeAllBreakpoints();
+    workspaceRelations.beforeDetach();
     if (!vmEmbedder.getJavascriptVm().detach()) {
       ChromiumDebugPlugin.logWarning(Messages.DebugTargetImpl_BadResultWhileDisconnecting);
     }
@@ -312,10 +251,6 @@
     return false;
   }
 
-  public IProject getDebugProject() {
-    return debugProject;
-  }
-
   /**
    * Fires a debug event
    *
@@ -370,98 +305,34 @@
   }
 
   public void breakpointAdded(IBreakpoint breakpoint) {
-    if (!supportsBreakpoint(breakpoint)) {
-      return;
-    }
-    try {
-      if (breakpoint.isEnabled()) {
-        // Class cast is ensured by the supportsBreakpoint implementation
-        final ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
-        IFile file = (IFile) breakpoint.getMarker().getResource();
-        if (getResourceManager().isAddingFile(file)) {
-          return; // restoring breakpoints in progress
-        }
-        final Script script = getResourceManager().getScript(file);
-        if (script == null) {
-          // Might be a script from a different debug target
-          return;
-        }
-        final int line = (lineBreakpoint.getLineNumber() - 1) + script.getStartLine();
-        BreakpointCallback callback = new BreakpointCallback() {
-          public void success(Breakpoint breakpoint) {
-            lineBreakpoint.setBreakpoint(breakpoint);
-            breakpointRegistry.add(script, line, breakpoint);
-          }
-
-          public void failure(String errorMessage) {
-            ChromiumDebugPlugin.logError(errorMessage);
-          }
-        };
-        // ILineBreakpoint lines are 1-based while V8 lines are 0-based
-        JavascriptVm javascriptVm = vmEmbedder.getJavascriptVm();
-        if (script.getName() != null) {
-          javascriptVm.setBreakpoint(Breakpoint.Type.SCRIPT_NAME,
-              script.getName(),
-              line,
-              Breakpoint.EMPTY_VALUE,
-              breakpoint.isEnabled(),
-              lineBreakpoint.getCondition(),
-              lineBreakpoint.getIgnoreCount(),
-              callback);
-        } else {
-          javascriptVm.setBreakpoint(Breakpoint.Type.SCRIPT_ID,
-              String.valueOf(script.getId()),
-              line,
-              Breakpoint.EMPTY_VALUE,
-              breakpoint.isEnabled(),
-              lineBreakpoint.getCondition(),
-              lineBreakpoint.getIgnoreCount(),
-              callback);
-        }
-      }
-    } catch (CoreException e) {
-      ChromiumDebugPlugin.log(e);
-    }
+    workspaceRelations.getBreakpointHandler().breakpointAdded(breakpoint);
   }
 
   public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
-    if (!supportsBreakpoint(breakpoint)) {
-      return;
-    }
-    // Class cast is ensured by the supportsBreakpoint implementation
-    ((ChromiumLineBreakpoint) breakpoint).changed();
+    workspaceRelations.getBreakpointHandler().breakpointChanged(breakpoint, delta);
   }
 
   public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
-    if (!supportsBreakpoint(breakpoint)) {
-      return;
-    }
-    try {
-      if (breakpoint.isEnabled()) {
-        // Class cast is ensured by the supportsBreakpoint implementation
-        ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
-        lineBreakpoint.clear();
-        breakpointRegistry.remove(
-            getResourceManager().getScript((IFile) breakpoint.getMarker().getResource()),
-            lineBreakpoint.getLineNumber() - 1,
-            lineBreakpoint.getBrowserBreakpoint());
-      }
-    } catch (CoreException e) {
-      ChromiumDebugPlugin.log(e);
-    }
+    workspaceRelations.getBreakpointHandler().breakpointRemoved(breakpoint, delta);
   }
 
   @SuppressWarnings("unchecked")
   @Override
   public Object getAdapter(Class adapter) {
-    if (ILaunch.class.equals(adapter)) {
+    if (adapter == EvaluateContext.class) {
+      JavascriptThread thread = getThread();
+      if (thread == null) {
+        return null;
+      }
+      return thread.getAdapter(adapter);
+    } else if (adapter == ILaunch.class) {
       return this.launch;
     }
     return super.getAdapter(adapter);
   }
 
-  public IResourceManager getResourceManager() {
-    return resourceManager;
+  public IFile getScriptResource(Script script) {
+    return workspaceRelations.getScriptResource(script);
   }
 
   public JavascriptThread getThread() {
@@ -470,21 +341,6 @@
         : threads[0];
   }
 
-  private static void breakpointsHit(Collection<? extends Breakpoint> breakpointsHit) {
-    if (breakpointsHit.isEmpty()) {
-      return;
-    }
-    IBreakpoint[] breakpoints =
-        DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(
-            ChromiumDebugPlugin.DEBUG_MODEL_ID);
-    for (IBreakpoint breakpoint : breakpoints) {
-      ChromiumLineBreakpoint jsBreakpoint = (ChromiumLineBreakpoint) breakpoint;
-      if (breakpointsHit.contains(jsBreakpoint.getBrowserBreakpoint())) {
-        jsBreakpoint.setIgnoreCount(-1); // reset ignore count as we've hit it
-      }
-    }
-  }
-
   private static String trim(String text, int maxLength) {
     if (text == null || text.length() <= maxLength) {
       return text;
@@ -496,28 +352,6 @@
     return debugContext;
   }
 
-  public ISourceLocator getSourceLocator() {
-    return sourceLocator;
-  }
-
-  private void removeAllBreakpoints() {
-    IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
-    IBreakpoint[] breakpoints =
-        breakpointManager.getBreakpoints(ChromiumDebugPlugin.DEBUG_MODEL_ID);
-    for (IBreakpoint bp : breakpoints) {
-      ChromiumLineBreakpoint clb = (ChromiumLineBreakpoint) bp;
-      if (clb.getBrowserBreakpoint() != null &&
-          clb.getBrowserBreakpoint().getId() != Breakpoint.INVALID_ID) {
-        clb.getBrowserBreakpoint().clear(null);
-      }
-    }
-    try {
-      breakpointManager.removeBreakpoints(breakpoints, true);
-    } catch (CoreException e) {
-      ChromiumDebugPlugin.log(e);
-    }
-  }
-
   private final DebugEventListenerImpl debugEventListener = new DebugEventListenerImpl();
 
   class DebugEventListenerImpl implements DebugEventListener {
@@ -543,6 +377,7 @@
     }
 
     public void resumed() {
+      listenerBlock.waitUntilReady();
       synchronized (suspendResumeMonitor) {
         DebugTargetImpl.this.resumed(DebugEvent.CLIENT_REQUEST);
         alreadyResumedOrSuspended = true;
@@ -550,13 +385,15 @@
     }
 
     public void scriptLoaded(Script newScript) {
-      getResourceManager().addScript(newScript);
+      listenerBlock.waitUntilReady();
+      workspaceRelations.scriptLoaded(newScript);
     }
 
     public void suspended(DebugContext context) {
+      listenerBlock.waitUntilReady();
       synchronized (suspendResumeMonitor) {
         DebugTargetImpl.this.debugContext = context;
-        breakpointsHit(context.getBreakpointsHit());
+        workspaceRelations.getBreakpointHandler().breakpointsHit(context.getBreakpointsHit());
         int suspendedDetail;
         if (context.getState() == State.EXCEPTION) {
           logExceptionFromContext(context);
@@ -593,7 +430,8 @@
   private final JavascriptVmEmbedder.Listener embedderListener =
       new JavascriptVmEmbedder.Listener() {
     public void reset() {
-      getResourceManager().clear();
+      listenerBlock.waitUntilReady();
+      workspaceRelations.handleVmResetEvent();
       fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.STATE));
     }
     public void closed() {
@@ -612,9 +450,7 @@
         return;
       }
       DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
-      if (debugProject != null) {
-        ChromiumDebugPluginUtil.deleteVirtualProjectAsync(debugProject);
-      }
+      workspaceRelations.launchRemoved();
     }
   };
 
@@ -635,23 +471,32 @@
     }
   };
 
-  /**
-   * This very simple source locator works because we provide our own source files.
-   * We'll have to try harder, once we link with resource js files.
-   */
-  private final ISourceLocator sourceLocator = new ISourceLocator() {
-    public Object getSourceElement(IStackFrame stackFrame) {
-      if (stackFrame instanceof StackFrame == false) {
-        return null;
+  public WorkspaceBridge.JsLabelProvider getLabelProvider() {
+    return workspaceBridgeFactory.getLabelProvider();
+  }
+
+  private static class ListenerBlock {
+    private volatile boolean isBlocked = true;
+    private final Object monitor = new Object();
+    void waitUntilReady() {
+      if (isBlocked) {
+        return;
       }
-      StackFrame jsStackFrame = (StackFrame) stackFrame;
-
-      Script script = jsStackFrame.getCallFrame().getScript();
-      if (script == null) {
-        return null;
+      synchronized (monitor) {
+        while (isBlocked) {
+          try {
+            monitor.wait();
+          } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+          }
+        }
       }
-
-      return resourceManager.getResource(script);
     }
-  };
+    void unblock() {
+      isBlocked = true;
+      synchronized (monitor) {
+        monitor.notifyAll();
+      }
+    }
+  }
 }