--- a/org.chromium.debug.core/plugin.properties Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/plugin.properties Mon Jun 07 16:51:19 2010 -0700
@@ -4,4 +4,7 @@
providerName = The Chromium Authors
-pluginName = Chromium JavaScript Remote Debugger Core
\ No newline at end of file
+pluginName = Chromium JavaScript Remote Debugger Core
+
+SourceNameMapperContainer.name = JavaScript Source Name Mapper
+SourceNameMapperContainer.description = Transforms name of source and delegates lookup to another source container.
\ No newline at end of file
--- a/org.chromium.debug.core/plugin.xml Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/plugin.xml Mon Jun 07 16:51:19 2010 -0700
@@ -41,4 +41,40 @@
<run class="org.chromium.debug.core.efs.ChromiumScriptFileSystem"/>
</filesystem>
</extension>
+
+ <extension point="org.eclipse.debug.core.sourceLocators">
+ <sourceLocator
+ id="org.chromium.debug.core.ChromiumSourceDirector"
+ class="org.chromium.debug.core.ChromiumSourceDirector"
+ name="a ChromiumSourceDirector">
+ </sourceLocator>
+ </extension>
+
+ <extension point="org.eclipse.debug.core.sourcePathComputers">
+ <sourcePathComputer
+ id="org.chromium.debug.core.ChromiumSourceComputer"
+ class="org.chromium.debug.core.ChromiumSourceComputer">
+ </sourcePathComputer>
+ </extension>
+
+ <extension point="org.eclipse.debug.core.sourceContainerTypes">
+ <sourceContainerType
+ name="JS Server Scripts"
+ class="org.chromium.debug.core.VProjectSourceContainer$TypeDelegate"
+ id="org.chromium.debug.core.VProjectSourceContainer.type"
+ description="Remote V8/Chrome VM JavaScript Scripts">
+ </sourceContainerType>
+ </extension>
+
+
+ <extension point="org.eclipse.debug.core.sourceContainerTypes">
+ <sourceContainerType
+ name="%SourceNameMapperContainer.name"
+ class="org.chromium.debug.core.SourceNameMapperContainer$TypeDelegate"
+ id="org.chromium.debug.core.SourceNameMapperContainer.type"
+ description="%SourceNameMapperContainer.description">
+ </sourceContainerType>
+ </extension>
+
+
</plugin>
--- a/org.chromium.debug.core/src/org/chromium/debug/core/ChromiumDebugPlugin.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/ChromiumDebugPlugin.java Mon Jun 07 16:51:19 2010 -0700
@@ -6,6 +6,7 @@
import java.text.MessageFormat;
+import org.chromium.debug.core.model.BreakpointMap;
import org.chromium.debug.core.model.ChromiumBreakpointWBAFactory;
import org.chromium.debug.core.model.ChromiumLineBreakpoint;
import org.eclipse.core.runtime.CoreException;
@@ -30,6 +31,8 @@
/** The shared instance. */
private static ChromiumDebugPlugin plugin;
+ private final BreakpointMap breakpointMap = new BreakpointMap();
+
private ChromiumBreakpointWBAFactory breakpointWorkbenchAdapterFactory;
public ChromiumDebugPlugin() {
@@ -52,6 +55,10 @@
super.stop(context);
}
+ public BreakpointMap getBreakpointMap() {
+ return breakpointMap;
+ }
+
/**
* @return the shared instance
*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/ChromiumSourceComputer.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,22 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourcePathComputerDelegate;
+
+/**
+ * A source path computer implementation that provides {@link VProjectSourceContainer} as
+ * a default source files container for V8/Chrome debug sessions.
+ */
+public class ChromiumSourceComputer implements ISourcePathComputerDelegate {
+ public ISourceContainer[] computeSourceContainers(ILaunchConfiguration configuration,
+ IProgressMonitor monitor) throws CoreException {
+ return new ISourceContainer[] { new VProjectSourceContainer() };
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/ChromiumSourceDirector.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,68 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core;
+
+import org.chromium.debug.core.model.ResourceManager;
+import org.chromium.debug.core.model.StackFrame;
+import org.chromium.debug.core.model.VmResourceId;
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.Script;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector;
+import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupParticipant;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
+
+/**
+ * A source lookup director implementation that provides a simple participant and
+ * accepts instance of virtual project once it is created.
+ */
+public class ChromiumSourceDirector extends AbstractSourceLookupDirector {
+ private volatile ResourceManager resourceManager = null;
+ private volatile IProject project = null;
+ private volatile ReverseSourceLookup reverseSourceLookup = null;
+
+
+ public void initializeParticipants() {
+ ISourceLookupParticipant participant = new AbstractSourceLookupParticipant() {
+ public String getSourceName(Object object) throws CoreException {
+ Script script = null;
+ if (object instanceof Script) {
+ script = (Script) object;
+ } else if (object instanceof StackFrame) {
+ StackFrame jsStackFrame = (StackFrame) object;
+ script = jsStackFrame.getCallFrame().getScript();
+ } else if (object instanceof Breakpoint) {
+ Breakpoint breakpoint = (Breakpoint) object;
+ return breakpoint.getScriptName();
+ }
+ if (script == null) {
+ return null;
+ }
+
+ return VmResourceId.forScript(script).getEclipseSourceName();
+ }
+ };
+ addParticipants(new ISourceLookupParticipant[] { participant } );
+ }
+
+ public void initializeVProjectContainers(IProject project, ResourceManager resourceManager) {
+ this.resourceManager = resourceManager;
+ this.project = project;
+ this.reverseSourceLookup = new ReverseSourceLookup(this);
+ }
+
+ public ReverseSourceLookup getReverseSourceLookup() {
+ return reverseSourceLookup;
+ }
+
+ public ResourceManager getResourceManager() {
+ return resourceManager;
+ }
+
+ IProject getProject() {
+ return project;
+ }
+}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/Messages.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/Messages.java Mon Jun 07 16:51:19 2010 -0700
@@ -15,6 +15,10 @@
public static String ChromiumDebugPlugin_InternalError;
+ public static String SourceNameMapperContainer_NAME;
+
+ public static String VProjectSourceContainer_DEFAULT_TYPE_NAME;
+
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/ReverseSourceLookup.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,100 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core;
+
+import org.chromium.debug.core.model.VmResourceId;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.debug.core.sourcelookup.containers.ContainerSourceContainer;
+import org.eclipse.debug.core.sourcelookup.containers.DefaultSourceContainer;
+
+/**
+ * Eclipse has a standard facility for looking up source file for a debug artifact.
+ * LiveEdit feature has an opposite problem: find script in remote VM for a particular js file.
+ * This class implements some approach to this problem. An instance of this class corresponds
+ * to a particular debug launch.
+ */
+public class ReverseSourceLookup {
+ private final ISourceLookupDirector sourceDirector;
+
+ public ReverseSourceLookup(ISourceLookupDirector sourceDirector) {
+ this.sourceDirector = sourceDirector;
+ }
+
+ /**
+ * Tries to find a corresponding script for a file from a user workspace. The method uses
+ * the file name and current source lookup rules to retrieve a resource id, regardless of
+ * whether the resource has actually been loaded into the VM (you may want to set a breakpoint
+ * on resource before it is actually loaded).
+ */
+ public VmResourceId findVmResource(IFile sourceFile) throws CoreException {
+ for (ISourceContainer container : sourceDirector.getSourceContainers()) {
+ VmResourceId scriptName = tryForContainer(sourceFile, container);
+ if (scriptName != null) {
+ return scriptName;
+ }
+ }
+ return null;
+ }
+
+ private VmResourceId tryForContainer(IFile sourceFile, ISourceContainer container)
+ throws CoreException {
+ if (container.isComposite() && isSupportedCompositeContainer(container)) {
+ ISourceContainer[] subContainers = container.getSourceContainers();
+ for (ISourceContainer subContainer : subContainers) {
+ VmResourceId res = tryForContainer(sourceFile, subContainer);
+ if (res != null) {
+ return res;
+ }
+ }
+ return null;
+ } else if (container instanceof VProjectSourceContainer) {
+ VProjectSourceContainer projectSourceContainer = (VProjectSourceContainer) container;
+ return projectSourceContainer.findScriptId(sourceFile);
+ } else {
+ String name = tryForNonVirtualContainer(sourceFile, container);
+ if (name == null) {
+ return null;
+ }
+ return VmResourceId.forName(name);
+ }
+ }
+
+ /**
+ * We use {@link ISourceContainer#getSourceContainers()} method to unwrap internal containers.
+ * However it doesn't make sense for all composite containers (some of them may return their
+ * subdirectories as containers, which is not what we need).
+ */
+ private boolean isSupportedCompositeContainer(ISourceContainer container) {
+ return container instanceof DefaultSourceContainer;
+ }
+
+ /**
+ * @param container that may not wrap VProjectSourceContainer
+ */
+ private String tryForNonVirtualContainer(IFile resource, ISourceContainer container) {
+ if (container instanceof ContainerSourceContainer) {
+ ContainerSourceContainer containerSourceContainer = (ContainerSourceContainer) container;
+ IContainer resourceContainer = containerSourceContainer.getContainer();
+ if (resourceContainer.getFullPath().isPrefixOf(resource.getFullPath())) {
+ String name = resource.getFullPath().makeRelativeTo(
+ resourceContainer.getFullPath()).toPortableString();
+ return name;
+ }
+ } else if (container instanceof SourceNameMapperContainer) {
+ SourceNameMapperContainer mappingContainer =
+ (SourceNameMapperContainer) container;
+ String subResult = tryForNonVirtualContainer(resource, mappingContainer.getTargetContainer());
+ if (subResult != null) {
+ return mappingContainer.getPrefix() + subResult;
+ }
+ }
+
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/SourceNameMapperContainer.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,188 @@
+package org.chromium.debug.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerTypeDelegate;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * A special type of container that translates names of the source and delegates lookup
+ * to another source container.
+ * This could be useful when JS resource name is like "http://localhost/scripts/util.js"; such
+ * source name could be converted into "scripts/util.js".
+ * Currently container supports only prefix-based translation: if source name starts with a prefix,
+ * the prefix is truncated; otherwise the source name is discarded.
+ */
+public class SourceNameMapperContainer implements ISourceContainer {
+
+ private static final String TYPE_ID =
+ "org.chromium.debug.core.SourceNameMapperContainer.type"; //$NON-NLS-1$
+
+ private final ISourceContainer targetContainer;
+ private final String prefix;
+
+ public SourceNameMapperContainer(String prefix, ISourceContainer targetContainer) {
+ this.targetContainer = targetContainer;
+ this.prefix = prefix;
+ }
+
+ public void dispose() {
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public ISourceContainer getTargetContainer() {
+ return targetContainer;
+ }
+
+ public Object[] findSourceElements(String name) throws CoreException {
+ if (!name.startsWith(prefix)) {
+ return new Object[0];
+ }
+ String shortName = name.substring(prefix.length());
+ return targetContainer.findSourceElements(shortName);
+ }
+
+
+ public String getName() {
+ return NLS.bind(Messages.SourceNameMapperContainer_NAME, prefix);
+ }
+
+
+ public ISourceContainer[] getSourceContainers() {
+ return new ISourceContainer[] { targetContainer };
+ }
+
+
+ public ISourceContainerType getType() {
+ return DebugPlugin.getDefault().getLaunchManager().getSourceContainerType(TYPE_ID);
+ }
+
+
+ public void init(ISourceLookupDirector director) {
+ }
+
+
+ public boolean isComposite() {
+ return true;
+ }
+
+ private String getMemento() throws CoreException {
+ StringBuilder builder = new StringBuilder();
+ MementoFormat.encodeComponent(prefix, builder);
+ MementoFormat.encodeComponent(targetContainer.getType().getId(), builder);
+ MementoFormat.encodeComponent(targetContainer.getType().getMemento(targetContainer), builder);
+ return builder.toString();
+ }
+
+
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ /**
+ * A type delegate that serializes/deserializes container instances into/from memento.
+ */
+ public static class TypeDelegate implements ISourceContainerTypeDelegate {
+ public ISourceContainer createSourceContainer(String memento) throws CoreException {
+ MementoFormat.Parser parser = new MementoFormat.Parser(memento);
+ String prefix;
+ String typeId;
+ String subContainerMemento;
+ try {
+ prefix = parser.nextComponent();
+ typeId = parser.nextComponent();
+ subContainerMemento = parser.nextComponent();
+ } catch (MementoFormat.ParserException e) {
+ throw new CoreException(new Status(IStatus.ERROR,
+ ChromiumDebugPlugin.PLUGIN_ID, "Failed to parse memento", e)); //$NON-NLS-1$
+ }
+ ISourceContainerType subContainerType =
+ DebugPlugin.getDefault().getLaunchManager().getSourceContainerType(typeId);
+ ISourceContainer subContainer = subContainerType.createSourceContainer(subContainerMemento);
+ return new SourceNameMapperContainer(prefix, subContainer);
+ }
+
+ public String getMemento(ISourceContainer container) throws CoreException {
+ SourceNameMapperContainer chromeContainer = (SourceNameMapperContainer) container;
+ return chromeContainer.getMemento();
+ }
+ }
+
+ /**
+ * Handles memento string format. The format is just a sequence of strings that are preceded
+ * with their lengths and decorated with parentheses to make it more human-readable.
+ */
+ private static class MementoFormat {
+
+ static void encodeComponent(String component, StringBuilder output) {
+ output.append(component.length());
+ output.append('(').append(component).append(')');
+ }
+
+ /**
+ * A simple parser that reads char sequence as a sequence of strings.
+ */
+ static class Parser {
+ private final CharSequence charSequence;
+ private int pos = 0;
+ Parser(CharSequence charSequence) {
+ this.charSequence = charSequence;
+ }
+ String nextComponent() throws ParserException {
+ if (pos >= charSequence.length()) {
+ throw new ParserException("Unexpected end of line"); //$NON-NLS-1$
+ }
+ char ch = charSequence.charAt(pos);
+ pos++;
+ int num = Character.digit(ch, 10);
+ if (num == -1) {
+ throw new ParserException("Digit expected"); //$NON-NLS-1$
+ }
+ int len = num;
+ while (true) {
+ if (pos >= charSequence.length()) {
+ throw new ParserException("Unexpected end of line"); //$NON-NLS-1$
+ }
+ ch = charSequence.charAt(pos);
+ if (!Character.isDigit(ch)) {
+ break;
+ }
+ pos++;
+ num = Character.digit(ch, 10);
+ if (num == -1) {
+ throw new ParserException("Digit expected"); //$NON-NLS-1$
+ }
+ len = len * 10 + num;
+ }
+ pos++;
+ if (pos + len + 1 > charSequence.length()) {
+ throw new ParserException("Unexpected end of line"); //$NON-NLS-1$
+ }
+ String result = charSequence.subSequence(pos, pos + len).toString();
+ pos += len + 1;
+ return result;
+ }
+ }
+ private static class ParserException extends Exception {
+ ParserException() {
+ }
+ ParserException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ ParserException(String message) {
+ super(message);
+ }
+ ParserException(Throwable cause) {
+ super(cause);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/VProjectSourceContainer.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,107 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core;
+
+import org.chromium.debug.core.model.ResourceManager;
+import org.chromium.debug.core.model.VmResourceId;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerTypeDelegate;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+
+/**
+ * A source container implementation that wraps V8 virtual project. Currently virtual project
+ * has a flat file structure, so the container is accordingly one-level.
+ * <p>
+ * Unlike other implementation of {@link ISourceContainer} this class initially gets instantiated
+ * with no data. In this state it serves as an empty container because actual VM scripts are
+ * not available yet. Launch configuration UI will use it in this state more as a symbolic
+ * place-holder in sources tab. Later when VM is connected, method
+ * {@link #init(ISourceLookupDirector)} will be called and the actual content will be set.
+ */
+public class VProjectSourceContainer implements ISourceContainer {
+
+ private static final String TYPE_ID =
+ "org.chromium.debug.core.VProjectSourceContainer.type"; //$NON-NLS-1$
+
+ private ChromiumSourceDirector chromiumSourceDirector = null;
+
+ VProjectSourceContainer() {
+ }
+
+ public void init(ISourceLookupDirector director) {
+ if (director instanceof ChromiumSourceDirector) {
+ chromiumSourceDirector = (ChromiumSourceDirector) director;
+ }
+ }
+
+ public void dispose() {
+ }
+
+ public Object[] findSourceElements(String name) {
+ if (chromiumSourceDirector == null) {
+ return new Object[0];
+ }
+ ResourceManager resourceManager = chromiumSourceDirector.getResourceManager();
+ IFile file = resourceManager.getFile(name);
+ if (file == null) {
+ return new Object[0];
+ }
+ return new Object[] { file };
+ }
+
+ public String getName() {
+ IProject project = null;
+ if (chromiumSourceDirector != null) {
+ project = chromiumSourceDirector.getProject();
+ }
+ if (project == null) {
+ return Messages.VProjectSourceContainer_DEFAULT_TYPE_NAME;
+ } else {
+ return project.getName();
+ }
+ }
+
+ public ISourceContainer[] getSourceContainers() {
+ return null;
+ }
+
+ public ISourceContainerType getType() {
+ return DebugPlugin.getDefault().getLaunchManager().getSourceContainerType(TYPE_ID);
+ }
+
+ public boolean isComposite() {
+ return false;
+ }
+
+ public VmResourceId findScriptId(IFile resource) {
+ if (chromiumSourceDirector == null) {
+ throw new IllegalStateException();
+ }
+ return chromiumSourceDirector.getResourceManager().getResourceId(resource);
+ }
+
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ /**
+ * A type delegate that implements a trivial memento. We do not save any actual data here,
+ * because it all should be derived from a current VM launch.
+ */
+ public static class TypeDelegate implements ISourceContainerTypeDelegate {
+ public ISourceContainer createSourceContainer(String memento) throws CoreException {
+ return new VProjectSourceContainer();
+ }
+
+ public String getMemento(ISourceContainer container) throws CoreException {
+ return "VProjectSourceContainer.memento.stub"; //$NON-NLS-1$
+ }
+ }
+}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/messages.properties Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/messages.properties Mon Jun 07 16:51:19 2010 -0700
@@ -3,3 +3,5 @@
# found in the LICENSE file.
ChromiumDebugPlugin_InternalError=Internal Error
+SourceNameMapperContainer_NAME=Source mapper with prefix {0}
+VProjectSourceContainer_DEFAULT_TYPE_NAME=Remote V8/Chrome Scripts
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointAdapterFactory.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointAdapterFactory.java Mon Jun 07 16:51:19 2010 -0700
@@ -23,7 +23,7 @@
(IResource) editorPart.getEditorInput().getAdapter(IResource.class);
if (resource != null) {
String extension = resource.getFileExtension();
- if (extension != null && ChromiumDebugPluginUtil.CHROMIUM_EXTENSION.equals(extension)) {
+ if (extension != null && ChromiumDebugPluginUtil.SUPPORTED_EXTENSIONS.contains(extension)) {
return new LineBreakpointAdapter.ForVirtualProject();
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointMap.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.sdk.Breakpoint;
+
+/**
+ * TODO(peter.rybin): this class is obsolete, it only holds useful inner class;
+ * consider removing this class.
+ */
+public class BreakpointMap {
+
+ /**
+ * A one-to-one map between SDK and UI breakpoints inside one debug target.
+ */
+ public static class InTargetMap {
+ private final Map<Breakpoint, ChromiumLineBreakpoint> sdkToUiMap =
+ new HashMap<Breakpoint, ChromiumLineBreakpoint>();
+ private final Map<ChromiumLineBreakpoint, Breakpoint> uiToSdkMap =
+ new HashMap<ChromiumLineBreakpoint, Breakpoint>();
+
+ public InTargetMap() {
+ }
+
+ public synchronized Breakpoint getSdkBreakpoint(ChromiumLineBreakpoint chromiumLineBreakpoint) {
+ return uiToSdkMap.get(chromiumLineBreakpoint);
+ }
+
+ public synchronized ChromiumLineBreakpoint getUiBreakpoint(Breakpoint sdkBreakpoint) {
+ return sdkToUiMap.get(sdkBreakpoint);
+ }
+
+ public synchronized void add(Breakpoint sdkBreakpoint, ChromiumLineBreakpoint uiBreakpoint) {
+ Object conflict1 = uiToSdkMap.put(uiBreakpoint, sdkBreakpoint);
+ Object conflict2 = sdkToUiMap.put(sdkBreakpoint, uiBreakpoint);
+ if (conflict1 != null || conflict2 != null) {
+ throw new RuntimeException();
+ }
+ }
+
+ public synchronized void remove(ChromiumLineBreakpoint lineBreakpoint) {
+ Breakpoint sdkBreakpoint = uiToSdkMap.remove(lineBreakpoint);
+ if (sdkBreakpoint == null) {
+ throw new RuntimeException();
+ }
+ sdkToUiMap.remove(sdkBreakpoint);
+ }
+
+ public synchronized void clear() {
+ sdkToUiMap.clear();
+ uiToSdkMap.clear();
+ }
+ }
+}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointRegistry.java Mon Jun 07 16:33:07 2010 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,227 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.debug.core.model;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Map;
-
-import org.chromium.sdk.Breakpoint;
-import org.chromium.sdk.Script;
-
-/**
- * A registry of existing breakpoints associated with their script locations. It
- * is used to restore
- */
-public class BreakpointRegistry {
-
- /**
- * Script identifier for a breakpoint location.
- */
- public static class ScriptIdentifier {
- private final String name;
-
- private final long id;
-
- private final int startLine;
-
- private final int endLine;
-
- public static ScriptIdentifier forScript(Script script) {
- String name = script.getName();
- return new ScriptIdentifier(
- name,
- name != null ? -1 : script.getId(),
- script.getStartLine(),
- script.getEndLine());
- }
-
- private ScriptIdentifier(String name, long id, int startLine, int endLine) {
- this.name = name;
- this.id = id;
- this.startLine = startLine;
- this.endLine = endLine;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + (int) (id ^ (id >>> 32));
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- result = prime * result + 17 * startLine + 19 * endLine;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof ScriptIdentifier)) {
- return false;
- }
- ScriptIdentifier that = (ScriptIdentifier) obj;
- if (this.startLine != that.startLine || this.endLine != that.endLine) {
- return false;
- }
- if (name == null) {
- // an unnamed script, only id is known
- return that.name == null && this.id == that.id;
- }
- // a named script
- return this.name.equals(that.name);
- }
- }
-
- static class BreakpointLocation {
- private final ScriptIdentifier scriptIdentifier;
-
- private final int line;
-
- public BreakpointLocation(ScriptIdentifier scriptIdentifier, int line) {
- this.scriptIdentifier = scriptIdentifier;
- this.line = line;
- }
-
- public ScriptIdentifier getScriptIdentifier() {
- return scriptIdentifier;
- }
-
- public int getLine() {
- return line;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + line;
- result = prime * result + ((scriptIdentifier == null)
- ? 0
- : scriptIdentifier.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof BreakpointLocation)) {
- return false;
- }
- BreakpointLocation that = (BreakpointLocation) obj;
- return (this.line == that.line && eq(this.scriptIdentifier, that.scriptIdentifier));
- }
- }
-
- /**
- * A breakpoint accompanied by its line number in the corresponding enclosing
- * resource.
- */
- public static class BreakpointEntry {
- public final Breakpoint breakpoint;
-
- public final int line;
-
- private BreakpointEntry(Breakpoint breakpoint, int line) {
- this.breakpoint = breakpoint;
- this.line = line;
- }
-
- boolean isWithinScriptRange(Script script) {
- return line >= script.getStartLine() && line <= script.getEndLine();
- }
-
- @Override
- public int hashCode() {
- return 31 * line + 17 * breakpoint.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof BreakpointEntry)) {
- return false;
- }
- BreakpointEntry that = (BreakpointEntry) obj;
- return this.line == that.line && this.breakpoint.equals(that.breakpoint);
- }
- }
-
- private final Map<ScriptIdentifier, Collection<BreakpointEntry>> scriptIdToBreakpointEntries =
- new HashMap<ScriptIdentifier, Collection<BreakpointEntry>>();
-
- /**
- * Adds the given line breakpoint.
- *
- * @param script where the breakpoint is set
- * @param line (0-based, like in V8) in the script
- * @param breakpoint
- */
- public void add(Script script, int line, Breakpoint breakpoint) {
- ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
- Collection<BreakpointEntry> entries = scriptIdToBreakpointEntries.get(scriptId);
- if (entries == null) {
- entries = new HashSet<BreakpointEntry>();
- scriptIdToBreakpointEntries.put(scriptId, entries);
- }
- entries.add(new BreakpointEntry(breakpoint, line));
- }
-
- /**
- * Gets breakpoint entries for the given script. An empty collection for a
- * {@code null} script.
- *
- * @param script to extract the breakpoints for
- * @return the breakpoints that fall within the given script line range
- */
- public Collection<? extends BreakpointEntry> getBreakpointEntries(Script script) {
- if (script == null) {
- return Collections.emptySet();
- }
- Collection<BreakpointEntry> entries =
- scriptIdToBreakpointEntries.get(ScriptIdentifier.forScript(script));
- if (entries == null) {
- return Collections.emptySet();
- }
- Collection<BreakpointEntry> scriptBreakpoints = new LinkedList<BreakpointEntry>();
- // Linear search should work fairly well for a reasonable number of
- // breakpoints per script
- for (BreakpointEntry entry : entries) {
- if (entry.isWithinScriptRange(script)) {
- scriptBreakpoints.add(entry);
- }
- }
- return scriptBreakpoints;
- }
-
- /**
- * Removes the given line breakpoint in the given script. Does nothing for a
- * {@code null} script (which may be the case after a navigation + disconnect
- * when the resource referenced by the breakpoint marker is absent.)
- *
- * @param script where the breakpoint is set
- * @param line (0-based, like in V8) in the script
- * @param breakpoint
- */
- public void remove(Script script, int line, Breakpoint breakpoint) {
- if (script == null) {
- return;
- }
- ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
- Collection<BreakpointEntry> entries = scriptIdToBreakpointEntries.get(scriptId);
- if (entries == null) {
- return;
- }
- if (entries.size() == 1) {
- scriptIdToBreakpointEntries.remove(scriptId);
- } else {
- entries.remove(new BreakpointEntry(breakpoint, line));
- }
- }
-
- protected static boolean eq(Object left, Object right) {
- return left == right || (left != null && left.equals(right));
- }
-
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointSynchronizer.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,631 @@
+package org.chromium.debug.core.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.ChromiumSourceDirector;
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.CallbackSemaphore;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.SyncCallback;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IBreakpointManager;
+import org.eclipse.debug.core.model.IBreakpoint;
+
+/**
+ * A class responsible for comparing breakpoints in workspace and on remote VM and synchronizing
+ * them in both directions. {@link Direction#RESET_REMOTE} allows several synchronization
+ * jobs to different VMs.
+ */
+public class BreakpointSynchronizer {
+ private final JavascriptVm javascriptVm;
+ private final BreakpointMap.InTargetMap breakpointInTargetMap;
+ private final ChromiumSourceDirector sourceDirector;
+ private final BreakpointHelper breakpointHelper;
+ private final String debugModelId;
+
+ public BreakpointSynchronizer(JavascriptVm javascriptVm,
+ BreakpointMap.InTargetMap breakpointInTargetMap,
+ ChromiumSourceDirector sourceDirector, BreakpointHelper breakpointHelper,
+ String debugModelId) {
+ this.javascriptVm = javascriptVm;
+ this.breakpointInTargetMap = breakpointInTargetMap;
+ this.sourceDirector = sourceDirector;
+ this.breakpointHelper = breakpointHelper;
+ this.debugModelId = debugModelId;
+ }
+
+ /**
+ * Describes a direction the breakpoint synchronization should be performed in.
+ */
+ public enum Direction {
+
+ /**
+ * All breakpoints in remote VM/VMs are cleared/updated/created to conform to breakpoints in
+ * Eclipse workspace.
+ */
+ RESET_REMOTE,
+
+ /**
+ * All breakpoints in local workspace are cleared/updated/created to conform to breakpoints in
+ * remote VM (not applicable for multiple VMs).
+ */
+ RESET_LOCAL,
+
+ /**
+ * Breakpoints are created locally or remotely or tied together so that every breakpoint
+ * has a counterpart on other side.
+ */
+ MERGE
+ }
+
+ /**
+ * Additional interface used by {@link BreakpointSynchronizer}.
+ */
+ public interface BreakpointHelper {
+ /**
+ * Create breakpoint on remote VM (asynchronously) and link it to uiBreakpoint.
+ */
+ void createBreakpointOnRemote(ChromiumLineBreakpoint uiBreakpoint,
+ VmResourceId vmResourceId,
+ CreateCallback createCallback, SyncCallback syncCallback);
+
+ interface CreateCallback {
+ void failure(Exception ex);
+ void success();
+ }
+ }
+
+ public interface Callback {
+ void onDone(IStatus status);
+ }
+
+ /**
+ * The main entry method of the class. Asynchronously performs synchronization job.
+ * TODO(peter.rybin): consider some end-of-job notification for this method and possibly locks
+ */
+ public void syncBreakpoints(Direction direction, Callback callback) {
+ ReportBuilder reportBuilder = new ReportBuilder(direction);
+ StatusBuilder statusBuilder = new StatusBuilder(callback, reportBuilder);
+
+ statusBuilder.plan();
+ Exception ex = null;
+ try {
+ syncBreakpointsImpl(direction, statusBuilder);
+ } catch (RuntimeException e) {
+ ex = e;
+ } finally {
+ statusBuilder.done(ex);
+ }
+ }
+
+ private void syncBreakpointsImpl(final Direction direction, final StatusBuilder statusBuilder) {
+ // Collect the remote breakpoints.
+ Collection<? extends Breakpoint> sdkBreakpoints = readSdkBreakpoints(javascriptVm);
+ // Collect all local breakpoints.
+ Set<ChromiumLineBreakpoint> uiBreakpoints = getUiBreakpoints();
+
+ List<Breakpoint> sdkBreakpoints2 = new ArrayList<Breakpoint>(sdkBreakpoints.size());
+
+ if (direction != Direction.MERGE) {
+ breakpointInTargetMap.clear();
+ }
+
+ // Throw away all already linked breakpoints and put remaining into sdkBreakpoints2 list.
+ for (Breakpoint sdkBreakpoint : sdkBreakpoints) {
+ ChromiumLineBreakpoint uiBreakpoint = breakpointInTargetMap.getUiBreakpoint(sdkBreakpoint);
+ if (uiBreakpoint == null) {
+ // No mapping. Schedule for further processing.
+ sdkBreakpoints2.add(sdkBreakpoint);
+ } else {
+ // There is a live mapping. This set should also contain this breakpoint.
+ uiBreakpoints.remove(uiBreakpoint);
+ statusBuilder.getReportBuilder().increment(ReportBuilder.Property.LINKED);
+ }
+ }
+
+ // Sort all breakpoints by (script_name, line_number).
+ SortedBreakpoints<ChromiumLineBreakpoint> sortedUiBreakpoints =
+ sortBreakpoints(uiBreakpoints, uiBreakpointHandler);
+ SortedBreakpoints<Breakpoint> sortedSdkBreakpoints =
+ sortBreakpoints(sdkBreakpoints2, sdkBreakpointHandler);
+
+ BreakpointMerger breakpointMerger = new BreakpointMerger(direction, breakpointInTargetMap);
+
+ // Find all unlinked breakpoints on both sides.
+ mergeBreakpoints(breakpointMerger, sortedUiBreakpoints, sortedSdkBreakpoints);
+
+ List<Breakpoint> sdkBreakpointsToDelete;
+ List<Breakpoint> sdkBreakpointsToCreate;
+ List<ChromiumLineBreakpoint> uiBreakpointsToDelete;
+ List<ChromiumLineBreakpoint> uiBreakpointsToCreate;
+
+ // Plan actions for all breakpoints without pair.
+ if (direction == Direction.RESET_REMOTE) {
+ sdkBreakpointsToDelete = breakpointMerger.getMissingSdk();
+ sdkBreakpointsToCreate = Collections.emptyList();
+ } else {
+ sdkBreakpointsToCreate = breakpointMerger.getMissingSdk();
+ sdkBreakpointsToDelete = Collections.emptyList();
+ }
+
+ if (direction == Direction.RESET_LOCAL) {
+ uiBreakpointsToDelete = breakpointMerger.getMissingUi();
+ uiBreakpointsToCreate = Collections.emptyList();
+ } else {
+ uiBreakpointsToCreate = breakpointMerger.getMissingUi();
+ uiBreakpointsToDelete = Collections.emptyList();
+ }
+
+ // First delete everything, then create (we may need to re-create some breakpoints, so order
+ // is significant).
+ deteleBreakpoints(sdkBreakpointsToDelete, uiBreakpointsToDelete, statusBuilder);
+ createBreakpoints(sdkBreakpointsToCreate, uiBreakpointsToCreate, statusBuilder);
+ }
+
+ private void deteleBreakpoints(List<Breakpoint> sdkBreakpointsToDelete,
+ List<ChromiumLineBreakpoint> uiBreakpointsToDelete, final StatusBuilder statusBuilder) {
+ for (Breakpoint sdkBreakpoint : sdkBreakpointsToDelete) {
+ final PlannedTaskHelper deleteTaskHelper = new PlannedTaskHelper(statusBuilder);
+ JavascriptVm.BreakpointCallback callback = new JavascriptVm.BreakpointCallback() {
+ public void failure(String errorMessage) {
+ deleteTaskHelper.setException(new Exception(errorMessage));
+ }
+ public void success(Breakpoint breakpoint) {
+ statusBuilder.getReportBuilder().increment(ReportBuilder.Property.DELETED_ON_REMOTE);
+ }
+ };
+ sdkBreakpoint.clear(callback, deleteTaskHelper);
+ }
+ for (ChromiumLineBreakpoint uiBreakpoint : uiBreakpointsToDelete) {
+ ChromiumLineBreakpoint.getIgnoreList().add(uiBreakpoint);
+ try {
+ try {
+ uiBreakpoint.delete();
+ } catch (CoreException e) {
+ throw new RuntimeException(e);
+ }
+ } finally {
+ ChromiumLineBreakpoint.getIgnoreList().remove(uiBreakpoint);
+ }
+ statusBuilder.getReportBuilder().increment(ReportBuilder.Property.DELETED_LOCALLY);
+ }
+ }
+
+ private void createBreakpoints(List<Breakpoint> sdkBreakpointsToCreate,
+ List<ChromiumLineBreakpoint> uiBreakpointsToCreate, final StatusBuilder statusBuilder) {
+ IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
+ for (Breakpoint sdkBreakpoint : sdkBreakpointsToCreate) {
+ Object sourceElement = sourceDirector.getSourceElement(sdkBreakpoint);
+ if (sourceElement instanceof IFile == false) {
+ continue;
+ }
+ // We do not actually support working files for scripts with offset.
+ int script_line_offset = 0;
+ IFile resource = (IFile) sourceElement;
+ ChromiumLineBreakpoint uiBreakpoint;
+ try {
+ uiBreakpoint = ChromiumLineBreakpoint.Helper.createLocal(sdkBreakpoint, breakpointManager,
+ resource, script_line_offset, debugModelId);
+ breakpointInTargetMap.add(sdkBreakpoint, uiBreakpoint);
+ } catch (CoreException e) {
+ throw new RuntimeException(e);
+ }
+ statusBuilder.getReportBuilder().increment(ReportBuilder.Property.CREATED_LOCALLY);
+ }
+ for (ChromiumLineBreakpoint uiBreakpoint : uiBreakpointsToCreate) {
+ VmResourceId vmResourceId = uiBreakpointHandler.getVmResourceId(uiBreakpoint);
+ if (vmResourceId == null) {
+ // Actually we should not get here, because getScript call succeeded before.
+ continue;
+ }
+
+ final PlannedTaskHelper createTaskHelper = new PlannedTaskHelper(statusBuilder);
+ BreakpointHelper.CreateCallback createCallback = new BreakpointHelper.CreateCallback() {
+ public void success() {
+ statusBuilder.getReportBuilder().increment(ReportBuilder.Property.CREATED_ON_REMOTE);
+ }
+ public void failure(Exception ex) {
+ createTaskHelper.setException(ex);
+ }
+ };
+ breakpointHelper.createBreakpointOnRemote(uiBreakpoint, vmResourceId, createCallback,
+ createTaskHelper);
+ }
+ }
+
+ private static class BreakpointMerger extends Merger<ChromiumLineBreakpoint, Breakpoint> {
+ private final Direction direction;
+ private final List<ChromiumLineBreakpoint> missingUi = new ArrayList<ChromiumLineBreakpoint>();
+ private final List<Breakpoint> missingSdk = new ArrayList<Breakpoint>();
+ private final BreakpointMap.InTargetMap breakpointMap;
+
+ BreakpointMerger(Direction direction, BreakpointMap.InTargetMap breakpointMap) {
+ this.direction = direction;
+ this.breakpointMap = breakpointMap;
+ }
+ @Override
+ void both(ChromiumLineBreakpoint v1, Breakpoint v2) {
+ if (direction == Direction.MERGE) {
+ breakpointMap.add(v2, v1);
+ } else {
+ onlyFirst(v1);
+ onlySecond(v2);
+ }
+ }
+ @Override
+ void onlyFirst(ChromiumLineBreakpoint v1) {
+ missingUi.add(v1);
+ }
+ @Override
+ void onlySecond(Breakpoint v2) {
+ missingSdk.add(v2);
+ }
+ List<ChromiumLineBreakpoint> getMissingUi() {
+ return missingUi;
+ }
+ List<Breakpoint> getMissingSdk() {
+ return missingSdk;
+ }
+ }
+
+ /**
+ * A class responsible for creating a summary status of synchronization operation. The status
+ * is created once all asynchronous jobs have finished. Each job first registers itself
+ * via {@link #plan()} method and
+ * later reports its result via {@link #done(Exception)} method.
+ * When the last job is reporting its finishing, the status gets built and sent to
+ * {@link #callback}. If no exceptions were registered,
+ * status contains text report from {@link ReportBuilder}.
+ */
+ private static class StatusBuilder {
+ private final Callback callback;
+ private int plannedNumber = 0;
+ private final List<Exception> exceptions = new ArrayList<Exception>(0);
+ private boolean alreadyReported = false;
+ private final ReportBuilder reportBuilder;
+
+ StatusBuilder(Callback callback, ReportBuilder reportBuilder) {
+ this.callback = callback;
+ this.reportBuilder = reportBuilder;
+ }
+
+ ReportBuilder getReportBuilder() {
+ return reportBuilder;
+ }
+
+ public synchronized void plan() {
+ if (alreadyReported) {
+ throw new IllegalStateException();
+ }
+ plannedNumber++;
+ }
+
+ public void done(Exception ex) {
+ boolean timeToReport = doneImpl(ex);
+ if (timeToReport) {
+ reportResult();
+ }
+ }
+
+ private synchronized boolean doneImpl(Exception ex) {
+ if (ex != null) {
+ exceptions.add(ex);
+ }
+ plannedNumber--;
+ if (plannedNumber == 0) {
+ if (!alreadyReported) {
+ alreadyReported = true;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void reportResult() {
+ IStatus status;
+ if (exceptions.isEmpty()) {
+ status = new Status(IStatus.OK, ChromiumDebugPlugin.PLUGIN_ID,
+ "Breakpoint synchronization done: " + reportBuilder.build(), null); //$NON-NLS-1$
+ } else {
+ IStatus[] subStatuses = new IStatus[exceptions.size()];
+ for (int i = 0 ; i < subStatuses.length; i++) {
+ subStatuses[i] = new Status(IStatus.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+ exceptions.get(i).getMessage(), exceptions.get(i));
+ }
+ status = new MultiStatus(ChromiumDebugPlugin.PLUGIN_ID, IStatus.ERROR, subStatuses,
+ "Breakpoint synchronization errors", null); //$NON-NLS-1$
+ }
+ if (callback != null) {
+ callback.onDone(status);
+ }
+ }
+ }
+
+ private static class PlannedTaskHelper implements SyncCallback {
+ private final StatusBuilder statusBuilder;
+ private volatile Exception exception = null;
+ PlannedTaskHelper(StatusBuilder statusBuilder) {
+ this.statusBuilder = statusBuilder;
+ statusBuilder.plan();
+ }
+ public void callbackDone(RuntimeException e) {
+ if (e != null) {
+ exception = e;
+ }
+ statusBuilder.done(exception);
+ }
+ void setException(Exception ex) {
+ exception = ex;
+ }
+ }
+
+ /**
+ * A class that contains several conunters.
+ */
+ private static class ReportBuilder {
+ enum Property {
+ LINKED,
+ CREATED_LOCALLY,
+ DELETED_LOCALLY,
+ CREATED_ON_REMOTE,
+ DELETED_ON_REMOTE;
+ String getVisibleName() {
+ return toString();
+ }
+ }
+
+ private final Direction direction;
+ private final Map<Property, AtomicInteger> counters;
+
+ ReportBuilder(Direction direction) {
+ this.direction = direction;
+ counters = new EnumMap<Property, AtomicInteger>(Property.class);
+ for (Property property : Property.class.getEnumConstants()) {
+ counters.put(property, new AtomicInteger(0));
+ }
+ }
+
+ public void increment(Property property) {
+ counters.get(property).addAndGet(1);
+ }
+
+ public String build() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("direction=").append(direction); //$NON-NLS-1$
+ for (Map.Entry<Property, AtomicInteger> en : counters.entrySet()) {
+ int number = en.getValue().get();
+ if (number == 0) {
+ continue;
+ }
+ builder.append(" ").append(en.getKey().getVisibleName()); //$NON-NLS-1$
+ builder.append("=").append(number); //$NON-NLS-1$
+ }
+ return builder.toString();
+ }
+ }
+
+ /**
+ * A handler for properties of breakpoint type B that helps reading them.
+ */
+ private static abstract class PropertyHandler<B> {
+ /** @return vm resource name or null */
+ abstract VmResourceId getVmResourceId(B breakpoint);
+ /** @return 0-based number */
+ abstract long getLineNumber(B breakpoint);
+ }
+
+ private final PropertyHandler<ChromiumLineBreakpoint> uiBreakpointHandler =
+ new PropertyHandler<ChromiumLineBreakpoint>() {
+ @Override
+ long getLineNumber(ChromiumLineBreakpoint chromiumLineBreakpoint) {
+ int lineNumber;
+ try {
+ // TODO(peter.rybin): Consider supporting inline scripts here.
+ return chromiumLineBreakpoint.getLineNumber() - 1;
+ } catch (CoreException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ VmResourceId getVmResourceId(ChromiumLineBreakpoint chromiumLineBreakpoint) {
+ IMarker marker = chromiumLineBreakpoint.getMarker();
+ if (marker == null) {
+ return null;
+ }
+ IResource resource = marker.getResource();
+ if (resource instanceof IFile == false) {
+ return null;
+ }
+ IFile file = (IFile) resource;
+ try {
+ return sourceDirector.getReverseSourceLookup().findVmResource(file);
+ } catch (CoreException e) {
+ throw new RuntimeException("Failed to read script name from breakpoint", e); //$NON-NLS-1$
+ }
+ }
+ };
+
+ private static final PropertyHandler<Breakpoint> sdkBreakpointHandler =
+ new PropertyHandler<Breakpoint>() {
+ @Override
+ long getLineNumber(Breakpoint breakpoint) {
+ return breakpoint.getLineNumber();
+ }
+
+ @Override
+ VmResourceId getVmResourceId(Breakpoint breakpoint) {
+ if (breakpoint.getType() == Breakpoint.Type.SCRIPT_NAME) {
+ return VmResourceId.forName(breakpoint.getScriptName());
+ } else {
+ Long scriptId = breakpoint.getScriptId();
+ if (scriptId == null) {
+ return null;
+ }
+ return VmResourceId.forId(scriptId);
+ }
+ }
+ };
+
+ /**
+ * A helping structure that holds field of complicated type.
+ */
+ private static class SortedBreakpoints<B> {
+ final Map<VmResourceId, Map<Long, B>> data;
+
+ SortedBreakpoints(Map<VmResourceId, Map<Long, B>> data) {
+ this.data = data;
+ }
+ }
+
+ /**
+ * Put all breakpoints into map script_name -> line_number -> breakpoint.
+ */
+ private static <B> SortedBreakpoints<B> sortBreakpoints(Collection<? extends B> breakpoints,
+ PropertyHandler<B> handler) {
+ Map<VmResourceId, Map<Long, B>> result = new HashMap<VmResourceId, Map<Long, B>>();
+ for (B breakpoint : breakpoints) {
+ VmResourceId vmResourceId = handler.getVmResourceId(breakpoint);
+ if (vmResourceId == null) {
+ continue;
+ }
+ Map<Long, B> subMap = result.get(vmResourceId);
+ if (subMap == null) {
+ subMap = new HashMap<Long, B>(3);
+ result.put(vmResourceId, subMap);
+ }
+ long line = handler.getLineNumber(breakpoint);
+ // For simplicity we ignore multiple breakpoints on the same line.
+ subMap.put(line, breakpoint);
+ }
+ return new SortedBreakpoints<B>(result);
+ }
+
+ /**
+ * A class that implements merge operation for a particular complete/incomplete pair of values.
+ */
+ private static abstract class Merger<V1, V2> {
+ abstract void onlyFirst(V1 v1);
+ abstract void onlySecond(V2 v2);
+ abstract void both(V1 v1, V2 v2);
+ }
+
+ /**
+ * Merges values of 2 maps.
+ * @param map2 must implement {@link Map#remove} method.
+ */
+ private static <K, V1, V2> void mergeMaps(Map<K, V1> map1, Map<K, V2> map2,
+ Merger<V1, V2> merger) {
+ for (Map.Entry<K, V1> en : map1.entrySet()) {
+ V2 v2 = map2.remove(en.getKey());
+ if (v2 == null) {
+ merger.onlyFirst(en.getValue());
+ } else {
+ merger.both(en.getValue(), v2);
+ }
+ }
+ for (V2 v2 : map2.values()) {
+ merger.onlySecond(v2);
+ }
+ }
+
+ private static <B1, B2> void mergeBreakpoints(final Merger<B1, B2> perBreakpointMerger,
+ SortedBreakpoints<B1> side1, SortedBreakpoints<B2> side2) {
+ Merger<Map<Long, B1>, Map<Long, B2>> perScriptMerger =
+ new Merger<Map<Long,B1>, Map<Long,B2>>() {
+ @Override
+ void both(Map<Long, B1> v1, Map<Long, B2> v2) {
+ mergeMaps(v1, v2, perBreakpointMerger);
+ }
+
+ @Override
+ void onlyFirst(Map<Long, B1> v1) {
+ mergeMaps(v1, Collections.<Long, B2>emptyMap(), perBreakpointMerger);
+ }
+
+ @Override
+ void onlySecond(Map<Long, B2> v2) {
+ mergeMaps(Collections.<Long, B1>emptyMap(), v2, perBreakpointMerger);
+ }
+ };
+ mergeMaps(side1.data, side2.data, perScriptMerger);
+ }
+
+
+ private static Collection<? extends Breakpoint> readSdkBreakpoints(JavascriptVm javascriptVm) {
+ class CallbackImpl implements JavascriptVm.ListBreakpointsCallback {
+ public void failure(Exception exception) {
+ problem = exception;
+ }
+
+ public void success(Collection<? extends Breakpoint> breakpoints) {
+ result = breakpoints;
+ }
+ Collection<? extends Breakpoint> getResult() {
+ if (problem != null) {
+ throw new RuntimeException("Failed to synchronize breakpoints", problem); //$NON-NLS-1$
+ }
+ return result;
+ }
+ Exception problem = null;
+ Collection<? extends Breakpoint> result = null;
+ }
+
+ CallbackImpl callback = new CallbackImpl();
+ CallbackSemaphore callbackSemaphore = new CallbackSemaphore();
+
+ javascriptVm.listBreakpoints(callback, callbackSemaphore);
+ boolean res = callbackSemaphore.tryAcquireDefault();
+ if (!res) {
+ throw new RuntimeException("Timeout"); //$NON-NLS-1$
+ }
+
+ return callback.getResult();
+ }
+
+ // We need this method to return Set for future purposes.
+ private Set<ChromiumLineBreakpoint> getUiBreakpoints() {
+ IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
+ Set<ChromiumLineBreakpoint> result = new HashSet<ChromiumLineBreakpoint>();
+
+ for (IBreakpoint breakpoint: breakpointManager.getBreakpoints(debugModelId)) {
+ if (breakpoint instanceof ChromiumLineBreakpoint == false) {
+ continue;
+ }
+ ChromiumLineBreakpoint chromiumLineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
+ result.add(chromiumLineBreakpoint);
+ }
+ return result;
+ }
+
+ public static class ProtocolNotSupportedOnRemote extends Exception {
+ ProtocolNotSupportedOnRemote() {
+ }
+ ProtocolNotSupportedOnRemote(String message, Throwable cause) {
+ super(message, cause);
+ }
+ ProtocolNotSupportedOnRemote(String message) {
+ super(message);
+ }
+ ProtocolNotSupportedOnRemote(Throwable cause) {
+ super(cause);
+ }
+ }
+}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/ChromiumLineBreakpoint.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ChromiumLineBreakpoint.java Mon Jun 07 16:51:19 2010 -0700
@@ -4,13 +4,21 @@
package org.chromium.debug.core.model;
+import java.util.ArrayList;
+import java.util.List;
+
import org.chromium.debug.core.ChromiumDebugPlugin;
import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.JavascriptVm.BreakpointCallback;
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.LineBreakpoint;
import org.eclipse.osgi.util.NLS;
@@ -26,25 +34,14 @@
/** Condition */
private static final String CONDITION_ATTR = ChromiumDebugPlugin.PLUGIN_ID + ".condition"; //$NON-NLS-1$
- private Breakpoint browserBreakpoint;
-
/**
* Default constructor is required for the breakpoint manager to re-create
* persisted breakpoints. After instantiating a breakpoint, the setMarker
* method is called to restore this breakpoint's attributes.
*/
- // FIXME(apavlov): now this does not restore the browserBreakpoint value
public ChromiumLineBreakpoint() {
}
- public void setBreakpoint(Breakpoint breakpoint) {
- this.browserBreakpoint = breakpoint;
- }
-
- public Breakpoint getBrowserBreakpoint() {
- return browserBreakpoint;
- }
-
/**
* Constructs a line breakpoint on the given resource at the given line number
* (line number is 1-based).
@@ -53,15 +50,15 @@
* @param lineNumber 1-based line number of the breakpoint
* @throws CoreException if unable to create the breakpoint
*/
- public ChromiumLineBreakpoint(final IResource resource, final int lineNumber)
- throws CoreException {
+ public ChromiumLineBreakpoint(final IResource resource, final int lineNumber,
+ final String modelId) throws CoreException {
IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
IMarker marker = resource.createMarker(ChromiumDebugPlugin.BP_MARKER);
setMarker(marker);
marker.setAttribute(IBreakpoint.ENABLED, Boolean.TRUE);
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
- marker.setAttribute(IBreakpoint.ID, getModelIdentifier());
+ marker.setAttribute(IBreakpoint.ID, modelId);
marker.setAttribute(IMarker.MESSAGE, NLS.bind(
Messages.JsLineBreakpoint_MessageMarkerFormat, resource.getName(), lineNumber));
}
@@ -104,21 +101,100 @@
}
public String getModelIdentifier() {
- return VProjectWorkspaceBridge.DEBUG_MODEL_ID;
+ return getMarker().getAttribute(IBreakpoint.ID, "");
}
- public void changed() {
- if (browserBreakpoint != null) {
- browserBreakpoint.setCondition(getCondition());
- browserBreakpoint.setEnabled(isEnabled());
- browserBreakpoint.setIgnoreCount(getIgnoreCount());
- browserBreakpoint.flush(null);
+ /**
+ * A helper that propagates changes in Eclipse Debugger breakpoints (i.e.
+ * {@link ChromiumLineBreakpoint}) to ChromeDevTools SDK breakpoints. Note that
+ * {@link ChromiumLineBreakpoint} can't do it itself, because it may correspond to several
+ * SDK {@link JavascriptVm}'s simultaneously.
+ */
+ public static class Helper {
+ public interface CreateOnRemoveCallback {
+ void success(Breakpoint breakpoint);
+ void failure(String errorMessage);
+ }
+
+ public static void createOnRemote(ChromiumLineBreakpoint uiBreakpoint,
+ VmResourceId scriptId, DebugTargetImpl debugTarget,
+ final CreateOnRemoveCallback createOnRemoveCallback,
+ SyncCallback syncCallback) throws CoreException {
+ JavascriptVm javascriptVm = debugTarget.getJavascriptEmbedder().getJavascriptVm();
+
+ // ILineBreakpoint lines are 1-based while V8 lines are 0-based
+ final int line = (uiBreakpoint.getLineNumber() - 1);
+ BreakpointCallback callback = new BreakpointCallback() {
+ public void success(Breakpoint sdkBreakpoint) {
+ createOnRemoveCallback.success(sdkBreakpoint);
+ }
+ public void failure(String errorMessage) {
+ createOnRemoveCallback.failure(errorMessage);
+ }
+ };
+
+ javascriptVm.setBreakpoint(scriptId.getTypeForBreakpoint(),
+ scriptId.getTargetForBreakpoint(),
+ line,
+ Breakpoint.EMPTY_VALUE,
+ uiBreakpoint.isEnabled(),
+ uiBreakpoint.getCondition(),
+ uiBreakpoint.getIgnoreCount(),
+ callback, syncCallback);
+ }
+
+ public static void updateOnRemote(Breakpoint sdkBreakpoint,
+ ChromiumLineBreakpoint uiBreakpoint) {
+ sdkBreakpoint.setCondition(uiBreakpoint.getCondition());
+ sdkBreakpoint.setEnabled(uiBreakpoint.isEnabled());
+ sdkBreakpoint.setIgnoreCount(uiBreakpoint.getIgnoreCount());
+ sdkBreakpoint.flush(null, null);
+ }
+
+ public static ChromiumLineBreakpoint createLocal(Breakpoint sdkBreakpoint,
+ IBreakpointManager breakpointManager, IFile resource, int script_line_offset,
+ String debugModelId) throws CoreException {
+ ChromiumLineBreakpoint uiBreakpoint = new ChromiumLineBreakpoint(resource,
+ (int) sdkBreakpoint.getLineNumber() + 1 + script_line_offset,
+ debugModelId);
+ uiBreakpoint.setCondition(sdkBreakpoint.getCondition());
+ uiBreakpoint.setEnabled(sdkBreakpoint.isEnabled());
+ uiBreakpoint.setIgnoreCount(sdkBreakpoint.getIgnoreCount());
+ ignoreList.add(uiBreakpoint);
+ try {
+ breakpointManager.addBreakpoint(uiBreakpoint);
+ } finally {
+ ignoreList.remove(uiBreakpoint);
+ }
+ return uiBreakpoint;
}
}
- public void clear() {
- if (browserBreakpoint != null) {
- browserBreakpoint.clear(null);
+ private static final BreakpointIgnoreList ignoreList = new BreakpointIgnoreList();
+
+ public static BreakpointIgnoreList getIgnoreList() {
+ return ignoreList;
+ }
+
+ public static class BreakpointIgnoreList {
+ private final List<ChromiumLineBreakpoint> list = new ArrayList<ChromiumLineBreakpoint>(1);
+
+ public boolean contains(IBreakpoint breakpoint) {
+ return list.contains(breakpoint);
+ }
+
+ public void remove(ChromiumLineBreakpoint lineBreakpoint) {
+ boolean res = list.remove(lineBreakpoint);
+ if (!res) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public void add(ChromiumLineBreakpoint lineBreakpoint) {
+ if (list.contains(lineBreakpoint)) {
+ throw new IllegalStateException();
+ }
+ list.add(lineBreakpoint);
}
}
}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/DebugTargetImpl.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/DebugTargetImpl.java Mon Jun 07 16:51:19 2010 -0700
@@ -4,6 +4,7 @@
package org.chromium.debug.core.model;
+import java.util.ArrayList;
import java.util.List;
import org.chromium.debug.core.ChromiumDebugPlugin;
@@ -12,7 +13,9 @@
import org.chromium.sdk.DebugEventListener;
import org.chromium.sdk.ExceptionData;
import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.LiveEditDebugEventListener;
import org.chromium.sdk.Script;
+import org.chromium.sdk.UpdatableScript;
import org.chromium.sdk.DebugContext.State;
import org.chromium.sdk.DebugContext.StepAction;
import org.eclipse.core.resources.IFile;
@@ -338,8 +341,8 @@
return super.getAdapter(adapter);
}
- public IFile getScriptResource(Script script) {
- return workspaceRelations.getScriptResource(script);
+ public VmResource getVmResource(IFile resource) throws CoreException {
+ return workspaceRelations.findVmResourceFromWorkspaceFile(resource);
}
public JavascriptThread getThread() {
@@ -361,7 +364,7 @@
private final DebugEventListenerImpl debugEventListener = new DebugEventListenerImpl();
- class DebugEventListenerImpl implements DebugEventListener {
+ class DebugEventListenerImpl implements DebugEventListener, LiveEditDebugEventListener {
// Synchronizes calls from ReaderThread of Connection and one call from some worker thread
private final Object suspendResumeMonitor = new Object();
private boolean alreadyResumedOrSuspended = false;
@@ -396,6 +399,11 @@
workspaceRelations.scriptLoaded(newScript);
}
+ public void scriptContentChanged(UpdatableScript newScript) {
+ listenerBlock.waitUntilReady();
+ workspaceRelations.reloadScript(newScript);
+ }
+
public void suspended(DebugContext context) {
listenerBlock.waitUntilReady();
synchronized (suspendResumeMonitor) {
@@ -419,6 +427,12 @@
}
}
+ public void synchronizeBreakpoints(BreakpointSynchronizer.Direction direction,
+ BreakpointSynchronizer.Callback callback) {
+ workspaceRelations.synchronizeBreakpoints(direction, callback);
+ }
+
+
private void logExceptionFromContext(DebugContext context) {
ExceptionData exceptionData = context.getExceptionData();
List<? extends CallFrame> callFrames = context.getCallFrames();
@@ -491,9 +505,18 @@
public WorkspaceBridge.JsLabelProvider getLabelProvider() {
return workspaceBridgeFactory.getLabelProvider();
}
-
- public int getLineNumber(CallFrame stackFrame) {
- return workspaceRelations.getLineNumber(stackFrame);
+
+ public static List<DebugTargetImpl> getAllDebugTargetImpls() {
+ IDebugTarget[] array = DebugPlugin.getDefault().getLaunchManager().getDebugTargets();
+ List<DebugTargetImpl> result = new ArrayList<DebugTargetImpl>(array.length);
+ for (IDebugTarget target : array) {
+ if (target instanceof DebugTargetImpl == false) {
+ continue;
+ }
+ DebugTargetImpl debugTargetImpl = (DebugTargetImpl) target;
+ result.add(debugTargetImpl);
+ }
+ return result;
}
private static class ListenerBlock {
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/LineBreakpointAdapter.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/LineBreakpointAdapter.java Mon Jun 07 16:51:19 2010 -0700
@@ -28,7 +28,7 @@
ITextEditor editorPart = (ITextEditor) part;
IResource resource = (IResource) editorPart.getEditorInput().getAdapter(IResource.class);
if (resource != null &&
- ChromiumDebugPluginUtil.CHROMIUM_EXTENSION.equals(resource.getFileExtension())) {
+ ChromiumDebugPluginUtil.SUPPORTED_EXTENSIONS.contains(resource.getFileExtension())) {
return editorPart;
}
}
@@ -62,7 +62,8 @@
}
// Line numbers start with 0 in V8, with 1 in Eclipse.
- ChromiumLineBreakpoint lineBreakpoint = new ChromiumLineBreakpoint(resource, lineNumber + 1);
+ ChromiumLineBreakpoint lineBreakpoint = new ChromiumLineBreakpoint(resource, lineNumber + 1,
+ getDebugModelId());
DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(lineBreakpoint);
}
}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/Messages.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/Messages.java Mon Jun 07 16:51:19 2010 -0700
@@ -59,6 +59,12 @@
public static String JsThread_ThreadLabelSuspendedExceptionFormat;
+ public static String MockUpResourceWriter_NOT_A_JAVASCRIPT;
+
+ public static String MockUpResourceWriter_SCRIPT_WITHOUT_TEXT;
+
+ public static String MockUpResourceWriter_SCRIPTS_OVERLAPPED;
+
public static String ResourceManager_UnnamedScriptName;
public static String StackFrame_NameFormat;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/MockUpResourceWriter.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,140 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.chromium.sdk.Script;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Creates from a set of scripts a mock-up of full resource (scripts are positioned according
+ * to their line numbers and the whitespace is filled with text pattern).
+ */
+class MockUpResourceWriter {
+ static String writeScriptSource(List<Script> scripts) {
+ ArrayList<Script> sortedScriptsArrayList = new ArrayList<Script>(scripts);
+ Collections.sort(sortedScriptsArrayList, scriptPositionComparator);
+ MockUpResourceWriter writer = new MockUpResourceWriter();
+ for (Script script : sortedScriptsArrayList) {
+ writer.writeSript(script);
+ }
+ return writer.getResult();
+ }
+
+
+ private int line = 0;
+ private int col = 0;
+ private final StringBuilder builder = new StringBuilder();
+
+ private void writeSript(Script script) {
+ int scriptLine = script.getStartLine();
+ if (scriptLine > line) {
+ fillLines(scriptLine - line);
+ line = scriptLine;
+ } else if (scriptLine < line) {
+ writeLineMissMessage(scriptLine);
+ } else {
+ int scriptCol = script.getStartColumn();
+ if (col < scriptCol) {
+ fillColumns(scriptCol - col);
+ } else if (col > scriptCol) {
+ final boolean expectCorrectStartColumn = false;
+ if (expectCorrectStartColumn) {
+ writeln(""); //$NON-NLS-1$
+ writeLineMissMessage(scriptLine);
+ } else {
+ // Ignore.
+ }
+ }
+ }
+
+ if (script.hasSource()) {
+ writeText(script.getSource());
+ } else {
+ writeln(Messages.MockUpResourceWriter_SCRIPT_WITHOUT_TEXT);
+ }
+ }
+
+ private void writeLineMissMessage(int scriptLine) {
+ writeln(NLS.bind(Messages.MockUpResourceWriter_SCRIPTS_OVERLAPPED,
+ line + 1 - scriptLine, scriptLine + 1));
+ }
+
+ private void writeText(String text) {
+ int pos = 0;
+ while (true) {
+ int nlPos = text.indexOf('\n', pos);
+ if (nlPos == -1) {
+ String rest = text.substring(pos);
+ builder.append(rest);
+ col += rest.length();
+ break;
+ }
+ writeln(text.substring(pos, nlPos));
+ pos = nlPos + 1;
+ }
+ }
+
+ private void writeln(String str) {
+ builder.append(str).append('\n');
+ line++;
+ col = 0;
+ }
+
+ private void fillLines(int lines) {
+ if (col != 0) {
+ builder.append('\n');
+ line++;
+ }
+ for (int i = 0; i < lines; i++) {
+ builder.append(NOT_A_JAVASCRIPT_FILLER).append('\n');
+ }
+ line += lines;
+ col = 0;
+ }
+
+ private void fillColumns(int number) {
+ if (number < NOT_A_JAVASCRIPT_FILLER.length()) {
+ if (number < 1) {
+ // Nothing.
+ } else if (number == 1) {
+ builder.append('*');
+ col += 1;
+ } else {
+ builder.append('{');
+ for (int i = 2; i < number; i++) {
+ builder.append('*');
+ }
+ builder.append('}');
+ col += number;
+ }
+ }
+ }
+
+ private String getResult() {
+ return builder.toString();
+ }
+
+ private static final String NOT_A_JAVASCRIPT_FILLER =
+ Messages.MockUpResourceWriter_NOT_A_JAVASCRIPT;
+
+ private static final Comparator<Script> scriptPositionComparator = new Comparator<Script>() {
+ public int compare(Script o1, Script o2) {
+ int line1 = o1.getStartLine();
+ int line2 = o2.getStartLine();
+ if (line1 < line2) {
+ return -1;
+ } else if (line1 == line2) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ };
+}
\ No newline at end of file
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/ResourceManager.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ResourceManager.java Mon Jun 07 16:51:19 2010 -0700
@@ -1,134 +1,154 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.debug.core.model;
+import java.util.ArrayList;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.chromium.debug.core.ChromiumDebugPlugin;
-import org.chromium.debug.core.model.BreakpointRegistry.BreakpointEntry;
-import org.chromium.debug.core.model.BreakpointRegistry.ScriptIdentifier;
import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
import org.chromium.sdk.Script;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
-import org.eclipse.debug.core.DebugPlugin;
/**
* This object handles the mapping between {@link Script}s and their corresponding resources
* inside Eclipse.
*/
public class ResourceManager {
- private final Map<IFile, Script> resourceToScript = new HashMap<IFile, Script>();
- private final Map<ScriptIdentifier, IFile> scriptIdToResource =
- new HashMap<ScriptIdentifier, IFile>();
private final IProject debugProject;
- private final BreakpointRegistry breakpointRegistry;
- private Object fileBeingAdded;
+
+ private final Map<VmResourceId, VmResourceInfo> vmResourceId2Info =
+ new HashMap<VmResourceId, VmResourceInfo>();
+ private final Map<IFile, VmResourceInfo> file2Info = new HashMap<IFile, VmResourceInfo>();
+
+ public ResourceManager(IProject debugProject) {
+ this.debugProject = debugProject;
+ }
- public ResourceManager(IProject debugProject, BreakpointRegistry breakpointRegistry) {
- this.debugProject = debugProject;
- this.breakpointRegistry = breakpointRegistry;
+ public synchronized VmResource getVmResource(VmResourceId id) {
+ VmResourceInfo info = vmResourceId2Info.get(id);
+ if (info == null) {
+ return null;
+ }
+ return info.vmResourceImpl;
+ }
+
+ /**
+ * @param eclipseSourceName eclipse source file name
+ * (what {@link VmResourceId#getEclipseSourceName()} returns)
+ */
+ public IFile getFile(String eclipseSourceName) {
+ VmResourceId id = VmResourceId.parseString(eclipseSourceName);
+ VmResourceInfo info = vmResourceId2Info.get(id);
+ if (info == null) {
+ return null;
+ }
+ return info.file;
}
- public synchronized void putScript(Script script, IFile resource) {
- ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
- resourceToScript.put(resource, script);
- scriptIdToResource.put(scriptId, resource);
+ public synchronized VmResourceId getResourceId(IFile resource) {
+ VmResourceInfo info = file2Info.get(resource);
+ if (info == null) {
+ return null;
+ }
+ return info.id;
}
- public synchronized Script getScript(IFile resource) {
- return resourceToScript.get(resource);
+ public synchronized void addScript(Script newScript) {
+ VmResourceId id = VmResourceId.forScript(newScript);
+ VmResourceInfo info = vmResourceId2Info.get(id);
+ if (info == null) {
+ String fileNameTemplate = createFileNameTemplate(id, newScript);
+ IFile scriptFile = ChromiumDebugPluginUtil.createFile(debugProject, fileNameTemplate);
+ info = new VmResourceInfo(scriptFile, id);
+ vmResourceId2Info.put(id, info);
+ file2Info.put(scriptFile, info);
+
+ info.scripts.add(newScript);
+ writeScriptSource(info.scripts, info.file);
+ } else {
+ // TODO(peter.rybin): support adding scripts to one resource at once not to rewrite file
+ // every time.
+ info.scripts.add(newScript);
+ writeScriptSource(info.scripts, info.file);
+ }
}
- public synchronized IFile getResource(Script script) {
- return scriptIdToResource.get(ScriptIdentifier.forScript(script));
- }
-
- public synchronized boolean scriptHasResource(Script script) {
- return getResource(script) != null;
+ public synchronized void reloadScript(Script script) {
+ VmResourceId id = VmResourceId.forScript(script);
+ VmResourceInfo info = vmResourceId2Info.get(id);
+ if (info == null) {
+ throw new RuntimeException("Script file not found"); //$NON-NLS-1$
+ }
+ if (!info.scripts.contains(script)) {
+ throw new RuntimeException("Script not found in internal list"); //$NON-NLS-1$
+ }
+ writeScriptSource(info.scripts, info.file);
}
public synchronized void clear() {
deleteAllScriptFiles();
- resourceToScript.clear();
- scriptIdToResource.clear();
+
+ vmResourceId2Info.clear();
+ file2Info.clear();
}
private void deleteAllScriptFiles() {
- if (!resourceToScript.isEmpty()) {
- try {
- ResourcesPlugin.getWorkspace().delete(
- resourceToScript.keySet().toArray(new IFile[resourceToScript.size()]), true, null);
- } catch (CoreException e) {
- ChromiumDebugPlugin.log(e);
- }
+ try {
+ ResourcesPlugin.getWorkspace().delete(
+ file2Info.keySet().toArray(new IFile[file2Info.size()]), true, null);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+
+ private String createFileNameTemplate(VmResourceId id, Script newScript) {
+ return id.createFileNameTemplate(true);
+ }
+
+ private static void writeScriptSource(List<Script> scripts, IFile file) {
+ String fileSource = MockUpResourceWriter.writeScriptSource(scripts);
+
+ try {
+ ChromiumDebugPluginUtil.writeFile(file, fileSource);
+ } catch (final CoreException e) {
+ ChromiumDebugPlugin.log(e);
}
}
- public synchronized void addScript(Script script) {
- IFile scriptFile = getResource(script);
- if (scriptFile == null) {
- scriptFile = ChromiumDebugPluginUtil.createFile(debugProject, getScriptResourceName(script));
- fileBeingAdded = scriptFile;
- try {
- putScript(script, scriptFile);
- writeScriptSource(script, scriptFile);
- // Perhaps restore breakpoints for the reloaded script
- List<ChromiumLineBreakpoint> breakpoints = new LinkedList<ChromiumLineBreakpoint>();
- for (BreakpointEntry entry : breakpointRegistry.getBreakpointEntries(script)) {
- ChromiumLineBreakpoint lineBreakpoint;
- try {
- lineBreakpoint = new ChromiumLineBreakpoint(scriptFile, entry.line + 1);
- } catch (CoreException e) {
- ChromiumDebugPlugin.log(e);
- continue;
+ private class VmResourceInfo {
+ final IFile file;
+ final VmResourceId id;
+ final ArrayList<Script> scripts = new ArrayList<Script>(1);
+ VmResourceInfo(IFile file, VmResourceId id) {
+ this.file = file;
+ this.id = id;
+ }
+
+ final VmResource vmResourceImpl = new VmResource() {
+ public VmResourceId getId() {
+ return id;
+ }
+
+ public Script getScript() {
+ synchronized (ResourceManager.this) {
+ if (scripts.size() != 1) {
+ throw new UnsupportedOperationException(
+ "Not supported for complex resources"); //$NON-NLS-1$
}
- lineBreakpoint.setBreakpoint(entry.breakpoint);
- breakpoints.add(lineBreakpoint);
- }
- if (!breakpoints.isEmpty()) {
- try {
- DebugPlugin.getDefault().getBreakpointManager().addBreakpoints(
- breakpoints.toArray(new ChromiumLineBreakpoint[breakpoints.size()]));
- } catch (CoreException e) {
- ChromiumDebugPlugin.log(e);
- }
+ return scripts.get(0);
}
- } finally {
- fileBeingAdded = null;
}
- }
- }
-
- private String getScriptResourceName(Script script) {
- String name = script.getName();
- if (name == null) {
- name = Messages.ResourceManager_UnnamedScriptName;
- }
- return name;
- }
-
- private static void writeScriptSource(Script script, IFile file) {
- if (script.hasSource()) {
- try {
- ChromiumDebugPluginUtil.writeFile(file, script.getSource());
- } catch (final CoreException e) {
- ChromiumDebugPlugin.log(e);
+ public String getFileName() {
+ return file.getName();
}
- }
- }
-
- /**
- * @return whether the given file is being added to the target project
- */
- public boolean isAddingFile(IFile file) {
- return file.equals(fileBeingAdded);
+ };
}
}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/StackFrame.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/StackFrame.java Mon Jun 07 16:51:19 2010 -0700
@@ -201,7 +201,14 @@
}
public int getLineNumber() throws DebugException {
- return getDebugTarget().getLineNumber(stackFrame);
+ // convert 0-based to 1-based
+ int inScriptLine = stackFrame.getLineNumber() + 1;
+ Script script = stackFrame.getScript();
+ if (script != null) {
+ return inScriptLine + script.getStartLine();
+ } else {
+ return inScriptLine;
+ }
}
public int getCharStart() throws DebugException {
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/VProjectWorkspaceBridge.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/VProjectWorkspaceBridge.java Mon Jun 07 16:51:19 2010 -0700
@@ -7,6 +7,8 @@
import java.util.Collection;
import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.ChromiumSourceDirector;
+import org.chromium.debug.core.model.BreakpointSynchronizer.Callback;
import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
import org.chromium.sdk.Breakpoint;
import org.chromium.sdk.CallFrame;
@@ -14,19 +16,15 @@
import org.chromium.sdk.ExceptionData;
import org.chromium.sdk.JavascriptVm;
import org.chromium.sdk.Script;
-import org.chromium.sdk.JavascriptVm.BreakpointCallback;
+import org.chromium.sdk.SyncCallback;
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.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.model.IBreakpoint;
-import org.eclipse.debug.core.model.ISourceLocator;
-import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.osgi.util.NLS;
/**
@@ -61,17 +59,28 @@
private final IProject debugProject;
private final JavascriptVm javascriptVm;
private final ResourceManager resourceManager;
- private final BreakpointRegistry breakpointRegistry = new BreakpointRegistry();
private final DebugTargetImpl debugTargetImpl;
+ private final BreakpointMap.InTargetMap breakpointInTargetMap = new BreakpointMap.InTargetMap();
+ private final ChromiumSourceDirector sourceDirector;
public VProjectWorkspaceBridge(String projectName, DebugTargetImpl debugTargetImpl,
JavascriptVm javascriptVm) {
this.debugTargetImpl = debugTargetImpl;
this.javascriptVm = javascriptVm;
this.debugProject = ChromiumDebugPluginUtil.createEmptyProject(projectName);
- this.resourceManager = new ResourceManager(debugProject, breakpointRegistry);
+ this.resourceManager = new ResourceManager(debugProject);
+
ILaunch launch = debugTargetImpl.getLaunch();
- launch.setSourceLocator(sourceLocator);
+
+ sourceDirector = (ChromiumSourceDirector) launch.getSourceLocator();
+ sourceDirector.initializeVProjectContainers(debugProject, resourceManager);
+ }
+
+ public void synchronizeBreakpoints(BreakpointSynchronizer.Direction direction,
+ Callback callback) {
+ BreakpointSynchronizer synchronizer = new BreakpointSynchronizer(javascriptVm,
+ breakpointInTargetMap, sourceDirector, breakpointHandler, DEBUG_MODEL_ID);
+ synchronizer.syncBreakpoints(direction, callback);
}
public void launchRemoved() {
@@ -81,21 +90,6 @@
}
public void beforeDetach() {
- IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
- IBreakpoint[] breakpoints =
- breakpointManager.getBreakpoints(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);
- }
}
public void handleVmResetEvent() {
@@ -123,139 +117,183 @@
});
}
- public IFile getScriptResource(Script script) {
- return resourceManager.getResource(script);
+ public VmResource findVmResourceFromWorkspaceFile(IFile resource) throws CoreException {
+ VmResourceId id = findVmResourceIdFromWorkspaceFile(resource);
+ if (id == null) {
+ return null;
+ }
+ return resourceManager.getVmResource(id);
+ }
+
+ private VmResourceId findVmResourceIdFromWorkspaceFile(IFile resource) throws CoreException {
+ return sourceDirector.getReverseSourceLookup().findVmResource(resource);
+ }
+
+ public void reloadScript(Script script) {
+ resourceManager.reloadScript(script);
}
public BreakpointHandler getBreakpointHandler() {
return breakpointHandler;
}
- private final BreakpointHandler breakpointHandler = new BreakpointHandler() {
+ private final BreakpointHandlerImpl breakpointHandler = new BreakpointHandlerImpl();
+
+ private class BreakpointHandlerImpl implements BreakpointHandler,
+ BreakpointSynchronizer.BreakpointHelper {
public boolean supportsBreakpoint(IBreakpoint breakpoint) {
return DEBUG_MODEL_ID.equals(breakpoint.getModelIdentifier()) &&
!debugTargetImpl.isDisconnected();
}
+
+ public ChromiumLineBreakpoint tryCastBreakpoint(IBreakpoint breakpoint) {
+ if (!supportsBreakpoint(breakpoint)) {
+ return null;
+ }
+ if (breakpoint instanceof ChromiumLineBreakpoint == false) {
+ return null;
+ }
+ return (ChromiumLineBreakpoint) breakpoint;
+ }
+
public void breakpointAdded(IBreakpoint breakpoint) {
- if (!supportsBreakpoint(breakpoint)) {
+ ChromiumLineBreakpoint lineBreakpoint = tryCastBreakpoint(breakpoint);
+ if (lineBreakpoint == null) {
+ return;
+ }
+ if (ChromiumLineBreakpoint.getIgnoreList().contains(breakpoint)) {
+ return;
+ }
+ if (!lineBreakpoint.isEnabled()) {
+ return;
+ }
+ IFile file = (IFile) lineBreakpoint.getMarker().getResource();
+ VmResourceId vmResourceId;
+ try {
+ vmResourceId = findVmResourceIdFromWorkspaceFile(file);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(
+ new Exception("Failed to resolve script for the file " + file, e)); //$NON-NLS-1$
return;
}
+ if (vmResourceId == null) {
+ // Might be a script from a different debug target
+ return;
+ }
+
+ createBreakpointOnRemote(lineBreakpoint, vmResourceId, null, null);
+ }
+
+ public void createBreakpointOnRemote(final ChromiumLineBreakpoint lineBreakpoint,
+ final VmResourceId vmResourceId,
+ final CreateCallback createCallback, SyncCallback syncCallback) {
try {
- if (breakpoint.isEnabled()) {
- // Class cast is ensured by the supportsBreakpoint implementation
- final ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
- IFile file = (IFile) breakpoint.getMarker().getResource();
- if (resourceManager.isAddingFile(file)) {
- return; // restoring breakpoints in progress
- }
- final Script script = resourceManager.getScript(file);
- if (script == null) {
- // Might be a script from a different debug target
- return;
- }
- // ILineBreakpoint lines are 1-based while V8 lines are 0-based
- 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);
+ ChromiumLineBreakpoint.Helper.CreateOnRemoveCallback callback =
+ new ChromiumLineBreakpoint.Helper.CreateOnRemoveCallback() {
+ public void success(Breakpoint breakpoint) {
+ breakpointInTargetMap.add(breakpoint, lineBreakpoint);
+ if (createCallback != null) {
+ createCallback.success();
}
- };
- 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);
+ }
+ public void failure(String errorMessage) {
+ if (createCallback == null) {
+ ChromiumDebugPlugin.logError(errorMessage);
+ } else {
+ createCallback.failure(new Exception(errorMessage));
+ }
}
- }
+ };
+
+ ChromiumLineBreakpoint.Helper.createOnRemote(lineBreakpoint, vmResourceId, debugTargetImpl,
+ callback, syncCallback);
} catch (CoreException e) {
- ChromiumDebugPlugin.log(e);
+ ChromiumDebugPlugin.log(new Exception("Failed to create breakpoint in " + //$NON-NLS-1$
+ getTargetNameSafe(), e));
}
}
public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
- if (!supportsBreakpoint(breakpoint)) {
+ ChromiumLineBreakpoint lineBreakpoint = tryCastBreakpoint(breakpoint);
+ if (lineBreakpoint == null) {
+ return;
+ }
+ if (ChromiumLineBreakpoint.getIgnoreList().contains(lineBreakpoint)) {
return;
}
- // Class cast is ensured by the supportsBreakpoint implementation
- ((ChromiumLineBreakpoint) breakpoint).changed();
+ Breakpoint sdkBreakpoint = breakpointInTargetMap.getSdkBreakpoint(lineBreakpoint);
+ if (sdkBreakpoint == null) {
+ return;
+ }
+
+ try {
+ ChromiumLineBreakpoint.Helper.updateOnRemote(sdkBreakpoint, lineBreakpoint);
+ } catch (RuntimeException e) {
+ ChromiumDebugPlugin.log(new Exception("Failed to change breakpoint in " + //$NON-NLS-1$
+ getTargetNameSafe(), e));
+ }
+
}
public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
- if (!supportsBreakpoint(breakpoint)) {
+ ChromiumLineBreakpoint lineBreakpoint = tryCastBreakpoint(breakpoint);
+ if (lineBreakpoint == null) {
+ return;
+ }
+ if (ChromiumLineBreakpoint.getIgnoreList().contains(lineBreakpoint)) {
return;
}
+
+ Breakpoint sdkBreakpoint = breakpointInTargetMap.getSdkBreakpoint(lineBreakpoint);
+ if (sdkBreakpoint == null) {
+ return;
+ }
+
try {
- if (breakpoint.isEnabled()) {
- // Class cast is ensured by the supportsBreakpoint implementation
- ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
- lineBreakpoint.clear();
- breakpointRegistry.remove(
- resourceManager.getScript((IFile) breakpoint.getMarker().getResource()),
- lineBreakpoint.getLineNumber() - 1,
- lineBreakpoint.getBrowserBreakpoint());
+ if (!breakpoint.isEnabled()) {
+ return;
}
} catch (CoreException e) {
ChromiumDebugPlugin.log(e);
+ return;
}
+ JavascriptVm.BreakpointCallback callback = new JavascriptVm.BreakpointCallback() {
+ public void failure(String errorMessage) {
+ ChromiumDebugPlugin.log(new Exception("Failed to remove breakpoint in " + //$NON-NLS-1$
+ getTargetNameSafe() + ": " + errorMessage)); //$NON-NLS-1$
+ }
+ public void success(Breakpoint breakpoint) {
+ }
+ };
+ try {
+ sdkBreakpoint.clear(callback, null);
+ } catch (RuntimeException e) {
+ ChromiumDebugPlugin.log(new Exception("Failed to remove breakpoint in " + //$NON-NLS-1$
+ getTargetNameSafe(), e));
+ }
+ breakpointInTargetMap.remove(lineBreakpoint);
}
public void breakpointsHit(Collection<? extends Breakpoint> breakpointsHit) {
if (breakpointsHit.isEmpty()) {
return;
}
- IBreakpoint[] breakpoints =
- DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(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
+
+ for (Breakpoint sdkBreakpoint : breakpointsHit) {
+ ChromiumLineBreakpoint uiBreakpoint = breakpointInTargetMap.getUiBreakpoint(sdkBreakpoint);
+ if (uiBreakpoint != null) {
+ uiBreakpoint.setIgnoreCount(-1); // reset ignore count as we've hit it
}
}
}
- };
-
- public int getLineNumber(CallFrame stackFrame) {
- // convert 0-based to 1-based
- return stackFrame.getLineNumber() + 1;
+ private String getTargetNameSafe() {
+ try {
+ return debugTargetImpl.getLaunch().getLaunchConfiguration().getName();
+ } catch (RuntimeException e) {
+ return "<unknown>"; //$NON-NLS-1$
+ }
+ }
}
-
- /**
- * 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;
- }
- StackFrame jsStackFrame = (StackFrame) stackFrame;
-
- Script script = jsStackFrame.getCallFrame().getScript();
- if (script == null) {
- return null;
- }
-
- return resourceManager.getResource(script);
- }
- };
private final static JsLabelProvider LABEL_PROVIDER = new JsLabelProvider() {
public String getTargetLabel(DebugTargetImpl debugTarget) {
@@ -298,16 +336,18 @@
CallFrame callFrame = stackFrame.getCallFrame();
String name = callFrame.getFunctionName();
Script script = callFrame.getScript();
+ String scriptName;
if (script == null) {
- return Messages.StackFrame_UnknownScriptName;
+ scriptName = Messages.StackFrame_UnknownScriptName;
+ } else {
+ scriptName = VmResourceId.forScript(script).getEclipseSourceName();
}
- int line = script.getStartLine() + stackFrame.getLineNumber();
+ int line = stackFrame.getLineNumber();
if (line != -1) {
name = NLS.bind(Messages.StackFrame_NameFormat,
- new Object[] {name, script.getName(), line});
+ new Object[] {name, scriptName, line});
}
return name;
}
};
-
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/VmResource.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,25 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.sdk.Script;
+
+/**
+ * A representation of V8 VM resource. The exact nature of the resource is unspecified, we
+ * only know it may contain one or more {@link Script}s. Typically resource is .js or .html file.
+ */
+
+public interface VmResource {
+ VmResourceId getId();
+
+ /**
+ * @return script if this resource entirely consists of 1 script, otherwise throws exception
+ * @throws UnsupportedOperationException if this resource does not entirely consists of 1 script
+ * TODO(peter.rybin): redesign this method to normally work with html resources.
+ */
+ Script getScript();
+
+ String getFileName();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/VmResourceId.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,118 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.Script;
+
+/**
+ * Id of resources loaded in V8 VM. We only know that they may have name (typically filename or
+ * URL) or numerical id instead. This class reflects this.
+ * The class also contains several utility methods that probably should be separated in the future.
+ */
+public class VmResourceId {
+
+ public static VmResourceId forName(String scriptName) {
+ return new VmResourceId(scriptName);
+ }
+
+ public static VmResourceId forId(long scriptId) {
+ return new VmResourceId(Long.valueOf(scriptId));
+ }
+
+ public static VmResourceId forScript(Script script) {
+ if (script.getName() != null) {
+ return forName(script.getName());
+ } else {
+ return forId(script.getId());
+ }
+ }
+
+ private final Object value;
+
+ private VmResourceId(Object value) {
+ if (value == null) {
+ throw new IllegalArgumentException("Null id value"); //$NON-NLS-1$
+ }
+ this.value = value;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof VmResourceId == false) {
+ return false;
+ }
+ VmResourceId other = (VmResourceId) obj;
+ return this.value.equals(other.value);
+ }
+
+ /**
+ * @return parameter for {@link JavascriptVm#setBreakpoint} method.
+ */
+ public Breakpoint.Type getTypeForBreakpoint() {
+ if (value instanceof String) {
+ return Breakpoint.Type.SCRIPT_NAME;
+ } else {
+ return Breakpoint.Type.SCRIPT_ID;
+ }
+ }
+
+ /**
+ * @return parameter for {@link JavascriptVm#setBreakpoint} method.
+ */
+ public String getTargetForBreakpoint() {
+ return value.toString();
+ }
+
+ String createFileNameTemplate(boolean isEval) {
+ if (value instanceof String) {
+ return value.toString();
+ } else {
+ if (isEval) {
+ return "<eval #" + value + ">"; //$NON-NLS-1$ //$NON-NLS-2$
+ } else {
+ return "<no name #" + value + ">"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ }
+
+ @Override
+ public String toString() {
+ return getEclipseSourceName();
+ }
+
+ /**
+ * @return source name that is suitable for Eclipse debug source lookup.
+ */
+ public String getEclipseSourceName() {
+ if (value instanceof String) {
+ String stringValue = (String) value;
+ if (stringValue.startsWith("#")) {
+ // Quote it.
+ stringValue = "#" + stringValue;
+ }
+ return stringValue;
+ } else {
+ return "#" + value;
+ }
+ }
+
+ public static VmResourceId parseString(String name) {
+ if (name.startsWith("##")) {
+ return VmResourceId.forName(name.substring(1));
+ } else if (name.startsWith("#")) {
+ return VmResourceId.forId(Long.parseLong(name.substring(1)));
+ } else {
+ return VmResourceId.forName(name);
+ }
+ }
+}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/WorkspaceBridge.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/WorkspaceBridge.java Mon Jun 07 16:51:19 2010 -0700
@@ -7,10 +7,10 @@
import java.util.Collection;
import org.chromium.sdk.Breakpoint;
-import org.chromium.sdk.CallFrame;
import org.chromium.sdk.JavascriptVm;
import org.chromium.sdk.Script;
import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.IBreakpointListener;
import org.eclipse.debug.core.model.IBreakpoint;
@@ -48,10 +48,12 @@
JsLabelProvider getLabelProvider();
}
+ VmResource findVmResourceFromWorkspaceFile(IFile resource) throws CoreException;
+
/**
- * Helps UI actions to map from script to resource.
+ * Initiates script reloading from remote VM.
*/
- IFile getScriptResource(Script script);
+ void reloadScript(Script script);
/**
* Called at starting period of time, requires all scripts to be (re)loaded.
@@ -87,12 +89,6 @@
BreakpointHandler getBreakpointHandler();
/**
- * Returns editor line number for the provided call stack frame applying all required
- * editor-specific translations.
- */
- int getLineNumber(CallFrame stackFrame);
-
- /**
* Breakpoint-related aspect of {@link WorkspaceBridge} interface.
*/
interface BreakpointHandler extends IBreakpointListener {
@@ -119,4 +115,12 @@
*/
String getStackFrameLabel(StackFrame stackFrame) throws DebugException;
}
+
+ /**
+ * Performs breakpoint synchronization between remote VM and Eclipse IDE. This operation is
+ * partially asynchronous: it blocks for reading breakpoints, but returns before all remote
+ * changes are performed. When operations is fully complete, callback gets invoked.
+ */
+ void synchronizeBreakpoints(BreakpointSynchronizer.Direction direction,
+ BreakpointSynchronizer.Callback callback);
}
--- a/org.chromium.debug.core/src/org/chromium/debug/core/model/messages.properties Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/messages.properties Mon Jun 07 16:51:19 2010 -0700
@@ -25,6 +25,9 @@
JsThread_ThreadLabelRunning=Running
JsThread_ThreadLabelSuspended=Suspended
JsThread_ThreadLabelSuspendedExceptionFormat=Suspended (exception "{0}")
+MockUpResourceWriter_NOT_A_JAVASCRIPT={not a JavaScript}
+MockUpResourceWriter_SCRIPT_WITHOUT_TEXT={ JavaScript script without text }
+MockUpResourceWriter_SCRIPTS_OVERLAPPED={ scripts overlapped. the following script must be {0} line(s) upper (at line {1}) }
ResourceManager_UnnamedScriptName=(program)
StackFrame_NameFormat={0} [{1}:{2}]
StackFrame_UnknownScriptName=<unknown>
--- a/org.chromium.debug.core/src/org/chromium/debug/core/util/ChromiumDebugPluginUtil.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/util/ChromiumDebugPluginUtil.java Mon Jun 07 16:51:19 2010 -0700
@@ -7,6 +7,11 @@
import java.io.ByteArrayInputStream;
import java.io.File;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import org.chromium.debug.core.ChromiumDebugPlugin;
import org.chromium.debug.core.efs.ChromiumScriptFileSystem;
@@ -36,7 +41,19 @@
*/
public class ChromiumDebugPluginUtil {
- public static final String CHROMIUM_EXTENSION = "chromium"; //$NON-NLS-1$
+ private static final String CHROMIUM_EXTENSION = "chromium"; //$NON-NLS-1$
+
+ public static final Set<String> SUPPORTED_EXTENSIONS =
+ new HashSet<String>(Arrays.asList(CHROMIUM_EXTENSION, "js", //$NON-NLS-1$
+ "html", "htm")); //$NON-NLS-1$ //$NON-NLS-2$
+
+ public static final List<String> SUPPORTED_EXTENSIONS_SUFFIX_LIST;
+ static {
+ SUPPORTED_EXTENSIONS_SUFFIX_LIST = new ArrayList<String>(SUPPORTED_EXTENSIONS.size());
+ for (String extension : SUPPORTED_EXTENSIONS) {
+ SUPPORTED_EXTENSIONS_SUFFIX_LIST.add("." + extension); //$NON-NLS-1$
+ }
+ }
public static final String JS_DEBUG_PROJECT_NATURE = "org.chromium.debug.core.jsnature"; //$NON-NLS-1$
--- a/org.chromium.debug.ui/META-INF/MANIFEST.MF Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.ui/META-INF/MANIFEST.MF Mon Jun 07 16:51:19 2010 -0700
@@ -14,6 +14,7 @@
org.eclipse.core.variables;bundle-version="3.2.100",
org.eclipse.ui.ide;bundle-version="3.4.1",
org.chromium.debug.core;bundle-version="0.1.5",
- org.chromium.sdk;bundle-version="0.1.5"
+ org.chromium.sdk;bundle-version="0.1.5",
+ org.eclipse.compare;bundle-version="3.5.0"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.5
--- a/org.chromium.debug.ui/plugin.xml Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.ui/plugin.xml Mon Jun 07 16:51:19 2010 -0700
@@ -29,7 +29,9 @@
modes="debug"
name="%chromiumLaunchName"
delegateName="Debug Chromium JavaScript"
- delegateDescription="JavaScript debugger for Chromium">
+ delegateDescription="JavaScript debugger for Chromium"
+ sourceLocatorId="org.chromium.debug.core.ChromiumSourceDirector"
+ sourcePathComputerId="org.chromium.debug.core.ChromiumSourceComputer">
</launchConfigurationType>
<launchConfigurationType
id="org.chromium.debug.ui.LaunchType$StandaloneV8"
@@ -37,7 +39,9 @@
modes="debug"
name="%standaloneV8LaunchName"
delegateName="Debug Standalone V8 JavaScript"
- delegateDescription="JavaScript debugger for Standalone V8">
+ delegateDescription="JavaScript debugger for Standalone V8"
+ sourceLocatorId="org.chromium.debug.core.ChromiumSourceDirector"
+ sourcePathComputerId="org.chromium.debug.core.ChromiumSourceComputer">
</launchConfigurationType>
<launchConfigurationType
id="org.chromium.debug.ui.ConsolePseudoConfigurationType"
@@ -185,9 +189,9 @@
id="org.chromium.debug.ui.actions.EnableDisableBreakpointAction"/>
<action
label="Breakpoint Properties..."
- class="org.chromium.debug.ui.actions.JsBreakpointPropertiesRulerActionDelegate"
+ class="org.chromium.debug.ui.actions.JsBreakpointPropertiesRulerAction$Delegate"
menubarPath="group.properties"
- id="org.chromium.debug.ui.actions.JavaBreakpointPropertiesRulerActionDelegate">
+ id="org.chromium.debug.ui.actions.JavaBreakpointPropertiesRulerAction$Delegate">
</action>
<action
label="Toggle Enablement"
@@ -200,17 +204,111 @@
<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
+ objectClass="org.chromium.debug.core.model.ChromiumLineBreakpoint"
+ id="org.chromium.debug.core.model.ChromiumLineBreakpoint.object_actions">
+ <action
+ label="Breakpoint Properties..."
+ class="org.chromium.debug.ui.actions.JsBreakpointPropertiesAction"
+ menubarPath="group.properties"
+ id="org.chromium.debug.ui.actions.JsBreakpointPropertiesAction">
+ </action>
+ </objectContribution>
+
+ <objectContribution
objectClass="org.chromium.debug.core.model.Variable"
id="org.chromium.debug.ui.ChromiumVariableActions">
<action
label="%OpenFunctionAction.label"
- class="org.chromium.debug.ui.actions.OpenFunctionAction"
+ class="org.chromium.debug.ui.actions.OpenFunctionAction$ForVariable"
+ menubarPath="emptyNavigationGroup"
+ enablesFor="1"
+ id="org.chromium.debug.ui.actions.OpenFunctionAction$ForVariable">
+ </action>
+ </objectContribution>
+ <objectContribution
+ objectClass="org.eclipse.debug.core.model.IWatchExpression"
+ id="org.chromium.debug.ui.ChromiumExpressionActions">
+ <action
+ label="%OpenFunctionAction.label"
+ class="org.chromium.debug.ui.actions.OpenFunctionAction$ForExpression"
menubarPath="emptyNavigationGroup"
enablesFor="1"
- id="org.chromium.debug.ui.actions.OpenFunctionAction">
+ id="org.chromium.debug.ui.actions.OpenFunctionAction$ForExpression">
+ </action>
+ </objectContribution>
+ <!-- Experimental actions, temporary disabled. -->
+ <objectContribution
+ objectClass="org.eclipse.core.resources.mapping.ResourceMapping"
+ adaptable="true"
+ id="org.chromium.debug.ui.ChromiumSourceFileActionsId">
+ <menu id="org.chromium.debug.ui.ChromiumSourceFileActionsId.MenuId"
+ label="V8 Debugging"
+ >
+ <separator
+ name="group0">
+ </separator>
+ </menu>
+ <action
+ label="Compare with VM Source"
+ class="org.chromium.debug.ui.actions.CompareChangesAction"
+ menubarPath="org.chromium.debug.ui.ChromiumSourceFileActionsId.MenuId/group0"
+ enablesFor="1"
+ id="org.chromium.debug.ui.actions.CompareChangesAction">
+ </action>
+ <action
+ label="Push Changes to VM"
+ class="org.chromium.debug.ui.actions.PushChangesAction"
+ menubarPath="org.chromium.debug.ui.ChromiumSourceFileActionsId.MenuId/group0"
+ enablesFor="1"
+ id="org.chromium.debug.ui.actions.PushChangesAction">
</action>
</objectContribution>
</extension>
+
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.debug.ui.DebugPerspective">
+ <actionSet
+ id="org.chromium.debug.ui.ChromiumDebugActionSet">
+ </actionSet>
+ </perspectiveExtension>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.popupMenus">
+ <objectContribution
+ objectClass="org.eclipse.debug.core.ILaunch"
+ id="org.chromium.debug.ui.actions.actions-for-launch">
+ <menu id="org.chromium.debug.ui.actions.SynchronizeBreakpoints.MenuId"
+ label="Synchronize JavaScript Breakpoints"
+ path="launchGroup"
+ >
+ <separator
+ name="group0">
+ </separator>
+ </menu>
+ <action
+ label="Reset on Remote"
+ class="org.chromium.debug.ui.actions.SynchronizeBreakpoints$ResetRemote"
+ menubarPath="org.chromium.debug.ui.actions.SynchronizeBreakpoints.MenuId/group0"
+ id="org.chromium.debug.ui.actions.SynchronizeBreakpoints$ResetRemote.forLaunch">
+ </action>
+ <action
+ label="Reset on Local"
+ class="org.chromium.debug.ui.actions.SynchronizeBreakpoints$ResetLocal"
+ menubarPath="org.chromium.debug.ui.actions.SynchronizeBreakpoints.MenuId/group0"
+ id="org.chromium.debug.ui.actions.SynchronizeBreakpoints$ResetLocal.forLaunch">
+ </action>
+ <action
+ label="Merge Remote and Local"
+ class="org.chromium.debug.ui.actions.SynchronizeBreakpoints$Merge"
+ menubarPath="org.chromium.debug.ui.actions.SynchronizeBreakpoints.MenuId/group0"
+ id="org.chromium.debug.ui.actions.SynchronizeBreakpoints$Merge.forLaunch">
+ </action>
+ </objectContribution>
+ </extension>
+
<extension
point="org.eclipse.ui.propertyPages">
<page
@@ -229,4 +327,14 @@
</enabledWhen>
</page>
</extension>
+
+
+ <extension point="org.eclipse.debug.ui.sourceContainerPresentations">
+ <sourceContainerPresentation
+ browserClass="org.chromium.debug.ui.source.SourceNameMapperContainerPresentation"
+ containerTypeID="org.chromium.debug.core.SourceNameMapperContainer.type"
+ icon="res/standalone_v8_16.png"
+ id="org.chromium.debug.ui.SourceNameMapperContainerPresentation">
+ </sourceContainerPresentation>
+ </extension>
</plugin>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/DialogUtils.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,404 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * A small set of utility classes that help programming dialog window logic.
+ */
+public class DialogUtils {
+ /*
+ * Part 1. Update graph.
+ *
+ * All logical elements of dialog and dependencies between them are modeled as DAG.
+ * The data flows from vertices that corresponds to various input fields through
+ * some transformations to the terminal vertices that are in-dialog helper messages and OK button.
+ *
+ * A linear data flow (no forks, no merges) is suitable for data transformation and
+ * is programmed manually. Forks and merges are hard to dispatch manually and are managed by
+ * class Updater.
+ *
+ * Updater knows about "source" vertices that may have several outgoing edges and about
+ * "consumer" vertices that may have several incoming edges. Based on source vertex changes
+ * updater updates consumer vertices in topological order.
+ */
+
+ /**
+ * Represents source vertex for Updater. Technically updater uses this interface only as a flag
+ * interface, because the only methods it uses are {@link Object#equals}/Object{@link #hashCode}.
+ */
+ public interface ValueSource<T> {
+ /**
+ * Method is not used by updater, for convenience only.
+ * @return current value of the vertex
+ */
+ T getValue();
+ }
+
+ /**
+ * Represents consumer vertex for Updater. Each instance should be explicitly registered in
+ * Updater.
+ */
+ public interface ValueConsumer {
+ /**
+ * Called by updater when some linked sources have changed and it's time this vertex
+ * got updated. {@link Updater#reportChanged} may be called if some {@line ValueSource}s have
+ * changed during this update (but this should not break topological order of the graph).
+ */
+ void update(Updater updater);
+ }
+
+ /**
+ * Helps to conduct update for vertices in a value graph.
+ * Technically Updater does not see a real graph, because it doesn't support vertices
+ * that are simultaneously source and consumer. Programmer helps manage other edges manually by
+ * calling {@link #reportChanged} method.
+ */
+ public static class Updater {
+ private final LinkedHashMap<ValueConsumer, Boolean> needsUpdateMap =
+ new LinkedHashMap<ValueConsumer, Boolean>();
+ private final Map<ValueSource<?>, List<ValueConsumer>> reversedDependenciesMap =
+ new HashMap<ValueSource<?>, List<ValueConsumer>>();
+ private boolean alreadyUpdating = false;
+
+ public void addConsumer(ValueConsumer value, ValueSource<?> ... dependencies) {
+ addConsumer(value, Arrays.asList(dependencies));
+ }
+
+ /**
+ * Registers a consumer vertex with all its dependencies.
+ */
+ public void addConsumer(ValueConsumer value, List<? extends ValueSource<?>> dependencies) {
+ Boolean res = needsUpdateMap.put(value, Boolean.FALSE);
+ if (res != null) {
+ throw new IllegalArgumentException("Already added"); //$NON-NLS-1$
+ }
+ for (ValueSource<?> dep : dependencies) {
+ List<ValueConsumer> reversedDeps = reversedDependenciesMap.get(dep);
+ if (reversedDeps == null) {
+ reversedDeps = new ArrayList<ValueConsumer>(2);
+ reversedDependenciesMap.put(dep, reversedDeps);
+ }
+ reversedDeps.add(value);
+ }
+ }
+
+ /**
+ * Reports about sources that have been changed and plans future update of consumers. This
+ * method may be called at any time (it is not thread-safe though).
+ */
+ public void reportChanged(ValueSource<?> source) {
+ List<ValueConsumer> reversedDeps = reversedDependenciesMap.get(source);
+ if (reversedDeps != null) {
+ for (ValueConsumer consumer : reversedDeps) {
+ needsUpdateMap.put(consumer, Boolean.TRUE);
+ }
+ }
+ }
+
+ /**
+ * Performs update of all vertices that need it. If some sources are reported changed
+ * during the run of this method, their consumers are also updated.
+ */
+ public void update() {
+ if (alreadyUpdating) {
+ return;
+ }
+ alreadyUpdating = true;
+ try {
+ updateImpl();
+ } finally {
+ alreadyUpdating = false;
+ }
+ }
+
+ private void updateImpl() {
+ boolean hasChanges = true;
+ while (hasChanges) {
+ hasChanges = false;
+ for (Map.Entry<ValueConsumer, Boolean> en : needsUpdateMap.entrySet()) {
+ if (en.getValue() == Boolean.TRUE) {
+ en.setValue(Boolean.FALSE);
+ ValueConsumer currentValue = en.getKey();
+ currentValue.update(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates all consumer vertices in graph.
+ */
+ public void updateAll() {
+ for (Map.Entry<?, Boolean> en : needsUpdateMap.entrySet()) {
+ en.setValue(Boolean.TRUE);
+ }
+ update();
+ }
+ }
+
+ /**
+ * A basic implementation of object that is both consumer and source. Updater will treat
+ * as 2 separate objects.
+ */
+ public static abstract class ValueProcessor<T> implements ValueConsumer, ValueSource<T> {
+ private T currentValue = null;
+ public T getValue() {
+ return currentValue;
+ }
+ protected void setCurrentValue(T currentValue) {
+ this.currentValue = currentValue;
+ }
+ }
+
+ /*
+ * Part 2. Optional data type etc
+ *
+ * Since dialog should deal with error user entry, the typical data type is either value or error.
+ * This is implemented as Optional interface. Most of data transformations should work only
+ * when all inputs are non-error and generate error in return otherwise. This is implemented in
+ * ExpressionProcessor.
+ */
+
+
+ /**
+ * A primitive approach to "optional" algebraic type. This type is T + Set<Message>.
+ */
+ public interface Optional<V> {
+ V getNormal();
+ boolean isNormal();
+ Set<? extends Message> errorMessages();
+ }
+
+ public static <V> Optional<V> createOptional(final V value) {
+ return new Optional<V>() {
+ public Set<Message> errorMessages() {
+ return Collections.emptySet();
+ }
+ public V getNormal() {
+ return value;
+ }
+ public boolean isNormal() {
+ return true;
+ }
+ };
+ }
+
+ public static <V> Optional<V> createErrorOptional(Message message) {
+ return createErrorOptional(Collections.singleton(message));
+ }
+
+ public static <V> Optional<V> createErrorOptional(final Set<? extends Message> messages) {
+ return new Optional<V>() {
+ public Set<? extends Message> errorMessages() {
+ return messages;
+ }
+ public V getNormal() {
+ throw new UnsupportedOperationException();
+ }
+ public boolean isNormal() {
+ return false;
+ }
+ };
+ }
+
+ /**
+ * A user interface message for dialog window. It has text and priority that helps choosing
+ * the most important message it there are many of them.
+ */
+ public static class Message {
+ private final String text;
+ private final MessagePriority priority;
+ public Message(String text, MessagePriority priority) {
+ this.text = text;
+ this.priority = priority;
+ }
+ public String getText() {
+ return text;
+ }
+ public MessagePriority getPriority() {
+ return priority;
+ }
+ }
+
+ /**
+ * Priority of a user interface message.
+ * Constants are listed from most important to least important.
+ */
+ public enum MessagePriority {
+ BLOCKING_PROBLEM(IMessageProvider.ERROR),
+ BLOCKING_INFO(IMessageProvider.NONE),
+ WARNING(IMessageProvider.WARNING);
+
+ private final int messageProviderType;
+ private MessagePriority(int messageProviderType) {
+ this.messageProviderType = messageProviderType;
+ }
+ public int getMessageProviderType() {
+ return messageProviderType;
+ }
+ }
+
+ /**
+ * A base class for the source-consumer pair that accepts several values as a consumer,
+ * performs a calculation over them and gives it away the result via source interface.
+ * Some sources may be of Optional type. If some of sources have error value the corresponding
+ * error value is returned automatically.
+ * <p>
+ * The implementation should override a single method {@link #calculateNormal}.
+ */
+ public static abstract class ExpressionProcessor<T> extends ValueProcessor<Optional<T>> {
+ private final List<ValueSource<? extends Optional<?>>> optionalSources;
+ public ExpressionProcessor(List<ValueSource<? extends Optional<?>>> optionalSources) {
+ this.optionalSources = optionalSources;
+ }
+
+ protected abstract Optional<T> calculateNormal();
+
+ private Optional<T> calculateNewValue() {
+ Set<Message> errors = new LinkedHashSet<Message>(0);
+ for (ValueSource<? extends Optional<?>> source : optionalSources) {
+ if (!source.getValue().isNormal()) {
+ errors.addAll(source.getValue().errorMessages());
+ }
+ }
+ if (errors.isEmpty()) {
+ return calculateNormal();
+ } else {
+ return createErrorOptional(errors);
+ }
+ }
+ public void update(Updater updater) {
+ Optional<T> result = calculateNewValue();
+ Optional<T> oldValue = getValue();
+ setCurrentValue(result);
+ if (!result.equals(oldValue)) {
+ updater.reportChanged(this);
+ }
+ }
+ }
+
+ /*
+ * Part 3. Various utils.
+ */
+
+ /**
+ * A general-purpose implementation of OK button vertex. It works as a consumer of
+ * 1 result value and several warning sources. From its sources it decides whether
+ * OK button should be enabled and also provides dialog messages (errors, warnings, infos).
+ */
+ public static class OkButtonControl implements ValueConsumer {
+ private final ValueSource<? extends Optional<?>> resultSource;
+ private final List<? extends ValueSource<String>> warningSources;
+ private final DialogElements dialogElements;
+
+ public OkButtonControl(ValueSource<? extends Optional<?>> resultSource,
+ List<? extends ValueSource<String>> warningSources, DialogElements dialogElements) {
+ this.resultSource = resultSource;
+ this.warningSources = warningSources;
+ this.dialogElements = dialogElements;
+ }
+
+ /**
+ * Returns a list of dependencies for updater -- a convenience method.
+ */
+ public List<? extends ValueSource<?>> getDependencies() {
+ ArrayList<ValueSource<?>> result = new ArrayList<ValueSource<?>>();
+ result.add(resultSource);
+ result.addAll(warningSources);
+ return result;
+ }
+
+ public void update(Updater updater) {
+ Optional<?> result = resultSource.getValue();
+ List<Message> messages = new ArrayList<Message>();
+ for (ValueSource<String> warningSource : warningSources) {
+ if (warningSource.getValue() != null) {
+ messages.add(new Message(warningSource.getValue(), MessagePriority.WARNING));
+ }
+ }
+ boolean enabled;
+ if (result.isNormal()) {
+ enabled = true;
+ } else {
+ enabled = false;
+ messages.addAll(result.errorMessages());
+ }
+ dialogElements.getOkButton().setEnabled(enabled);
+ String errorMessage;
+ int type;
+ if (messages.isEmpty()) {
+ errorMessage = null;
+ type = IMessageProvider.NONE;
+ } else {
+ Message visibleMessage = Collections.max(messages, messageComparatorBySeverity);
+ errorMessage = visibleMessage.getText();
+ type = visibleMessage.getPriority().getMessageProviderType();
+ }
+ dialogElements.setMessage(errorMessage, type);
+ }
+
+ private static final Comparator<Message> messageComparatorBySeverity =
+ new Comparator<Message>() {
+ public int compare(Message o1, Message o2) {
+ int ordinal1 = o1.getPriority().ordinal();
+ int ordinal2 = o2.getPriority().ordinal();
+ if (ordinal1 < ordinal2) {
+ return +1;
+ } else if (ordinal1 == ordinal2) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ };
+ }
+
+ /**
+ * A basic interface to elements of the dialog window from dialog logic part. The user may extend
+ * this interface with more elements.
+ */
+ public interface DialogElements {
+ Shell getShell();
+ Button getOkButton();
+ void setMessage(String message, int type);
+ }
+
+ /**
+ * A wrapper around Combo that provides logic-level data-oriented access to the control.
+ * This is not a simply convenience wrapper, because Combo itself does not keep a real data,
+ * but only its string representation.
+ */
+ public static abstract class ComboWrapper<E> {
+ private final Combo combo;
+ public ComboWrapper(Combo combo) {
+ this.combo = combo;
+ }
+ public Combo getCombo() {
+ return combo;
+ }
+ public void addSelectionListener(SelectionListener listener) {
+ combo.addSelectionListener(listener);
+ }
+ public abstract E getSelected();
+ public abstract void setSelected(E element);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/CompareChangesAction.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,86 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+
+import org.chromium.debug.core.model.VmResource;
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.IModificationDate;
+import org.eclipse.compare.IStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * A very preliminary implementation of action that should let user compare his script with
+ * its current state on remote VM.
+ */
+public class CompareChangesAction extends V8ScriptAction {
+ @Override
+ protected void execute(FilePair filePair) {
+ LiveEditCompareInput input = new LiveEditCompareInput(filePair.getFile(), filePair.getVmResource());
+ CompareUI.openCompareEditor(input);
+ }
+
+ private static class LiveEditCompareInput extends CompareEditorInput {
+ private final IFile file;
+ private final VmResource script;
+
+ LiveEditCompareInput(IFile file, VmResource vmResource) {
+ super(createCompareConfiguration());
+ this.file = file;
+ this.script = vmResource;
+ }
+
+ private static CompareConfiguration createCompareConfiguration() {
+ return new CompareConfiguration();
+ }
+
+ @Override
+ protected Object prepareInput(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+
+ abstract class CompareItem implements ITypedElement, IStreamContentAccessor,
+ IModificationDate {
+ public Image getImage() {
+ return null;
+ }
+ public String getType() {
+ return TEXT_TYPE;
+ }
+ public long getModificationDate() {
+ return 0;
+ }
+ }
+ CompareItem left = new CompareItem() {
+ public String getName() {
+ return "Local file " + file.getName(); //$NON-NLS-1$
+ }
+ public InputStream getContents() throws CoreException {
+ return file.getContents();
+ }
+ };
+ CompareItem right = new CompareItem() {
+ public String getName() {
+ return "File in VM " + script.getFileName(); //$NON-NLS-1$
+ }
+ public InputStream getContents() throws CoreException {
+ return new ByteArrayInputStream(script.getScript().getSource().getBytes());
+ }
+ };
+ DiffNode diffNode = new DiffNode(null, Differencer.PSEUDO_CONFLICT, null, left, right);
+ return diffNode;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsBreakpointPropertiesAction.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,83 @@
+// Copyright (c) 20109 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import org.chromium.debug.core.model.ChromiumLineBreakpoint;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.window.IShellProvider;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.dialogs.PropertyDialogAction;
+
+/**
+ * Action to bring up the breakpoint properties dialog.
+ */
+public class JsBreakpointPropertiesAction implements IObjectActionDelegate {
+
+ private Runnable currentRunnable;
+ private IWorkbenchPartSite site = null;
+
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ site = targetPart.getSite();
+ }
+
+ public void run(IAction action) {
+ currentRunnable.run();
+ }
+
+ public void selectionChanged(IAction action, ISelection selection) {
+ currentRunnable = createRunnable(selection);
+ action.setEnabled(currentRunnable != null);
+ }
+
+
+ private Runnable createRunnable(ISelection selection) {
+ if (selection instanceof IStructuredSelection == false) {
+ return null;
+ }
+ IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+ if (structuredSelection.size() != 1) {
+ return null;
+ }
+ Object element = structuredSelection.getFirstElement();
+ if (element instanceof ChromiumLineBreakpoint == false) {
+ return null;
+ }
+ final ChromiumLineBreakpoint breakpoint = (ChromiumLineBreakpoint) element;
+
+ return new Runnable() {
+ public void run() {
+ runAction(breakpoint, site);
+ }
+ };
+ }
+
+ protected static void runAction(final IBreakpoint breakpoint, IShellProvider shell) {
+ PropertyDialogAction action =
+ new PropertyDialogAction(shell,
+ new ISelectionProvider() {
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ }
+
+ public ISelection getSelection() {
+ return new StructuredSelection(breakpoint);
+ }
+
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ }
+
+ public void setSelection(ISelection selection) {
+ }
+ });
+ action.run();
+ }
+}
--- a/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerAction.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerAction.java Mon Jun 07 16:51:19 2010 -0700
@@ -7,12 +7,9 @@
import org.chromium.debug.core.model.ChromiumLineBreakpoint;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.ui.actions.RulerBreakpointAction;
+import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.ISelectionProvider;
-import org.eclipse.jface.viewers.StructuredSelection;
-import org.eclipse.ui.dialogs.PropertyDialogAction;
+import org.eclipse.ui.texteditor.AbstractRulerActionDelegate;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.IUpdate;
@@ -31,23 +28,7 @@
@Override
public void run() {
if (getBreakpoint() != null) {
- PropertyDialogAction action =
- new PropertyDialogAction(getEditor().getEditorSite(),
- new ISelectionProvider() {
- public void addSelectionChangedListener(ISelectionChangedListener listener) {
- }
-
- public ISelection getSelection() {
- return new StructuredSelection(getBreakpoint());
- }
-
- public void removeSelectionChangedListener(ISelectionChangedListener listener) {
- }
-
- public void setSelection(ISelection selection) {
- }
- });
- action.run();
+ JsBreakpointPropertiesAction.runAction(getBreakpoint(), getEditor().getEditorSite());
}
}
@@ -61,4 +42,13 @@
setEnabled(breakpoint != null);
}
+
+ public static class Delegate extends AbstractRulerActionDelegate {
+
+ @Override
+ protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) {
+ return new JsBreakpointPropertiesRulerAction(editor, rulerInfo);
+ }
+
+ }
}
--- a/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.java Mon Jun 07 16:33:07 2010 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.debug.ui.actions;
-
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.text.source.IVerticalRulerInfo;
-import org.eclipse.ui.texteditor.AbstractRulerActionDelegate;
-import org.eclipse.ui.texteditor.ITextEditor;
-
-/**
- * Delegate for JsBreakpointPropertiesRulerAction.
- */
-public class JsBreakpointPropertiesRulerActionDelegate extends AbstractRulerActionDelegate {
-
- @Override
- protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) {
- return new JsBreakpointPropertiesRulerAction(editor, rulerInfo);
- }
-
-}
--- a/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/Messages.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/Messages.java Mon Jun 07 16:51:19 2010 -0700
@@ -26,6 +26,8 @@
public static String ExpressionEvaluator_UnableToEvaluateExpression;
public static String JsBreakpointPropertiesRulerAction_ItemLabel;
+
+ public static String SynchronizeBreakpoints_JOB_TITLE;
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
--- a/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/OpenFunctionAction.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/OpenFunctionAction.java Mon Jun 07 16:51:19 2010 -0700
@@ -1,5 +1,7 @@
package org.chromium.debug.ui.actions;
+import org.chromium.debug.core.model.DebugTargetImpl;
+import org.chromium.debug.core.model.Value;
import org.chromium.debug.core.model.Variable;
import org.chromium.debug.ui.JsDebugModelPresentation;
import org.chromium.debug.ui.editors.JsEditor;
@@ -9,6 +11,11 @@
import org.chromium.sdk.JsVariable;
import org.chromium.sdk.Script;
import org.eclipse.core.resources.IFile;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.debug.core.model.IWatchExpression;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
@@ -27,7 +34,53 @@
/**
* The action for context view in Variable view that opens selected function source text in editor.
*/
-public class OpenFunctionAction implements IObjectActionDelegate, IActionDelegate2 {
+public abstract class OpenFunctionAction<ELEMENT> implements IObjectActionDelegate,
+ IActionDelegate2 {
+ public static class ForVariable extends OpenFunctionAction<Variable> {
+ @Override
+ protected Variable castElement(Object element) {
+ if (element instanceof Variable == false) {
+ return null;
+ }
+ return (Variable) element;
+ }
+ @Override
+ protected JsValue getJsValue(Variable variable) {
+ JsVariable jsVariable = variable.getJsVariable();
+ return jsVariable.getValue();
+ }
+ @Override
+ protected DebugTargetImpl getDebugTarget(Variable variable) {
+ return variable.getDebugTarget();
+ }
+ }
+ public static class ForExpression extends OpenFunctionAction<IWatchExpression> {
+ @Override
+ protected IWatchExpression castElement(Object element) {
+ if (element instanceof IWatchExpression == false) {
+ return null;
+ }
+ return (IWatchExpression) element;
+ }
+ @Override
+ protected DebugTargetImpl getDebugTarget(IWatchExpression expression) {
+ IDebugTarget debugTarget = expression.getDebugTarget();
+ if (debugTarget instanceof DebugTargetImpl == false) {
+ return null;
+ }
+ return (DebugTargetImpl) debugTarget;
+ }
+ @Override
+ protected JsValue getJsValue(IWatchExpression expression) {
+ IValue value = expression.getValue();
+ if (value instanceof Value == false) {
+ return null;
+ }
+ Value chromiumValue = (Value) value;
+ return chromiumValue.getJsValue();
+ }
+ }
+
private Runnable currentRunnable = null;
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
@@ -41,14 +94,14 @@
}
public void selectionChanged(IAction action, ISelection selection) {
- final Variable variable = getVariableFromSelection(selection);
- final JsFunction jsFunction = getJsFunctionFromVariable(variable);
+ final ELEMENT variable = getElementFromSelection(selection);
+ final JsFunction jsFunction = getJsFunctionFromElement(variable);
currentRunnable = createRunnable(variable, jsFunction);
action.setEnabled(currentRunnable != null);
}
- private Runnable createRunnable(final Variable variable, final JsFunction jsFunction) {
+ private Runnable createRunnable(final ELEMENT element, final JsFunction jsFunction) {
if (jsFunction == null) {
return null;
}
@@ -63,7 +116,20 @@
if (script == null) {
return;
}
- IFile resource = variable.getDebugTarget().getScriptResource(script);
+ DebugTargetImpl debugTarget = getDebugTarget(element);
+ if (debugTarget == null) {
+ return;
+ }
+ ISourceLocator sourceLocator = debugTarget.getLaunch().getSourceLocator();
+ if (sourceLocator instanceof ISourceLookupDirector == false) {
+ return;
+ }
+ ISourceLookupDirector director = (ISourceLookupDirector) sourceLocator;
+ Object sourceObject = director.getSourceElement(script);
+ if (sourceObject instanceof IFile == false) {
+ return;
+ }
+ IFile resource = (IFile) sourceObject;
IEditorInput input = JsDebugModelPresentation.toEditorInput(resource);
IEditorPart editor;
try {
@@ -94,12 +160,14 @@
currentRunnable.run();
}
- private JsFunction getJsFunctionFromVariable(Variable variable) {
- if (variable == null) {
+ private JsFunction getJsFunctionFromElement(ELEMENT element) {
+ if (element == null) {
return null;
}
- JsVariable jsVariable = variable.getJsVariable();
- JsValue jsValue = jsVariable.getValue();
+ JsValue jsValue = getJsValue(element);
+ if (jsValue == null) {
+ return null;
+ }
JsObject jsObject = jsValue.asObject();
if (jsObject == null) {
return null;
@@ -107,7 +175,7 @@
return jsObject.asFunction();
}
- private Variable getVariableFromSelection(ISelection selection) {
+ private ELEMENT getElementFromSelection(ISelection selection) {
if (selection instanceof IStructuredSelection == false) {
return null;
}
@@ -117,9 +185,13 @@
return null;
}
Object element = structuredSelection.getFirstElement();
- if (element instanceof Variable == false) {
- return null;
- }
- return (Variable) element;
+ ELEMENT typedElement = castElement(element);
+ return typedElement;
}
+
+ protected abstract ELEMENT castElement(Object element);
+
+ protected abstract JsValue getJsValue(ELEMENT element);
+
+ protected abstract DebugTargetImpl getDebugTarget(ELEMENT element);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/PushChangesAction.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,81 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.sdk.LiveEditExtension;
+import org.chromium.sdk.UpdatableScript;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * The main action of LiveEdit feature. It gets the current state of a working file and pushes
+ * it into running V8 VM.
+ */
+public class PushChangesAction extends V8ScriptAction {
+ @Override
+ protected void execute(final FilePair filePair) {
+ UpdatableScript updatableScript =
+ LiveEditExtension.castToUpdatableScript(filePair.getVmResource().getScript());
+
+ if (updatableScript == null) {
+ throw new RuntimeException();
+ }
+
+ byte[] fileData;
+ try {
+ fileData = readFileContents(filePair.getFile());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (CoreException e) {
+ throw new RuntimeException(e);
+ }
+
+ // We are using default charset here like usually.
+ String newSource = new String(fileData);
+
+ UpdatableScript.UpdateCallback callback = new UpdatableScript.UpdateCallback() {
+ public void success(Object report) {
+ ChromiumDebugPlugin.log(new Status(IStatus.OK, ChromiumDebugPlugin.PLUGIN_ID,
+ "Script has been successfully updated on remote: " + report)); //$NON-NLS-1$
+ }
+ public void failure(String message) {
+ ChromiumDebugPlugin.log(new Status(IStatus.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+ "Failed to change script on remote: " + message)); //$NON-NLS-1$
+ }
+ };
+
+ updatableScript.setSourceOnRemote(newSource, callback, null);
+ }
+
+
+ private static byte[] readFileContents(IFile file) throws IOException, CoreException {
+ InputStream inputStream = file.getContents();
+ try {
+ return readBytes(inputStream);
+ } finally {
+ inputStream.close();
+ }
+ }
+
+ private static byte[] readBytes(InputStream inputStream) throws IOException {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ byte[] array = new byte[1024];
+ while (true) {
+ int len = inputStream.read(array);
+ if (len == -1) {
+ break;
+ }
+ buffer.write(array, 0, len);
+ }
+ return buffer.toByteArray();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/SynchronizeBreakpoints.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,127 @@
+package org.chromium.debug.ui.actions;
+
+import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.model.BreakpointSynchronizer;
+import org.chromium.debug.core.model.DebugTargetImpl;
+import org.chromium.debug.core.model.BreakpointSynchronizer.Direction;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class SynchronizeBreakpoints implements IWorkbenchWindowActionDelegate {
+
+ public static class ResetRemote extends SynchronizeBreakpoints {
+ public ResetRemote() {
+ super(BreakpointSynchronizer.Direction.RESET_REMOTE);
+ }
+ }
+
+ public static class ResetLocal extends SynchronizeBreakpoints {
+ public ResetLocal() {
+ super(BreakpointSynchronizer.Direction.RESET_LOCAL);
+ }
+ }
+
+ public static class Merge extends SynchronizeBreakpoints {
+ public Merge() {
+ super(BreakpointSynchronizer.Direction.MERGE);
+ }
+ }
+
+ private final BreakpointSynchronizer.Direction direction;
+
+ protected SynchronizeBreakpoints(Direction direction) {
+ this.direction = direction;
+ }
+
+ public void dispose() {
+ }
+
+ public void init(IWorkbenchWindow window) {
+ }
+
+ public void run(IAction action) {
+ if (currentRunnable == null) {
+ return;
+ }
+ currentRunnable.run();
+ currentRunnable = null;
+ }
+
+ public void selectionChanged(IAction action, ISelection selection) {
+ currentRunnable = createRunnable(selection);
+ action.setEnabled(currentRunnable != null);
+ }
+
+ private Runnable createRunnable(ISelection selection) {
+ if (selection instanceof IStructuredSelection == false) {
+ return null;
+ }
+ IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+ final Set<DebugTargetImpl> targets = new HashSet<DebugTargetImpl>(3);
+ for (Iterator<?> it = structuredSelection.iterator(); it.hasNext(); ) {
+ Object element = it.next();
+ IDebugTarget debugTarget;
+ if (element instanceof ILaunch) {
+ ILaunch launch = (ILaunch) element;
+ debugTarget = launch.getDebugTarget();
+ } else if (element instanceof IDebugElement) {
+ IDebugElement debugElement = (IDebugElement) element;
+ debugTarget = debugElement.getDebugTarget();
+ } else {
+ continue;
+ }
+ if (debugTarget instanceof DebugTargetImpl == false) {
+ continue;
+ }
+ DebugTargetImpl debugTargetImpl = (DebugTargetImpl) debugTarget;
+ targets.add(debugTargetImpl);
+ }
+ if (targets.isEmpty()) {
+ return null;
+ }
+ if (direction != BreakpointSynchronizer.Direction.RESET_REMOTE && targets.size() > 1) {
+ // Only "reset remote" mode is implemented for a multiple selection.
+ return null;
+ }
+
+ return new Runnable() {
+ public void run() {
+ new Job(MessageFormat.format(Messages.SynchronizeBreakpoints_JOB_TITLE, targets.size())) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ // TODO(peter.rybin): consider blocking this method until callback is invoked to
+ // keep the UI jobs open while something is still happening.
+ BreakpointSynchronizer.Callback callback = new BreakpointSynchronizer.Callback() {
+ public void onDone(IStatus status) {
+ ChromiumDebugPlugin.log(status);
+ }
+ };
+
+ // TODO(peter.rybin): consider showing progress for several targets.
+ for (DebugTargetImpl target : targets) {
+ target.synchronizeBreakpoints(direction, callback);
+ }
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+ };
+ }
+
+ private Runnable currentRunnable;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/V8ScriptAction.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,193 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.chromium.debug.core.model.DebugTargetImpl;
+import org.chromium.debug.core.model.VmResource;
+import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
+import org.chromium.sdk.JavascriptVm;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.ui.IActionDelegate2;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * A base class for all LiveEdit actions that are scoped to a working file from user workspace.
+ * It makes all necessary checks and prepares data in form of {@link FilePair} class.
+ * The concrete actions implement the {@link #execute(FilePair)} method.
+ */
+abstract class V8ScriptAction implements IObjectActionDelegate, IActionDelegate2 {
+ private Runnable currentRunnable = null;
+
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ }
+
+ public void run(IAction action) {
+ if (currentRunnable == null) {
+ return;
+ }
+ currentRunnable.run();
+ currentRunnable = null;
+ }
+
+ public void selectionChanged(IAction action, ISelection selection) {
+ currentRunnable = createRunnable(selection);
+ action.setEnabled(currentRunnable != null);
+ }
+
+ private Runnable createRunnable(ISelection selection) {
+ if (selection instanceof IStructuredSelection == false) {
+ return null;
+ }
+ IStructuredSelection structured = (IStructuredSelection) selection;
+ if (structured.size() != 1) {
+ return null;
+ }
+
+ Object firstElement = structured.getFirstElement();
+ if (firstElement instanceof ResourceMapping == false) {
+ return null;
+ }
+ ResourceMapping resourceMapping = (ResourceMapping) firstElement;
+ final List<IResource> resourceList = new ArrayList<IResource>(1);
+ IResourceVisitor visitor = new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ resourceList.add(resource);
+ return false;
+ }
+ };
+ try {
+ resourceMapping.accept(null, visitor, null);
+ } catch (CoreException e) {
+ throw new RuntimeException(e);
+ }
+ if (resourceList.size() != 1) {
+ return null;
+ }
+ if (resourceList.get(0) instanceof IFile == false) {
+ return null;
+ }
+ final IFile file = (IFile) resourceList.get(0);
+ if (!filterFileName(file.getName())) {
+ return null;
+ }
+ return new Runnable() {
+ public void run() {
+ try {
+ execute(file);
+ } catch (RuntimeException e) {
+ // TODO(peter.rybin): Handle it.
+ throw e;
+ }
+ }
+ };
+ }
+
+ private void execute(IFile file) {
+ List<? extends FilePair> filePairList = getFilePairs(file);
+ FilePair filePair = getSingleFilePair(filePairList);
+ execute(filePair);
+ }
+
+ protected abstract void execute(FilePair filePair);
+
+ /**
+ * A temporary method that excludes all cases when there are more than one file pair for a
+ * user file. The proper solution ought to provide a UI for user so that he could review
+ * which debug sessions should be included in action.
+ */
+ private static FilePair getSingleFilePair(List<? extends FilePair> pairs) {
+ if (pairs.size() == 0) {
+ throw new RuntimeException("File is not associated with any V8 VM");
+ }
+ if (pairs.size() != 1) {
+ throw new RuntimeException(
+ "File is associated with several V8 VMs, this is not supported yet.");
+ }
+ return pairs.get(0);
+ }
+
+ /**
+ * Finds all file pairs for a user working file. One working file may correspond to several
+ * scripts if there are more than one debug sessions.
+ */
+ private static List<? extends FilePair> getFilePairs(IFile localFile) {
+ List<DebugTargetImpl> targetList = DebugTargetImpl.getAllDebugTargetImpls();
+ ArrayList<FilePair> result = new ArrayList<FilePair>(targetList.size());
+
+ for (DebugTargetImpl target : targetList) {
+ VmResource script;
+ try {
+ script = target.getVmResource(localFile);
+ } catch (CoreException e) {
+ throw new RuntimeException("Failed to resolve script from the file " + localFile, e);
+ }
+ if (script == null) {
+ continue;
+ }
+ result.add(new FilePair(localFile, script, target));
+ }
+ return result;
+ }
+
+ protected static class FilePair {
+ private final IFile file;
+ private final VmResource vmResource;
+ private final DebugTargetImpl debugTargetImpl;
+
+ FilePair(IFile file, VmResource vmResource, DebugTargetImpl debugTargetImpl) {
+ this.file = file;
+ this.vmResource = vmResource;
+ this.debugTargetImpl = debugTargetImpl;
+ }
+ protected IFile getFile() {
+ return file;
+ }
+ protected VmResource getVmResource() {
+ return vmResource;
+ }
+ protected JavascriptVm getJavascriptVm() {
+ return debugTargetImpl.getJavascriptEmbedder().getJavascriptVm();
+ }
+ protected DebugTargetImpl getDebugTarget() {
+ return debugTargetImpl;
+ }
+ }
+
+ /**
+ * @return true if action should be enabled for this file name
+ */
+ private boolean filterFileName(String name) {
+ for (String suffix : ChromiumDebugPluginUtil.SUPPORTED_EXTENSIONS_SUFFIX_LIST) {
+ if (name.endsWith(suffix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void dispose() {
+ currentRunnable = null;
+ }
+
+ public void init(IAction action) {
+ }
+
+ public void runWithEvent(IAction action, Event event) {
+ run(action);
+ }
+}
+
--- a/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/messages.properties Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/messages.properties Mon Jun 07 16:51:19 2010 -0700
@@ -5,3 +5,4 @@
ExpressionEvaluator_SocketError=Socket error while evaluating expression
ExpressionEvaluator_UnableToEvaluateExpression=Unable to evaluate expression
JsBreakpointPropertiesRulerAction_ItemLabel=Breakpoint Properties...
+SynchronizeBreakpoints_JOB_TITLE=Synchronize breakpoints in {0} target(s)
--- a/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsDocumentProvider.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsDocumentProvider.java Mon Jun 07 16:51:19 2010 -0700
@@ -4,10 +4,12 @@
package org.chromium.debug.ui.editors;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.rules.FastPartitioner;
+import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.editors.text.FileDocumentProvider;
/**
@@ -27,4 +29,22 @@
return doc;
}
+ /**
+ * Alternative implementation of the method that does not require file to be a physical file.
+ */
+ @Override
+ public boolean isDeleted(Object element) {
+ if (element instanceof IFileEditorInput) {
+ IFileEditorInput input= (IFileEditorInput) element;
+
+ IProject project = input.getFile().getProject();
+ if (project != null && !project.exists()) {
+ return true;
+ }
+
+ return !input.getFile().exists();
+ }
+ return super.isDeleted(element);
+ }
+
}
--- a/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/LaunchTabGroup.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/LaunchTabGroup.java Mon Jun 07 16:51:19 2010 -0700
@@ -8,6 +8,7 @@
import org.eclipse.debug.ui.CommonTab;
import org.eclipse.debug.ui.ILaunchConfigurationDialog;
import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.debug.ui.sourcelookup.SourceLookupTab;
/**
* The Chromium JavaScript debugger launch configuration tab group.
@@ -20,7 +21,8 @@
}
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
- setTabs(new ILaunchConfigurationTab[] { new ChromiumRemoteTab(), new CommonTab() });
+ setTabs(new ILaunchConfigurationTab[] { new ChromiumRemoteTab(),
+ new SourceLookupTab(), new CommonTab() });
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/source/Messages.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,30 @@
+package org.chromium.debug.ui.source;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.chromium.debug.ui.source.messages"; //$NON-NLS-1$
+ public static String SourceNameMapperContainerDialog_CONFIGURE_BUTTON;
+ public static String SourceNameMapperContainerDialog_CONFIGURE_TARGET_CONTAINER;
+ public static String SourceNameMapperContainerDialog_CONFIGURED_CONTAINER;
+ public static String SourceNameMapperContainerDialog_CONTAINER_GROUP;
+ public static String SourceNameMapperContainerDialog_DIALOG_SUBTITLE;
+ public static String SourceNameMapperContainerDialog_DIALOG_TITLE;
+ public static String SourceNameMapperContainerDialog_ENTER_PREFIX;
+ public static String SourceNameMapperContainerDialog_EXAMPLE_1;
+ public static String SourceNameMapperContainerDialog_EXAMPLE_2;
+ public static String SourceNameMapperContainerDialog_EXPLANATION_1;
+ public static String SourceNameMapperContainerDialog_EXPLANATION_2;
+ public static String SourceNameMapperContainerDialog_NOTHING_CONFIGURED;
+ public static String SourceNameMapperContainerDialog_PREFIX_GROUP;
+ public static String SourceNameMapperContainerDialog_PREFIX_NORMALLY_ENDS;
+ public static String SourceNameMapperContainerDialog_SAMPLE_FILE_NAME;
+ public static String SourceNameMapperContainerDialog_TYPE_LABEL;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/source/SourceNameMapperContainerDialog.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,316 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.source;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.chromium.debug.ui.DialogUtils.ComboWrapper;
+import org.chromium.debug.ui.DialogUtils.DialogElements;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.sourcelookup.ISourceContainerBrowser;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * A dialog for adding and editing JavaScript source name mapper containers.
+ */
+public class SourceNameMapperContainerDialog extends TitleAreaDialog {
+ private final ISourceLookupDirector director;
+ private final PresetFieldValues initialParams;
+
+ private Result result = null;
+ private SourceNameMapperContainerDialogLogic logic = null;
+
+ /**
+ * An optional set of preset dialog field values. Useful in "edit" (not "add") mode of dialog.
+ */
+ public interface PresetFieldValues {
+ String getPrefix();
+ ISourceContainer getContainer();
+ }
+
+ public interface Result {
+ String getResultPrefix();
+ ISourceContainer getResultContainer();
+ }
+
+ public Result getResult() {
+ return result;
+ }
+
+ public SourceNameMapperContainerDialog(Shell shell, ISourceLookupDirector director,
+ PresetFieldValues initialParams) {
+ super(shell);
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ this.director = director;
+ this.initialParams = initialParams;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite ancestor) {
+ getShell().setText(Messages.SourceNameMapperContainerDialog_DIALOG_TITLE);
+ setTitle(Messages.SourceNameMapperContainerDialog_DIALOG_SUBTITLE);
+
+ Composite parent = new Composite(ancestor, SWT.NULL);
+ {
+ GridLayout topLayout = new GridLayout();
+ topLayout.numColumns = 1;
+ parent.setLayout(topLayout);
+ parent.setLayoutData(new GridData(GridData.FILL_BOTH));
+ }
+
+ Label explanationOne = new Label(parent, 0);
+ explanationOne.setText(
+ Messages.SourceNameMapperContainerDialog_EXPLANATION_1);
+
+ Group prefixGroup = new Group(parent, SWT.NONE);
+ prefixGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ prefixGroup.setText(Messages.SourceNameMapperContainerDialog_PREFIX_GROUP);
+ prefixGroup.setLayout(new GridLayout(1, false));
+ final Text prefixEditor = new Text(prefixGroup, SWT.SINGLE | SWT.BORDER);
+ prefixEditor.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ final Label prefixExampleLine1Label = new Label(prefixGroup, 0);
+ prefixExampleLine1Label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ final Label prefixExampleLine2Label = new Label(prefixGroup, 0);
+ prefixExampleLine2Label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ Label explanationTwo = new Label(parent, 0);
+ explanationTwo.setText(Messages.SourceNameMapperContainerDialog_EXPLANATION_2);
+
+ Group containerGroup = new Group(parent, SWT.NONE);
+ containerGroup.setLayout(new GridLayout(1, false));
+ containerGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ containerGroup.setText(Messages.SourceNameMapperContainerDialog_CONTAINER_GROUP);
+
+ Composite typeBlock = new Composite(containerGroup, SWT.NULL);
+ typeBlock.setLayout(new GridLayout(3, false));
+ typeBlock.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ final List<ISourceContainerType> types =
+ filterTypes(DebugPlugin.getDefault().getLaunchManager().getSourceContainerTypes());
+
+ Collections.sort(types, TYPE_COMPARATOR_BY_NAME);
+
+ String[] typeNameArray = new String[types.size()];
+ for (int i = 0; i < typeNameArray.length; i++) {
+ typeNameArray[i] = types.get(i).getName();
+ }
+
+ Label comboLabel = new Label(typeBlock, 0);
+ comboLabel.setText(Messages.SourceNameMapperContainerDialog_TYPE_LABEL);
+
+ Combo typesCombo = new Combo(typeBlock, SWT.READ_ONLY);
+ typesCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ typesCombo.setFont(parent.getFont());
+ typesCombo.setItems(typeNameArray);
+ if (typeNameArray.length > 0) {
+ typesCombo.select(0);
+ }
+ final Button configureButton = new Button(typeBlock, SWT.PUSH);
+ configureButton.setText(Messages.SourceNameMapperContainerDialog_CONFIGURE_BUTTON);
+
+ final Composite statusBox = new Composite(containerGroup, SWT.NULL);
+ statusBox.setLayout(new GridLayout(3, false));
+ statusBox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ final Label statusLabel = new Label(statusBox, 0);
+ final Label containerTypeIconLabel = new Label(statusBox, 0);
+ final Label containerNameLabel = new Label(statusBox, 0);
+
+ Dialog.applyDialogFont(parent);
+
+ // Implementing Elements interface
+ final ComboWrapper<ISourceContainerType> comboWrapper =
+ new ComboWrapper<ISourceContainerType>(typesCombo) {
+ @Override
+ public ISourceContainerType getSelected() {
+ return types.get(getCombo().getSelectionIndex());
+ }
+ @Override
+ public void setSelected(ISourceContainerType element) {
+ int index = types.indexOf(element);
+ if (index != -1) {
+ getCombo().select(index);
+ }
+ }
+ };
+
+ final ContainerStatusGroup containerStatusGroup = new ContainerStatusGroup() {
+ public Label getStatusLabel() {
+ return statusLabel;
+ }
+ public Label getTypeImageLabel() {
+ return containerTypeIconLabel;
+ }
+ public Label getContainerNameLabel() {
+ return containerNameLabel;
+ }
+ public void layout() {
+ statusBox.layout();
+ }
+ public void setEnabled(boolean enabled) {
+ statusLabel.setEnabled(enabled);
+ containerTypeIconLabel.setEnabled(enabled);
+ containerNameLabel.setEnabled(enabled);
+ }
+ };
+
+ Elements elements = new Elements() {
+ public Text getPrefixField() {
+ return prefixEditor;
+ }
+ public Label getPrefixExampleLine1Label() {
+ return prefixExampleLine1Label;
+ }
+ public Label getPrefixExampleLine2Label() {
+ return prefixExampleLine2Label;
+ }
+ public Button getConfigureButton() {
+ return configureButton;
+ }
+ public ComboWrapper<ISourceContainerType> getContainerTypeCombo() {
+ return comboWrapper;
+ }
+ public Shell getShell() {
+ return SourceNameMapperContainerDialog.this.getShell();
+ }
+ public ContainerStatusGroup getContainerStatusGroup() {
+ return containerStatusGroup;
+ }
+ public Button getOkButton() {
+ return SourceNameMapperContainerDialog.this.getButton(IDialogConstants.OK_ID);
+ }
+ public void setMessage(String message, int type) {
+ SourceNameMapperContainerDialog.this.setMessage(message, type);
+ }
+ };
+
+ logic = SourceNameMapperContainerDialogLogic.create(elements, director, initialParams);
+
+ return parent;
+ }
+
+ @Override
+ public void create() {
+ super.create();
+ logic.updateAll();
+ }
+
+ @Override
+ protected void okPressed() {
+ result = logic.getResult();
+ super.okPressed();
+ }
+
+ /**
+ * A main interface to dialog elements, that are used from logic engine.
+ */
+ interface Elements extends DialogElements {
+ Text getPrefixField();
+ Label getPrefixExampleLine1Label();
+ Label getPrefixExampleLine2Label();
+ ComboWrapper<ISourceContainerType> getContainerTypeCombo();
+ Button getConfigureButton();
+ ContainerStatusGroup getContainerStatusGroup();
+ }
+
+ interface ContainerStatusGroup {
+ Label getStatusLabel();
+ Label getTypeImageLabel();
+ Label getContainerNameLabel();
+ void layout();
+ void setEnabled(boolean enabled);
+ }
+
+ interface ConfigureButtonAction {
+ ISourceContainer run(Shell shell);
+ }
+
+ // Creates action implementation for a configure button or return null.
+ static ConfigureButtonAction prepareConfigureAction(ISourceContainerType type,
+ ISourceContainer alreadyCreatedContainer,
+ final ISourceLookupDirector director) {
+ if (type == null) {
+ return null;
+ }
+ final ISourceContainerBrowser browser = DebugUITools.getSourceContainerBrowser(type.getId());
+ if (browser == null) {
+ return null;
+ }
+ abstract class ActionBase implements ConfigureButtonAction {
+ public ISourceContainer run(Shell shell) {
+ ISourceContainer[] containers = runImpl(shell);
+ if (containers.length != 1) {
+ return null;
+ }
+ return containers[0];
+ }
+ abstract ISourceContainer[] runImpl(Shell shell);
+ }
+ ISourceContainer[] containers;
+ if (alreadyCreatedContainer != null && alreadyCreatedContainer.getType().equals(type)) {
+ // Edit existing.
+ final ISourceContainer[] alreadyCreatedContainerArray = { alreadyCreatedContainer };
+ if (browser.canEditSourceContainers(director, alreadyCreatedContainerArray)) {
+ return new ActionBase() {
+ @Override ISourceContainer[] runImpl(Shell shell) {
+ return browser.editSourceContainers(shell, director, alreadyCreatedContainerArray);
+ }
+ };
+ }
+ }
+ // Add new.
+ if (browser.canAddSourceContainers(director)) {
+ return new ActionBase() {
+ @Override ISourceContainer[] runImpl(Shell shell) {
+ return browser.addSourceContainers(shell, director);
+ }
+ };
+ }
+ return null;
+ }
+
+ private static final Comparator<ISourceContainerType> TYPE_COMPARATOR_BY_NAME =
+ new Comparator<ISourceContainerType>() {
+ public int compare(ISourceContainerType o1, ISourceContainerType o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ };
+
+ private List<ISourceContainerType> filterTypes(ISourceContainerType[] types){
+ ArrayList<ISourceContainerType> result = new ArrayList<ISourceContainerType>();
+ for (int i = 0; i< types.length; i++) {
+ ISourceContainerType type = types[i];
+ if (director.supportsSourceContainerType(type)) {
+ ISourceContainerBrowser sourceContainerBrowser =
+ DebugUITools.getSourceContainerBrowser(type.getId());
+ if(sourceContainerBrowser != null &&
+ sourceContainerBrowser.canAddSourceContainers(director)) {
+ result.add(type);
+ }
+ }
+ }
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/source/SourceNameMapperContainerDialogLogic.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,272 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.source;
+
+import static org.chromium.debug.ui.DialogUtils.createErrorOptional;
+import static org.chromium.debug.ui.DialogUtils.createOptional;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.chromium.debug.ui.DialogUtils.ExpressionProcessor;
+import org.chromium.debug.ui.DialogUtils.Message;
+import org.chromium.debug.ui.DialogUtils.MessagePriority;
+import org.chromium.debug.ui.DialogUtils.OkButtonControl;
+import org.chromium.debug.ui.DialogUtils.Optional;
+import org.chromium.debug.ui.DialogUtils.Updater;
+import org.chromium.debug.ui.DialogUtils.ValueConsumer;
+import org.chromium.debug.ui.DialogUtils.ValueProcessor;
+import org.chromium.debug.ui.DialogUtils.ValueSource;
+import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.ConfigureButtonAction;
+import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.ContainerStatusGroup;
+import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.Elements;
+import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.PresetFieldValues;
+import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.Result;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * A separated logic of {@link SourceNameMapperContainerDialog}. It describes how data flows
+ * from input elements to the OK button with all necessary asserts. Basically it only uses
+ * {@link SourceNameMapperContainerDialog.Elements} interface from the dialog.
+ */
+abstract class SourceNameMapperContainerDialogLogic {
+ abstract Result getResult();
+ abstract void updateAll();
+
+ static SourceNameMapperContainerDialogLogic create(
+ final Elements elements, final ISourceLookupDirector director,
+ final PresetFieldValues initialParams) {
+ final Updater updater = new Updater();
+
+ final List<ValueSource<String>> warningSources = new ArrayList<ValueSource<String>>(2);
+
+ // Represents value entered as prefix.
+ final ValueSource<String> prefixEditor = new ValueSource<String>() {
+ public String getValue() {
+ return elements.getPrefixField().getText();
+ }
+ {
+ if (initialParams != null) {
+ elements.getPrefixField().setText(initialParams.getPrefix());
+ }
+ final ValueSource<String> updatableThis = this;
+ ModifyListener listener = new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updater.reportChanged(updatableThis);
+ updater.update();
+ }
+ };
+ elements.getPrefixField().addModifyListener(listener);
+ }
+ };
+
+ // Represents prefix value after it has been validated.
+ final ValueProcessor<Optional<String>> prefixValue = new ExpressionProcessor<String>(
+ Collections.<ValueSource<? extends Optional<?>>>emptyList()) {
+ @Override
+ protected Optional<String> calculateNormal() {
+ String prefix = prefixEditor.getValue();
+ Optional<String> result;
+ if (prefix == null || prefix.length() == 0) {
+ return createErrorOptional(new Message(
+ Messages.SourceNameMapperContainerDialog_ENTER_PREFIX,
+ MessagePriority.BLOCKING_INFO));
+ } else {
+ return createOptional(prefix);
+ }
+ }
+ };
+ updater.addConsumer(prefixValue, prefixEditor);
+
+ // Represents possible warning about prefix value having no trailing slash.
+ ValueProcessor<String> noSlashWarning = new ValueProcessor<String>() {
+ public void update(Updater updater) {
+ Optional<String> prefix = prefixValue.getValue();
+ String result;
+ if (prefix.isNormal() && !prefix.getNormal().endsWith("/")) { //$NON-NLS-1$
+ result = Messages.SourceNameMapperContainerDialog_PREFIX_NORMALLY_ENDS;
+ } else {
+ result = null;
+ }
+ setCurrentValue(result);
+ updater.reportChanged(this);
+ }
+ };
+ updater.addConsumer(noSlashWarning, prefixValue);
+ warningSources.add(noSlashWarning);
+
+ // Represents prefix rule example printer.
+ ValueConsumer prefixExample = new ValueConsumer() {
+ public void update(Updater updater) {
+ Optional<String> prefix = prefixValue.getValue();
+ String line1;
+ String line2;
+ if (prefix.isNormal()) {
+ String sampleFileName = Messages.SourceNameMapperContainerDialog_SAMPLE_FILE_NAME;
+ line1 = NLS.bind(Messages.SourceNameMapperContainerDialog_EXAMPLE_1,
+ prefix.getNormal() + sampleFileName);
+ line2 = NLS.bind(Messages.SourceNameMapperContainerDialog_EXAMPLE_2, sampleFileName);
+ } else {
+ line1 = ""; //$NON-NLS-1$
+ line2 = ""; //$NON-NLS-1$
+ }
+ elements.getPrefixExampleLine1Label().setText(line1);
+ elements.getPrefixExampleLine2Label().setText(line2);
+ }
+ };
+ updater.addConsumer(prefixExample, prefixValue);
+
+ // Represents container type combo box.
+ final ValueSource<ISourceContainerType> selectedTypeValue =
+ new ValueSource<ISourceContainerType>() {
+ public ISourceContainerType getValue() {
+ return elements.getContainerTypeCombo().getSelected();
+ }
+ {
+ if (initialParams != null) {
+ ISourceContainerType type = initialParams.getContainer().getType();
+ elements.getContainerTypeCombo().setSelected(type);
+ }
+ final ValueSource<ISourceContainerType> updatableThis = this;
+ SelectionListener listener = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ updater.reportChanged(updatableThis);
+ updater.update();
+ }
+ };
+ elements.getContainerTypeCombo().addSelectionListener(listener);
+ }
+ };
+
+ // Represents "Configure" button that acts like a container factory.
+ final ValueProcessor<ISourceContainer> containerFactoryButtonValue =
+ new ValueProcessor<ISourceContainer>() {
+ private ConfigureButtonAction preparedAction = null;
+ {
+ if (initialParams != null) {
+ setCurrentValue(initialParams.getContainer());
+ }
+ final ValueSource<ISourceContainer> valueSourceThis = this;
+ elements.getConfigureButton().addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (preparedAction != null) {
+ ISourceContainer value = preparedAction.run(elements.getShell());
+ if (value != null) {
+ setCurrentValue(value);
+ }
+ updater.reportChanged(valueSourceThis);
+ updater.update();
+ updateAction();
+ }
+ }
+ });
+ }
+ public void update(Updater updater) {
+ if (getValue() != null && !getValue().getType().equals(selectedTypeValue.getValue())) {
+ setCurrentValue(null);
+ updater.reportChanged(this);
+ }
+ updateAction();
+ }
+ private void updateAction() {
+ preparedAction = SourceNameMapperContainerDialog.prepareConfigureAction(
+ selectedTypeValue.getValue(), getValue(), director);
+ elements.getConfigureButton().setEnabled(preparedAction != null);
+ }
+ };
+ updater.addConsumer(containerFactoryButtonValue, selectedTypeValue);
+
+ // Represents printer that shows type and name of the created container.
+ ValueConsumer showContainerTypeValue = new ValueConsumer() {
+ public void update(Updater updater) {
+ ISourceContainer container = containerFactoryButtonValue.getValue();
+ String status;
+ Image image;
+ String name;
+ boolean enabled;
+ if (container == null) {
+ status = Messages.SourceNameMapperContainerDialog_NOTHING_CONFIGURED;
+ name = ""; //$NON-NLS-1$
+ image = null;
+ enabled = false;
+ } else {
+ status = Messages.SourceNameMapperContainerDialog_CONFIGURED_CONTAINER;
+ ISourceContainerType type = container.getType();
+ name = container.getName();
+ image = DebugUITools.getSourceContainerImage(type.getId());
+ enabled = true;
+ }
+ ContainerStatusGroup group = elements.getContainerStatusGroup();
+ group.getStatusLabel().setText(status);
+ group.getTypeImageLabel().setImage(image);
+ group.getContainerNameLabel().setText(name);
+ group.setEnabled(enabled);
+ group.layout();
+ }
+ };
+ updater.addConsumer(showContainerTypeValue, containerFactoryButtonValue);
+
+ // Represents expression that constructs dialog window result.
+ final ValueProcessor<? extends Optional<Result>> resultValue =
+ new ExpressionProcessor<Result>(
+ Arrays.<ValueSource<? extends Optional<?>>>asList(prefixValue) ) {
+ @Override
+ protected Optional<Result> calculateNormal() {
+ final String prefix = prefixValue.getValue().getNormal();
+ final ISourceContainer container = containerFactoryButtonValue.getValue();
+ if (container == null) {
+ return createErrorOptional(
+ new Message(Messages.SourceNameMapperContainerDialog_CONFIGURE_TARGET_CONTAINER,
+ MessagePriority.BLOCKING_INFO));
+ }
+ Result result = new Result() {
+ public ISourceContainer getResultContainer() {
+ return container;
+ }
+ public String getResultPrefix() {
+ return prefix;
+ }
+ };
+ return createOptional(result);
+ }
+ };
+ updater.addConsumer(resultValue, prefixValue, containerFactoryButtonValue);
+
+ // Represents controller that updates state of OK button and error messages.
+ OkButtonControl okButtonControl = new OkButtonControl(resultValue, warningSources, elements);
+ updater.addConsumer(okButtonControl, okButtonControl.getDependencies());
+
+ return new SourceNameMapperContainerDialogLogic() {
+ @Override
+ Result getResult() {
+ Optional<Result> optional = resultValue.getValue();
+ if (optional.isNormal()) {
+ return optional.getNormal();
+ } else {
+ // Normally should not be reachable, because UI should have disabled OK button.
+ return null;
+ }
+ }
+ @Override
+ void updateAll() {
+ updater.updateAll();
+ }
+ };
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/source/SourceNameMapperContainerPresentation.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,60 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.source;
+
+import org.chromium.debug.core.SourceNameMapperContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.debug.ui.sourcelookup.ISourceContainerBrowser;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * A presentation for JavaScript Source Name Mapper container that supports adding and editing.
+ */
+public class SourceNameMapperContainerPresentation implements ISourceContainerBrowser {
+
+ public ISourceContainer[] addSourceContainers(Shell shell, ISourceLookupDirector director) {
+ return openDialog(shell, director, null);
+ }
+
+ public boolean canAddSourceContainers(ISourceLookupDirector director) {
+ return true;
+ }
+
+ public boolean canEditSourceContainers(ISourceLookupDirector director,
+ ISourceContainer[] containers) {
+ return containers.length == 1;
+ }
+
+ public ISourceContainer[] editSourceContainers(Shell shell, ISourceLookupDirector director,
+ ISourceContainer[] containers) {
+ final SourceNameMapperContainer originalContainer = (SourceNameMapperContainer) containers[0];
+ SourceNameMapperContainerDialog.PresetFieldValues params =
+ new SourceNameMapperContainerDialog.PresetFieldValues() {
+ public ISourceContainer getContainer() {
+ return originalContainer.getTargetContainer();
+ }
+ public String getPrefix() {
+ return originalContainer.getPrefix();
+ }
+ };
+
+ return openDialog(shell, director, params);
+ }
+
+ private ISourceContainer[] openDialog(Shell shell, ISourceLookupDirector director,
+ SourceNameMapperContainerDialog.PresetFieldValues params) {
+ SourceNameMapperContainerDialog dialog =
+ new SourceNameMapperContainerDialog(shell, director, params);
+ dialog.open();
+ SourceNameMapperContainerDialog.Result dialogResult = dialog.getResult();
+ if (dialogResult == null) {
+ return new ISourceContainer[0];
+ }
+ ISourceContainer result = new SourceNameMapperContainer(dialogResult.getResultPrefix(),
+ dialogResult.getResultContainer());
+ return new ISourceContainer[] { result };
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/source/messages.properties Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,16 @@
+SourceNameMapperContainerDialog_CONFIGURE_BUTTON=Configure ...
+SourceNameMapperContainerDialog_CONFIGURE_TARGET_CONTAINER=Configure target container
+SourceNameMapperContainerDialog_CONFIGURED_CONTAINER=Configured container:
+SourceNameMapperContainerDialog_CONTAINER_GROUP=Target source container
+SourceNameMapperContainerDialog_DIALOG_SUBTITLE=Configure how source names get translated and applied to target source container
+SourceNameMapperContainerDialog_DIALOG_TITLE=JavaScript Source Name Mapper
+SourceNameMapperContainerDialog_ENTER_PREFIX=Enter prefix
+SourceNameMapperContainerDialog_EXAMPLE_1=E.g.: source name ''{0}''
+SourceNameMapperContainerDialog_EXAMPLE_2=gets converted into ''{0}''
+SourceNameMapperContainerDialog_EXPLANATION_1=All resource names that start with prefix will have this prefix removed...
+SourceNameMapperContainerDialog_EXPLANATION_2=... and the short name will be looked up in source container.
+SourceNameMapperContainerDialog_NOTHING_CONFIGURED=<nothing configured>
+SourceNameMapperContainerDialog_PREFIX_GROUP=Prefix of source name
+SourceNameMapperContainerDialog_PREFIX_NORMALLY_ENDS=Prefix normally ends with '/'
+SourceNameMapperContainerDialog_SAMPLE_FILE_NAME=utils/timer.js
+SourceNameMapperContainerDialog_TYPE_LABEL=Type:
--- a/org.chromium.sdk/src/org/chromium/sdk/Breakpoint.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/Breakpoint.java Mon Jun 07 16:51:19 2010 -0700
@@ -7,7 +7,7 @@
/**
* A breakpoint in the browser JavaScript virtual machine. The {@code set*}
* method invocations will not take effect until
- * {@link #flush(org.chromium.sdk.BrowserTab.BreakpointCallback)} is called.
+ * {@link #flush(org.chromium.sdk.JavascriptVm.BreakpointCallback)} is called.
*/
public interface Breakpoint {
@@ -25,7 +25,7 @@
*
* @see #getIgnoreCount()
* @see #setIgnoreCount(int)
- * @see BrowserTab#setBreakpoint(Type, String, int, int, boolean, String, int, org.chromium.sdk.BrowserTab.BreakpointCallback)
+ * @see JavascriptVm#setBreakpoint
*/
int EMPTY_VALUE = -1;
@@ -46,6 +46,26 @@
long getId();
/**
+ * @return scriptName as reported by the JavaScript VM debugger; may be null
+ */
+ String getScriptName();
+
+ /**
+ * @return scriptId as reported by the JavaScript VM debugger; may be null
+ */
+ Long getScriptId();
+
+ /**
+ * Returns line number of the breakpoint. As source is changed (typically with LiveEdit feature,
+ * and particularly by calling {@link UpdatableScript#setSourceOnRemote}) this value
+ * may become stale. It gets updated when {@link JavascriptVm#listBreakpoints} asynchronous
+ * method completes.
+ *
+ * @return 1-based line number in script source
+ */
+ long getLineNumber();
+
+ /**
* @return whether this breakpoint is enabled
*/
boolean isEnabled();
@@ -88,7 +108,7 @@
*
* @param callback to invoke once the operation result is available
*/
- void clear(BrowserTab.BreakpointCallback callback);
+ void clear(JavascriptVm.BreakpointCallback callback, SyncCallback syncCallback);
/**
* Flushes the breakpoint parameter changes (set* methods) into the browser
@@ -97,5 +117,5 @@
*
* @param callback to invoke once the operation result is available
*/
- void flush(BrowserTab.BreakpointCallback callback);
+ void flush(JavascriptVm.BreakpointCallback callback, SyncCallback syncCallback);
}
--- a/org.chromium.sdk/src/org/chromium/sdk/CallFrame.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/CallFrame.java Mon Jun 07 16:51:19 2010 -0700
@@ -32,6 +32,7 @@
/**
* @return the current line number in the Script corresponding to this frame
* (0-based) or {@code -1} if unknown
+ * TODO(peter.rybin): consider returning absolute line number here, not in-script number.
*/
int getLineNumber();
--- a/org.chromium.sdk/src/org/chromium/sdk/JavascriptVm.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/JavascriptVm.java Mon Jun 07 16:51:19 2010 -0700
@@ -98,7 +98,7 @@
* may be {@code null}
*/
void setBreakpoint(Breakpoint.Type type, String target, int line, int position, boolean enabled,
- String condition, int ignoreCount, BreakpointCallback callback);
+ String condition, int ignoreCount, BreakpointCallback callback, SyncCallback syncCallback);
/**
* Tries to suspend VM. If successful, {@link DebugEventListener#suspended(DebugContext)}
@@ -107,4 +107,15 @@
* may be {@code null}
*/
void suspend(SuspendCallback callback);
+
+ interface ListBreakpointsCallback {
+ void success(Collection<? extends Breakpoint> breakpoints);
+ void failure(Exception exception);
+ }
+
+ /**
+ * Asynchronously reads breakpoints from remote VM. The now-effective collection of breakpoints
+ * is returned to callback. Already existing {@link Breakpoint} instances are preserved.
+ */
+ void listBreakpoints(ListBreakpointsCallback callback, SyncCallback syncCallback);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/LiveEditDebugEventListener.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,16 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * An optional extension of {@link DebugEventListener} that supports experimental LiveEdit-related
+ * events.
+ */
+public interface LiveEditDebugEventListener extends DebugEventListener {
+ /**
+ * Reports that script source has been altered in remote VM.
+ */
+ void scriptContentChanged(UpdatableScript newScript);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/LiveEditExtension.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,38 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * Helper class for experimental support of LiveEdit feature. While regular API does not
+ * support LiveEdit (not to break compatibility with existing clients), it gives access to extended
+ * interfaces.
+ * <p>
+ * This class encapsulates all instanceofs/casts that are considered to be untrackable
+ * in the main code and therefore harmful.
+ */
+public class LiveEditExtension {
+ /**
+ * Casts script to the interface that supports updating source on remote VM.
+ * @return extended interface or null if unsupported
+ */
+ public static UpdatableScript castToUpdatableScript(Script script) {
+ if (script instanceof UpdatableScript == false) {
+ return null;
+ }
+ return (UpdatableScript) script;
+ }
+
+ /**
+ * Casts listener to interface that accepts LiveEdit-related events.
+ * @return extended interface or null if unsupported
+ */
+ public static LiveEditDebugEventListener castToLiveEditListener(
+ DebugEventListener debugEventListener) {
+ if (debugEventListener instanceof LiveEditDebugEventListener == false) {
+ return null;
+ }
+ return (LiveEditDebugEventListener) debugEventListener;
+ }
+}
--- a/org.chromium.sdk/src/org/chromium/sdk/Script.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/Script.java Mon Jun 07 16:51:19 2010 -0700
@@ -44,11 +44,17 @@
/**
* @return the start line of this script in the original document
- * (zero-based), inclusive
+ * (zero-based)
*/
int getStartLine();
/**
+ * @return the start column of this script in the original document
+ * (zero-based)
+ */
+ int getStartColumn();
+
+ /**
* @return the end line of this script in the original document (zero-based),
* inclusive
*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/UpdatableScript.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * This interface is a part of {@link Script} interface. It extends {@link Script} in order
+ * to support experimental feature and is under development.
+ */
+public interface UpdatableScript extends Script {
+ /**
+ * Demands that script text should be replaced with a new one if possible.
+ * @param newSource new text of script
+ */
+ void setSourceOnRemote(String newSource, UpdateCallback callback, SyncCallback syncCallback);
+
+ interface UpdateCallback {
+ /**
+ * Script text has been successfully changed. {@link DebugEventListener#scriptChanged} will
+ * be called additionally. Besides, a current context may be dismissed and recreated after this
+ * event. The order of all listed event notifications is not currently specified.
+ */
+ void success(Object report);
+ void failure(String message);
+ }
+}
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/ContextBuilder.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ContextBuilder.java Mon Jun 07 16:51:19 2010 -0700
@@ -237,6 +237,17 @@
}
}
+ private boolean sendNoMessageAndInvalidate() {
+ synchronized (sendContextCommandsMonitor) {
+ if (!isValid) {
+ return false;
+ }
+ isValid = false;
+ return true;
+ }
+ }
+
+
private class UserContext implements DebugContext {
private final DebugContextData data;
@@ -266,6 +277,14 @@
return data.exceptionData;
}
+ Collection<Breakpoint> getBreakpointsHitSafe() {
+ return data.breakpointsHit;
+ }
+
+ ExceptionData getExceptionDataSafe() {
+ return data.exceptionData;
+ }
+
public JsEvaluateContext getGlobalEvaluateContext() {
return evaluateContext;
}
@@ -306,6 +325,19 @@
sendMessageAsyncAndIvalidate(message, commandCallback, true, null);
}
+ /**
+ * Behaves as continue but does not send any messages to remote VM.
+ * This brings ContextBuilder to state when we are about to build a new context.
+ */
+ boolean continueLocally() {
+ if (!sendNoMessageAndInvalidate()) {
+ return false;
+ }
+ contextDismissed(UserContext.this);
+ getDebugSession().getDebugEventListener().resumed();
+ return true;
+ }
+
InternalContext getInternalContextForTests() {
return PreContext.this;
}
@@ -385,6 +417,24 @@
}
}
+ /**
+ * Drops the current context and initiates reading data and building a new one.
+ * Must be called from Dispatch thread.
+ * @return ExpectingBacktraceStep or null if there is no active context currently
+ */
+ ExpectingBacktraceStep startRebuildCurrentContext() {
+ // We can use currentStep as long as we are being operated from Dispatch thread.
+ if (currentStep instanceof PreContext.UserContext == false) {
+ return null;
+ }
+ PreContext.UserContext userContext = (PreContext.UserContext) currentStep;
+ if (!userContext.continueLocally()) {
+ return null;
+ }
+ return buildNewContext().setContextState(userContext.getBreakpointsHitSafe(),
+ userContext.getExceptionDataSafe());
+ }
+
static InternalContext getInternalContextForTests(DebugContext debugContext) {
PreContext.UserContext userContext = (PreContext.UserContext) debugContext;
return userContext.getInternalContextForTests();
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/DebugSession.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/DebugSession.java Mon Jun 07 16:51:19 2010 -0700
@@ -10,6 +10,7 @@
import java.util.List;
import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.DebugContext;
import org.chromium.sdk.DebugEventListener;
import org.chromium.sdk.InvalidContextException;
import org.chromium.sdk.Script;
@@ -32,7 +33,7 @@
import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
/**
- * A default, thread-safe implementation of the JsDebugContext interface.
+ * A class that holds and administers main parts of debug protocol implementation.
*/
public class DebugSession {
@@ -58,7 +59,7 @@
public DebugSession(DebugSessionManager sessionManager, V8ContextFilter contextFilter,
V8CommandOutput v8CommandOutput) {
- this.scriptManager = new ScriptManager(contextFilter);
+ this.scriptManager = new ScriptManager(contextFilter, this);
this.sessionManager = sessionManager;
this.breakpointManager = new BreakpointManager(this);
@@ -125,6 +126,27 @@
return contextBuilder;
}
+ /**
+ * Drops current context and creates a new one. This is useful if context is known to have changed
+ * (e.g. experimental feature LiveEdit may change current stack while execution is suspended).
+ * The method is asynchronous and returns immediately.
+ * Does nothing if currently there is no active context. Otherwise dismisses current context,
+ * invokes {@link DebugEventListener#resumed()} and initiates downloading stack frame descriptions
+ * and building new context. When the context is built,
+ * calls {@link DebugEventListener#suspended(DebugContext)}.
+ * <p>
+ * Must be called from Dispatch Thread.
+ * @return true if context has been actually dropped.
+ */
+ public boolean recreateCurrentContext() {
+ ContextBuilder.ExpectingBacktraceStep step = contextBuilder.startRebuildCurrentContext();
+ if (step == null) {
+ return false;
+ }
+ defaultResponseHandler.getBreakpointProcessor().processNextStep(step);
+ return true;
+ }
+
public void suspend(final SuspendCallback suspendCallback) {
V8CommandProcessor.V8HandlerCallback v8Callback = new V8CommandCallbackBase() {
@Override
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/JavascriptVmImpl.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JavascriptVmImpl.java Mon Jun 07 16:51:19 2010 -0700
@@ -9,6 +9,7 @@
import org.chromium.sdk.Breakpoint;
import org.chromium.sdk.CallbackSemaphore;
import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.SyncCallback;
import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
/**
@@ -35,9 +36,14 @@
public void setBreakpoint(Breakpoint.Type type, String target, int line,
int position, boolean enabled, String condition, int ignoreCount,
- BreakpointCallback callback) {
+ BreakpointCallback callback, SyncCallback syncCallback) {
getDebugSession().getBreakpointManager()
- .setBreakpoint(type, target, line, position, enabled, condition, ignoreCount, callback);
+ .setBreakpoint(type, target, line, position, enabled, condition, ignoreCount, callback,
+ syncCallback);
+ }
+
+ public void listBreakpoints(final ListBreakpointsCallback callback, SyncCallback syncCallback) {
+ getDebugSession().getBreakpointManager().reloadBreakpoints(callback, syncCallback);
}
protected abstract DebugSession getDebugSession();
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/JsObjectImpl.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsObjectImpl.java Mon Jun 07 16:51:19 2010 -0700
@@ -167,10 +167,10 @@
List<JsVariableImpl> getPropertiesLazily() throws MethodIsBlockingException {
synchronized (this) {
if (properties == null) {
-
- List<? extends PropertyReference> propertyRefs = getPropertyRefs(getSubpropertiesMirror());
- ValueLoader valueLoader = context.getValueLoader();
- List<ValueMirror> subMirrors = valueLoader.getOrLoadValueFromRefs(propertyRefs);
+ List<? extends PropertyReference> propertyRefs =
+ getPropertyRefs(getSubpropertiesMirror());
+ ValueLoader valueLoader = context.getValueLoader();
+ List<ValueMirror> subMirrors = valueLoader.getOrLoadValueFromRefs(propertyRefs);
List<JsVariableImpl> wrappedProperties = createPropertiesFromMirror(subMirrors,
propertyRefs);
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/ScriptImpl.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ScriptImpl.java Mon Jun 07 16:51:19 2010 -0700
@@ -4,20 +4,37 @@
package org.chromium.sdk.internal;
+import java.util.Collections;
import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.chromium.sdk.LiveEditDebugEventListener;
+import org.chromium.sdk.LiveEditExtension;
import org.chromium.sdk.Script;
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.UpdatableScript;
+import org.chromium.sdk.internal.protocol.ChangeLiveBody;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
import org.chromium.sdk.internal.protocol.data.ScriptHandle;
import org.chromium.sdk.internal.protocol.data.SomeHandle;
import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.V8CommandCallbackBase;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor;
+import org.chromium.sdk.internal.tools.v8.V8Helper;
import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+import org.chromium.sdk.internal.tools.v8.V8Helper.ScriptLoadCallback;
+import org.chromium.sdk.internal.tools.v8.request.ChangeLiveMessage;
/**
* An objects that holds data for a "script" which is a part of a resource
* loaded into the browser, identified by its original document URL, line offset
* in the original document, and the line count this script spans.
*/
-public class ScriptImpl implements Script {
+public class ScriptImpl implements Script, UpdatableScript {
+
+ /** The class logger. */
+ private static final Logger LOGGER = Logger.getLogger(ScriptImpl.class.getName());
/**
* An object containing data that uniquely identify a V8 script chunk.
@@ -29,15 +46,19 @@
public final int lineOffset;
+ public final int columnOffset;
+
public final int endLine;
public final long id;
- public Descriptor(Type type, long id, String name, int lineOffset, int lineCount) {
+ public Descriptor(Type type, long id, String name, int lineOffset, int columnOffset,
+ int lineCount) {
this.type = type;
this.id = id;
this.name = name;
this.lineOffset = lineOffset;
+ this.columnOffset = columnOffset;
this.endLine = lineOffset + lineCount - 1;
}
@@ -45,8 +66,8 @@
public int hashCode() {
return
name != null ? name.hashCode() : (int) id * 0x101 +
- lineOffset * 0x1001 +
- endLine * 0x10001;
+ lineOffset * 0x1001 + columnOffset * 0x10001 +
+ endLine * 0x100001;
}
@Override
@@ -61,6 +82,7 @@
// The id equality is stronger than the name equality.
return this.id == that.id &&
this.lineOffset == that.lineOffset &&
+ this.columnOffset == that.columnOffset &&
this.endLine == that.endLine;
}
@@ -78,9 +100,10 @@
return null;
}
int lineOffset = (int) script.lineOffset();
+ int columnOffset = (int) script.columnOffset();
int lineCount = (int) script.lineCount();
int id = V8ProtocolUtil.getScriptIdFromResponse(script).intValue();
- return new Descriptor(type, id, name, lineOffset, lineCount);
+ return new Descriptor(type, id, name, lineOffset, columnOffset, lineCount);
} catch (Exception e) {
// not a script object has been passed in
return null;
@@ -92,12 +115,15 @@
private volatile String source = null;
+ private final DebugSession debugSession;
+
/**
* @param descriptor of the script retrieved from a "scripts" response
*/
- public ScriptImpl(Descriptor descriptor) {
+ public ScriptImpl(Descriptor descriptor, DebugSession debugSession) {
this.descriptor = descriptor;
this.source = null;
+ this.debugSession = debugSession;
}
public Type getType() {
@@ -112,6 +138,10 @@
return descriptor.lineOffset;
}
+ public int getStartColumn() {
+ return descriptor.columnOffset;
+ }
+
public int getEndLine() {
return descriptor.endLine;
}
@@ -132,6 +162,56 @@
this.source = source;
}
+ public void setSourceOnRemote(String newSource, UpdateCallback callback,
+ SyncCallback syncCallback) {
+ V8CommandProcessor.V8HandlerCallback v8Callback = createScriptUpdateCallback(callback);
+ debugSession.sendMessageAsync(new ChangeLiveMessage(getId(), newSource),
+ true, v8Callback, syncCallback);
+ }
+
+ private V8CommandProcessor.V8HandlerCallback createScriptUpdateCallback(
+ final UpdateCallback callback) {
+ if (callback == null) {
+ return null;
+ }
+ return new V8CommandCallbackBase() {
+ @Override
+ public void success(SuccessCommandResponse successResponse) {
+ ChangeLiveBody body;
+ try {
+ body = successResponse.getBody().asChangeLiveBody();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+
+ ScriptLoadCallback scriptCallback = new ScriptLoadCallback() {
+ public void failure(String message) {
+ LOGGER.log(Level.SEVERE,
+ "Failed to reload script after LiveEdit script update; " + message);
+ }
+ public void success() {
+ LiveEditDebugEventListener listener =
+ LiveEditExtension.castToLiveEditListener(debugSession.getDebugEventListener());
+ if (listener != null) {
+ listener.scriptContentChanged(ScriptImpl.this);
+ }
+ }
+ };
+ V8Helper.reloadScriptAsync(debugSession, Collections.singletonList(getId()),
+ scriptCallback, null);
+
+ debugSession.recreateCurrentContext();
+
+ callback.success(body.getChangeLog());
+ }
+
+ @Override
+ public void failure(String message) {
+ callback.failure(message);
+ }
+ };
+ }
+
@Override
public int hashCode() {
return
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/ScriptManager.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ScriptManager.java Mon Jun 07 16:51:19 2010 -0700
@@ -40,9 +40,11 @@
Collections.synchronizedMap(new HashMap<Long, ScriptImpl>());
private final V8ContextFilter contextFilter;
+ private final DebugSession debugSession;
- ScriptManager(V8ContextFilter contextFilter) {
+ ScriptManager(V8ContextFilter contextFilter, DebugSession debugSession) {
this.contextFilter = contextFilter;
+ this.debugSession = debugSession;
}
/**
@@ -62,7 +64,7 @@
if (desc == null) {
return null;
}
- theScript = new ScriptImpl(desc);
+ theScript = new ScriptImpl(desc, debugSession);
idToScript.put(desc.id, theScript);
}
if (scriptBody.source() != null) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/ChangeLiveBody.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,15 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocolparser.JsonField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface ChangeLiveBody extends JsonSubtype<CommandResponseBody> {
+ @JsonField(jsonLiteralName="change_log")
+ Object getChangeLog();
+}
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/CommandResponse.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/CommandResponse.java Mon Jun 07 16:51:19 2010 -0700
@@ -8,6 +8,7 @@
import org.chromium.sdk.internal.protocolparser.EnumValueCondition;
import org.chromium.sdk.internal.protocolparser.JsonField;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
import org.chromium.sdk.internal.protocolparser.JsonSubtype;
import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
@@ -37,6 +38,7 @@
@JsonField(jsonLiteralName="request_seq")
long getRequestSeq();
+ @JsonOptionalField
String getCommand();
boolean success();
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/CommandResponseBody.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/CommandResponseBody.java Mon Jun 07 16:51:19 2010 -0700
@@ -41,4 +41,10 @@
@JsonSubtypeCasting
VersionBody asVersionBody() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ ChangeLiveBody asChangeLiveBody() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ ListBreakpointsBody asListBreakpointsBody() throws JsonProtocolParseException;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/ListBreakpointsBody.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,16 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.protocol.data.BreakpointInfo;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface ListBreakpointsBody extends JsonSubtype<CommandResponseBody> {
+ List<BreakpointInfo> breakpoints();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/BreakpointInfo.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,43 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonNullable;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface BreakpointInfo {
+ Type type();
+
+ @JsonOptionalField
+ String script_name();
+
+ @JsonOptionalField
+ Long script_id();
+
+ long number();
+
+ long line();
+
+ Long column();
+
+ Long groupId();
+
+ long hit_count();
+
+ boolean active();
+
+ @JsonNullable
+ String condition();
+
+ long ignoreCount();
+
+ enum Type {
+ scriptName,
+ scriptId,
+ function
+ }
+}
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser.java Mon Jun 07 16:51:19 2010 -0700
@@ -374,7 +374,7 @@
if (fieldTypeParser.asQuickParser() != null) {
LazyParseFieldMethodHandler onDemandHandler = new LazyParseFieldMethodHandler(
- fieldTypeParser.asQuickParser(), isOptional, fieldName);
+ fieldTypeParser.asQuickParser(), isOptional, fieldName, typeClass);
onDemandHanlers.add(onDemandHandler);
methodHandler = onDemandHandler;
} else {
@@ -534,11 +534,14 @@
private final QuickParser<?> quickParser;
private final boolean isOptional;
private final String fieldName;
+ private final Class<?> typeClass;
- LazyParseFieldMethodHandler(QuickParser<?> quickParser, boolean isOptional, String fieldName) {
+ LazyParseFieldMethodHandler(QuickParser<?> quickParser, boolean isOptional, String fieldName,
+ Class<?> typeClass) {
this.quickParser = quickParser;
this.isOptional = isOptional;
this.fieldName = fieldName;
+ this.typeClass = typeClass;
}
@Override
@@ -546,7 +549,8 @@
try {
return parse(objectData);
} catch (JsonProtocolParseException e) {
- throw new JsonProtocolParseRuntimeException("On demand parsing failed", e);
+ throw new JsonProtocolParseRuntimeException(
+ "On demand parsing failed for " + objectData.getUnderlyingObject(), e);
}
}
@@ -568,11 +572,13 @@
try {
return quickParser.parseValueQuick(value);
} catch (JsonProtocolParseException e) {
- throw new JsonProtocolParseException("Failed to parse field " + fieldName, e);
+ throw new JsonProtocolParseException("Failed to parse field " + fieldName + " in type " +
+ typeClass.getName(), e);
}
} else {
if (!isOptional) {
- throw new JsonProtocolParseException("Field is not optional: " + fieldName);
+ throw new JsonProtocolParseException("Field is not optional: " + fieldName +
+ " (in type " + typeClass.getName() + ")");
}
return null;
}
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/BreakpointImpl.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/BreakpointImpl.java Mon Jun 07 16:51:19 2010 -0700
@@ -5,7 +5,9 @@
package org.chromium.sdk.internal.tools.v8;
import org.chromium.sdk.Breakpoint;
-import org.chromium.sdk.BrowserTab;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.internal.protocol.data.BreakpointInfo;
/**
* A generic implementation of the Breakpoint interface.
@@ -23,6 +25,21 @@
private long id;
/**
+ * The corresponding script name as reported by the JavaScript VM. May be null.
+ */
+ private String scriptName;
+
+ /**
+ * The corresponding script id as reported by the JavaScript VM. May be null.
+ */
+ private Long scriptId;
+
+ /**
+ * Breakpoint line number. May become invalidated by LiveEdit actions.
+ */
+ private long lineNumber;
+
+ /**
* Whether the breakpoint is enabled.
*/
private boolean isEnabled;
@@ -50,16 +67,40 @@
*/
private volatile boolean isDirty = false;
- public BreakpointImpl(Type type, long id, boolean enabled, int ignoreCount, String condition,
- BreakpointManager breakpointManager) {
+ public BreakpointImpl(Type type, long id, String scriptName, Long scriptId, long lineNumber,
+ boolean enabled, int ignoreCount, String condition, BreakpointManager breakpointManager) {
this.type = type;
+ this.scriptName = scriptName;
+ this.scriptId = scriptId;
this.id = id;
this.isEnabled = enabled;
this.ignoreCount = ignoreCount;
this.condition = condition;
+ this.lineNumber = lineNumber;
this.breakpointManager = breakpointManager;
}
+ public BreakpointImpl(BreakpointInfo info, BreakpointManager breakpointManager) {
+ this.type = getType(info);
+ this.id = info.number();
+ this.breakpointManager = breakpointManager;
+ updateFromRemote(info);
+ }
+ public void updateFromRemote(BreakpointInfo info) {
+ if (this.type != getType(info)) {
+ throw new IllegalArgumentException();
+ }
+ if (this.id != info.number()) {
+ throw new IllegalArgumentException();
+ }
+ this.lineNumber = info.line();
+ this.isEnabled = info.active();
+ this.ignoreCount = (int) info.ignoreCount();
+ this.condition = info.condition();
+ this.scriptName = info.script_name();
+ this.scriptId = info.script_id();
+ }
+
public boolean isEnabled() {
return isEnabled;
}
@@ -72,6 +113,14 @@
return id;
}
+ public String getScriptName() {
+ return scriptName;
+ }
+
+ public Long getScriptId() {
+ return scriptId;
+ }
+
public int getIgnoreCount() {
return ignoreCount;
}
@@ -80,6 +129,10 @@
return condition;
}
+ public long getLineNumber() {
+ return lineNumber;
+ }
+
public void setEnabled(boolean enabled) {
if (this.isEnabled != enabled) {
setDirty(true);
@@ -106,21 +159,21 @@
return left == right || (left != null && left.equals(right));
}
- public void clear(final BrowserTab.BreakpointCallback callback) {
- breakpointManager.clearBreakpoint(this, callback);
+ public void clear(JavascriptVm.BreakpointCallback callback, SyncCallback syncCallback) {
+ breakpointManager.clearBreakpoint(this, callback, syncCallback);
// The order must be preserved, otherwise the breakpointProcessor will not be able
// to identify the original breakpoint ID.
this.id = INVALID_ID;
}
- public void flush(final BrowserTab.BreakpointCallback callback) {
+ public void flush(final JavascriptVm.BreakpointCallback callback, SyncCallback syncCallback) {
if (!isDirty()) {
if (callback != null) {
callback.success(this);
}
return;
}
- breakpointManager.changeBreakpoint(this, callback);
+ breakpointManager.changeBreakpoint(this, callback, syncCallback);
setDirty(false);
}
@@ -132,4 +185,13 @@
return isDirty;
}
-}
\ No newline at end of file
+ private static Type getType(BreakpointInfo info) {
+ BreakpointInfo.Type infoType = info.type();
+ switch (infoType) {
+ case scriptId: return Type.SCRIPT_ID;
+ case scriptName: return Type.SCRIPT_NAME;
+ case function: return Type.FUNCTION;
+ }
+ throw new RuntimeException("Unknown type: " + infoType);
+ }
+}
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/BreakpointManager.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/BreakpointManager.java Mon Jun 07 16:51:19 2010 -0700
@@ -1,22 +1,35 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
package org.chromium.sdk.internal.tools.v8;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import org.chromium.sdk.Breakpoint;
-import org.chromium.sdk.BrowserTab;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.SyncCallback;
import org.chromium.sdk.JavascriptVm.BreakpointCallback;
+import org.chromium.sdk.JavascriptVm.ListBreakpointsCallback;
import org.chromium.sdk.internal.DebugSession;
import org.chromium.sdk.internal.protocol.BreakpointBody;
+import org.chromium.sdk.internal.protocol.CommandResponseBody;
+import org.chromium.sdk.internal.protocol.ListBreakpointsBody;
import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.data.BreakpointInfo;
import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+import org.chromium.sdk.internal.tools.v8.request.ListBreakpointsMessage;
public class BreakpointManager {
/**
* This map shall contain only breakpoints with valid IDs.
*/
- private final Map<Long, Breakpoint> idToBreakpoint = new HashMap<Long, Breakpoint>();
+ private final Map<Long, BreakpointImpl> idToBreakpoint = new HashMap<Long, BreakpointImpl>();
private final DebugSession debugSession;
@@ -24,11 +37,28 @@
this.debugSession = debugSession;
}
- public void setBreakpoint(final Breakpoint.Type type, String target, int line, int position,
- final boolean enabled, final String condition, final int ignoreCount,
- final BrowserTab.BreakpointCallback callback) {
+ public void setBreakpoint(final Breakpoint.Type type, final String target,
+ final int line, int position, final boolean enabled, final String condition,
+ final int ignoreCount, final JavascriptVm.BreakpointCallback callback,
+ SyncCallback syncCallback) {
+
+ final String scriptName;
+ final Long scriptId;
+ Object targetObject;
+ if (type == Breakpoint.Type.SCRIPT_ID) {
+ scriptName = null;
+ scriptId = Long.parseLong(target);
+ targetObject = scriptId;
+ } else if (type == Breakpoint.Type.SCRIPT_NAME) {
+ scriptName = target;
+ scriptId = null;
+ targetObject = scriptName;
+ } else {
+ throw new IllegalArgumentException("Unsupported breakpoint type " + type);
+ }
+
debugSession.sendMessageAsync(
- DebuggerMessageFactory.setBreakpoint(type, target, toNullableInteger(line),
+ DebuggerMessageFactory.setBreakpoint(type, targetObject, toNullableInteger(line),
toNullableInteger(position), enabled, condition,
toNullableInteger(ignoreCount)),
true,
@@ -46,7 +76,7 @@
long id = body.getBreakpoint();
final BreakpointImpl breakpoint =
- new BreakpointImpl(type, id, enabled, ignoreCount,
+ new BreakpointImpl(type, id, scriptName, scriptId, line, enabled, ignoreCount,
condition, BreakpointManager.this);
callback.success(breakpoint);
@@ -59,7 +89,7 @@
}
}
},
- null);
+ syncCallback);
}
public Breakpoint getBreakpoint(Long id) {
@@ -67,7 +97,8 @@
}
public void clearBreakpoint(
- final BreakpointImpl breakpointImpl, final BreakpointCallback callback) {
+ final BreakpointImpl breakpointImpl, final BreakpointCallback callback,
+ SyncCallback syncCallback) {
long id = breakpointImpl.getId();
if (id == Breakpoint.INVALID_ID) {
return;
@@ -90,11 +121,11 @@
}
}
},
- null);
+ syncCallback);
}
public void changeBreakpoint(final BreakpointImpl breakpointImpl,
- final BreakpointCallback callback) {
+ final BreakpointCallback callback, SyncCallback syncCallback) {
debugSession.sendMessageAsync(
DebuggerMessageFactory.changeBreakpoint(breakpointImpl),
true,
@@ -112,7 +143,39 @@
}
}
},
- null);
+ syncCallback);
+ }
+
+ /**
+ * Reads a list of breakpoints from remote and updates local instances and the map.
+ */
+ public void reloadBreakpoints(final ListBreakpointsCallback callback, SyncCallback syncCallback) {
+ V8CommandCallbackBase v8Callback = new V8CommandCallbackBase() {
+ @Override
+ public void failure(String message) {
+ callback.failure(new Exception(message));
+ }
+ @Override
+ public void success(SuccessCommandResponse successResponse) {
+ CommandResponseBody body = successResponse.getBody();
+ ListBreakpointsBody listBreakpointsBody;
+ try {
+ listBreakpointsBody = body.asListBreakpointsBody();
+ } catch (JsonProtocolParseException e) {
+ callback.failure(new Exception("Failed to read server response", e));
+ return;
+ }
+ List<BreakpointInfo> infos = listBreakpointsBody.breakpoints();
+ try {
+ syncBreakpoints(infos);
+ } catch (RuntimeException e) {
+ callback.failure(new Exception("Failed to read server response", e));
+ return;
+ }
+ callback.success(Collections.unmodifiableCollection(idToBreakpoint.values()));
+ }
+ };
+ debugSession.sendMessageAsync(new ListBreakpointsMessage(), true, v8Callback, syncCallback);
}
private static Integer toNullableInteger(int value) {
@@ -120,4 +183,41 @@
? null
: value;
}
+
+ private void syncBreakpoints(List<BreakpointInfo> infoList) {
+ Map<Long, BreakpointImpl> actualBreakpoints = new HashMap<Long, BreakpointImpl>();
+ // Wrap all loaded BreakpointInfo as BreakpointImpl, possibly reusing old instances.
+ // Also check that all breakpoint id's in loaded list are unique.
+ for (BreakpointInfo info : infoList) {
+ if (info.type() == BreakpointInfo.Type.function) {
+ // We does not support function type breakpoints and ignore them.
+ continue;
+ }
+ BreakpointImpl breakpoint = idToBreakpoint.get(info.number());
+ if (breakpoint == null) {
+ breakpoint = new BreakpointImpl(info, this);
+ } else {
+ breakpoint.updateFromRemote(info);
+ }
+ Object conflict = actualBreakpoints.put(info.number(), breakpoint);
+ if (conflict != null) {
+ throw new RuntimeException("Duplicated breakpoint number " + info.number());
+ }
+ }
+
+ // Remove all obsolete breakpoints from the map.
+ for (Iterator<Long> it = idToBreakpoint.keySet().iterator(); it.hasNext(); ) {
+ Long id = it.next();
+ if (!actualBreakpoints.containsKey(id)) {
+ it.remove();
+ }
+ }
+
+ // Add breakpoints that are not in the main map yet.
+ for (BreakpointImpl breakpoint : actualBreakpoints.values()) {
+ if (!idToBreakpoint.containsKey(breakpoint.getId())) {
+ idToBreakpoint.put(breakpoint.getId(), breakpoint);
+ }
+ }
+ }
}
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/DebuggerCommand.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/DebuggerCommand.java Mon Jun 07 16:51:19 2010 -0700
@@ -16,11 +16,13 @@
BACKTRACE("backtrace"),
FRAME("frame"),
SCRIPTS("scripts"),
+ CHANGELIVE("changelive"),
SOURCE("source"),
SCOPE("scope"),
SETBREAKPOINT("setbreakpoint"),
CHANGEBREAKPOINT("changebreakpoint"),
CLEARBREAKPOINT("clearbreakpoint"),
+ LISTBREAKPOINTS("listbreakpoints"),
LOOKUP("lookup"),
SUSPEND("suspend"),
VERSION("version"),
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandCallbackBase.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandCallbackBase.java Mon Jun 07 16:51:19 2010 -0700
@@ -19,7 +19,7 @@
public void messageReceived(CommandResponse response) {
SuccessCommandResponse successResponse = response.asSuccess();
if (successResponse == null) {
- this.failure(response.asFailure().getMessage());
+ this.failure("Remote error: " + response.asFailure().getMessage());
return;
} else {
success(successResponse);
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8Helper.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8Helper.java Mon Jun 07 16:51:19 2010 -0700
@@ -19,6 +19,7 @@
import org.chromium.sdk.internal.PropertyHoldingValueMirror;
import org.chromium.sdk.internal.PropertyReference;
import org.chromium.sdk.internal.ScopeMirror;
+import org.chromium.sdk.internal.ScriptImpl;
import org.chromium.sdk.internal.ScriptManager;
import org.chromium.sdk.internal.SubpropertiesMirror;
import org.chromium.sdk.internal.ValueLoadException;
@@ -34,6 +35,7 @@
import org.chromium.sdk.internal.protocol.data.ScriptHandle;
import org.chromium.sdk.internal.protocol.data.ValueHandle;
import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.request.ContextlessDebuggerMessage;
import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
import org.chromium.sdk.internal.tools.v8.request.ScriptsMessage;
@@ -42,13 +44,7 @@
*/
public class V8Helper {
- /**
- * The debug context in which the operations are performed.
- */
- private final DebugSession debugSession;
-
public V8Helper(DebugSession debugSession) {
- this.debugSession = debugSession;
}
public interface ScriptLoadCallback {
@@ -63,13 +59,33 @@
*/
public static void reloadAllScriptsAsync(final DebugSession debugSession,
final ScriptLoadCallback callback, SyncCallback syncCallback) {
+ reloadScriptAsync(debugSession, null, callback, syncCallback);
+ }
+
+ /**
+ * Loads specified scripts or all existing scripts and stores them in ScriptManager.
+ * @param ids ids of requested scripts or null for all scripts
+ * @param callback to invoke when the script reloading has completed
+ * @param syncCallback to invoke after callback, regardless of whether it has returned normally
+ * or thrown an exception
+ */
+ public static void reloadScriptAsync(final DebugSession debugSession, final List<Long> ids,
+ final ScriptLoadCallback callback, SyncCallback syncCallback) {
+ ContextlessDebuggerMessage message = DebuggerMessageFactory.scripts(ids, true);
+ if (ids == null) {
+ message = DebuggerMessageFactory.scripts(ScriptsMessage.SCRIPTS_NORMAL, true);
+ } else {
+ message = DebuggerMessageFactory.scripts(ids, true);
+ }
debugSession.sendMessageAsync(
- DebuggerMessageFactory.scripts(ScriptsMessage.SCRIPTS_NORMAL, true),
+ message,
true,
new V8CommandCallbackBase() {
@Override
public void failure(String message) {
- callback.failure(message);
+ if (callback != null) {
+ callback.failure(message);
+ }
}
@Override
@@ -83,13 +99,24 @@
ScriptManager scriptManager = debugSession.getScriptManager();
for (int i = 0; i < body.size(); ++i) {
ScriptHandle scriptHandle = body.get(i);
+ if (ChromeDevToolSessionManager.JAVASCRIPT_VOID.equals(scriptHandle.source())) {
+ continue;
+ }
Long id = V8ProtocolUtil.getScriptIdFromResponse(scriptHandle);
- if (scriptManager.findById(id) == null &&
- !ChromeDevToolSessionManager.JAVASCRIPT_VOID.equals(scriptHandle.source())) {
+ ScriptImpl scriptById = scriptManager.findById(id);
+ if (scriptById == null) {
scriptManager.addScript(scriptHandle, successResponse.getRefs());
+ } else {
+ // A scrupulous refactoring note:
+ // do not call setSource in a legacy case, when ids parameter is null.
+ if (ids != null) {
+ scriptById.setSource(scriptHandle.source());
+ }
}
}
- callback.success();
+ if (callback != null) {
+ callback.success();
+ }
}
},
syncCallback);
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil.java Mon Jun 07 16:51:19 2010 -0700
@@ -19,6 +19,7 @@
import org.chromium.sdk.internal.protocol.BacktraceCommandBody;
import org.chromium.sdk.internal.protocol.BreakEventBody;
import org.chromium.sdk.internal.protocol.BreakpointBody;
+import org.chromium.sdk.internal.protocol.ChangeLiveBody;
import org.chromium.sdk.internal.protocol.CommandResponse;
import org.chromium.sdk.internal.protocol.CommandResponseBody;
import org.chromium.sdk.internal.protocol.EventNotification;
@@ -26,10 +27,12 @@
import org.chromium.sdk.internal.protocol.FailedCommandResponse;
import org.chromium.sdk.internal.protocol.FrameObject;
import org.chromium.sdk.internal.protocol.IncomingMessage;
+import org.chromium.sdk.internal.protocol.ListBreakpointsBody;
import org.chromium.sdk.internal.protocol.ScopeBody;
import org.chromium.sdk.internal.protocol.ScopeRef;
import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
import org.chromium.sdk.internal.protocol.VersionBody;
+import org.chromium.sdk.internal.protocol.data.BreakpointInfo;
import org.chromium.sdk.internal.protocol.data.ContextData;
import org.chromium.sdk.internal.protocol.data.ContextHandle;
import org.chromium.sdk.internal.protocol.data.FunctionValueHandle;
@@ -367,6 +370,8 @@
ScopeRef.class,
VersionBody.class,
AfterCompileBody.class,
+ ChangeLiveBody.class,
+ ListBreakpointsBody.class,
SomeHandle.class,
ScriptHandle.class,
@@ -381,6 +386,7 @@
SomeSerialized.class,
ContextHandle.class,
ContextData.class,
+ BreakpointInfo.class,
});
} catch (JsonProtocolModelParseException e) {
throw new RuntimeException(e);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ChangeLiveMessage.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,18 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "changelive" experimental V8 request message.
+ */
+public class ChangeLiveMessage extends ContextlessDebuggerMessage {
+ public ChangeLiveMessage(long scriptId, String newSource) {
+ super(DebuggerCommand.CHANGELIVE.value);
+ putArgument("script_id", scriptId);
+ putArgument("new_source", newSource);
+ }
+}
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/DebuggerMessageFactory.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/DebuggerMessageFactory.java Mon Jun 07 16:51:19 2010 -0700
@@ -45,7 +45,7 @@
return new SourceMessage(frame, fromLine, toLine);
}
- public static ContextlessDebuggerMessage setBreakpoint(Breakpoint.Type type, String target,
+ public static ContextlessDebuggerMessage setBreakpoint(Breakpoint.Type type, Object target,
Integer line, Integer position, Boolean enabled, String condition, Integer ignoreCount) {
return new SetBreakpointMessage(type, target, line, position, enabled, condition, ignoreCount);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ListBreakpointsMessage.java Mon Jun 07 16:51:19 2010 -0700
@@ -0,0 +1,17 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "listbreakpoints" V8 request message.
+ */
+public class ListBreakpointsMessage extends ContextlessDebuggerMessage {
+
+ public ListBreakpointsMessage() {
+ super(DebuggerCommand.LISTBREAKPOINTS.value);
+ }
+}
--- a/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SetBreakpointMessage.java Mon Jun 07 16:33:07 2010 -0700
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SetBreakpointMessage.java Mon Jun 07 16:51:19 2010 -0700
@@ -34,7 +34,7 @@
* @param ignoreCount nullable number specifying the amount of break point hits to ignore.
* Default is 0
*/
- public SetBreakpointMessage(Breakpoint.Type type, String target,
+ public SetBreakpointMessage(Breakpoint.Type type, Object target,
Integer line, Integer position, Boolean enabled, String condition,
Integer ignoreCount) {
super(DebuggerCommand.SETBREAKPOINT.value);