Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model')
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ICastToType.java21
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IDetailsProvider.java16
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISourceNotFoundPresentation.java47
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISymbolOwner.java18
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IWatchInExpressions.java18
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/StyledStringBuffer.java110
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationImageProvider.java31
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationManager.java622
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildren.java226
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExecContext.java157
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExpressions.java63
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenHoverExpressions.java70
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLocalVariables.java80
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLogExpressions.java67
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenModules.java51
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenRegisters.java87
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenStackTrace.java161
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenSubExpressions.java249
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationExpression.java82
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationModules.java91
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationRegister.java108
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFConsole.java236
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDebugTask.java59
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPane.java293
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPaneFactory.java49
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFMemoryBlockRetrieval.java508
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModel.java1801
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelManager.java156
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelPresentation.java269
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java510
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelSelectionPolicy.java129
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNode.java471
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeArrayPartition.java107
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExecContext.java1541
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExpression.java1618
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeLaunch.java139
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeModule.java134
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeRegister.java631
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java541
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeSymbol.java173
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNumberFormat.java230
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFRunnable.java33
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFSnapshot.java353
43 files changed, 12356 insertions, 0 deletions
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ICastToType.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ICastToType.java
new file mode 100644
index 000000000..4c9a8b773
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ICastToType.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.util.TCFDataCache;
+
+public interface ICastToType {
+
+ void onCastToTypeChanged();
+
+ TCFDataCache<ISymbols.Symbol> getType();
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IDetailsProvider.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IDetailsProvider.java
new file mode 100644
index 000000000..43595cd2c
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IDetailsProvider.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+public interface IDetailsProvider {
+
+ boolean getDetailText(StyledStringBuffer buf, Runnable done);
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISourceNotFoundPresentation.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISourceNotFoundPresentation.java
new file mode 100644
index 000000000..1b098052d
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISourceNotFoundPresentation.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.ui.IEditorInput;
+
+/**
+ * Adapter to customize source presentation for the source-not-found case.
+ */
+public interface ISourceNotFoundPresentation {
+
+ /**
+ * Returns an editor input that should be used to display a source-not-found editor
+ * for the given object or <code>null</code> if unable to provide an editor input
+ * for the given object.
+ *
+ * @param element a debug model element
+ * @param cfg the launch configuration of the debug element
+ * @param file unresolved source file path
+ * @return an editor input, or <code>null</code> if none
+ */
+ public IEditorInput getEditorInput(Object element, ILaunchConfiguration cfg, String file);
+
+ /**
+ * Returns the id of the source-not-found editor to use to display the
+ * given editor input and object, or <code>null</code> if
+ * unable to provide an editor id.
+ *
+ * @param input an editor input that was previously retrieved from this
+ * presentation's <code>getEditorInput</code> method
+ * @param element the object that was used in the call to
+ * <code>getEditorInput</code>, that corresponds to the given editor
+ * input
+ * @return an editor id, or <code>null</code> if none
+ */
+ public String getEditorId(IEditorInput input, Object element);
+
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISymbolOwner.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISymbolOwner.java
new file mode 100644
index 000000000..36ca46fea
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISymbolOwner.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+public interface ISymbolOwner {
+
+ void addSymbol(TCFNodeSymbol s);
+
+ void removeSymbol(TCFNodeSymbol s);
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IWatchInExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IWatchInExpressions.java
new file mode 100644
index 000000000..6fe499bd0
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IWatchInExpressions.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.tcf.util.TCFDataCache;
+
+public interface IWatchInExpressions {
+
+ TCFDataCache<String> getExpressionText();
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/StyledStringBuffer.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/StyledStringBuffer.java
new file mode 100644
index 000000000..eb0b82526
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/StyledStringBuffer.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.swt.graphics.RGB;
+
+class StyledStringBuffer {
+
+ private final StringBuffer bf = new StringBuffer();
+ private final ArrayList<Style> styles = new ArrayList<Style>();
+
+ static class Style {
+ int pos;
+ int len;
+ int font;
+ RGB bg;
+ RGB fg;
+ }
+
+ StyledStringBuffer append(int pos, int font, RGB bg, RGB fg) {
+ Style x = new Style();
+ x.pos = pos;
+ x.len = bf.length() - pos;
+ x.font = font;
+ x.bg = bg;
+ x.fg = fg;
+ styles.add(x);
+ return this;
+ }
+
+ StyledStringBuffer append(String s) {
+ bf.append(s);
+ return this;
+ }
+
+ StyledStringBuffer append(char ch) {
+ bf.append(ch);
+ return this;
+ }
+
+ StyledStringBuffer append(int i) {
+ bf.append(i);
+ return this;
+ }
+
+ StyledStringBuffer append(String s, int font) {
+ Style x = new Style();
+ x.pos = bf.length();
+ x.len = s.length();
+ x.font = font;
+ styles.add(x);
+ bf.append(s);
+ return this;
+ }
+
+ StyledStringBuffer append(String s, int font, RGB bg, RGB fg) {
+ Style x = new Style();
+ x.pos = bf.length();
+ x.len = s.length();
+ x.font = font;
+ x.bg = bg;
+ x.fg = fg;
+ styles.add(x);
+ bf.append(s);
+ return this;
+ }
+
+ StyledStringBuffer append(StyledStringBuffer s) {
+ int offs = bf.length();
+ for (Style y : s.styles) {
+ Style x = new Style();
+ x.pos = y.pos + offs;
+ x.len = y.len;
+ x.font = y.font;
+ x.bg = y.bg;
+ x.fg = y.fg;
+ styles.add(x);
+ }
+ bf.append(s.bf);
+ return this;
+ }
+
+ StringBuffer getStringBuffer() {
+ return bf;
+ }
+
+ Collection<Style> getStyle() {
+ return styles;
+ }
+
+ int length() {
+ return bf.length();
+ }
+
+ @Override
+ public String toString() {
+ return bf.toString();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationImageProvider.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationImageProvider.java
new file mode 100644
index 000000000..a3dcf8b2a
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationImageProvider.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.texteditor.IAnnotationImageProvider;
+
+public class TCFAnnotationImageProvider implements IAnnotationImageProvider {
+
+ public Image getManagedImage(Annotation annotation) {
+ return ((TCFAnnotationManager.TCFAnnotation)annotation).image;
+ }
+
+ public String getImageDescriptorId(Annotation annotation) {
+ return null;
+ }
+
+ public ImageDescriptor getImageDescriptor(String imageDescritporId) {
+ return null;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationManager.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationManager.java
new file mode 100644
index 000000000..22e7ba3f4
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationManager.java
@@ -0,0 +1,622 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationListener;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.debug.ui.ISourcePresentation;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tcf.internal.debug.launch.TCFSourceLookupDirector;
+import org.eclipse.tcf.internal.debug.model.ITCFBreakpointListener;
+import org.eclipse.tcf.internal.debug.model.TCFBreakpointsStatus;
+import org.eclipse.tcf.internal.debug.model.TCFContextState;
+import org.eclipse.tcf.internal.debug.model.TCFLaunch;
+import org.eclipse.tcf.internal.debug.model.TCFSourceRef;
+import org.eclipse.tcf.internal.debug.ui.Activator;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+import org.eclipse.tcf.protocol.JSON;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IBreakpoints;
+import org.eclipse.tcf.services.ILineNumbers;
+import org.eclipse.tcf.services.IRunControl;
+import org.eclipse.tcf.util.TCFDataCache;
+import org.eclipse.tcf.util.TCFTask;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+public class TCFAnnotationManager {
+
+ private static final String
+ TYPE_BP_INSTANCE = "org.eclipse.tcf.debug.breakpoint_instance",
+ TYPE_TOP_FRAME = "org.eclipse.tcf.debug.top_frame",
+ TYPE_STACK_FRAME = "org.eclipse.tcf.debug.stack_frame";
+
+ class TCFAnnotation extends Annotation {
+
+ final ILineNumbers.CodeArea area;
+ final Image image;
+ final String text;
+ final String type;
+ final int hash_code;
+
+ IAnnotationModel model;
+
+ TCFAnnotation(ILineNumbers.CodeArea area, Image image, String text, String type) {
+ this.area = area;
+ this.image = image;
+ this.text = text;
+ this.type = type;
+ hash_code = area.hashCode() + image.hashCode() + text.hashCode() + type.hashCode();
+ setText(text);
+ setType(type);
+ }
+
+ protected Image getImage() {
+ return image;
+ }
+
+ void dispose() {
+ assert Thread.currentThread() == display.getThread();
+ if (model != null) {
+ model.removeAnnotation(this);
+ model = null;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof TCFAnnotation)) return false;
+ TCFAnnotation a = (TCFAnnotation)o;
+ if (!area.equals(a.area)) return false;
+ if (!image.equals(a.image)) return false;
+ if (!text.equals(a.text)) return false;
+ if (!type.equals(a.type)) return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return hash_code;
+ }
+ }
+
+ private class WorkbenchWindowInfo {
+ final LinkedList<TCFAnnotation> annotations = new LinkedList<TCFAnnotation>();
+
+ void dispose() {
+ for (TCFAnnotation a : annotations) a.dispose();
+ annotations.clear();
+ }
+ }
+
+ private final HashMap<IWorkbenchWindow,WorkbenchWindowInfo> windows =
+ new HashMap<IWorkbenchWindow,WorkbenchWindowInfo>();
+
+ private final HashSet<IWorkbenchWindow> dirty_windows = new HashSet<IWorkbenchWindow>();
+ private final HashSet<TCFLaunch> dirty_launches = new HashSet<TCFLaunch>();
+ private final HashSet<TCFLaunch> changed_launch_cfgs = new HashSet<TCFLaunch>();
+
+ private final TCFLaunch.LaunchListener launch_listener = new TCFLaunch.LaunchListener() {
+
+ public void onCreated(TCFLaunch launch) {
+ }
+
+ public void onConnected(final TCFLaunch launch) {
+ updateAnnotations(null, launch);
+ TCFBreakpointsStatus bps = launch.getBreakpointsStatus();
+ if (bps == null) return;
+ bps.addListener(new ITCFBreakpointListener() {
+
+ public void breakpointStatusChanged(String id) {
+ updateAnnotations(null, launch);
+ }
+
+ public void breakpointRemoved(String id) {
+ updateAnnotations(null, launch);
+ }
+
+ public void breakpointChanged(String id) {
+ }
+ });
+ }
+
+ public void onDisconnected(final TCFLaunch launch) {
+ assert Protocol.isDispatchThread();
+ updateAnnotations(null, launch);
+ }
+
+ public void onProcessOutput(TCFLaunch launch, String process_id, int stream_id, byte[] data) {
+ }
+
+ public void onProcessStreamError(TCFLaunch launch, String process_id,
+ int stream_id, Exception error, int lost_size) {
+ }
+ };
+
+ private final ISelectionListener selection_listener = new ISelectionListener() {
+
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+ updateAnnotations(part.getSite().getWorkbenchWindow(), (TCFLaunch)null);
+ if (selection instanceof IStructuredSelection) {
+ final Object obj = ((IStructuredSelection)selection).getFirstElement();
+ if (obj instanceof TCFNodeStackFrame && ((TCFNodeStackFrame)obj).isTraceLimit()) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ ((TCFNodeStackFrame)obj).riseTraceLimit();
+ }
+ });
+ }
+ }
+ }
+ };
+
+ private final IWindowListener window_listener = new IWindowListener() {
+
+ public void windowActivated(IWorkbenchWindow window) {
+ }
+
+ public void windowClosed(IWorkbenchWindow window) {
+ assert windows.get(window) != null;
+ window.getSelectionService().removeSelectionListener(
+ IDebugUIConstants.ID_DEBUG_VIEW, selection_listener);
+ windows.remove(window).dispose();
+ }
+
+ public void windowDeactivated(IWorkbenchWindow window) {
+ }
+
+ public void windowOpened(IWorkbenchWindow window) {
+ if (windows.get(window) != null) return;
+ window.getSelectionService().addSelectionListener(
+ IDebugUIConstants.ID_DEBUG_VIEW, selection_listener);
+ windows.put(window, new WorkbenchWindowInfo());
+ updateAnnotations(window, (TCFLaunch)null);
+ }
+ };
+
+ private final ILaunchConfigurationListener launch_conf_listener = new ILaunchConfigurationListener() {
+
+ public void launchConfigurationAdded(ILaunchConfiguration cfg) {
+ }
+
+ public void launchConfigurationChanged(final ILaunchConfiguration cfg) {
+ displayExec(new Runnable() {
+ public void run() {
+ ILaunch[] arr = launch_manager.getLaunches();
+ for (ILaunch l : arr) {
+ if (l instanceof TCFLaunch) {
+ TCFLaunch t = (TCFLaunch)l;
+ if (cfg.equals(t.getLaunchConfiguration())) {
+ changed_launch_cfgs.add(t);
+ updateAnnotations(null, t);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ public void launchConfigurationRemoved(ILaunchConfiguration cfg) {
+ }
+ };
+
+ private final Display display = Display.getDefault();
+ private final ILaunchManager launch_manager = DebugPlugin.getDefault().getLaunchManager();
+ private int update_unnotations_cnt = 0;
+ private boolean started;
+ private boolean disposed;
+
+ public TCFAnnotationManager() {
+ assert Protocol.isDispatchThread();
+ TCFLaunch.addListener(launch_listener);
+ launch_manager.addLaunchConfigurationListener(launch_conf_listener);
+ displayExec(new Runnable() {
+ public void run() {
+ if (!PlatformUI.isWorkbenchRunning() || PlatformUI.getWorkbench().isStarting()) {
+ display.timerExec(200, this);
+ }
+ else if (!PlatformUI.getWorkbench().isClosing()) {
+ started = true;
+ PlatformUI.getWorkbench().addWindowListener(window_listener);
+ for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
+ window_listener.windowOpened(window);
+ }
+ IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (w != null) window_listener.windowActivated(w);
+ }
+ }
+ });
+ }
+
+ public void dispose() {
+ if (disposed) return;
+ assert Protocol.isDispatchThread();
+ disposed = true;
+ launch_manager.removeLaunchConfigurationListener(launch_conf_listener);
+ TCFLaunch.removeListener(launch_listener);
+ displayExec(new Runnable() {
+ public void run() {
+ if (!started) return;
+ PlatformUI.getWorkbench().removeWindowListener(window_listener);
+ for (IWorkbenchWindow window : windows.keySet()) {
+ window.getSelectionService().removeSelectionListener(
+ IDebugUIConstants.ID_DEBUG_VIEW, selection_listener);
+ windows.get(window).dispose();
+ }
+ windows.clear();
+ }
+ });
+ }
+
+ private void displayExec(Runnable r) {
+ synchronized (Device.class) {
+ if (!display.isDisposed()) {
+ display.asyncExec(r);
+ }
+ }
+ }
+
+ /**
+ * Return breakpoint status info for all active TCF debug sessions.
+ * @param breakpoint
+ * @return breakpoint status as defined by TCF Breakpoints service.
+ */
+ Map<TCFLaunch,Map<String,Object>> getBreakpointStatus(IBreakpoint breakpoint) {
+ assert Protocol.isDispatchThread();
+ Map<TCFLaunch,Map<String,Object>> map = new HashMap<TCFLaunch,Map<String,Object>>();
+ if (disposed) return null;
+ ILaunch[] launches = DebugPlugin.getDefault().getLaunchManager().getLaunches();
+ for (ILaunch l : launches) {
+ if (l instanceof TCFLaunch) {
+ TCFLaunch launch = (TCFLaunch)l;
+ TCFBreakpointsStatus bs = launch.getBreakpointsStatus();
+ if (bs != null) map.put(launch, bs.getStatus(breakpoint));
+ }
+ }
+ return map;
+ }
+
+ /**
+ * Return breakpoint status text for all active TCF debug sessions.
+ * @param breakpoint
+ * @return breakpoint status as a string.
+ */
+ @SuppressWarnings("unchecked")
+ String getBreakpointStatusText(IBreakpoint breakpoint) {
+ assert Protocol.isDispatchThread();
+ String error = null;
+ for (Map<String,Object> map : getBreakpointStatus(breakpoint).values()) {
+ if (map != null) {
+ String s = (String)map.get(IBreakpoints.STATUS_ERROR);
+ if (s != null && error == null) error = s;
+ Object planted = map.get(IBreakpoints.STATUS_INSTANCES);
+ if (planted != null) {
+ Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)planted;
+ for (Map<String,Object> m : list) {
+ if (m.get(IBreakpoints.INSTANCE_ERROR) == null) {
+ return "Planted";
+ }
+ }
+ }
+ }
+ }
+ return error;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object[] toObjectArray(Object o) {
+ if (o == null) return null;
+ Collection<Object> c = (Collection<Object>)o;
+ return (Object[])c.toArray(new Object[c.size()]);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<String,Object> toObjectMap(Object o) {
+ return (Map<String,Object>)o;
+ }
+
+ private void addBreakpointErrorAnnotation(List<TCFAnnotation> set, TCFLaunch launch, String id, String error) {
+ Map<String,Object> props = launch.getBreakpointsStatus().getProperties(id);
+ if (props != null) {
+ String file = (String)props.get(IBreakpoints.PROP_FILE);
+ Number line = (Number)props.get(IBreakpoints.PROP_LINE);
+ if (file != null && line != null) {
+ ILineNumbers.CodeArea area = new ILineNumbers.CodeArea(null, file,
+ line.intValue(), 0, line.intValue() + 1, 0,
+ null, null, 0, false, false, false, false);
+ TCFAnnotation a = new TCFAnnotation(area,
+ ImageCache.getImage(ImageCache.IMG_BREAKPOINT_ERROR),
+ "Cannot plant breakpoint: " + error,
+ TYPE_BP_INSTANCE);
+ set.add(a);
+ }
+ }
+ }
+
+ private void updateAnnotations(IWorkbenchWindow window, final TCFNode node) {
+ if (disposed) return;
+ assert Thread.currentThread() == display.getThread();
+ final WorkbenchWindowInfo win_info = windows.get(window);
+ if (win_info == null) return;
+ List<TCFAnnotation> set = null;
+ if (node != null) {
+ set = new TCFTask<List<TCFAnnotation>>(node.getChannel()) {
+ public void run() {
+ if (node.isDisposed()) {
+ done(null);
+ return;
+ }
+ TCFNodeExecContext thread = null;
+ TCFNodeExecContext memory = null;
+ TCFNodeStackFrame frame = null;
+ TCFNodeStackFrame last_top_frame = null;
+ String bp_group = null;
+ boolean suspended = false;
+ if (node instanceof TCFNodeStackFrame) {
+ thread = (TCFNodeExecContext)node.parent;
+ frame = (TCFNodeStackFrame)node;
+ }
+ else if (node instanceof TCFNodeExecContext) {
+ thread = (TCFNodeExecContext)node;
+ TCFChildrenStackTrace trace = thread.getStackTrace();
+ if (!trace.validate(this)) return;
+ frame = trace.getTopFrame();
+ }
+ if (thread != null) {
+ TCFDataCache<IRunControl.RunControlContext> rc_ctx_cache = thread.getRunContext();
+ if (!rc_ctx_cache.validate(this)) return;
+ IRunControl.RunControlContext rc_ctx_data = rc_ctx_cache.getData();
+ if (rc_ctx_data != null) bp_group = rc_ctx_data.getBPGroup();
+ TCFDataCache<TCFNodeExecContext> mem_cache = thread.getMemoryNode();
+ if (!mem_cache.validate(this)) return;
+ memory = mem_cache.getData();
+ if (bp_group == null && memory != null && rc_ctx_data != null && rc_ctx_data.hasState()) bp_group = memory.id;
+ last_top_frame = thread.getLastTopFrame();
+ TCFDataCache<TCFContextState> state_cache = thread.getState();
+ if (!state_cache.validate(this)) return;
+ suspended = state_cache.getData() != null && state_cache.getData().is_suspended;
+ }
+ List<TCFAnnotation> set = new ArrayList<TCFAnnotation>();
+ if (memory != null) {
+ TCFLaunch launch = node.launch;
+ TCFBreakpointsStatus bs = launch.getBreakpointsStatus();
+ if (bs != null) {
+ for (String id : bs.getStatusIDs()) {
+ Map<String,Object> map = bs.getStatus(id);
+ if (map == null) continue;
+ String error = (String)map.get(IBreakpoints.STATUS_ERROR);
+ if (error != null) addBreakpointErrorAnnotation(set, launch, id, error);
+ Object[] arr = toObjectArray(map.get(IBreakpoints.STATUS_INSTANCES));
+ if (arr == null) continue;
+ for (Object o : arr) {
+ Map<String,Object> m = toObjectMap(o);
+ String ctx_id = (String)m.get(IBreakpoints.INSTANCE_CONTEXT);
+ if (ctx_id == null) continue;
+ if (!ctx_id.equals(node.id) && !ctx_id.equals(bp_group)) continue;
+ error = (String)m.get(IBreakpoints.INSTANCE_ERROR);
+ BigInteger addr = JSON.toBigInteger((Number)m.get(IBreakpoints.INSTANCE_ADDRESS));
+ if (addr != null) {
+ ILineNumbers.CodeArea area = null;
+ TCFDataCache<TCFSourceRef> line_cache = memory.getLineInfo(addr);
+ if (line_cache != null) {
+ if (!line_cache.validate(this)) return;
+ TCFSourceRef line_data = line_cache.getData();
+ if (line_data != null && line_data.area != null) area = line_data.area;
+ }
+ if (area == null) {
+ Map<String,Object> props = bs.getProperties(id);
+ if (props != null) {
+ String file = (String)props.get(IBreakpoints.PROP_FILE);
+ Number line = (Number)props.get(IBreakpoints.PROP_LINE);
+ if (file != null && line != null) {
+ area = new ILineNumbers.CodeArea(null, file,
+ line.intValue(), 0, line.intValue() + 1, 0,
+ null, null, 0, false, false, false, false);
+ }
+ }
+ }
+ if (area != null) {
+ if (error != null) {
+ TCFAnnotation a = new TCFAnnotation(area,
+ ImageCache.getImage(ImageCache.IMG_BREAKPOINT_ERROR),
+ "Cannot plant breakpoint at 0x" + addr.toString(16) + ": " + error,
+ TYPE_BP_INSTANCE);
+ set.add(a);
+ error = null;
+ }
+ else {
+ TCFAnnotation a = new TCFAnnotation(area,
+ ImageCache.getImage(ImageCache.IMG_BREAKPOINT_INSTALLED),
+ "Breakpoint planted at 0x" + addr.toString(16),
+ TYPE_BP_INSTANCE);
+ set.add(a);
+ }
+ }
+ }
+ if (error != null) addBreakpointErrorAnnotation(set, launch, id, error);
+ }
+ }
+ }
+ }
+ if (suspended && frame != null && frame.getFrameNo() >= 0) {
+ TCFDataCache<TCFSourceRef> line_cache = frame.getLineInfo();
+ if (!line_cache.validate(this)) return;
+ TCFSourceRef line_data = line_cache.getData();
+ if (line_data != null && line_data.area != null) {
+ TCFAnnotation a = null;
+ if (frame.getFrameNo() == 0) {
+ a = new TCFAnnotation(line_data.area,
+ DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER_TOP),
+ "Current Instruction Pointer",
+ TYPE_TOP_FRAME);
+ }
+ else {
+ a = new TCFAnnotation(line_data.area,
+ DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER),
+ "Stack Frame",
+ TYPE_STACK_FRAME);
+ }
+ set.add(a);
+ }
+ }
+ if (!suspended && last_top_frame != null) {
+ TCFDataCache<TCFSourceRef> line_cache = last_top_frame.getLineInfo();
+ if (!line_cache.validate(this)) return;
+ TCFSourceRef line_data = line_cache.getData();
+ if (line_data != null && line_data.area != null) {
+ TCFAnnotation a = new TCFAnnotation(line_data.area,
+ DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER),
+ "Last Instruction Pointer position",
+ TYPE_STACK_FRAME);
+ set.add(a);
+ }
+ }
+ done(set);
+ }
+ }.getE();
+ }
+ boolean flush_all = node == null || changed_launch_cfgs.contains(node.launch);
+ Iterator<TCFAnnotation> i = win_info.annotations.iterator();
+ while (i.hasNext()) {
+ TCFAnnotation a = i.next();
+ if (!flush_all && set != null && set.remove(a)) continue;
+ a.dispose();
+ i.remove();
+ }
+ if (set == null || set.size() == 0) return;
+ Map<IEditorInput,ITextEditor> editors = new HashMap<IEditorInput,ITextEditor>();
+ for (IEditorReference ref : window.getActivePage().getEditorReferences()) {
+ IEditorPart part = ref.getEditor(false);
+ if (!(part instanceof ITextEditor)) continue;
+ ITextEditor editor = (ITextEditor)part;
+ editors.put(editor.getEditorInput(), editor);
+ }
+ ISourcePresentation presentation = TCFModelPresentation.getDefault();
+ for (TCFAnnotation a : set) {
+ Object source_element = TCFSourceLookupDirector.lookup(node.launch, a.area);
+ if (source_element == null) continue;
+ IEditorInput editor_input = presentation.getEditorInput(source_element);
+ ITextEditor editor = editors.get(editor_input);
+ if (editor == null) continue;
+ IDocumentProvider doc_provider = editor.getDocumentProvider();
+ IAnnotationModel ann_model = doc_provider.getAnnotationModel(editor_input);
+ if (ann_model == null) continue;
+ IRegion region = null;
+ try {
+ doc_provider.connect(editor_input);
+ }
+ catch (CoreException e) {
+ }
+ try {
+ IDocument document = doc_provider.getDocument(editor_input);
+ if (document != null) region = document.getLineInformation(a.area.start_line - 1);
+ }
+ catch (BadLocationException e) {
+ }
+ finally {
+ doc_provider.disconnect(editor_input);
+ }
+ if (region == null) continue;
+ ann_model.addAnnotation(a, new Position(region.getOffset(), region.getLength()));
+ a.model = ann_model;
+ win_info.annotations.add(a);
+ }
+ }
+
+ private void updateAnnotations(final int cnt) {
+ displayExec(new Runnable() {
+ public void run() {
+ synchronized (TCFAnnotationManager.this) {
+ if (cnt != update_unnotations_cnt) return;
+ }
+ for (IWorkbenchWindow window : windows.keySet()) {
+ if (dirty_windows.contains(null) || dirty_windows.contains(window)) {
+ TCFNode node = null;
+ try {
+ ISelection active_context = DebugUITools.getDebugContextManager()
+ .getContextService(window).getActiveContext();
+ if (active_context instanceof IStructuredSelection) {
+ IStructuredSelection selection = (IStructuredSelection)active_context;
+ if (!selection.isEmpty()) {
+ Object first_element = selection.getFirstElement();
+ if (first_element instanceof IAdaptable) {
+ node = (TCFNode)((IAdaptable)first_element).getAdapter(TCFNode.class);
+ }
+ }
+ }
+ if (dirty_launches.contains(null) || node != null && dirty_launches.contains(node.launch)) {
+ updateAnnotations(window, node);
+ }
+ }
+ catch (Throwable x) {
+ if (node == null || !node.isDisposed()) {
+ Activator.log("Cannot update editor annotations", x);
+ }
+ }
+ }
+ }
+ for (TCFLaunch launch : dirty_launches) {
+ if (launch != null) launch.removePendingClient(TCFAnnotationManager.this);
+ }
+ changed_launch_cfgs.clear();
+ dirty_windows.clear();
+ dirty_launches.clear();
+ }
+ });
+ }
+
+ synchronized void updateAnnotations(final IWorkbenchWindow window, final TCFLaunch launch) {
+ final int cnt = ++update_unnotations_cnt;
+ displayExec(new Runnable() {
+ public void run() {
+ dirty_windows.add(window);
+ dirty_launches.add(launch);
+ updateAnnotations(cnt);
+ }
+ });
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildren.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildren.java
new file mode 100644
index 000000000..1f98fa578
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildren.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.util.TCFDataCache;
+
+/**
+ * TCFChildren is a concrete type of TCF data cache that is used to cache a list of children.
+ */
+public abstract class TCFChildren extends TCFDataCache<Map<String,TCFNode>> {
+
+ private final int pool_margin;
+ private final Map<String,TCFNode> node_pool = new LinkedHashMap<String,TCFNode>(32, 0.75f, true);
+
+ protected final TCFNode node;
+
+ private static final TCFNode[] EMPTY_NODE_ARRAY = new TCFNode[0];
+
+ private TCFNode[] array;
+
+ TCFChildren(TCFNode node) {
+ super(node.channel);
+ this.node = node;
+ pool_margin = 0;
+ node.addDataCache(this);
+ }
+
+ TCFChildren(TCFNode node, int pool_margin) {
+ super(node.channel);
+ this.node = node;
+ this.pool_margin = pool_margin;
+ node.addDataCache(this);
+ }
+
+ /**
+ * Dispose the cache and all nodes in the nodes pool.
+ */
+ @Override
+ public void dispose() {
+ assert !isDisposed();
+ node.removeDataCache(this);
+ for (TCFNode n : node_pool.values()) n.dispose();
+ node_pool.clear();
+ super.dispose();
+ }
+
+ /**
+ * Remove a node from cache.
+ * The method is called every time a node is disposed.
+ * @param id - node ID
+ */
+ void onNodeDisposed(String id) {
+ node_pool.remove(id);
+ if (isValid()) {
+ array = null;
+ Map<String,TCFNode> data = getData();
+ if (data != null) data.remove(id);
+ }
+ }
+
+ private void addToPool(Map<String,TCFNode> data) {
+ assert !isDisposed();
+ for (TCFNode n : data.values()) {
+ assert data.get(n.id) == n;
+ assert n.parent == node;
+ node_pool.put(n.id, n);
+ }
+ if (node_pool.size() > data.size() + pool_margin) {
+ String[] arr = node_pool.keySet().toArray(new String[node_pool.size()]);
+ for (String id : arr) {
+ if (data.get(id) == null) {
+ node_pool.get(id).dispose();
+ if (node_pool.size() <= data.size() + pool_margin) break;
+ }
+ }
+ }
+ }
+
+ /**
+ * End cache pending state.
+ * @param token - pending command handle.
+ * @param error - data retrieval error or null
+ * @param data - up-to-date map of children nodes
+ */
+ @Override
+ public void set(IToken token, Throwable error, Map<String,TCFNode> data) {
+ array = null;
+ if (isDisposed()) {
+ // A command can return data after the cache element has been disposed.
+ // Just ignore the data in such case.
+ super.set(token, null, null);
+ assert node_pool.isEmpty();
+ }
+ else if (data != null) {
+ super.set(token, error, data);
+ addToPool(data);
+ }
+ else {
+ super.set(token, error, new HashMap<String,TCFNode>());
+ }
+ }
+
+ /**
+ * Set given data to the cache, mark cache as valid, cancel any pending data retrieval.
+ * @param data - up-to-date data to store in the cache, null means empty collection of nodes.
+ */
+ @Override
+ public void reset(Map<String,TCFNode> data) {
+ assert !isDisposed();
+ array = null;
+ if (data != null) {
+ super.reset(data);
+ addToPool(data);
+ }
+ else {
+ super.reset(new HashMap<String,TCFNode>());
+ }
+ }
+
+ /**
+ * Invalidate the cache. If retrieval is in progress - let it continue.
+ */
+ @Override
+ public void reset() {
+ super.reset();
+ array = null;
+ }
+
+ /**
+ * Force cache to invalid state, cancel pending data retrieval if any.
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ array = null;
+ }
+
+ /**
+ * Add a node to collection of children.
+ * @param n - a node.
+ */
+ void add(TCFNode n) {
+ assert !isDisposed();
+ assert !n.isDisposed();
+ assert node_pool.get(n.id) == null;
+ assert n.parent == node;
+ node_pool.put(n.id, n);
+ if (isValid()) {
+ array = null;
+ Map<String,TCFNode> data = getData();
+ if (data != null) data.put(n.id, n);
+ }
+ }
+
+ /**
+ * Return collection of all nodes, including current children as well as
+ * currently unused nodes from the pool.
+ * To get only current children use getData() method.
+ * @return Collection of nodes.
+ */
+ Collection<TCFNode> getNodes() {
+ return node_pool.values();
+ }
+
+ /**
+ * Return current number of children.
+ * The cache must be valid for the method to work.
+ * @return number of children.
+ */
+ public int size() {
+ assert isValid();
+ Map<String,TCFNode> data = getData();
+ return data == null ? 0 : data.size();
+ }
+
+ /**
+ * Return current children nodes as a sorted array.
+ * @return array of nodes.
+ */
+ public TCFNode[] toArray() {
+ assert isValid();
+ if (array != null) return array;
+ Map<String,TCFNode> data = getData();
+ if (data == null || data.size() == 0) return array = EMPTY_NODE_ARRAY;
+ array = data.values().toArray(new TCFNode[data.size()]);
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return current children nodes in IChildrenUpdate object.
+ * @param update - children update request object.
+ * @param done - a call-back object, it is called when cache state changes.
+ * @return true if all done, false if data request is pending.
+ */
+ boolean getData(IChildrenUpdate update, Runnable done) {
+ if (!validate(done)) return false;
+ TCFNode[] arr = toArray();
+ int offset = 0;
+ int r_offset = update.getOffset();
+ int r_length = update.getLength();
+ for (TCFNode n : arr) {
+ if (offset >= r_offset && offset < r_offset + r_length) {
+ update.setChild(n, offset);
+ }
+ offset++;
+ }
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExecContext.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExecContext.java
new file mode 100644
index 000000000..eb4d6d85b
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExecContext.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IMemory;
+import org.eclipse.tcf.services.IRunControl;
+import org.eclipse.tcf.util.TCFDataCache;
+
+
+/**
+ * This class is used to maintain a dynamic list of both executable contexts and memory spaces
+ * that are children of a given parent context. The job is slightly complicated by necessity
+ * to merge results from two independent services.
+ */
+public class TCFChildrenExecContext extends TCFChildren {
+
+ private final TCFChildren mem_children;
+ private final TCFChildren run_children;
+
+ TCFChildrenExecContext(final TCFNode node) {
+ super(node);
+ mem_children = new TCFChildren(node) {
+ @Override
+ protected boolean startDataRetrieval() {
+ IMemory mem = node.model.getLaunch().getService(IMemory.class);
+ if (mem == null) {
+ set(null, null, new HashMap<String,TCFNode>());
+ return true;
+ }
+ assert command == null;
+ command = mem.getChildren(node.id, new IMemory.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] contexts) {
+ Map<String,TCFNode> data = null;
+ if (command == token && error == null) {
+ int cnt = 0;
+ data = new HashMap<String,TCFNode>();
+ for (String id : contexts) {
+ TCFNode n = node.model.getNode(id);
+ if (n == null) n = new TCFNodeExecContext(node, id);
+ ((TCFNodeExecContext)n).setMemSeqNo(cnt++);
+ assert n.parent == node;
+ data.put(id, n);
+ }
+ }
+ set(token, error, data);
+ }
+ });
+ return false;
+ }
+ };
+ run_children = new TCFChildren(node) {
+ @Override
+ protected boolean startDataRetrieval() {
+ IRunControl run = node.model.getLaunch().getService(IRunControl.class);
+ if (run == null) {
+ set(null, null, new HashMap<String,TCFNode>());
+ return true;
+ }
+ assert command == null;
+ command = run.getChildren(node.id, new IRunControl.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] contexts) {
+ Map<String,TCFNode> data = null;
+ if (command == token && error == null) {
+ int cnt = 0;
+ data = new HashMap<String,TCFNode>();
+ for (String id : contexts) {
+ TCFNode n = node.model.getNode(id);
+ if (n == null) n = new TCFNodeExecContext(node, id);
+ ((TCFNodeExecContext)n).setExeSeqNo(cnt++);
+ assert n.parent == node;
+ data.put(id, n);
+ }
+ }
+ set(token, error, data);
+ }
+ });
+ return false;
+ }
+ };
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ TCFDataCache<?> pending = null;
+ if (!mem_children.validate()) pending = mem_children;
+ if (!run_children.validate()) pending = run_children;
+ if (pending != null) {
+ pending.wait(this);
+ return false;
+ }
+ Throwable error = mem_children.getError();
+ if (error == null) error = run_children.getError();
+ Map<String,TCFNode> data = new HashMap<String,TCFNode>();
+ Map<String,TCFNode> m1 = mem_children.getData();
+ Map<String,TCFNode> m2 = run_children.getData();
+ if (m1 != null) data.putAll(m1);
+ if (m2 != null) data.putAll(m2);
+ set(null, error, data);
+ return true;
+ }
+
+ void onContextAdded(IRunControl.RunControlContext context) {
+ // To preserve children order need to reset children list.
+ reset();
+ run_children.reset();
+ mem_children.reset();
+ assert !node.isDisposed();
+ String id = context.getID();
+ TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id);
+ if (n == null) {
+ n = new TCFNodeExecContext(node, id);
+ n.postContextAddedDelta();
+ add(n);
+ }
+ else {
+ n.postAllChangedDelta();
+ }
+ run_children.add(n);
+ n.setRunContext(context);
+ }
+
+ void onContextAdded(IMemory.MemoryContext context) {
+ // To preserve children order need to reset children list.
+ reset();
+ run_children.reset();
+ mem_children.reset();
+ assert !node.isDisposed();
+ String id = context.getID();
+ TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id);
+ if (n == null) {
+ n = new TCFNodeExecContext(node, id);
+ n.postContextAddedDelta();
+ add(n);
+ }
+ else {
+ n.postAllChangedDelta();
+ }
+ mem_children.add(n);
+ n.setMemoryContext(context);
+ }
+
+ void onAncestorContextChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExecContext)n).onAncestorContextChanged();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExpressions.java
new file mode 100644
index 000000000..66deb3200
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExpressions.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+
+import org.eclipse.debug.core.model.IExpression;
+import org.eclipse.debug.core.model.IWatchExpression;
+
+public class TCFChildrenExpressions extends TCFChildren {
+
+ TCFChildrenExpressions(TCFNode node) {
+ super(node, 128);
+ }
+
+ void onSuspended() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onSuspended();
+ }
+
+ void onRegisterValueChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onRegisterValueChanged();
+ }
+
+ void onMemoryChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryChanged();
+ }
+
+ void onMemoryMapChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryMapChanged();
+ }
+
+ private TCFNodeExpression findScript(String text) {
+ for (TCFNode n : getNodes()) {
+ TCFNodeExpression e = (TCFNodeExpression)n;
+ if (text.equals(e.getScript())) return e;
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ int cnt = 0;
+ HashMap<String,TCFNode> data = new HashMap<String,TCFNode>();
+ for (final IExpression e : node.model.getExpressionManager().getExpressions()) {
+ String text = e.getExpressionText();
+ TCFNodeExpression n = findScript(text);
+ if (n == null) add(n = new TCFNodeExpression(node, text, null, null, null, -1, false));
+ n.setSortPosition(cnt++);
+ if (e instanceof IWatchExpression) n.setEnabled(((IWatchExpression)e).isEnabled());
+ data.put(n.id, n);
+ }
+ set(null, null, data);
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenHoverExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenHoverExpressions.java
new file mode 100644
index 000000000..79eef7dd3
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenHoverExpressions.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+
+/**
+ * Provides the cache of root nodes for the expression hover.
+ */
+class TCFChildrenHoverExpressions extends TCFChildren {
+
+ private String expression;
+
+ TCFChildrenHoverExpressions(TCFNode parent) {
+ super(parent, 16);
+ }
+
+ void setExpression(String expression) {
+ if (expression == this.expression) return;
+ if (expression != null && expression.equals(this.expression)) return;
+ this.expression = expression;
+ cancel();
+ }
+
+ void onSuspended() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onSuspended();
+ }
+
+ void onRegisterValueChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onRegisterValueChanged();
+ }
+
+ void onMemoryChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryChanged();
+ }
+
+ void onMemoryMapChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryMapChanged();
+ }
+
+ private TCFNodeExpression findScript(String text) {
+ for (TCFNode n : getNodes()) {
+ TCFNodeExpression e = (TCFNodeExpression)n;
+ if (text.equals(e.getScript())) return e;
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ HashMap<String,TCFNode> data = new HashMap<String,TCFNode>();
+ if (expression != null) {
+ TCFNodeExpression expression_node = findScript(expression);
+ if (expression_node == null) {
+ add(expression_node = new TCFNodeExpression(node, expression, null, null, null, -1, false));
+ }
+ data.put(expression_node.id, expression_node);
+ }
+ set(null, null, data);
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLocalVariables.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLocalVariables.java
new file mode 100644
index 000000000..e87ef5b5c
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLocalVariables.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IExpressions;
+
+public class TCFChildrenLocalVariables extends TCFChildren {
+
+ private final TCFNodeStackFrame node;
+
+ TCFChildrenLocalVariables(TCFNodeStackFrame node) {
+ super(node, 128);
+ this.node = node;
+ }
+
+ void onSuspended() {
+ reset();
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onSuspended();
+ }
+
+ void onRegisterValueChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onRegisterValueChanged();
+ }
+
+ void onMemoryChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryChanged();
+ }
+
+ void onMemoryMapChanged() {
+ reset();
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryMapChanged();
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ IExpressions exps = node.model.getLaunch().getService(IExpressions.class);
+ if (exps == null || node.isEmulated()) {
+ set(null, null, new HashMap<String,TCFNode>());
+ return true;
+ }
+ TCFChildrenStackTrace stack_trace_cache = ((TCFNodeExecContext)node.parent).getStackTrace();
+ if (!stack_trace_cache.validate(this)) return false; // node.getFrameNo() is not valid
+ if (node.getFrameNo() < 0) {
+ set(null, null, new HashMap<String,TCFNode>());
+ return true;
+ }
+ assert command == null;
+ command = exps.getChildren(node.id, new IExpressions.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] contexts) {
+ Map<String,TCFNode> data = null;
+ if (command == token && error == null) {
+ int cnt = 0;
+ data = new HashMap<String,TCFNode>();
+ for (String id : contexts) {
+ TCFNodeExpression n = (TCFNodeExpression)node.model.getNode(id);
+ if (n == null) n = new TCFNodeExpression(node, null, null, id, null, -1, false);
+ assert n.id.equals(id);
+ assert n.parent == node;
+ n.setSortPosition(cnt++);
+ data.put(n.id, n);
+ }
+ }
+ set(token, error, data);
+ }
+ });
+ return false;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLogExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLogExpressions.java
new file mode 100644
index 000000000..f5c2f8476
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLogExpressions.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class TCFChildrenLogExpressions extends TCFChildren {
+
+ private final HashSet<String> scripts = new HashSet<String>();
+
+ TCFChildrenLogExpressions(TCFNodeExecContext node) {
+ super(node, 16);
+ }
+
+ void onSuspended() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onSuspended();
+ scripts.clear();
+ reset();
+ }
+
+ void onRegisterValueChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onRegisterValueChanged();
+ }
+
+ void onMemoryChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryChanged();
+ }
+
+ void onMemoryMapChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryMapChanged();
+ }
+
+ public TCFNodeExpression findScript(String script) {
+ for (TCFNode n : getNodes()) {
+ TCFNodeExpression e = (TCFNodeExpression)n;
+ if (script.equals(e.getScript())) return e;
+ }
+ return null;
+ }
+
+ public void addScript(String script) {
+ if (scripts.add(script)) reset();
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ HashMap<String,TCFNode> data = new HashMap<String,TCFNode>();
+ for (String script : scripts) {
+ TCFNodeExpression expression_node = findScript(script);
+ if (expression_node == null) {
+ add(expression_node = new TCFNodeExpression(node, script, null, null, null, -1, false));
+ }
+ data.put(expression_node.id, expression_node);
+ }
+ set(null, null, data);
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenModules.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenModules.java
new file mode 100644
index 000000000..8e37eb98f
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenModules.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext.MemoryRegion;
+import org.eclipse.tcf.util.TCFDataCache;
+
+/**
+ * Provides and caches memory regions (modules) for a context.
+ */
+public class TCFChildrenModules extends TCFChildren {
+
+ public TCFChildrenModules(TCFNode node) {
+ super(node, 128);
+ }
+
+ void onMemoryMapChanged() {
+ reset();
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ TCFNodeExecContext exe = (TCFNodeExecContext)node;
+ TCFDataCache<MemoryRegion[]> map_cache = exe.getMemoryMap();
+ if (!map_cache.validate(this)) return false;
+ MemoryRegion[] map = map_cache.getData();
+ Map<String, TCFNode> data = new HashMap<String, TCFNode>();
+ if (map != null) {
+ for (MemoryRegion region : map) {
+ String id = node.id + ".Module-" + region.region.getFileName() + '@' + region.region.getAddress();
+ TCFNodeModule module = (TCFNodeModule) node.model.getNode(id);
+ if (module == null) module = new TCFNodeModule(node, id);
+ module.setRegion(region.region);
+ data.put(id, module);
+ }
+ }
+ set(null, map_cache.getError(), data);
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenRegisters.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenRegisters.java
new file mode 100644
index 000000000..c2a91bb2e
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenRegisters.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IRegisters;
+
+
+public class TCFChildrenRegisters extends TCFChildren {
+
+ TCFChildrenRegisters(TCFNode node) {
+ super(node, 128);
+ }
+
+ void onSuspended() {
+ for (TCFNode n : getNodes()) ((TCFNodeRegister)n).onSuspended();
+ }
+
+ void onParentValueChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeRegister)n).onParentValueChanged();
+ }
+
+ void onRegistersChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeRegister)n).onRegistersChanged();
+ reset();
+ }
+
+ @Override
+ void onNodeDisposed(String id) {
+ super.onNodeDisposed(id);
+ if (node instanceof TCFNodeExecContext) {
+ // CPU register nodes are special case:
+ // they have executable node as parent,
+ // but they are also referenced as children of stack frames
+ for (TCFNode n : ((TCFNodeExecContext)node).getStackTrace().getNodes()) {
+ ((TCFNodeStackFrame)n).getRegisters().onNodeDisposed(id);
+ }
+ }
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ IRegisters regs = node.model.getLaunch().getService(IRegisters.class);
+ if (regs == null) {
+ set(null, null, new HashMap<String,TCFNode>());
+ return true;
+ }
+ if (node instanceof TCFNodeStackFrame) {
+ TCFChildrenStackTrace stack_trace_cache = ((TCFNodeExecContext)node.parent).getStackTrace();
+ if (!stack_trace_cache.validate(this)) return false; // node.getFrameNo() is not valid
+ final int frame_no = ((TCFNodeStackFrame)node).getFrameNo();
+ if (frame_no < 0) {
+ set(null, null, new HashMap<String,TCFNode>());
+ return true;
+ }
+ }
+ assert command == null;
+ command = regs.getChildren(node.id, new IRegisters.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] contexts) {
+ Map<String,TCFNode> data = null;
+ if (command == token && error == null) {
+ int index = 0;
+ data = new HashMap<String,TCFNode>();
+ for (String id : contexts) {
+ TCFNodeRegister n = (TCFNodeRegister)node.model.getNode(id);
+ if (n == null) n = new TCFNodeRegister(node, id);
+ n.setIndex(index++);
+ data.put(id, n);
+ }
+ }
+ set(token, error, data);
+ }
+ });
+ return false;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenStackTrace.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenStackTrace.java
new file mode 100644
index 000000000..e62ec500a
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenStackTrace.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.tcf.internal.debug.model.TCFContextState;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IStackTrace;
+import org.eclipse.tcf.util.TCFDataCache;
+
+
+public class TCFChildrenStackTrace extends TCFChildren {
+
+ private final TCFNodeExecContext node;
+
+ private String top_frame_id;
+ private int limit_factor = 1;
+
+ TCFChildrenStackTrace(TCFNodeExecContext node) {
+ super(node, 16);
+ this.node = node;
+ }
+
+ void onSourceMappingChange() {
+ for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onSourceMappingChange();
+ }
+
+ void onExpressionAddedOrRemoved() {
+ for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onExpressionAddedOrRemoved();
+ }
+
+ void onSuspended() {
+ limit_factor = 1;
+ for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onSuspended();
+ reset();
+ }
+
+ void onRegistersChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onRegistersChanged();
+ reset();
+ }
+
+ void onMemoryMapChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onMemoryMapChanged();
+ reset();
+ }
+
+ void onMemoryChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onMemoryChanged();
+ reset();
+ }
+
+ void onRegisterValueChanged() {
+ for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onRegisterValueChanged();
+ reset();
+ }
+
+ void onResumed() {
+ reset(null);
+ }
+
+ void onPreferencesChanged() {
+ reset();
+ }
+
+ void riseTraceLimit() {
+ limit_factor++;
+ reset();
+ }
+
+ void postAllChangedDelta() {
+ for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).postAllChangedDelta();
+ }
+
+ public TCFNodeStackFrame getTopFrame() {
+ assert isValid();
+ return (TCFNodeStackFrame)node.model.getNode(top_frame_id);
+ }
+
+ @Override
+ public void set(IToken token, Throwable error, Map<String,TCFNode> data) {
+ for (TCFNode n : getNodes()) {
+ if (data == null || data.get(n.id) == null) ((TCFNodeStackFrame)n).setFrameNo(-1);
+ }
+ super.set(token, error, data);
+ }
+
+ private void addEmulatedTopFrame(HashMap<String,TCFNode> data) {
+ top_frame_id = node.id + "-TF";
+ TCFNodeStackFrame n = (TCFNodeStackFrame)node.model.getNode(top_frame_id);
+ if (n == null) n = new TCFNodeStackFrame(node, top_frame_id, true);
+ n.setFrameNo(0);
+ n.setTraceLimit(false);
+ data.put(n.id, n);
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ TCFDataCache<TCFContextState> state = node.getState();
+ if (!state.validate(this)) return false;
+ if (node.isNotActive()) {
+ top_frame_id = null;
+ set(null, null, new HashMap<String,TCFNode>());
+ return true;
+ }
+ Throwable state_error = state.getError();
+ TCFContextState state_data = state.getData();
+ if (state_error != null || state_data == null || !state_data.is_suspended) {
+ set(null, state_error, null);
+ return true;
+ }
+ final HashMap<String,TCFNode> data = new HashMap<String,TCFNode>();
+ IStackTrace st = node.model.getLaunch().getService(IStackTrace.class);
+ if (st == null) {
+ addEmulatedTopFrame(data);
+ set(null, null, data);
+ return true;
+ }
+ assert command == null;
+ command = st.getChildren(node.id, new IStackTrace.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] contexts) {
+ if (command == token) {
+ if (error == null && contexts != null) {
+ int limit_value = 0;
+ boolean limit_enabled = node.model.getStackFramesLimitEnabled();
+ if (limit_enabled) {
+ limit_value = node.model.getStackFramesLimitValue() * limit_factor;
+ if (limit_value <= 0) limit_value = limit_factor;
+ }
+ int cnt = contexts.length;
+ for (String id : contexts) {
+ cnt--;
+ if (!limit_enabled || cnt <= limit_value) {
+ TCFNodeStackFrame n = (TCFNodeStackFrame)node.model.getNode(id);
+ if (n == null) n = new TCFNodeStackFrame(node, id, false);
+ assert n.parent == node;
+ n.setFrameNo(cnt);
+ n.setTraceLimit(limit_enabled && cnt == limit_value);
+ data.put(id, n);
+ if (cnt == 0) top_frame_id = id;
+ }
+ }
+ }
+ if (data.size() == 0) addEmulatedTopFrame(data);
+ }
+ set(token, error, data);
+ }
+ });
+ return false;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenSubExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenSubExpressions.java
new file mode 100644
index 000000000..84907d355
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenSubExpressions.java
@@ -0,0 +1,249 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.tcf.services.IExpressions;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.util.TCFDataCache;
+
+public class TCFChildrenSubExpressions extends TCFChildren {
+
+ private final int par_level;
+ private final int par_offs;
+ private final int par_size;
+
+ TCFChildrenSubExpressions(TCFNode node, int par_level, int par_offs, int par_size) {
+ super(node, 128);
+ this.par_level = par_level;
+ this.par_offs = par_offs;
+ this.par_size = par_size;
+ }
+
+ void onSuspended() {
+ reset();
+ for (TCFNode n : getNodes()) {
+ if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onSuspended();
+ if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onSuspended();
+ }
+ }
+
+ void onValueChanged() {
+ reset();
+ for (TCFNode n : getNodes()) {
+ if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onValueChanged();
+ if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onValueChanged();
+ }
+ }
+
+ void onRegisterValueChanged() {
+ reset();
+ for (TCFNode n : getNodes()) {
+ if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onRegisterValueChanged();
+ if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onRegisterValueChanged();
+ }
+ }
+
+ void onMemoryChanged() {
+ reset();
+ for (TCFNode n : getNodes()) {
+ if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onMemoryChanged();
+ if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onMemoryChanged();
+ }
+ }
+
+ void onMemoryMapChanged() {
+ reset();
+ for (TCFNode n : getNodes()) {
+ if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onMemoryMapChanged();
+ if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onMemoryMapChanged();
+ }
+ }
+
+ void onCastToTypeChanged() {
+ cancel();
+ TCFNode a[] = getNodes().toArray(new TCFNode[getNodes().size()]);
+ for (int i = 0; i < a.length; i++) a[i].dispose();
+ }
+
+ private TCFNodeExpression findField(String field_id, boolean deref) {
+ assert field_id != null;
+ for (TCFNode n : getNodes()) {
+ TCFNodeExpression e = (TCFNodeExpression)n;
+ if (field_id.equals(e.getFieldID()) && e.isDeref() == deref) return e;
+ }
+ return null;
+ }
+
+ private boolean findFields(ISymbols.Symbol type, Map<String,TCFNode> map, boolean deref) {
+ TCFDataCache<String[]> children_cache = node.model.getSymbolChildrenCache(type.getID());
+ if (children_cache == null) return true;
+ if (!children_cache.validate(this)) return false;
+ String[] children = children_cache.getData();
+ if (children == null) return true;
+ TCFDataCache<?> pending = null;
+ for (String id : children) {
+ TCFDataCache<ISymbols.Symbol> sym_cache = node.model.getSymbolInfoCache(id);
+ if (!sym_cache.validate()) {
+ pending = sym_cache;
+ }
+ else {
+ ISymbols.Symbol sym_data = sym_cache.getData();
+ if (sym_data == null) continue;
+ if (sym_data.getSymbolClass() != ISymbols.SymbolClass.reference) continue;
+ if (sym_data.getName() == null) {
+ if (!findFields(sym_data, map, deref)) return false;
+ }
+ else {
+ TCFNodeExpression n = findField(id, deref);
+ if (n == null) add(n = new TCFNodeExpression(node, null, id, null, null, -1, deref));
+ n.setSortPosition(map.size());
+ map.put(n.id, n);
+ }
+ }
+ }
+ if (pending == null) return true;
+ pending.wait(this);
+ return false;
+ }
+
+ private TCFNodeExpression findReg(String reg_id) {
+ assert reg_id != null;
+ for (TCFNode n : getNodes()) {
+ TCFNodeExpression e = (TCFNodeExpression)n;
+ if (reg_id.equals(e.getRegisterID())) return e;
+ }
+ return null;
+ }
+
+ private boolean findRegs(TCFNodeRegister reg_node, Map<String,TCFNode> map) {
+ TCFChildren reg_children = reg_node.getChildren();
+ if (!reg_children.validate(this)) return false;
+ for (TCFNode subnode : reg_children.toArray()) {
+ TCFNodeExpression n = findReg(subnode.id);
+ if (n == null) add(n = new TCFNodeExpression(node, null, null, null, subnode.id, -1, false));
+ n.setSortPosition(map.size());
+ map.put(n.id, n);
+ }
+ return true;
+ }
+
+ private TCFNodeExpression findIndex(int index, boolean deref) {
+ assert index >= 0;
+ for (TCFNode n : getNodes()) {
+ TCFNodeExpression e = (TCFNodeExpression)n;
+ if (e.getIndex() == index && e.isDeref() == deref) return e;
+ }
+ return null;
+ }
+
+ private TCFNodeArrayPartition findPartition(int offs, int size) {
+ assert offs >= 0;
+ for (TCFNode n : getNodes()) {
+ TCFNodeArrayPartition e = (TCFNodeArrayPartition)n;
+ if (e.getOffset() == offs && e.getSize() == size) return e;
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean startDataRetrieval() {
+ assert !isDisposed();
+ TCFNode exp = node;
+ while (!(exp instanceof TCFNodeExpression)) exp = exp.parent;
+ TCFDataCache<ISymbols.Symbol> type_cache = ((TCFNodeExpression)exp).getType();
+ if (!type_cache.validate(this)) return false;
+ ISymbols.Symbol type_data = type_cache.getData();
+ if (type_data == null) {
+ HashMap<String,TCFNode> data = new HashMap<String,TCFNode>();
+ TCFDataCache<IExpressions.Value> val_cache = ((TCFNodeExpression)exp).getValue();
+ if (!val_cache.validate(this)) return false;
+ IExpressions.Value val_data = val_cache.getData();
+ if (val_data != null) {
+ String reg_id = val_data.getRegisterID();
+ if (reg_id != null) {
+ if (!node.model.createNode(reg_id, this)) return false;
+ if (isValid()) return true;
+ TCFNodeRegister reg_node = (TCFNodeRegister)node.model.getNode(reg_id);
+ if (!findRegs(reg_node, data)) return false;
+ }
+ }
+ set(null, null, data);
+ return true;
+ }
+ ISymbols.TypeClass type_class = type_data.getTypeClass();
+ Map<String,TCFNode> data = new HashMap<String,TCFNode>();
+ if (par_level > 0 && type_class != ISymbols.TypeClass.array) {
+ // Nothing
+ }
+ else if (type_class == ISymbols.TypeClass.composite) {
+ if (!findFields(type_data, data, false)) return false;
+ }
+ else if (type_class == ISymbols.TypeClass.array) {
+ int offs = par_level > 0 ? par_offs : 0;
+ int size = par_level > 0 ? par_size : type_data.getLength();
+ if (size <= 100) {
+ for (int i = offs; i < offs + size; i++) {
+ TCFNodeExpression n = findIndex(i, false);
+ if (n == null) n = new TCFNodeExpression(node, null, null, null, null, i, false);
+ n.setSortPosition(i);
+ data.put(n.id, n);
+ }
+ }
+ else {
+ int next_size = 100;
+ while (size / next_size > 100) next_size *= 100;
+ for (int i = offs; i < offs + size; i += next_size) {
+ int sz = next_size;
+ if (i + sz > offs + size) sz = offs + size - i;
+ TCFNodeArrayPartition n = findPartition(i, sz);
+ if (n == null) n = new TCFNodeArrayPartition(node, par_level + 1, i, sz);
+ data.put(n.id, n);
+ }
+ }
+ }
+ else if (type_class == ISymbols.TypeClass.pointer) {
+ TCFDataCache<IExpressions.Value> val_cache = ((TCFNodeExpression)exp).getValue();
+ if (!val_cache.validate(this)) return false;
+ IExpressions.Value val_data = val_cache.getData();
+ if (val_data != null && !isNull(val_data.getValue())) {
+ TCFDataCache<ISymbols.Symbol> base_type_cache = node.model.getSymbolInfoCache(type_data.getBaseTypeID());
+ if (base_type_cache != null) {
+ if (!base_type_cache.validate(this)) return false;
+ ISymbols.Symbol base_type_data = base_type_cache.getData();
+ if (base_type_data != null && base_type_data.getTypeClass() != ISymbols.TypeClass.function && base_type_data.getSize() > 0) {
+ if (base_type_data.getTypeClass() == ISymbols.TypeClass.composite) {
+ if (!findFields(base_type_data, data, true)) return false;
+ }
+ else {
+ TCFNodeExpression n = findIndex(0, true);
+ if (n == null) n = new TCFNodeExpression(node, null, null, null, null, 0, true);
+ n.setSortPosition(0);
+ data.put(n.id, n);
+ }
+ }
+ }
+ }
+ }
+ set(null, null, data);
+ return true;
+ }
+
+ private boolean isNull(byte[] data) {
+ if (data == null) return true;
+ for (byte b : data) {
+ if (b != 0) return false;
+ }
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationExpression.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationExpression.java
new file mode 100644
index 000000000..44604a4b5
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationExpression.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+public class TCFColumnPresentationExpression implements IColumnPresentation {
+
+ public static final String PRESENTATION_ID = "Expressions";
+
+ /**
+ * Presentation column IDs.
+ */
+ public static final String
+ COL_NAME = "Name",
+ COL_TYPE = "Type",
+ COL_HEX_VALUE = "HexValue",
+ COL_DEC_VALUE = "DecValue";
+
+ private static String[] cols_all = {
+ COL_NAME,
+ COL_TYPE,
+ COL_DEC_VALUE,
+ COL_HEX_VALUE,
+ };
+
+ private static String[] headers = {
+ "Name",
+ "Type",
+ "Decimal",
+ "Hex",
+ };
+
+ private static String[] cols_ini = {
+ COL_NAME,
+ COL_DEC_VALUE,
+ COL_HEX_VALUE,
+ };
+
+ public void dispose() {
+ }
+
+ public String[] getAvailableColumns() {
+ return cols_all;
+ }
+
+ public String getHeader(String id) {
+ for (int i = 0; i < cols_all.length; i++) {
+ if (id.equals(cols_all[i])) return headers[i];
+ }
+ return null;
+ }
+
+ public String getId() {
+ return PRESENTATION_ID;
+ }
+
+ public ImageDescriptor getImageDescriptor(String id) {
+ return null;
+ }
+
+ public String[] getInitialColumns() {
+ return cols_ini;
+ }
+
+ public void init(IPresentationContext context) {
+ }
+
+ public boolean isOptional() {
+ return false;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationModules.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationModules.java
new file mode 100644
index 000000000..0a0e8c9c0
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationModules.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+/**
+ * Column presentation for the Modules view.
+ */
+public class TCFColumnPresentationModules implements IColumnPresentation {
+
+ public static final String PRESENTATION_ID = "Modules";
+
+ /**
+ * Presentation column IDs.
+ */
+ public static final String
+ COL_NAME = "Name",
+ COL_ADDRESS = "Address",
+ COL_SIZE = "Size",
+ COL_FLAGS = "Flags",
+ COL_OFFSET = "Offset",
+ COL_SECTION = "Section";
+
+ private static String[] cols_all = {
+ COL_NAME,
+ COL_ADDRESS,
+ COL_SIZE,
+ COL_FLAGS,
+ COL_OFFSET,
+ COL_SECTION
+ };
+
+ private static String[] headers = {
+ "File Name",
+ "Address",
+ "Size",
+ "Flags",
+ "Offset",
+ "Section"
+ };
+
+ private static String[] cols_ini = {
+ COL_NAME,
+ COL_ADDRESS,
+ COL_SIZE
+ };
+
+ public void dispose() {
+ }
+
+ public String[] getAvailableColumns() {
+ return cols_all;
+ }
+
+ public String getHeader(String id) {
+ for (int i = 0; i < cols_all.length; i++) {
+ if (id.equals(cols_all[i])) return headers[i];
+ }
+ return null;
+ }
+
+ public String getId() {
+ return PRESENTATION_ID;
+ }
+
+ public ImageDescriptor getImageDescriptor(String id) {
+ return null;
+ }
+
+ public String[] getInitialColumns() {
+ return cols_ini;
+ }
+
+ public void init(IPresentationContext context) {
+ }
+
+ public boolean isOptional() {
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationRegister.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationRegister.java
new file mode 100644
index 000000000..10bdc8947
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationRegister.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+public class TCFColumnPresentationRegister implements IColumnPresentation {
+
+ public static final String PRESENTATION_ID = "Registers";
+
+ /**
+ * Presentation column IDs.
+ */
+ public static final String
+ COL_NAME = "Name",
+ COL_HEX_VALUE = "HexValue",
+ COL_DEC_VALUE = "DecValue",
+ COL_DESCRIPTION = "Description",
+ COL_READBLE = "Readable",
+ COL_READ_ONCE = "ReadOnce",
+ COL_WRITEABLE = "Writeable",
+ COL_WRITE_ONCE = "WriteOnce",
+ COL_SIDE_EFFECTS = "SideEffects",
+ COL_VOLATILE = "Volatile",
+ COL_FLOAT = "Float",
+ COL_MNEMONIC = "Menimonic";
+
+ private static String[] cols_all = {
+ COL_NAME,
+ COL_HEX_VALUE,
+ COL_DEC_VALUE,
+ COL_DESCRIPTION,
+ COL_READBLE,
+ COL_READ_ONCE,
+ COL_WRITEABLE,
+ COL_WRITE_ONCE,
+ COL_SIDE_EFFECTS,
+ COL_VOLATILE,
+ COL_FLOAT,
+ COL_MNEMONIC
+ };
+
+ private static String[] headers = {
+ "Name",
+ "Hex",
+ "Decimal",
+ "Description",
+ "Readable",
+ "Read Once",
+ "Writable",
+ "Write Once",
+ "Side Effects",
+ "Volatile",
+ "Float",
+ "Mnemonic"
+ };
+
+ private static String[] cols_ini = {
+ COL_NAME,
+ COL_HEX_VALUE,
+ COL_DEC_VALUE,
+ COL_DESCRIPTION,
+ COL_MNEMONIC
+ };
+
+ public void dispose() {
+ }
+
+ public String[] getAvailableColumns() {
+ return cols_all;
+ }
+
+ public String getHeader(String id) {
+ for (int i = 0; i < cols_all.length; i++) {
+ if (id.equals(cols_all[i])) return headers[i];
+ }
+ return null;
+ }
+
+ public String getId() {
+ return PRESENTATION_ID;
+ }
+
+ public ImageDescriptor getImageDescriptor(String id) {
+ return null;
+ }
+
+ public String[] getInitialColumns() {
+ return cols_ini;
+ }
+
+ public void init(IPresentationContext context) {
+ }
+
+ public boolean isOptional() {
+ return false;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFConsole.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFConsole.java
new file mode 100644
index 000000000..4977940d2
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFConsole.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tcf.internal.debug.cmdline.TCFCommandLine;
+import org.eclipse.tcf.internal.debug.ui.Activator;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IConsoleConstants;
+import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.console.IConsoleView;
+import org.eclipse.ui.console.IOConsole;
+import org.eclipse.ui.console.IOConsoleInputStream;
+import org.eclipse.ui.console.IOConsoleOutputStream;
+
+class TCFConsole {
+ private final TCFModel model;
+ private final IOConsole console;
+ private final Display display;
+ private final String process_id;
+ private final LinkedList<Message> out_queue;
+ private final TCFCommandLine cmd_line;
+
+ private final byte[] prompt = { 't', 'c', 'f', '>' };
+ private final StringBuffer cmd_buf = new StringBuffer();
+
+ private static class Message {
+ int stream_id;
+ byte[] data;
+ }
+
+ private final Thread inp_thread = new Thread() {
+ public void run() {
+ try {
+ IOConsoleInputStream inp = console.getInputStream();
+ final byte[] buf = new byte[0x100];
+ for (;;) {
+ int len = inp.read(buf);
+ if (len < 0) break;
+ // TODO: Eclipse Console view has a bad habit of replacing CR with CR/LF
+ if (len == 2 && buf[0] == '\r' && buf[1] == '\n') len = 1;
+ final int n = len;
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ try {
+ if (cmd_line != null) {
+ String s = new String(buf, 0, n, "UTF-8");
+ int l = s.length();
+ for (int i = 0; i < l; i++) {
+ char ch = s.charAt(i);
+ if (ch == '\r') {
+ String res = cmd_line.command(cmd_buf.toString());
+ cmd_buf.setLength(0);
+ if (res != null) {
+ if (res.length() > 0 && res.charAt(res.length() - 1) != '\n') {
+ res += '\n';
+ }
+ write(0, res.getBytes("UTF-8"));
+ }
+ write(0, prompt);
+ }
+ else if (ch == '\b') {
+ int n = cmd_buf.length();
+ if (n > 0) n--;
+ cmd_buf.setLength(n);
+ }
+ else {
+ cmd_buf.append(ch);
+ }
+ }
+ }
+ else {
+ model.getLaunch().writeProcessInputStream(process_id, buf, 0, n);
+ }
+ }
+ catch (Exception x) {
+ model.onProcessStreamError(process_id, 0, x, 0);
+ }
+ }
+ });
+ }
+ }
+ catch (Throwable x) {
+ Activator.log("Cannot read console input", x);
+ }
+ }
+ };
+
+ private final Thread out_thread = new Thread() {
+ public void run() {
+ Map<Integer,IOConsoleOutputStream> out_streams =
+ new HashMap<Integer,IOConsoleOutputStream>();
+ try {
+ for (;;) {
+ Message m = null;
+ synchronized (out_queue) {
+ while (out_queue.size() == 0) out_queue.wait();
+ m = out_queue.removeFirst();
+ }
+ if (m.data == null) break;
+ IOConsoleOutputStream stream = out_streams.get(m.stream_id);
+ if (stream == null) {
+ final int id = m.stream_id;
+ final IOConsoleOutputStream s = stream = console.newOutputStream();
+ display.syncExec(new Runnable() {
+ public void run() {
+ try {
+ int color_id = SWT.COLOR_BLACK;
+ switch (id) {
+ case 1: color_id = SWT.COLOR_RED; break;
+ case 2: color_id = SWT.COLOR_BLUE; break;
+ case 3: color_id = SWT.COLOR_GREEN; break;
+ }
+ s.setColor(display.getSystemColor(color_id));
+ }
+ catch (Throwable x) {
+ Activator.log("Cannot open console view", x);
+ }
+ }
+ });
+ out_streams.put(m.stream_id, stream);
+ }
+ stream.write(m.data, 0, m.data.length);
+ }
+ }
+ catch (Throwable x) {
+ Activator.log("Cannot write console output", x);
+ }
+ for (IOConsoleOutputStream stream : out_streams.values()) {
+ try {
+ stream.close();
+ }
+ catch (IOException x) {
+ Activator.log("Cannot close console stream", x);
+ }
+ }
+ try {
+ console.getInputStream().close();
+ }
+ catch (IOException x) {
+ Activator.log("Cannot close console stream", x);
+ }
+ try {
+ display.syncExec(new Runnable() {
+ public void run() {
+ IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager();
+ manager.removeConsoles(new IOConsole[]{ console });
+ }
+ });
+ }
+ catch (SWTException x) {
+ if (x.code == SWT.ERROR_DEVICE_DISPOSED) return;
+ Activator.log("Cannot remove console", x);
+ }
+ }
+ };
+
+ /* process_id == null means debug console */
+ TCFConsole(final TCFModel model, String process_id) {
+ this.model = model;
+ this.process_id = process_id;
+ display = model.getDisplay();
+ out_queue = new LinkedList<Message>();
+ String image = process_id != null ? ImageCache.IMG_PROCESS_RUNNING : ImageCache.IMG_TCF;
+ console = new IOConsole(
+ "TCF " + (process_id != null ? process_id : "Debugger"), null,
+ ImageCache.getImageDescriptor(image), "UTF-8", true);
+ cmd_line = process_id != null ? null : new TCFCommandLine();
+ if (cmd_line != null) write(0, prompt);
+ display.asyncExec(new Runnable() {
+ public void run() {
+ if (!PlatformUI.isWorkbenchRunning() || PlatformUI.getWorkbench().isStarting()) {
+ display.timerExec(200, this);
+ }
+ else if (!PlatformUI.getWorkbench().isClosing()) {
+ try {
+ IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager();
+ manager.addConsoles(new IConsole[]{ console });
+ IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (w == null) return;
+ IWorkbenchPage page = w.getActivePage();
+ if (page == null) return;
+ IConsoleView view = (IConsoleView)page.showView(IConsoleConstants.ID_CONSOLE_VIEW);
+ view.display(console);
+ }
+ catch (Throwable x) {
+ Activator.log("Cannot open console view", x);
+ }
+ }
+ }
+ });
+ inp_thread.setName("TCF Console Input");
+ out_thread.setName("TCF Console Output");
+ inp_thread.start();
+ out_thread.start();
+ }
+
+ void write(final int stream_id, byte[] data) {
+ if (data == null || data.length == 0) return;
+ synchronized (out_queue) {
+ Message m = new Message();
+ m.stream_id = stream_id;
+ m.data = data;
+ out_queue.add(m);
+ out_queue.notify();
+ }
+ }
+
+ void close() {
+ synchronized (out_queue) {
+ out_queue.add(new Message());
+ out_queue.notify();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDebugTask.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDebugTask.java
new file mode 100644
index 000000000..5c888b809
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDebugTask.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.tcf.internal.debug.ui.Activator;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.util.TCFTask;
+
+/**
+ * An extension of TCFTask class that adds support for throwing DebugException.
+ */
+public abstract class TCFDebugTask<V> extends TCFTask<V> {
+
+ public TCFDebugTask() {
+ }
+
+ public TCFDebugTask(IChannel channel) {
+ super(channel);
+ }
+
+ public synchronized V getD() throws DebugException {
+ assert !Protocol.isDispatchThread();
+ while (!isDone()) {
+ try {
+ wait();
+ }
+ catch (InterruptedException x) {
+ throw new DebugException(new Status(
+ IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ "Debugger requiest interrupted", x));
+ }
+ }
+ assert isDone();
+ Throwable x = getError();
+ if (x instanceof DebugException) throw (DebugException)x;
+ if (x != null) throw new DebugException(new Status(
+ IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ "Debugger requiest failed", x));
+ return getResult();
+ }
+
+ public void error(String msg) {
+ error(new DebugException(new Status(
+ IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ msg, null)));
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPane.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPane.java
new file mode 100644
index 000000000..632147ece
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPane.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.debug.ui.IDebugView;
+import org.eclipse.debug.ui.IDetailPane;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextPresentationListener;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.text.source.SourceViewerConfiguration;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+import org.eclipse.ui.IWorkbenchPartSite;
+
+/**
+ * This detail pane uses a source viewer to display detailed information about the current
+ * selection.
+ */
+public class TCFDetailPane implements IDetailPane {
+
+ public static final String ID = "org.eclipse.tcf.debug.DetailPaneFactory";
+ public static final String NAME = "TCF Detail Pane";
+ public static final String DESC = "TCF Detail Pane";
+
+ private static final String DETAIL_COPY_ACTION = IDebugView.COPY_ACTION + ".DetailPane"; //$NON-NLS-1$
+ private static final String DETAIL_SELECT_ALL_ACTION = IDebugView.SELECT_ALL_ACTION + ".DetailPane"; //$NON-NLS-1$
+
+ private SourceViewer source_viewer;
+ private Display display;
+ private int generation;
+ private IWorkbenchPartSite part_site;
+ private final Document document = new Document();
+ private final ArrayList<StyleRange> style_ranges = new ArrayList<StyleRange>();
+ private final HashMap<RGB,Color> colors = new HashMap<RGB,Color>();
+ private final Map<String,IAction> action_map = new HashMap<String,IAction>();
+ private final List<String> selection_actions = new ArrayList<String>();
+
+ private final ITextPresentationListener presentation_listener = new ITextPresentationListener() {
+ public void applyTextPresentation(TextPresentation presentation) {
+ for (StyleRange r : style_ranges) presentation.addStyleRange(r);
+ }
+ };
+
+ private class DetailPaneAction extends Action {
+ final int op_code;
+ DetailPaneAction(String text, int op_code) {
+ super(text);
+ this.op_code = op_code;
+ }
+ void update() {
+ boolean was_enabled = isEnabled();
+ boolean is_enabled = (source_viewer != null && source_viewer.canDoOperation(op_code));
+ setEnabled(is_enabled);
+ if (was_enabled == is_enabled) return;
+ firePropertyChange(ENABLED, was_enabled, is_enabled);
+ }
+ @Override
+ public void run() {
+ if (!isEnabled()) return;
+ source_viewer.doOperation(op_code);
+ }
+ }
+
+ private void createActions() {
+ DetailPaneAction action = null;
+
+ action = new DetailPaneAction("Select &All", ITextOperationTarget.SELECT_ALL);
+ action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
+ action_map.put(DETAIL_SELECT_ALL_ACTION, action);
+
+ action = new DetailPaneAction("&Copy", ITextOperationTarget.COPY);
+ action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
+ action_map.put(DETAIL_COPY_ACTION, action);
+
+ selection_actions.add(DETAIL_COPY_ACTION);
+
+ updateSelectionDependentActions();
+ }
+
+ private IAction getAction(String id) {
+ return action_map.get(id);
+ }
+
+ private void setGlobalAction(String id, IAction action){
+ if (part_site instanceof IViewSite) {
+ ((IViewSite)part_site).getActionBars().setGlobalActionHandler(id, action);
+ }
+ }
+
+ private void createDetailContextMenu(Control menuControl) {
+ if (part_site == null) return;
+ MenuManager manager = new MenuManager();
+ manager.setRemoveAllWhenShown(true);
+ manager.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager mgr) {
+ fillDetailContextMenu(mgr);
+ }
+ });
+ Menu menu = manager.createContextMenu(menuControl);
+ menuControl.setMenu(menu);
+ part_site.registerContextMenu(ID, manager, source_viewer.getSelectionProvider());
+ }
+
+ private void fillDetailContextMenu(IMenuManager menu) {
+ //menu.add(new Separator(MODULES_GROUP));
+ //menu.add(new Separator());
+ menu.add(getAction(DETAIL_COPY_ACTION));
+ menu.add(getAction(DETAIL_SELECT_ALL_ACTION));
+ menu.add(new Separator());
+ menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+
+ private void updateAction(String id) {
+ IAction action = getAction(id);
+ if (action instanceof DetailPaneAction) ((DetailPaneAction)action).update();
+ }
+
+ private void updateSelectionDependentActions() {
+ for (String id : selection_actions) updateAction(id);
+ }
+
+ public Control createControl(Composite parent) {
+ assert source_viewer == null;
+ source_viewer = new SourceViewer(parent, null, SWT.V_SCROLL | SWT.H_SCROLL);
+ source_viewer.configure(new SourceViewerConfiguration());
+ source_viewer.setDocument(document);
+ source_viewer.setEditable(false);
+ source_viewer.addTextPresentationListener(presentation_listener);
+ Control control = source_viewer.getControl();
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ control.setLayoutData(gd);
+ display = control.getDisplay();
+ createActions();
+ // Add the selection listener so selection dependent actions get updated.
+ document.addDocumentListener(new IDocumentListener() {
+ public void documentAboutToBeChanged(DocumentEvent event) {}
+ public void documentChanged(DocumentEvent event) {
+ updateSelectionDependentActions();
+ }
+ });
+ // Add the selection listener so selection dependent actions get updated.
+ source_viewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ updateSelectionDependentActions();
+ }
+ });
+ // Add a focus listener to update actions when details area gains focus
+ source_viewer.getControl().addFocusListener(new FocusAdapter() {
+ public void focusGained(FocusEvent e) {
+ if (part_site != null) part_site.setSelectionProvider(source_viewer.getSelectionProvider());
+ setGlobalAction(IDebugView.SELECT_ALL_ACTION, getAction(DETAIL_SELECT_ALL_ACTION));
+ setGlobalAction(IDebugView.COPY_ACTION, getAction(DETAIL_COPY_ACTION));
+ if (part_site instanceof IViewSite) ((IViewSite)part_site).getActionBars().updateActionBars();
+ }
+ public void focusLost(FocusEvent e) {
+ if (part_site != null) part_site.setSelectionProvider(null);
+ setGlobalAction(IDebugView.SELECT_ALL_ACTION, null);
+ setGlobalAction(IDebugView.COPY_ACTION, null);
+ if (part_site instanceof IViewSite) ((IViewSite)part_site).getActionBars().updateActionBars();
+ }
+ });
+ // Add a context menu to the detail area
+ createDetailContextMenu(source_viewer.getTextWidget());
+ return control;
+ }
+
+ public void display(IStructuredSelection selection) {
+ if (source_viewer == null) return;
+ generation++;
+ final int g = generation;
+ final ArrayList<TCFNode> nodes = new ArrayList<TCFNode>();
+ if (selection != null) {
+ Iterator<?> iterator = selection.iterator();
+ while (iterator.hasNext()) {
+ Object next = iterator.next();
+ if (next instanceof TCFNode) nodes.add((TCFNode)next);
+ }
+ }
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ if (g != generation) return;
+ final StyledStringBuffer s = getDetailText(nodes, this);
+ if (s == null) return;
+ display.asyncExec(new Runnable() {
+ public void run() {
+ if (g != generation) return;
+ document.set(getStyleRanges(s));
+ }
+ });
+ }
+ });
+ }
+
+ private StyledStringBuffer getDetailText(ArrayList<TCFNode> nodes, Runnable done) {
+ StyledStringBuffer bf = new StyledStringBuffer();
+ for (TCFNode n : nodes) {
+ if (n instanceof IDetailsProvider) {
+ if (!((IDetailsProvider)n).getDetailText(bf, done)) return null;
+ }
+ }
+ return bf;
+ }
+
+ private String getStyleRanges(StyledStringBuffer s) {
+ style_ranges.clear();
+ for (StyledStringBuffer.Style x : s.getStyle()) {
+ style_ranges.add(new StyleRange(x.pos, x.len, getColor(x.fg), getColor(x.bg), x.font));
+ }
+ return s.toString();
+ }
+
+ private Color getColor(RGB rgb) {
+ if (rgb == null) return null;
+ Color c = colors.get(rgb);
+ if (c == null) colors.put(rgb, c = new Color(display, rgb));
+ return c;
+ }
+
+ public void dispose() {
+ for (Color c : colors.values()) c.dispose();
+ colors.clear();
+ if (source_viewer == null) return;
+ generation++;
+ if (source_viewer.getControl() != null) {
+ source_viewer.getControl().dispose();
+ }
+ source_viewer = null;
+ selection_actions.clear();
+ action_map.clear();
+ }
+
+ public String getDescription() {
+ return DESC;
+ }
+
+ public String getID() {
+ return ID;
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public void init(IWorkbenchPartSite part_site) {
+ this.part_site = part_site;
+ }
+
+ public boolean setFocus() {
+ if (source_viewer == null) return false;
+ source_viewer.getTextWidget().setFocus();
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPaneFactory.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPaneFactory.java
new file mode 100644
index 000000000..25d988da9
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPaneFactory.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.debug.ui.IDetailPane;
+import org.eclipse.debug.ui.IDetailPaneFactory;
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+/**
+ * The TCF detail pane factory is contributed to the <code>org.eclipse.debug.ui.detailPaneFactories</code>
+ * extension. For any selection that contains TCFNode the factory can produce a <code>IDetailPane</code> object.
+ */
+public class TCFDetailPaneFactory implements IDetailPaneFactory {
+
+ public IDetailPane createDetailPane(String paneID) {
+ assert paneID.equals(TCFDetailPane.ID);
+ return new TCFDetailPane();
+ }
+
+ public String getDefaultDetailPane(IStructuredSelection selection) {
+ return TCFDetailPane.ID;
+ }
+
+ public String getDetailPaneDescription(String paneID) {
+ return TCFDetailPane.NAME;
+ }
+
+ public String getDetailPaneName(String paneID) {
+ return TCFDetailPane.DESC;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Set getDetailPaneTypes(IStructuredSelection selection) {
+ HashSet<String> set = new HashSet<String>();
+ set.add(TCFDetailPane.ID);
+ return set;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFMemoryBlockRetrieval.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFMemoryBlockRetrieval.java
new file mode 100644
index 000000000..219d01301
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFMemoryBlockRetrieval.java
@@ -0,0 +1,508 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IMemoryBlockExtension;
+import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
+import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
+import org.eclipse.debug.core.model.MemoryByte;
+import org.eclipse.tcf.internal.debug.model.ITCFConstants;
+import org.eclipse.tcf.internal.debug.model.TCFLaunch;
+import org.eclipse.tcf.internal.debug.ui.Activator;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IExpressions;
+import org.eclipse.tcf.services.IMemory;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.services.IMemory.MemoryError;
+import org.eclipse.tcf.util.TCFDataCache;
+
+/**
+ * A memory block retrieval allows the user interface to request a memory block from a debugger when needed.
+ * TCF memory block retrieval is based on TCF Memory service.
+ */
+class TCFMemoryBlockRetrieval implements IMemoryBlockRetrievalExtension {
+
+ private final TCFNodeExecContext exec_ctx;
+ private final HashSet<MemoryBlock> mem_blocks = new HashSet<MemoryBlock>();
+
+ private static class MemData {
+ final BigInteger addr;
+ final MemoryByte[] data;
+ final byte[] bytes;
+
+ MemData(BigInteger addr, MemoryByte[] data) {
+ int i = 0;
+ this.addr = addr;
+ this.data = data;
+ this.bytes = new byte[data.length];
+ for (MemoryByte b : data) bytes[i++] = b.getValue();
+ }
+ }
+
+ private class MemoryBlock extends PlatformObject implements IMemoryBlockExtension {
+
+ private final String expression;
+ private final long length;
+ private final Set<Object> connections = new HashSet<Object>();
+ private final TCFDataCache<IExpressions.Expression> remote_expression;
+ private final TCFDataCache<IExpressions.Value> expression_value;
+ private final TCFDataCache<ISymbols.Symbol> expression_type;
+
+ private boolean disposed;
+
+ private MemData mem_data; // current memory block data
+ private MemData mem_prev; // previous data - before last suspend
+ private MemData mem_last; // last retrieved memory block data
+
+ MemoryBlock(final String expression, long length) {
+ this.expression = expression;
+ this.length = length;
+ mem_blocks.add(this);
+ final TCFLaunch launch = exec_ctx.model.getLaunch();
+ final IChannel channel = launch.getChannel();
+ remote_expression = new TCFDataCache<IExpressions.Expression>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ IExpressions exps = launch.getService(IExpressions.class);
+ if (exps == null) {
+ set(null, new Exception("Expressions service not available"), null);
+ return true;
+ }
+ command = exps.create(exec_ctx.id, null, expression, new IExpressions.DoneCreate() {
+ public void doneCreate(IToken token, Exception error, IExpressions.Expression context) {
+ if (disposed) {
+ IExpressions exps = channel.getRemoteService(IExpressions.class);
+ exps.dispose(context.getID(), new IExpressions.DoneDispose() {
+ public void doneDispose(IToken token, Exception error) {
+ if (error == null) return;
+ if (channel.getState() != IChannel.STATE_OPEN) return;
+ Activator.log("Error disposing remote expression evaluator", error);
+ }
+ });
+ return;
+ }
+ set(token, error, context);
+ }
+ });
+ return false;
+ }
+ };
+ expression_value = new TCFDataCache<IExpressions.Value>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!remote_expression.validate(this)) return false;
+ final IExpressions.Expression ctx = remote_expression.getData();
+ if (ctx == null) {
+ set(null, null, null);
+ return true;
+ }
+ IExpressions exps = launch.getService(IExpressions.class);
+ command = exps.evaluate(ctx.getID(), new IExpressions.DoneEvaluate() {
+ public void doneEvaluate(IToken token, Exception error, IExpressions.Value value) {
+ set(token, error, value);
+ }
+ });
+ return false;
+ }
+ };
+ expression_type = new TCFDataCache<ISymbols.Symbol>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!expression_value.validate(this)) return false;
+ IExpressions.Value val = expression_value.getData();
+ if (val == null) {
+ set(null, expression_value.getError(), null);
+ return true;
+ }
+ TCFDataCache<ISymbols.Symbol> type_cache = exec_ctx.model.getSymbolInfoCache(val.getTypeID());
+ if (type_cache == null) {
+ set(null, null, null);
+ return true;
+ }
+ if (!type_cache.validate(this)) return false;
+ set(null, type_cache.getError(), type_cache.getData());
+ return true;
+ }
+ };
+ }
+
+ public synchronized void connect(Object client) {
+ connections.add(client);
+ }
+
+ public synchronized void disconnect(Object client) {
+ connections.remove(client);
+ }
+
+ public synchronized Object[] getConnections() {
+ return connections.toArray(new Object[connections.size()]);
+ }
+
+ public void dispose() throws DebugException {
+ new TCFDebugTask<Boolean>(exec_ctx.getChannel()) {
+ public void run() {
+ disposed = true;
+ expression_value.dispose();
+ expression_type.dispose();
+ if (remote_expression.isValid() && remote_expression.getData() != null) {
+ final IChannel channel = exec_ctx.channel;
+ if (channel.getState() == IChannel.STATE_OPEN) {
+ IExpressions exps = channel.getRemoteService(IExpressions.class);
+ exps.dispose(remote_expression.getData().getID(), new IExpressions.DoneDispose() {
+ public void doneDispose(IToken token, Exception error) {
+ if (error == null) return;
+ if (channel.getState() != IChannel.STATE_OPEN) return;
+ Activator.log("Error disposing remote expression evaluator", error);
+ }
+ });
+ }
+ }
+ remote_expression.dispose();
+ mem_blocks.remove(MemoryBlock.this);
+ done(Boolean.TRUE);
+ }
+ }.getD();
+ }
+
+ public int getAddressSize() throws DebugException {
+ return new TCFDebugTask<Integer>(exec_ctx.getChannel()) {
+ public void run() {
+ if (exec_ctx.isDisposed()) {
+ error("Context is disposed");
+ }
+ else {
+ TCFDataCache<IMemory.MemoryContext> cache = exec_ctx.getMemoryContext();
+ if (!cache.validate(this)) return;
+ if (cache.getError() != null) {
+ error(cache.getError());
+ }
+ else {
+ IMemory.MemoryContext mem = cache.getData();
+ if (mem == null) {
+ error("Context does not provide memory access");
+ }
+ else {
+ done(mem.getAddressSize());
+ }
+ }
+ }
+ }
+ }.getD();
+ }
+
+ public int getAddressableSize() throws DebugException {
+ // TODO: support for addressable size other then 1 byte
+ return 1;
+ }
+
+ public BigInteger getBigBaseAddress() throws DebugException {
+ return new TCFDebugTask<BigInteger>(exec_ctx.getChannel()) {
+ public void run() {
+ if (!expression_value.validate()) {
+ expression_value.wait(this);
+ }
+ else if (expression_value.getError() != null) {
+ error(expression_value.getError());
+ }
+ else if (expression_value.getData() == null) {
+ error("Address expression evaluation failed");
+ }
+ else if (!expression_type.validate()) {
+ expression_type.wait(this);
+ }
+ else if (expression_type.getError() != null) {
+ error(expression_type.getError());
+ }
+ else {
+ IExpressions.Value value = expression_value.getData();
+ byte[] data = value.getValue();
+ if (data == null || data.length == 0) {
+ error("Address expression value is empty (void)");
+ }
+ else {
+ ISymbols.Symbol type = expression_type.getData();
+ boolean signed = type != null && type.getTypeClass() == ISymbols.TypeClass.integer;
+ done(TCFNumberFormat.toBigInteger(data, 0, data.length, value.isBigEndian(), signed));
+ }
+ }
+ }
+ }.getD();
+ }
+
+ public MemoryByte[] getBytesFromAddress(final BigInteger address, final long units) throws DebugException {
+ return new TCFDebugTask<MemoryByte[]>(exec_ctx.getChannel()) {
+ int offs = 0;
+ public void run() {
+ if (mem_data != null &&
+ address.compareTo(mem_data.addr) >= 0 &&
+ address.add(BigInteger.valueOf(units)).compareTo(
+ mem_data.addr.add(BigInteger.valueOf(mem_data.data.length))) <= 0) {
+ offs = address.subtract(mem_data.addr).intValue();
+ MemoryByte[] res = mem_data.data;
+ if (units < mem_data.data.length) {
+ res = new MemoryByte[(int)units];
+ System.arraycopy(mem_data, offs, res, 0, res.length);
+ }
+ setHistoryFlags();
+ done(res);
+ return;
+ }
+ if (exec_ctx.isDisposed()) {
+ error("Context is disposed");
+ return;
+ }
+ TCFDataCache<IMemory.MemoryContext> cache = exec_ctx.getMemoryContext();
+ if (!cache.validate(this)) return;
+ if (cache.getError() != null) {
+ error(cache.getError());
+ return;
+ }
+ final IMemory.MemoryContext mem = cache.getData();
+ if (mem == null) {
+ error("Context does not provide memory access");
+ return;
+ }
+ final int size = (int)units;
+ final int mode = IMemory.MODE_CONTINUEONERROR | IMemory.MODE_VERIFY;
+ final byte[] buf = new byte[size];
+ final MemoryByte[] res = new MemoryByte[size];
+ mem.get(address, 1, buf, 0, size, mode, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ int big_endian = 0;
+ if (mem.getProperties().get(IMemory.PROP_BIG_ENDIAN) != null) {
+ big_endian |= MemoryByte.ENDIANESS_KNOWN;
+ if (mem.isBigEndian()) big_endian |= MemoryByte.BIG_ENDIAN;
+ }
+ int cnt = 0;
+ while (offs < size) {
+ int flags = big_endian;
+ if (error instanceof IMemory.ErrorOffset) {
+ IMemory.ErrorOffset ofs = (IMemory.ErrorOffset)error;
+ int status = ofs.getStatus(cnt);
+ if (status == IMemory.ErrorOffset.BYTE_VALID) {
+ flags |= MemoryByte.READABLE | MemoryByte.WRITABLE;
+ }
+ else if ((status & IMemory.ErrorOffset.BYTE_UNKNOWN) != 0) {
+ if (cnt > 0) break;
+ }
+ }
+ else if (error == null) {
+ flags |= MemoryByte.READABLE | MemoryByte.WRITABLE;
+ }
+ res[offs] = new MemoryByte(buf[offs], (byte)flags);
+ offs++;
+ cnt++;
+ }
+ if (offs < size) {
+ mem.get(address.add(BigInteger.valueOf(offs)), 1, buf, offs, size - offs, mode, this);
+ }
+ else {
+ mem_last = mem_data = new MemData(address, res);
+ setHistoryFlags();
+ done(res);
+ }
+ }
+ });
+ }
+ }.getD();
+ }
+
+ private void setHistoryFlags() {
+ if (mem_data == null) return;
+ BigInteger addr = mem_data.addr;
+ BigInteger his_start = null;
+ BigInteger his_end = null;
+ if (mem_prev != null) {
+ his_start = mem_prev.addr;
+ his_end = mem_prev.addr.add(BigInteger.valueOf(mem_prev.data.length));
+ }
+ for (MemoryByte b : mem_data.data) {
+ int flags = b.getFlags();
+ if (mem_prev != null && addr.compareTo(his_start) >= 0 && addr.compareTo(his_end) < 0) {
+ flags |= MemoryByte.HISTORY_KNOWN;
+ int offs = addr.subtract(his_start).intValue();
+ if (b.getValue() != mem_prev.data[offs].getValue()) {
+ flags |= MemoryByte.CHANGED;
+ }
+ }
+ else {
+ flags &= ~(MemoryByte.HISTORY_KNOWN | MemoryByte.CHANGED);
+ }
+ b.setFlags((byte)flags);
+ addr = addr.add(BigInteger.valueOf(1));
+ }
+ }
+
+ public MemoryByte[] getBytesFromOffset(BigInteger offset, long units) throws DebugException {
+ return getBytesFromAddress(getBigBaseAddress().add(offset), units);
+ }
+
+ public String getExpression() {
+ return expression;
+ }
+
+ public IMemoryBlockRetrieval getMemoryBlockRetrieval() {
+ return TCFMemoryBlockRetrieval.this;
+ }
+
+ public long getStartAddress() {
+ return 0; // Unbounded
+ }
+
+ public long getLength() {
+ return length;
+ }
+
+ public BigInteger getMemoryBlockStartAddress() throws DebugException {
+ return null; // Unbounded
+ }
+
+ public BigInteger getMemoryBlockEndAddress() throws DebugException {
+ return null; // Unbounded
+ }
+
+ public BigInteger getBigLength() throws DebugException {
+ return BigInteger.valueOf(length);
+ }
+
+ public void setBaseAddress(BigInteger address) throws DebugException {
+ }
+
+ public void setValue(BigInteger offset, final byte[] bytes) throws DebugException {
+ final BigInteger address = getBigBaseAddress().add(offset);
+ new TCFDebugTask<Object>(exec_ctx.getChannel()) {
+ public void run() {
+ if (exec_ctx.isDisposed()) {
+ error("Context is disposed");
+ return;
+ }
+ TCFDataCache<IMemory.MemoryContext> cache = exec_ctx.getMemoryContext();
+ if (!cache.validate(this)) return;
+ if (cache.getError() != null) {
+ error(cache.getError());
+ return;
+ }
+ final IMemory.MemoryContext mem = cache.getData();
+ if (mem == null) {
+ error("Context does not provide memory access");
+ return;
+ }
+ final int mode = IMemory.MODE_CONTINUEONERROR | IMemory.MODE_VERIFY;
+ mem.set(address, 1, bytes, 0, bytes.length, mode, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ if (error != null) {
+ error(error);
+ }
+ else {
+ done(null);
+ }
+ }
+ });
+ }
+ }.getD();
+ }
+
+ public boolean supportBaseAddressModification() throws DebugException {
+ return false;
+ }
+
+ public boolean supportsChangeManagement() {
+ return true;
+ }
+
+ public byte[] getBytes() throws DebugException {
+ if (mem_data == null) return null;
+ return mem_data.bytes;
+ }
+
+ public void setValue(long offset, byte[] bytes) throws DebugException {
+ setValue(BigInteger.valueOf(offset), bytes);
+ }
+
+ public boolean supportsValueModification() {
+ return true;
+ }
+
+ public IDebugTarget getDebugTarget() {
+ return null;
+ }
+
+ public ILaunch getLaunch() {
+ return exec_ctx.model.getLaunch();
+ }
+
+ public String getModelIdentifier() {
+ return ITCFConstants.ID_TCF_DEBUG_MODEL;
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Object getAdapter(Class adapter) {
+ if (adapter == IMemoryBlockRetrieval.class) return TCFMemoryBlockRetrieval.this;
+ if (adapter == IMemoryBlockRetrievalExtension.class) return TCFMemoryBlockRetrieval.this;
+ return super.getAdapter(adapter);
+ }
+ }
+
+ TCFMemoryBlockRetrieval(TCFNodeExecContext exec_ctx) {
+ this.exec_ctx = exec_ctx;
+ }
+
+ public IMemoryBlockExtension getExtendedMemoryBlock(final String expression, Object context) throws DebugException {
+ return new TCFDebugTask<IMemoryBlockExtension>() {
+ public void run() {
+ done(new MemoryBlock(expression, -1));
+ }
+ }.getD();
+ }
+
+ public IMemoryBlock getMemoryBlock(final long address, final long length) throws DebugException {
+ return new TCFDebugTask<IMemoryBlockExtension>() {
+ public void run() {
+ done(new MemoryBlock("0x" + Long.toHexString(address), length));
+ }
+ }.getD();
+ }
+
+ public boolean supportsStorageRetrieval() {
+ return true;
+ }
+
+ public String getMemoryID() {
+ return exec_ctx.id;
+ }
+
+ void onMemoryChanged(boolean suspended) {
+ assert Protocol.isDispatchThread();
+ if (mem_blocks.size() == 0) return;
+ ArrayList<DebugEvent> list = new ArrayList<DebugEvent>();
+ for (MemoryBlock b : mem_blocks) {
+ if (suspended) b.mem_prev = b.mem_last;
+ b.mem_data = null;
+ list.add(new DebugEvent(b, DebugEvent.CHANGE, DebugEvent.CONTENT));
+ }
+ DebugPlugin.getDefault().fireDebugEventSet(list.toArray(new DebugEvent[list.size()]));
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModel.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModel.java
new file mode 100644
index 000000000..83faae53e
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModel.java
@@ -0,0 +1,1801 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IExpressionManager;
+import org.eclipse.debug.core.IExpressionsListener;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.commands.IDisconnectHandler;
+import org.eclipse.debug.core.commands.IDropToFrameHandler;
+import org.eclipse.debug.core.commands.IResumeHandler;
+import org.eclipse.debug.core.commands.IStepIntoHandler;
+import org.eclipse.debug.core.commands.IStepOverHandler;
+import org.eclipse.debug.core.commands.IStepReturnHandler;
+import org.eclipse.debug.core.commands.ISuspendHandler;
+import org.eclipse.debug.core.commands.ITerminateHandler;
+import org.eclipse.debug.core.model.IDebugModelProvider;
+import org.eclipse.debug.core.model.IExpression;
+import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
+import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
+import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.debug.ui.IDebugView;
+import org.eclipse.debug.ui.ISourcePresentation;
+import org.eclipse.debug.ui.contexts.ISuspendTrigger;
+import org.eclipse.debug.ui.contexts.ISuspendTriggerListener;
+import org.eclipse.debug.ui.sourcelookup.CommonSourceNotFoundEditorInput;
+import org.eclipse.debug.ui.sourcelookup.ISourceDisplay;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tcf.internal.debug.actions.TCFAction;
+import org.eclipse.tcf.internal.debug.launch.TCFSourceLookupDirector;
+import org.eclipse.tcf.internal.debug.launch.TCFSourceLookupParticipant;
+import org.eclipse.tcf.internal.debug.model.ITCFConstants;
+import org.eclipse.tcf.internal.debug.model.TCFContextState;
+import org.eclipse.tcf.internal.debug.model.TCFLaunch;
+import org.eclipse.tcf.internal.debug.model.TCFSourceRef;
+import org.eclipse.tcf.internal.debug.ui.Activator;
+import org.eclipse.tcf.internal.debug.ui.adapters.TCFNodePropertySource;
+import org.eclipse.tcf.internal.debug.ui.commands.BackIntoCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.BackOverCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.BackResumeCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.BackReturnCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.DisconnectCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.DropToFrameCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.ResumeCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.StepIntoCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.StepOverCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.StepReturnCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.SuspendCommand;
+import org.eclipse.tcf.internal.debug.ui.commands.TerminateCommand;
+import org.eclipse.tcf.internal.debug.ui.preferences.TCFPreferences;
+import org.eclipse.tcf.core.Command;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IErrorReport;
+import org.eclipse.tcf.protocol.IService;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IDisassembly;
+import org.eclipse.tcf.services.ILineNumbers;
+import org.eclipse.tcf.services.IMemory;
+import org.eclipse.tcf.services.IMemoryMap;
+import org.eclipse.tcf.services.IProcesses;
+import org.eclipse.tcf.services.IRegisters;
+import org.eclipse.tcf.services.IRunControl;
+import org.eclipse.tcf.services.IStackTrace;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.util.TCFDataCache;
+import org.eclipse.tcf.util.TCFTask;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IPersistableElement;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * TCFModel represents remote target state as it is known to host.
+ * The main job of the model is caching remote data,
+ * keeping the cache in a coherent state,
+ * and feeding UI with up-to-date data.
+ */
+@SuppressWarnings("restriction")
+public class TCFModel implements IElementContentProvider, IElementLabelProvider, IViewerInputProvider,
+ IModelProxyFactory, IColumnPresentationFactory, ISourceDisplay, ISuspendTrigger {
+
+ /** The id of the expression hover presentation context */
+ public static final String ID_EXPRESSION_HOVER = Activator.PLUGIN_ID + ".expression_hover";
+
+ /** The id of a pinned view description presentation context */
+ public static final String ID_PINNED_VIEW = Activator.PLUGIN_ID + ".pinned_view";
+
+ public static final int
+ UPDATE_POLICY_AUTOMATIC = 0,
+ UPDATE_POLICY_MANUAL = 1,
+ UPDATE_POLICY_BREAKPOINT = 2;
+
+ /**
+ * A dummy editor input to open the disassembly view as editor.
+ */
+ public static class DisassemblyEditorInput implements IEditorInput {
+ final static String EDITOR_ID = "org.eclipse.cdt.dsf.ui.disassembly";
+ final static DisassemblyEditorInput INSTANCE = new DisassemblyEditorInput();
+
+ @SuppressWarnings("rawtypes")
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ public boolean exists() {
+ return false;
+ }
+
+ public ImageDescriptor getImageDescriptor() {
+ return null;
+ }
+
+ public String getName() {
+ return "Disassembly";
+ }
+
+ public IPersistableElement getPersistable() {
+ return null;
+ }
+
+ public String getToolTipText() {
+ return "Disassembly";
+ }
+ }
+
+ private final TCFLaunch launch;
+ private final Display display;
+ private final IExpressionManager expr_manager;
+ private final TCFAnnotationManager annotation_manager;
+
+ private final List<ISuspendTriggerListener> suspend_trigger_listeners =
+ new LinkedList<ISuspendTriggerListener>();
+
+ private int display_source_generation;
+ private int suspend_trigger_generation;
+ private int auto_disconnect_generation;
+
+ // Debugger preferences:
+ private long min_view_updates_interval;
+ private boolean view_updates_throttle_enabled;
+ private boolean channel_throttle_enabled;
+ private boolean wait_for_pc_update_after_step;
+ private boolean wait_for_views_update_after_step;
+ private boolean delay_stack_update_until_last_step;
+ private boolean stack_frames_limit_enabled;
+ private int stack_frames_limit_value;
+ private boolean show_function_arg_names;
+ private boolean show_function_arg_values;
+ private boolean delay_children_list_updates;
+ private boolean auto_children_list_updates;
+
+ private final Map<String,String> action_results = new HashMap<String,String>();
+ private final HashMap<String,TCFAction> active_actions = new HashMap<String,TCFAction>();
+
+ private final Map<IPresentationContext,TCFModelProxy> model_proxies =
+ new HashMap<IPresentationContext,TCFModelProxy>();
+
+ private final Map<String,TCFNode> id2node = new HashMap<String,TCFNode>();
+
+ private final Map<Class<?>,Object> adapters = new HashMap<Class<?>,Object>();
+
+ private class MemoryBlocksUpdate extends TCFDataCache<Map<String,TCFMemoryBlockRetrieval>> {
+
+ final Set<String> changeset = new HashSet<String>();
+ final Set<String> suspended = new HashSet<String>();
+
+ MemoryBlocksUpdate(IChannel channel) {
+ super(channel);
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ if (!validate(this)) return;
+ Map<String,TCFMemoryBlockRetrieval> map = getData();
+ if (map != null) { // map can be null if, for example, the channel was closed
+ for (TCFMemoryBlockRetrieval r : map.values()) {
+ r.onMemoryChanged(suspended.contains(r.getMemoryID()));
+ }
+ }
+ launch.removePendingClient(mem_blocks_update);
+ mem_blocks_update = null;
+ }
+ });
+ }
+
+ void add(String id, boolean suspended) {
+ changeset.add(id);
+ if (suspended) this.suspended.add(id);
+ }
+
+ public boolean startDataRetrieval() {
+ // Map changed contexts to memory nodes, and then to memory block retrieval objects
+ Map<String,TCFMemoryBlockRetrieval> map = new HashMap<String,TCFMemoryBlockRetrieval>();
+ for (String id : changeset) {
+ if (map.get(id) != null) continue;
+ TCFNode node = id2node.get(id);
+ if (node == null) {
+ if (!createNode(id, this)) return false;
+ if (isValid()) {
+ Activator.log("Cannot create debug model node", getError());
+ reset();
+ continue;
+ }
+ node = id2node.get(id);
+ }
+ if (node instanceof TCFNodeExecContext) {
+ TCFDataCache<TCFNodeExecContext> c = ((TCFNodeExecContext)node).getMemoryNode();
+ if (!c.validate(this)) return false;
+ node = c.getData();
+ if (node == null) continue;
+ TCFMemoryBlockRetrieval r = mem_retrieval.get(node.id);
+ if (r != null) {
+ map.put(node.id, r);
+ if (suspended.contains(id)) suspended.add(node.id);
+ }
+ }
+ }
+ set(null, null, map);
+ return true;
+ }
+ }
+
+ private final Map<String,TCFMemoryBlockRetrieval> mem_retrieval = new HashMap<String,TCFMemoryBlockRetrieval>();
+ private MemoryBlocksUpdate mem_blocks_update;
+
+ private final Map<String,String> cast_to_type_map = new HashMap<String,String>();
+
+ private final Map<String,Object> context_map = new HashMap<String,Object>();
+
+ private final Set<String> expanded_nodes = new HashSet<String>();
+
+ private final Map<IWorkbenchPart,TCFNode> pins = new HashMap<IWorkbenchPart,TCFNode>();
+ private final Map<IWorkbenchPart,TCFSnapshot> locks = new HashMap<IWorkbenchPart,TCFSnapshot>();
+ private final Map<IWorkbenchPart,Integer> lock_policy = new HashMap<IWorkbenchPart,Integer>();
+
+ private final Map<String,TCFConsole> process_consoles = new HashMap<String,TCFConsole>();;
+ private final List<TCFConsole> debug_consoles = new ArrayList<TCFConsole>();
+
+ private static final Map<ILaunchConfiguration,IEditorInput> editor_not_found =
+ new HashMap<ILaunchConfiguration,IEditorInput>();
+
+ private final IModelSelectionPolicyFactory model_selection_factory = new IModelSelectionPolicyFactory() {
+
+ public IModelSelectionPolicy createModelSelectionPolicyAdapter(
+ Object element, IPresentationContext context) {
+ return selection_policy;
+ }
+ };
+
+ private final IModelSelectionPolicy selection_policy;
+
+ private IChannel channel;
+ private TCFNodeLaunch launch_node;
+ private boolean disposed;
+
+ private final IMemory.MemoryListener mem_listener = new IMemory.MemoryListener() {
+
+ public void contextAdded(IMemory.MemoryContext[] contexts) {
+ for (IMemory.MemoryContext ctx : contexts) {
+ String id = ctx.getParentID();
+ if (id == null) {
+ launch_node.onContextAdded(ctx);
+ }
+ else {
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextAdded(ctx);
+ }
+ }
+ }
+ launch_node.onAnyContextAddedOrRemoved();
+ }
+
+ public void contextChanged(IMemory.MemoryContext[] contexts) {
+ for (IMemory.MemoryContext ctx : contexts) {
+ String id = ctx.getID();
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextChanged(ctx);
+ }
+ onMemoryChanged(id, true, false);
+ }
+ }
+
+ public void contextRemoved(final String[] context_ids) {
+ onContextRemoved(context_ids);
+ }
+
+ public void memoryChanged(String id, Number[] addr, long[] size) {
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onMemoryChanged(addr, size);
+ }
+ onMemoryChanged(id, true, false);
+ }
+ };
+
+ private final IRunControl.RunControlListener run_listener = new IRunControl.RunControlListener() {
+
+ public void containerResumed(String[] context_ids) {
+ for (String id : context_ids) {
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContainerResumed();
+ }
+ }
+ annotation_manager.updateAnnotations(null, launch);
+ }
+
+ public void containerSuspended(String context, String pc, String reason,
+ Map<String,Object> params, String[] suspended_ids) {
+ int action_cnt = 0;
+ for (String id : suspended_ids) {
+ TCFNode node = getNode(id);
+ action_results.remove(id);
+ if (active_actions.get(id) != null) action_cnt++;
+ if (!id.equals(context) && node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContainerSuspended();
+ }
+ onMemoryChanged(id, false, true);
+ }
+ TCFNode node = getNode(context);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params);
+ }
+ launch_node.onAnyContextSuspendedOrChanged();
+ if (action_cnt == 0) {
+ setDebugViewSelection(node, reason);
+ annotation_manager.updateAnnotations(null, launch);
+ TCFNodePropertySource.refresh(node);
+ }
+ action_results.remove(context);
+ }
+
+ public void contextAdded(IRunControl.RunControlContext[] contexts) {
+ for (IRunControl.RunControlContext ctx : contexts) {
+ String id = ctx.getParentID();
+ if (id == null) {
+ launch_node.onContextAdded(ctx);
+ }
+ else {
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextAdded(ctx);
+ }
+ }
+ context_map.put(ctx.getID(), ctx);
+ }
+ launch_node.onAnyContextAddedOrRemoved();
+ }
+
+ public void contextChanged(IRunControl.RunControlContext[] contexts) {
+ for (IRunControl.RunControlContext ctx : contexts) {
+ String id = ctx.getID();
+ context_map.put(id, ctx);
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextChanged(ctx);
+ }
+ onMemoryChanged(id, true, false);
+ if (active_actions.get(id) == null) {
+ TCFNodePropertySource.refresh(node);
+ }
+ }
+ launch_node.onAnyContextSuspendedOrChanged();
+ }
+
+ public void contextException(String context, String msg) {
+ TCFNode node = getNode(context);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextException(msg);
+ }
+ }
+
+ public void contextRemoved(final String[] context_ids) {
+ onContextRemoved(context_ids);
+ }
+
+ public void contextResumed(String id) {
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextResumed();
+ }
+ annotation_manager.updateAnnotations(null, launch);
+ }
+
+ public void contextSuspended(String id, String pc, String reason, Map<String,Object> params) {
+ TCFNode node = getNode(id);
+ action_results.remove(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params);
+ }
+ launch_node.onAnyContextSuspendedOrChanged();
+ if (active_actions.get(id) == null) {
+ setDebugViewSelection(node, reason);
+ annotation_manager.updateAnnotations(null, launch);
+ TCFNodePropertySource.refresh(node);
+ }
+ onMemoryChanged(id, false, true);
+ }
+ };
+
+ private final IMemoryMap.MemoryMapListener mmap_listenr = new IMemoryMap.MemoryMapListener() {
+
+ public void changed(String id) {
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ TCFNodeExecContext exe = (TCFNodeExecContext)node;
+ exe.onMemoryMapChanged();
+ }
+ onMemoryChanged(id, true, false);
+ display.asyncExec(new Runnable() {
+ public void run() {
+ if (PlatformUI.isWorkbenchRunning()) {
+ for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
+ IWorkbenchPage page = window.getActivePage();
+ if (page != null) displaySource(null, page, true);
+ }
+ }
+ }
+ });
+ }
+ };
+
+ private final IRegisters.RegistersListener reg_listener = new IRegisters.RegistersListener() {
+
+ public void contextChanged() {
+ for (TCFNode node : id2node.values()) {
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onRegistersChanged();
+ }
+ }
+ }
+
+ public void registerChanged(String context) {
+ TCFNode node = getNode(context);
+ if (node instanceof TCFNodeRegister) {
+ ((TCFNodeRegister)node).onValueChanged();
+ }
+ }
+ };
+
+ private final IProcesses.ProcessesListener prs_listener = new IProcesses.ProcessesListener() {
+
+ public void exited(String process_id, int exit_code) {
+ IProcesses.ProcessContext prs = launch.getProcessContext();
+ if (prs != null && process_id.equals(prs.getID())) onContextOrProcessRemoved();
+ }
+ };
+
+ private final IExpressionsListener expressions_listener = new IExpressionsListener() {
+
+ int generation;
+
+ public void expressionsAdded(IExpression[] expressions) {
+ expressionsRemoved(expressions);
+ }
+
+ public void expressionsChanged(IExpression[] expressions) {
+ expressionsRemoved(expressions);
+ }
+
+ public void expressionsRemoved(IExpression[] expressions) {
+ final int g = ++generation;
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ if (g != generation) return;
+ for (TCFNode n : id2node.values()) {
+ if (n instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)n).onExpressionAddedOrRemoved();
+ }
+ }
+ for (TCFModelProxy p : model_proxies.values()) {
+ String id = p.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id)) {
+ Object o = p.getInput();
+ if (o instanceof TCFNode) {
+ TCFNode n = (TCFNode)o;
+ if (n.model == TCFModel.this) p.addDelta(n, IModelDelta.CONTENT);
+ }
+ }
+ }
+ }
+ });
+ }
+ };
+
+ private final TCFLaunch.ActionsListener actions_listener = new TCFLaunch.ActionsListener() {
+
+ public void onContextActionStart(TCFAction action) {
+ final String id = action.getContextID();
+ active_actions.put(id, action);
+ annotation_manager.updateAnnotations(null, launch);
+ }
+
+ public void onContextActionResult(String id, String reason) {
+ if (reason == null) action_results.remove(id);
+ else action_results.put(id, reason);
+ }
+
+ public void onContextActionDone(TCFAction action) {
+ String id = action.getContextID();
+ active_actions.remove(id);
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextActionDone();
+ }
+ setDebugViewSelection(id2node.get(id), "Action");
+ for (TCFModelProxy p : model_proxies.values()) p.post();
+ annotation_manager.updateAnnotations(null, launch);
+ TCFNodePropertySource.refresh(node);
+ }
+ };
+
+ private final IDebugModelProvider debug_model_provider = new IDebugModelProvider() {
+ public String[] getModelIdentifiers() {
+ return new String[] { ITCFConstants.ID_TCF_DEBUG_MODEL };
+ }
+ };
+
+ private class InitialSelection implements Runnable {
+ boolean done;
+ public void run() {
+ if (done) return;
+ ArrayList<TCFNodeExecContext> nodes = new ArrayList<TCFNodeExecContext>();
+ if (!searchSuspendedThreads(launch_node.getFilteredChildren(), nodes)) return;
+ if (nodes.size() == 0) {
+ setDebugViewSelection(launch_node, "Launch");
+ }
+ else if (nodes.size() == 1) {
+ TCFNodeExecContext n = nodes.get(0);
+ setDebugViewSelection(n, "Launch");
+ }
+ else {
+ for (TCFNodeExecContext n : nodes) {
+ String reason = n.getState().getData().suspend_reason;
+ setDebugViewSelection(n, reason);
+ }
+ }
+ done = true;
+ }
+ private boolean searchSuspendedThreads(TCFChildren c, ArrayList<TCFNodeExecContext> nodes) {
+ if (!c.validate(this)) return false;
+ for (TCFNode n : c.toArray()) {
+ if (!searchSuspendedThreads((TCFNodeExecContext)n, nodes)) return false;
+ }
+ return true;
+ }
+ private boolean searchSuspendedThreads(TCFNodeExecContext n, ArrayList<TCFNodeExecContext> nodes) {
+ TCFDataCache<IRunControl.RunControlContext> run_context = n.getRunContext();
+ if (!run_context.validate(this)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) {
+ TCFDataCache<TCFContextState> state = n.getState();
+ if (!state.validate(this)) return false;
+ TCFContextState s = state.getData();
+ if (s != null && s.is_suspended) nodes.add(n);
+ return true;
+ }
+ return searchSuspendedThreads(n.getChildren(), nodes);
+ }
+ }
+
+ private volatile boolean instruction_stepping_enabled;
+
+ TCFModel(final TCFLaunch launch) {
+ this.launch = launch;
+ display = PlatformUI.getWorkbench().getDisplay();
+ selection_policy = new TCFModelSelectionPolicy(this);
+ adapters.put(ILaunch.class, launch);
+ adapters.put(IModelSelectionPolicy.class, selection_policy);
+ adapters.put(IModelSelectionPolicyFactory.class, model_selection_factory);
+ adapters.put(IDebugModelProvider.class, debug_model_provider);
+ adapters.put(ISuspendHandler.class, new SuspendCommand(this));
+ adapters.put(IResumeHandler.class, new ResumeCommand(this));
+ adapters.put(BackResumeCommand.class, new BackResumeCommand(this));
+ adapters.put(ITerminateHandler.class, new TerminateCommand(this));
+ adapters.put(IDisconnectHandler.class, new DisconnectCommand(this));
+ adapters.put(IStepIntoHandler.class, new StepIntoCommand(this));
+ adapters.put(IStepOverHandler.class, new StepOverCommand(this));
+ adapters.put(IStepReturnHandler.class, new StepReturnCommand(this));
+ adapters.put(BackIntoCommand.class, new BackIntoCommand(this));
+ adapters.put(BackOverCommand.class, new BackOverCommand(this));
+ adapters.put(BackReturnCommand.class, new BackReturnCommand(this));
+ adapters.put(IDropToFrameHandler.class, new DropToFrameCommand(this));
+ expr_manager = DebugPlugin.getDefault().getExpressionManager();
+ expr_manager.addExpressionListener(expressions_listener);
+ annotation_manager = Activator.getAnnotationManager();
+ launch.addActionsListener(actions_listener);
+ final IPreferenceStore prefs = TCFPreferences.getPreferenceStore();
+ IPropertyChangeListener listener = new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ launch.setContextActionsInterval(prefs.getLong(TCFPreferences.PREF_MIN_STEP_INTERVAL));
+ min_view_updates_interval = prefs.getLong(TCFPreferences.PREF_MIN_UPDATE_INTERVAL);
+ view_updates_throttle_enabled = prefs.getBoolean(TCFPreferences.PREF_VIEW_UPDATES_THROTTLE);
+ channel_throttle_enabled = prefs.getBoolean(TCFPreferences.PREF_TARGET_TRAFFIC_THROTTLE);
+ wait_for_pc_update_after_step = prefs.getBoolean(TCFPreferences.PREF_WAIT_FOR_PC_UPDATE_AFTER_STEP);
+ wait_for_views_update_after_step = prefs.getBoolean(TCFPreferences.PREF_WAIT_FOR_VIEWS_UPDATE_AFTER_STEP);
+ delay_stack_update_until_last_step = prefs.getBoolean(TCFPreferences.PREF_DELAY_STACK_UPDATE_UNTIL_LAST_STEP);
+ stack_frames_limit_enabled = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_LIMIT_ENABLED);
+ stack_frames_limit_value = prefs.getInt(TCFPreferences.PREF_STACK_FRAME_LIMIT_VALUE);
+ show_function_arg_names = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_ARG_NAMES);
+ show_function_arg_values = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_ARG_VALUES);
+ auto_children_list_updates = prefs.getBoolean(TCFPreferences.PREF_AUTO_CHILDREN_LIST_UPDATES);
+ delay_children_list_updates = prefs.getBoolean(TCFPreferences.PREF_DELAY_CHILDREN_LIST_UPDATES);
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ for (TCFNode n : id2node.values()) {
+ if (n instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)n).onPreferencesChanged();
+ }
+ }
+ }
+ });
+ }
+ };
+ listener.propertyChange(null);
+ prefs.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Add an adapter for given type.
+ *
+ * @param adapterType the type the adapter implements
+ * @param adapter the adapter implementing <code>adapterType</code>
+ */
+ public void setAdapter(Class<?> adapterType, Object adapter) {
+ synchronized (adapters) {
+ assert adapterType.isInstance(adapter);
+ adapters.put(adapterType, adapter);
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Object getAdapter(final Class adapter, final TCFNode node) {
+ synchronized (adapters) {
+ Object o = adapters.get(adapter);
+ if (o != null) return o;
+ }
+ if (adapter == IMemoryBlockRetrieval.class || adapter == IMemoryBlockRetrievalExtension.class) {
+ return new TCFTask<Object>() {
+ public void run() {
+ Object o = null;
+ TCFDataCache<TCFNodeExecContext> cache = searchMemoryContext(node);
+ if (cache != null) {
+ if (!cache.validate(this)) return;
+ if (cache.getData() != null) {
+ TCFNodeExecContext ctx = cache.getData();
+ o = mem_retrieval.get(ctx.id);
+ if (o == null) {
+ TCFMemoryBlockRetrieval m = new TCFMemoryBlockRetrieval(ctx);
+ mem_retrieval.put(ctx.id, m);
+ o = m;
+ }
+ }
+ }
+ assert o == null || adapter.isInstance(o);
+ done(o);
+ }
+ }.getE();
+ }
+ return null;
+ }
+
+ void onConnected() {
+ assert Protocol.isDispatchThread();
+ assert launch_node == null;
+ channel = launch.getChannel();
+ launch_node = new TCFNodeLaunch(this);
+ IMemory mem = launch.getService(IMemory.class);
+ if (mem != null) mem.addListener(mem_listener);
+ IRunControl run = launch.getService(IRunControl.class);
+ if (run != null) run.addListener(run_listener);
+ IMemoryMap mmap = launch.getService(IMemoryMap.class);
+ if (mmap != null) mmap.addListener(mmap_listenr);
+ IRegisters reg = launch.getService(IRegisters.class);
+ if (reg != null) reg.addListener(reg_listener);
+ IProcesses prs = launch.getService(IProcesses.class);
+ if (prs != null) prs.addListener(prs_listener);
+ launchChanged();
+ for (TCFModelProxy p : model_proxies.values()) {
+ String id = p.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(id)) {
+ Protocol.invokeLater(new InitialSelection());
+ }
+ }
+ }
+
+ void onDisconnected() {
+ assert Protocol.isDispatchThread();
+ if (locks.size() > 0) {
+ TCFSnapshot[] arr = locks.values().toArray(new TCFSnapshot[locks.size()]);
+ locks.clear();
+ for (TCFSnapshot s : arr) s.dispose();
+ }
+ if (launch_node != null) {
+ launch_node.dispose();
+ launch_node = null;
+ }
+ refreshLaunchView();
+ assert id2node.size() == 0;
+ }
+
+ void onProcessOutput(String process_id, final int stream_id, byte[] data) {
+ TCFConsole c = process_consoles.get(process_id);
+ if (c == null) process_consoles.put(process_id, c = new TCFConsole(this, process_id));
+ c.write(stream_id, data);
+ }
+
+ void onProcessStreamError(String process_id, int stream_id, Exception x, int lost_size) {
+ if (channel != null && channel.getState() == IChannel.STATE_CLOSED) return;
+ StringBuffer bf = new StringBuffer();
+ bf.append("Debugger console IO error");
+ if (process_id != null) {
+ bf.append(". Process ID ");
+ bf.append(process_id);
+ }
+ bf.append(". Stream ");
+ bf.append(stream_id);
+ if (lost_size > 0) {
+ bf.append(". Lost data size ");
+ bf.append(lost_size);
+ }
+ Activator.log(bf.toString(), x);
+ }
+
+ void onMemoryChanged(String id, boolean notify_references, boolean context_suspended) {
+ if (channel == null) return;
+ if (notify_references) {
+ for (Object obj : context_map.values()) {
+ if (obj instanceof IRunControl.RunControlContext) {
+ IRunControl.RunControlContext subctx = (IRunControl.RunControlContext)obj;
+ if (id.equals(subctx.getProcessID()) && !id.equals(subctx.getID())) {
+ TCFNode subnode = getNode(subctx.getID());
+ if (subnode instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)subnode).onMemoryChanged(null, null);
+ }
+ }
+ }
+ }
+ }
+ if (mem_retrieval.size() == 0) return;
+ if (mem_blocks_update == null) {
+ mem_blocks_update = new MemoryBlocksUpdate(channel);
+ if (wait_for_views_update_after_step) {
+ launch.addPendingClient(mem_blocks_update);
+ }
+ }
+ mem_blocks_update.add(id, context_suspended);
+ }
+
+ public TCFAction getActiveAction(String id) {
+ return active_actions.get(id);
+ }
+
+ String getContextActionResult(String id) {
+ return action_results.get(id);
+ }
+
+ public long getMinViewUpdatesInterval() {
+ return min_view_updates_interval;
+ }
+
+ public boolean getViewUpdatesThrottleEnabled() {
+ return view_updates_throttle_enabled;
+ }
+
+ public boolean getWaitForViewsUpdateAfterStep() {
+ return wait_for_views_update_after_step;
+ }
+
+ public boolean getDelayStackUpdateUtilLastStep() {
+ return delay_stack_update_until_last_step;
+ }
+
+ public boolean getChannelThrottleEnabled() {
+ return channel_throttle_enabled;
+ }
+
+ public boolean getStackFramesLimitEnabled() {
+ return stack_frames_limit_enabled;
+ }
+
+ public int getStackFramesLimitValue() {
+ return stack_frames_limit_value;
+ }
+
+ public boolean getShowFunctionArgNames() {
+ return show_function_arg_names;
+ }
+
+ public boolean getShowFunctionArgValues() {
+ return show_function_arg_values;
+ }
+
+ public boolean getAutoChildrenListUpdates() {
+ return auto_children_list_updates;
+ }
+
+ public boolean getDelayChildrenListUpdates() {
+ return delay_children_list_updates;
+ }
+
+ void onProxyInstalled(TCFModelProxy mp) {
+ IPresentationContext pc = mp.getPresentationContext();
+ model_proxies.put(mp.getPresentationContext(), mp);
+ if (launch_node != null && pc.getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) {
+ Protocol.invokeLater(new InitialSelection());
+ }
+ }
+
+ void onProxyDisposed(TCFModelProxy mp) {
+ IPresentationContext ctx = mp.getPresentationContext();
+ assert model_proxies.get(ctx) == mp;
+ model_proxies.remove(ctx);
+ }
+
+ private void onContextRemoved(String[] context_ids) {
+ for (String id : context_ids) {
+ TCFNode node = getNode(id);
+ if (node instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)node).onContextRemoved();
+ }
+ action_results.remove(id);
+ context_map.remove(id);
+ expanded_nodes.remove(id);
+ if (mem_blocks_update != null) mem_blocks_update.changeset.remove(id);
+ }
+ launch_node.onAnyContextAddedOrRemoved();
+ // Close debug session if the last context is removed:
+ onContextOrProcessRemoved();
+ annotation_manager.updateAnnotations(null, launch);
+ }
+
+ void onContextRunning() {
+ annotation_manager.updateAnnotations(null, launch);
+ }
+
+ void updateContextMap(String id, IRunControl.RunControlContext ctx) {
+ context_map.put(id, ctx);
+ }
+
+ private void onContextOrProcessRemoved() {
+ final int generation = ++auto_disconnect_generation;
+ Protocol.invokeLater(1000, new Runnable() {
+ public void run() {
+ if (generation != auto_disconnect_generation) return;
+ if (launch_node == null) return;
+ if (launch_node.isDisposed()) return;
+ TCFChildren children = launch_node.getFilteredChildren();
+ if (!children.validate(this)) return;
+ if (children.size() > 0) return;
+ launch.onLastContextRemoved();
+ }
+ });
+ }
+
+ void launchChanged() {
+ if (launch_node != null) {
+ for (TCFModelProxy p : model_proxies.values()) {
+ String id = p.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(id)) {
+ p.addDelta(launch_node, IModelDelta.STATE | IModelDelta.CONTENT);
+ }
+ }
+ }
+ else {
+ refreshLaunchView();
+ }
+ }
+
+ Collection<TCFModelProxy> getModelProxies() {
+ return model_proxies.values();
+ }
+
+ void dispose() {
+ launch.removeActionsListener(actions_listener);
+ expr_manager.removeExpressionListener(expressions_listener);
+ for (TCFConsole c : process_consoles.values()) c.close();
+ for (TCFConsole c : debug_consoles) c.close();
+ }
+
+ void addNode(String id, TCFNode node) {
+ assert id != null;
+ assert Protocol.isDispatchThread();
+ assert id2node.get(id) == null;
+ assert !node.isDisposed();
+ id2node.put(id, node);
+ }
+
+ void removeNode(String id) {
+ assert id != null;
+ assert Protocol.isDispatchThread();
+ id2node.remove(id);
+ mem_retrieval.remove(id);
+ }
+
+ void flushAllCaches() {
+ for (TCFNode n : id2node.values()) n.flushAllCaches();
+ }
+
+ public IExpressionManager getExpressionManager() {
+ return expr_manager;
+ }
+
+ public Display getDisplay() {
+ return display;
+ }
+
+ /**
+ * @return debug model launch object.
+ */
+ public TCFLaunch getLaunch() {
+ return launch;
+ }
+
+ /**
+ * @return communication channel that this model is using.
+ */
+ public IChannel getChannel() {
+ return channel;
+ }
+
+ /**
+ * Get top level (root) debug model node.
+ * Same as getNode("").
+ * @return root node.
+ */
+ public TCFNodeLaunch getRootNode() {
+ return launch_node;
+ }
+
+ /**
+ * Set current hover expression for a given model node,
+ * and return a cache of expression nodes that represents given expression.
+ * The model allows only one current hover expression per node at any time,
+ * however it will cache results of recent expression evaluations,
+ * and it will re-use cached results when current hover expression changes.
+ * The cache getData() method should not return more then 1 node,
+ * and it can return an empty collection.
+ * @param parent - a thread or stack frame where the expression should be evaluated.
+ * @param expression - the expression text, can be null.
+ * @return a cache of expression nodes.
+ */
+ public TCFChildren getHoverExpressionCache(TCFNode parent, String expression) {
+ assert Protocol.isDispatchThread();
+ if (parent instanceof TCFNodeStackFrame) {
+ return ((TCFNodeStackFrame)parent).getHoverExpressionCache(expression);
+ }
+ if (parent instanceof TCFNodeExecContext) {
+ return ((TCFNodeExecContext)parent).getHoverExpressionCache(expression);
+ }
+ return null;
+ }
+
+ /**
+ * Get a model node with given ID.
+ * ID == "" means launch node.
+ * @param id - node ID.
+ * @return debug model node or null if no node exists with such ID.
+ */
+ public TCFNode getNode(String id) {
+ if (id == null) return null;
+ if (id.equals("")) return launch_node;
+ assert Protocol.isDispatchThread();
+ return id2node.get(id);
+ }
+
+ /**
+ * Get a type that should be used to cast a value of an expression when it is shown in a view.
+ * Return null if original type of the value should be used.
+ * @param id - expression node ID.
+ * @return a string that designates a type or null.
+ */
+ public String getCastToType(String id) {
+ return cast_to_type_map.get(id);
+ }
+
+ /**
+ * Register a type that should be used to cast a value of an expression when it is shown in a view.
+ * 'type' == null means original type of the value should be used.
+ * @param id - expression node ID.
+ * @param type - a string that designates a type.
+ */
+ public void setCastToType(String id, String type) {
+ if (type != null && type.trim().length() == 0) type = null;
+ if (type == null) cast_to_type_map.remove(id);
+ else cast_to_type_map.put(id, type);
+ TCFNode node = id2node.get(id);
+ if (node instanceof ICastToType) {
+ ((ICastToType)node).onCastToTypeChanged();
+ }
+ }
+
+ /**
+ * Get a data cache that contains properties of a symbol.
+ * New cache object is created if it does not exist yet.
+ * @param sym_id - the symbol ID.
+ * @return data cache object.
+ */
+ public TCFDataCache<ISymbols.Symbol> getSymbolInfoCache(final String sym_id) {
+ if (sym_id == null) return null;
+ TCFNodeSymbol n = (TCFNodeSymbol)getNode(sym_id);
+ if (n == null) n = new TCFNodeSymbol(launch_node, sym_id);
+ return n.getContext();
+ }
+
+ /**
+ * Get a data cache that contains children of a symbol.
+ * New cache object is created if it does not exist yet.
+ * @param sym_id - the symbol ID.
+ * @return data cache object.
+ */
+ public TCFDataCache<String[]> getSymbolChildrenCache(final String sym_id) {
+ if (sym_id == null) return null;
+ TCFNodeSymbol n = (TCFNodeSymbol)getNode(sym_id);
+ if (n == null) n = new TCFNodeSymbol(launch_node, sym_id);
+ return n.getChildren();
+ }
+
+ /**
+ * Search memory context that owns the object represented by given node.
+ * @return data cache item that holds the memory context node.
+ */
+ public TCFDataCache<TCFNodeExecContext> searchMemoryContext(final TCFNode node) {
+ TCFNode n = node;
+ while (n != null && !n.isDisposed()) {
+ if (n instanceof TCFNodeExecContext) return ((TCFNodeExecContext)n).getMemoryNode();
+ n = n.parent;
+ }
+ return null;
+ }
+
+ /**
+ * Asynchronously create model node for given ID.
+ * If 'done' is TCFDataCache and it is valid after the method returns,
+ * the node cannot be created because of an error,
+ * and the cache will contain the error report.
+ * @param id - context ID.
+ * @param done - an object waiting for cache validation.
+ * @return - true if all done, false if 'done' is waiting for remote data.
+ */
+ public boolean createNode(String id, final Runnable done) {
+ TCFNode parent = getNode(id);
+ if (parent != null) return true;
+ LinkedList<Object> path = null;
+ for (;;) {
+ Object obj = context_map.get(id);
+ if (obj == null) obj = new CreateNodeRunnable(id);
+ if (obj instanceof CreateNodeRunnable) {
+ ((CreateNodeRunnable)obj).wait(done);
+ return false;
+ }
+ if (obj instanceof Throwable) {
+ if (done instanceof TCFDataCache<?>) {
+ ((TCFDataCache<?>)done).set(null, (Throwable)obj, null);
+ }
+ return true;
+ }
+ if (path == null) path = new LinkedList<Object>();
+ path.add(obj);
+ String parent_id = null;
+ if (obj instanceof IRunControl.RunControlContext) {
+ parent_id = ((IRunControl.RunControlContext)obj).getParentID();
+ }
+ else if (obj instanceof IStackTrace.StackTraceContext) {
+ parent_id = ((IStackTrace.StackTraceContext)obj).getParentID();
+ }
+ else {
+ parent_id = ((IRegisters.RegistersContext)obj).getParentID();
+ }
+ parent = parent_id == null ? launch_node : getNode(parent_id);
+ if (parent != null) break;
+ id = parent_id;
+ }
+ while (path.size() > 0) {
+ Object obj = path.removeLast();
+ if (obj instanceof IRunControl.RunControlContext) {
+ IRunControl.RunControlContext ctx = (IRunControl.RunControlContext)obj;
+ TCFNodeExecContext n = new TCFNodeExecContext(parent, ctx.getID());
+ if (parent instanceof TCFNodeLaunch) ((TCFNodeLaunch)parent).getChildren().add(n);
+ else ((TCFNodeExecContext)parent).getChildren().add(n);
+ n.setRunContext(ctx);
+ parent = n;
+ }
+ else if (obj instanceof IStackTrace.StackTraceContext) {
+ IStackTrace.StackTraceContext ctx = (IStackTrace.StackTraceContext)obj;
+ TCFNodeStackFrame n = new TCFNodeStackFrame((TCFNodeExecContext)parent, ctx.getID(), false);
+ ((TCFNodeExecContext)parent).getStackTrace().add(n);
+ parent = n;
+ }
+ else if (obj instanceof IRegisters.RegistersContext) {
+ IRegisters.RegistersContext ctx = (IRegisters.RegistersContext)obj;
+ TCFNodeRegister n = new TCFNodeRegister(parent, ctx.getID());
+ if (parent instanceof TCFNodeRegister) ((TCFNodeRegister)parent).getChildren().add(n);
+ else if (parent instanceof TCFNodeStackFrame) ((TCFNodeStackFrame)parent).getRegisters().add(n);
+ else ((TCFNodeExecContext)parent).getRegisters().add(n);
+ parent = n;
+ }
+ else {
+ assert false;
+ }
+ }
+ return true;
+ }
+
+ private class CreateNodeRunnable implements Runnable {
+
+ final String id;
+ final ArrayList<Runnable> waiting_list = new ArrayList<Runnable>();
+ final ArrayList<IService> service_list = new ArrayList<IService>();
+
+ CreateNodeRunnable(String id) {
+ this.id = id;
+ assert context_map.get(id) == null;
+ String[] arr = { IRunControl.NAME, IStackTrace.NAME, IRegisters.NAME };
+ for (String nm : arr) {
+ IService s = channel.getRemoteService(nm);
+ if (s != null) service_list.add(s);
+ }
+ context_map.put(id, this);
+ Protocol.invokeLater(this);
+ }
+
+ void wait(Runnable r) {
+ assert context_map.get(id) == this;
+ waiting_list.add(r);
+ }
+
+ public void run() {
+ assert context_map.get(id) == this;
+ if (service_list.size() == 0) {
+ context_map.put(id, new Exception("Invalid context ID"));
+ for (Runnable r : waiting_list) r.run();
+ }
+ else {
+ IService s = service_list.remove(0);
+ if (s instanceof IRunControl) {
+ ((IRunControl)s).getContext(id, new IRunControl.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext context) {
+ if (error == null && context != null) {
+ context_map.put(id, context);
+ for (Runnable r : waiting_list) r.run();
+ }
+ else {
+ run();
+ }
+ }
+ });
+ }
+ else if (s instanceof IStackTrace) {
+ ((IStackTrace)s).getContext(new String[]{ id }, new IStackTrace.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) {
+ if (error == null && context != null && context.length == 1 && context[0] != null) {
+ context_map.put(id, context[0]);
+ for (Runnable r : waiting_list) r.run();
+ }
+ else {
+ run();
+ }
+ }
+ });
+ }
+ else {
+ ((IRegisters)s).getContext(id, new IRegisters.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) {
+ if (error == null && context != null) {
+ context_map.put(id, context);
+ for (Runnable r : waiting_list) r.run();
+ }
+ else {
+ run();
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+
+ public void update(IChildrenCountUpdate[] updates) {
+ for (IChildrenCountUpdate update : updates) {
+ Object o = update.getElement();
+ if (o instanceof TCFLaunch) {
+ if (launch_node != null) {
+ launch_node.update(update);
+ }
+ else {
+ update.setChildCount(0);
+ update.done();
+ }
+ }
+ else {
+ ((TCFNode)o).update(update);
+ }
+ }
+ }
+
+ public void update(IChildrenUpdate[] updates) {
+ for (IChildrenUpdate update : updates) {
+ Object o = update.getElement();
+ if (o instanceof TCFLaunch) {
+ if (launch_node != null) {
+ launch_node.update(update);
+ }
+ else {
+ update.done();
+ }
+ }
+ else {
+ ((TCFNode)o).update(update);
+ }
+ }
+ }
+
+ public void update(IHasChildrenUpdate[] updates) {
+ for (IHasChildrenUpdate update : updates) {
+ Object o = update.getElement();
+ if (o instanceof TCFLaunch) {
+ if (launch_node != null) {
+ launch_node.update(update);
+ }
+ else {
+ update.setHasChilren(false);
+ update.done();
+ }
+ }
+ else {
+ ((TCFNode)o).update(update);
+ }
+ }
+ }
+
+ public void update(ILabelUpdate[] updates) {
+ for (ILabelUpdate update : updates) {
+ Object o = update.getElement();
+ // Launch label is provided by TCFLaunchLabelProvider class.
+ assert !(o instanceof TCFLaunch);
+ ((TCFNode)o).update(update);
+ }
+ }
+
+ public void update(final IViewerInputUpdate update) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ TCFNode node = pins.get(update.getPresentationContext().getPart());
+ if (node != null) {
+ node.update(update);
+ }
+ else {
+ if (IDebugUIConstants.ID_BREAKPOINT_VIEW.equals(update.getPresentationContext().getId())) {
+ // Current implementation does not support flexible hierarchy for breakpoints
+ IViewerInputProvider p = (IViewerInputProvider)launch.getAdapter(IViewerInputProvider.class);
+ if (p != null) {
+ p.update(update);
+ return;
+ }
+ }
+ Object o = update.getElement();
+ if (o instanceof TCFLaunch) {
+ update.setInputElement(o);
+ update.done();
+ }
+ else {
+ ((TCFNode)o).update(update);
+ }
+ }
+ }
+ });
+ }
+
+ public IModelProxy createModelProxy(Object element, IPresentationContext context) {
+ return new TCFModelProxy(this);
+ }
+
+ public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) {
+ String id = getColumnPresentationId(context, element);
+ if (id == null) return null;
+ if (id.equals(TCFColumnPresentationRegister.PRESENTATION_ID)) return new TCFColumnPresentationRegister();
+ if (id.equals(TCFColumnPresentationExpression.PRESENTATION_ID)) return new TCFColumnPresentationExpression();
+ if (id.equals(TCFColumnPresentationModules.PRESENTATION_ID)) return new TCFColumnPresentationModules();
+ return null;
+ }
+
+ public String getColumnPresentationId(IPresentationContext context, Object element) {
+ if (IDebugUIConstants.ID_REGISTER_VIEW.equals(context.getId())) {
+ return TCFColumnPresentationRegister.PRESENTATION_ID;
+ }
+ if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(context.getId())) {
+ return TCFColumnPresentationExpression.PRESENTATION_ID;
+ }
+ if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(context.getId())) {
+ return TCFColumnPresentationExpression.PRESENTATION_ID;
+ }
+ if (ID_EXPRESSION_HOVER.equals(context.getId())) {
+ return TCFColumnPresentationExpression.PRESENTATION_ID;
+ }
+ if (IDebugUIConstants.ID_MODULE_VIEW.equals(context.getId())) {
+ return TCFColumnPresentationModules.PRESENTATION_ID;
+ }
+ return null;
+ }
+
+ public void setPin(IWorkbenchPart part, TCFNode node) {
+ assert Protocol.isDispatchThread();
+ if (node == null) pins.remove(part);
+ else pins.put(part, node);
+ }
+
+ private IPresentationContext getPresentationContext(IWorkbenchPart part) {
+ if (part instanceof IDebugView) {
+ Viewer viewer = ((IDebugView)part).getViewer();
+ if (viewer instanceof ITreeModelViewer) {
+ ITreeModelViewer t = ((ITreeModelViewer)viewer);
+ return t.getPresentationContext();
+ }
+ }
+ return null;
+ }
+
+ public void setLock(IWorkbenchPart part) {
+ if (launch_node == null) return;
+ IPresentationContext ctx = getPresentationContext(part);
+ if (ctx == null) return;
+ locks.put(part, new TCFSnapshot(ctx));
+ TCFModelProxy proxy = model_proxies.get(ctx);
+ if (proxy == null) return;
+ proxy.addDelta((TCFNode)proxy.getInput(), IModelDelta.CONTENT);
+ }
+
+ public boolean isLocked(IWorkbenchPart part) {
+ return locks.get(part) != null;
+ }
+
+ public boolean clearLock(IWorkbenchPart part) {
+ TCFSnapshot snapshot = locks.remove(part);
+ if (snapshot == null) return false;
+ snapshot.dispose();
+ IPresentationContext ctx = getPresentationContext(part);
+ if (ctx != null) {
+ TCFModelProxy proxy = model_proxies.get(ctx);
+ if (proxy != null) proxy.addDelta((TCFNode)proxy.getInput(), IModelDelta.CONTENT);
+ }
+ return true;
+ }
+
+ public void setLockPolicy(IWorkbenchPart part, int policy) {
+ if (policy == UPDATE_POLICY_AUTOMATIC) {
+ clearLock(part);
+ lock_policy.remove(part);
+ }
+ else {
+ if (!isLocked(part)) setLock(part);
+ lock_policy.put(part, policy);
+ }
+ }
+
+ public int getLockPolicy(IWorkbenchPart part) {
+ if (locks.get(part) == null) return UPDATE_POLICY_AUTOMATIC;
+ Integer i = lock_policy.get(part);
+ if (i == null || i.intValue() == 0) return UPDATE_POLICY_MANUAL;
+ return i.intValue();
+ }
+
+ TCFSnapshot getSnapshot(IPresentationContext ctx) {
+ return locks.get(ctx.getPart());
+ }
+
+ public void setDebugViewSelection(TCFNode node, String reason) {
+ assert Protocol.isDispatchThread();
+ if (node == null) return;
+ if (node.isDisposed()) return;
+ runSuspendTrigger(node);
+ if (reason == null) return;
+ if (reason.equals(IRunControl.REASON_USER_REQUEST)) return;
+ for (TCFModelProxy proxy : model_proxies.values()) {
+ if (proxy.getPresentationContext().getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) {
+ proxy.setSelection(node);
+ if (reason.equals(IRunControl.REASON_STEP)) continue;
+ if (reason.equals(IRunControl.REASON_CONTAINER)) continue;
+ if (delay_stack_update_until_last_step && launch.getContextActionsCount(node.id) != 0) continue;
+ if (expanded_nodes.add(node.id)) proxy.expand(node);
+ }
+ if (reason.equals(IRunControl.REASON_BREAKPOINT)) {
+ IWorkbenchPart part = proxy.getPresentationContext().getPart();
+ int policy = getLockPolicy(part);
+ if (policy == UPDATE_POLICY_BREAKPOINT) {
+ clearLock(part);
+ setLock(part);
+ }
+ }
+ }
+ }
+
+ /**
+ * Reveal source code associated with given model element.
+ * The method is part of ISourceDisplay interface.
+ * The method is normally called from SourceLookupService.
+ */
+ public void displaySource(Object model_element, final IWorkbenchPage page, boolean forceSourceLookup) {
+ if (wait_for_pc_update_after_step) launch.addPendingClient(TCFModel.this);
+ final int cnt = ++display_source_generation;
+ /* Because of racing in Eclipse Debug infrastructure, 'model_element' value can be invalid.
+ * As a workaround, get current debug view selection.
+ */
+ if (page != null) {
+ ISelection context = DebugUITools.getDebugContextManager().getContextService(page.getWorkbenchWindow()).getActiveContext();
+ if (context instanceof IStructuredSelection) {
+ IStructuredSelection selection = (IStructuredSelection)context;
+ model_element = selection.isEmpty() ? null : selection.getFirstElement();
+ }
+ }
+ final Object element = model_element;
+ Protocol.invokeLater(25, new Runnable() {
+ public void run() {
+ if (cnt != display_source_generation) return;
+ TCFNodeStackFrame stack_frame = null;
+ if (!disposed && channel.getState() == IChannel.STATE_OPEN) {
+ if (element instanceof TCFNodeExecContext) {
+ TCFNodeExecContext exec_ctx = (TCFNodeExecContext)element;
+ if (!exec_ctx.isDisposed() && active_actions.get(exec_ctx.id) == null) {
+ TCFDataCache<TCFContextState> state_cache = exec_ctx.getState();
+ if (!state_cache.validate(this)) return;
+ if (!exec_ctx.isNotActive()) {
+ TCFContextState state_data = state_cache.getData();
+ if (state_data != null && state_data.is_suspended) {
+ TCFChildrenStackTrace stack_trace = exec_ctx.getStackTrace();
+ if (!stack_trace.validate(this)) return;
+ stack_frame = stack_trace.getTopFrame();
+ }
+ }
+ }
+ }
+ else if (element instanceof TCFNodeStackFrame) {
+ TCFNodeStackFrame f = (TCFNodeStackFrame)element;
+ TCFNodeExecContext exec_ctx = (TCFNodeExecContext)f.parent;
+ if (!f.isDisposed() && !exec_ctx.isDisposed() && active_actions.get(exec_ctx.id) == null) {
+ TCFDataCache<TCFContextState> state_cache = exec_ctx.getState();
+ if (!state_cache.validate(this)) return;
+ if (!exec_ctx.isNotActive()) {
+ TCFContextState state_data = state_cache.getData();
+ if (state_data != null && state_data.is_suspended) stack_frame = f;
+ }
+ }
+ }
+ }
+ String ctx_id = null;
+ boolean top_frame = false;
+ ILineNumbers.CodeArea area = null;
+ if (stack_frame != null) {
+ TCFDataCache<TCFSourceRef> line_info = stack_frame.getLineInfo();
+ if (!line_info.validate(this)) return;
+ Throwable error = line_info.getError();
+ TCFSourceRef src_ref = line_info.getData();
+ if (error == null && src_ref != null) error = src_ref.error;
+ if (error != null) Activator.log("Error retrieving source mapping for a stack frame", error);
+ if (src_ref != null) area = src_ref.area;
+ top_frame = stack_frame.getFrameNo() == 0;
+ ctx_id = stack_frame.parent.id;
+ }
+ displaySource(cnt, page, element, ctx_id, top_frame, area);
+ }
+ });
+ }
+
+ private void displaySource(final int cnt, final IWorkbenchPage page,
+ final Object element, final String exe_id, final boolean top_frame, final ILineNumbers.CodeArea area) {
+ final boolean disassembly_available = channel.getRemoteService(IDisassembly.class) != null;
+ display.asyncExec(new Runnable() {
+ public void run() {
+ try {
+ if (cnt != display_source_generation) return;
+ String editor_id = null;
+ IEditorInput editor_input = null;
+ int line = 0;
+ if (area != null) {
+ Object source_element = TCFSourceLookupDirector.lookup(launch, area);
+ if (source_element != null) {
+ ISourcePresentation presentation = TCFModelPresentation.getDefault();
+ editor_input = presentation.getEditorInput(source_element);
+ if (editor_input != null) editor_id = presentation.getEditorId(editor_input, source_element);
+ line = area.start_line;
+ }
+ }
+ if (area != null && !instruction_stepping_enabled && (editor_input == null || editor_id == null)) {
+ ILaunchConfiguration cfg = launch.getLaunchConfiguration();
+ ISourceNotFoundPresentation presentation = (ISourceNotFoundPresentation)DebugPlugin.getAdapter(element, ISourceNotFoundPresentation.class);
+ if (presentation != null) {
+ String filename = TCFSourceLookupParticipant.toFileName(area);
+ editor_input = presentation.getEditorInput(element, cfg, filename);
+ editor_id = presentation.getEditorId(editor_input, element);
+ }
+ if (editor_id == null || editor_input == null) {
+ editor_id = IDebugUIConstants.ID_COMMON_SOURCE_NOT_FOUND_EDITOR;
+ editor_input = editor_not_found.get(cfg);
+ if (editor_input == null) {
+ editor_input = new CommonSourceNotFoundEditorInput(cfg);
+ editor_not_found.put(cfg, editor_input);
+ }
+ }
+ }
+ if (exe_id != null && disassembly_available &&
+ (editor_input == null || editor_id == null || instruction_stepping_enabled) &&
+ PlatformUI.getWorkbench().getEditorRegistry().findEditor(
+ DisassemblyEditorInput.EDITOR_ID) != null) {
+ editor_id = DisassemblyEditorInput.EDITOR_ID;
+ editor_input = DisassemblyEditorInput.INSTANCE;
+ }
+ if (cnt != display_source_generation) return;
+ ITextEditor text_editor = null;
+ if (page != null && editor_input != null && editor_id != null) {
+ IEditorPart editor = openEditor(editor_input, editor_id, page);
+ if (editor instanceof ITextEditor) {
+ text_editor = (ITextEditor)editor;
+ }
+ else {
+ text_editor = (ITextEditor)editor.getAdapter(ITextEditor.class);
+ }
+ }
+ IRegion region = null;
+ if (text_editor != null) {
+ region = getLineInformation(text_editor, line);
+ if (region != null) text_editor.selectAndReveal(region.getOffset(), 0);
+ }
+ if (wait_for_pc_update_after_step) launch.addPendingClient(annotation_manager);
+ annotation_manager.updateAnnotations(page.getWorkbenchWindow(), launch);
+ }
+ finally {
+ if (cnt == display_source_generation) launch.removePendingClient(TCFModel.this);
+ }
+ }
+ });
+ }
+
+ /*
+ * Refresh Launch View.
+ * Normally the view is updated by sending deltas through model proxy.
+ * This method is used only when launch is not yet connected or already disconnected.
+ */
+ private void refreshLaunchView() {
+ // TODO: there should be a better way to refresh Launch View
+ synchronized (Device.class) {
+ if (display.isDisposed()) return;
+ display.asyncExec(new Runnable() {
+ public void run() {
+ IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
+ if (windows == null) return;
+ for (IWorkbenchWindow window : windows) {
+ IDebugView view = (IDebugView)window.getActivePage().findView(IDebugUIConstants.ID_DEBUG_VIEW);
+ if (view != null) ((StructuredViewer)view.getViewer()).refresh(launch);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Open debugger console that provide command line UI for the debugger.
+ */
+ public void showDebugConsole() {
+ debug_consoles.add(new TCFConsole(this, null));
+ }
+
+ /**
+ * Show error message box in active workbench window.
+ * @param title - message box title.
+ * @param error - error to be shown.
+ */
+ public void showMessageBox(final String title, final Throwable error) {
+ display.asyncExec(new Runnable() {
+ public void run() {
+ Shell shell = display.getActiveShell();
+ if (shell == null) {
+ Shell[] shells = display.getShells();
+ HashSet<Shell> set = new HashSet<Shell>();
+ for (Shell s : shells) set.add(s);
+ for (Shell s : shells) {
+ if (s.getParent() != null) set.remove(s.getParent().getShell());
+ }
+ for (Shell s : shells) shell = s;
+ }
+ MessageBox mb = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
+ mb.setText(title);
+ mb.setMessage(getErrorMessage(error, true));
+ mb.open();
+ }
+ });
+ }
+
+ /**
+ * Create human readable error message from a Throwable object.
+ * @param error - a Throwable object.
+ * @param multiline - true if multi-line text is allowed.
+ * @return
+ */
+ public static String getErrorMessage(Throwable error, boolean multiline) {
+ StringBuffer buf = new StringBuffer();
+ while (error != null) {
+ String msg = null;
+ if (!multiline && error instanceof IErrorReport) {
+ msg = Command.toErrorString(((IErrorReport)error).getAttributes());
+ }
+ else {
+ msg = error.getLocalizedMessage();
+ }
+ if (msg == null || msg.length() == 0) msg = error.getClass().getName();
+ buf.append(msg);
+ error = error.getCause();
+ if (error != null) {
+ char ch = buf.charAt(buf.length() - 1);
+ if (multiline && ch != '\n') {
+ buf.append('\n');
+ }
+ else if (ch != '.' && ch != ';') {
+ buf.append(';');
+ }
+ buf.append("Caused by:");
+ buf.append(multiline ? '\n' : ' ');
+ }
+ }
+ if (buf.length() > 0) {
+ char ch = buf.charAt(buf.length() - 1);
+ if (multiline && ch != '\n') {
+ buf.append('\n');
+ }
+ }
+ return buf.toString();
+ }
+
+ /*
+ * Open an editor for given editor input.
+ * @param input - IEditorInput representing a source file to be shown in the editor
+ * @param id - editor type ID
+ * @param page - workbench page that will contain the editor
+ * @return - IEditorPart if the editor was opened successfully, or null otherwise.
+ */
+ private IEditorPart openEditor(final IEditorInput input, final String id, final IWorkbenchPage page) {
+ final IEditorPart[] editor = new IEditorPart[]{ null };
+ Runnable r = new Runnable() {
+ public void run() {
+ if (!page.getWorkbenchWindow().getWorkbench().isClosing()) {
+ try {
+ editor[0] = page.openEditor(input, id, false, IWorkbenchPage.MATCH_ID|IWorkbenchPage.MATCH_INPUT);
+ }
+ catch (PartInitException e) {
+ Activator.log("Cannot open editor", e);
+ }
+ }
+ }
+ };
+ BusyIndicator.showWhile(display, r);
+ return editor[0];
+ }
+
+ /*
+ * Returns the line information for the given line in the given editor
+ */
+ private IRegion getLineInformation(ITextEditor editor, int line) {
+ IDocumentProvider provider = editor.getDocumentProvider();
+ IEditorInput input = editor.getEditorInput();
+ try {
+ provider.connect(input);
+ }
+ catch (CoreException e) {
+ return null;
+ }
+ try {
+ IDocument document = provider.getDocument(input);
+ if (document != null) return document.getLineInformation(line - 1);
+ }
+ catch (BadLocationException e) {
+ }
+ finally {
+ provider.disconnect(input);
+ }
+ return null;
+ }
+
+ /**
+ * Registers the given listener for suspend notifications.
+ * @param listener suspend listener
+ */
+ public synchronized void addSuspendTriggerListener(ISuspendTriggerListener listener) {
+ suspend_trigger_listeners.add(listener);
+ }
+
+ /**
+ * Unregisters the given listener for suspend notifications.
+ * @param listener suspend listener
+ */
+ public synchronized void removeSuspendTriggerListener(ISuspendTriggerListener listener) {
+ suspend_trigger_listeners.remove(listener);
+ }
+
+ /*
+ * Lazily run registered suspend listeners.
+ * @param node - suspended context.
+ */
+ private synchronized void runSuspendTrigger(final TCFNode node) {
+ if (suspend_trigger_listeners.size() == 0) return;
+ final ISuspendTriggerListener[] listeners = suspend_trigger_listeners.toArray(
+ new ISuspendTriggerListener[suspend_trigger_listeners.size()]);
+
+ final int generation = ++suspend_trigger_generation;
+ if (wait_for_pc_update_after_step || wait_for_views_update_after_step) {
+ launch.addPendingClient(suspend_trigger_listeners);
+ }
+ display.asyncExec(new Runnable() {
+ public void run() {
+ synchronized (TCFModel.this) {
+ if (generation != suspend_trigger_generation) return;
+ }
+ for (final ISuspendTriggerListener listener : listeners) {
+ try {
+ listener.suspended(launch, node);
+ }
+ catch (Throwable x) {
+ Activator.log(x);
+ }
+ }
+ synchronized (TCFModel.this) {
+ if (generation != suspend_trigger_generation) return;
+ launch.removePendingClient(suspend_trigger_listeners);
+ }
+ }
+ });
+ }
+
+ /**
+ * Set whether instruction stepping mode should be enabled or not.
+ * @param enabled
+ */
+ public void setInstructionSteppingEnabled(boolean enabled) {
+ instruction_stepping_enabled = enabled;
+ }
+
+ /**
+ * @return whether instruction stepping is enabled
+ */
+ public boolean isInstructionSteppingEnabled() {
+ return instruction_stepping_enabled;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelManager.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelManager.java
new file mode 100644
index 000000000..47c05a3da
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelManager.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchesListener;
+import org.eclipse.tcf.internal.debug.model.TCFLaunch;
+import org.eclipse.tcf.internal.debug.ui.Activator;
+import org.eclipse.tcf.protocol.Protocol;
+
+
+public class TCFModelManager {
+
+ public interface ModelManagerListener {
+ public void onConnected(TCFLaunch launch, TCFModel model);
+ public void onDisconnected(TCFLaunch launch, TCFModel model);
+ }
+
+ private final Map<TCFLaunch,TCFModel> models = new HashMap<TCFLaunch,TCFModel>();
+ private final List<ModelManagerListener> listeners = new ArrayList<ModelManagerListener>();
+
+ private final TCFLaunch.LaunchListener tcf_launch_listener = new TCFLaunch.LaunchListener() {
+
+ public void onCreated(TCFLaunch launch) {
+ assert Protocol.isDispatchThread();
+ assert models.get(launch) == null;
+ TCFModel model = new TCFModel(launch);
+ models.put(launch, model);
+ }
+
+ public void onConnected(TCFLaunch launch) {
+ assert Protocol.isDispatchThread();
+ TCFModel model = models.get(launch);
+ if (model != null) model.onConnected();
+ for (ModelManagerListener l : listeners) {
+ try {
+ l.onConnected(launch, model);
+ }
+ catch (Throwable x) {
+ Activator.log(x);
+ }
+ }
+ }
+
+ public void onDisconnected(TCFLaunch launch) {
+ assert Protocol.isDispatchThread();
+ TCFModel model = models.get(launch);
+ if (model != null) model.onDisconnected();
+ for (ModelManagerListener l : listeners) {
+ try {
+ l.onDisconnected(launch, model);
+ }
+ catch (Throwable x) {
+ Activator.log(x);
+ }
+ }
+ }
+
+ public void onProcessOutput(TCFLaunch launch, String process_id, int stream_id, byte[] data) {
+ assert Protocol.isDispatchThread();
+ TCFModel model = models.get(launch);
+ if (model != null) model.onProcessOutput(process_id, stream_id, data);
+ }
+
+ public void onProcessStreamError(TCFLaunch launch, String process_id,
+ int stream_id, Exception error, int lost_size) {
+ assert Protocol.isDispatchThread();
+ TCFModel model = models.get(launch);
+ if (model != null) model.onProcessStreamError(process_id, stream_id, error, lost_size);
+ }
+ };
+
+ private final ILaunchesListener debug_launch_listener = new ILaunchesListener() {
+
+ public void launchesAdded(final ILaunch[] launches) {
+ }
+
+ public void launchesChanged(final ILaunch[] launches) {
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ for (ILaunch launch : launches) {
+ TCFModel model = models.get(launch);
+ if (model != null) model.launchChanged();
+ }
+ }
+ });
+ }
+
+ public void launchesRemoved(final ILaunch[] launches) {
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ for (ILaunch launch : launches) {
+ TCFModel model = models.remove(launch);
+ if (model != null) model.dispose();
+ }
+ }
+ });
+ }
+ };
+
+ public TCFModelManager() {
+ assert Protocol.isDispatchThread();
+ DebugPlugin.getDefault().getLaunchManager().addLaunchListener(debug_launch_listener);
+ TCFLaunch.addListener(tcf_launch_listener);
+ }
+
+ public void dispose() {
+ assert Protocol.isDispatchThread();
+ DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(debug_launch_listener);
+ TCFLaunch.removeListener(tcf_launch_listener);
+ for (Iterator<TCFModel> i = models.values().iterator(); i.hasNext();) {
+ TCFModel model = i.next();
+ model.dispose();
+ i.remove();
+ }
+ assert models.isEmpty();
+ }
+
+ public void addListener(ModelManagerListener l) {
+ listeners.add(l);
+ }
+
+ public void removeListener(ModelManagerListener l) {
+ listeners.remove(l);
+ }
+
+ public TCFModel getModel(TCFLaunch launch) {
+ assert Protocol.isDispatchThread();
+ return models.get(launch);
+ }
+
+ public TCFNode getRootNode(TCFLaunch launch) {
+ TCFModel model = getModel(launch);
+ if (model == null) return null;
+ return model.getRootNode();
+ }
+
+ public static TCFModelManager getModelManager() {
+ return Activator.getModelManager();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelPresentation.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelPresentation.java
new file mode 100644
index 000000000..2e644e46a
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelPresentation.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigInteger;
+import java.net.URI;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.ILineBreakpoint;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.debug.ui.IDebugModelPresentation;
+import org.eclipse.debug.ui.IValueDetailListener;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tcf.internal.debug.model.TCFBreakpointsModel;
+import org.eclipse.tcf.internal.debug.model.TCFLaunch;
+import org.eclipse.tcf.internal.debug.model.TCFSourceRef;
+import org.eclipse.tcf.internal.debug.ui.Activator;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+import org.eclipse.tcf.protocol.JSON;
+import org.eclipse.tcf.services.IBreakpoints;
+import org.eclipse.tcf.services.ILineNumbers;
+import org.eclipse.tcf.util.TCFDataCache;
+import org.eclipse.tcf.util.TCFTask;
+import org.eclipse.ui.IEditorDescriptor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorRegistry;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.FileStoreEditorInput;
+import org.eclipse.ui.part.FileEditorInput;
+
+
+public class TCFModelPresentation implements IDebugModelPresentation {
+
+ public final static String DISPLAY_FULL_PATHS = "DISPLAY_FULL_PATHS"; //$NON-NLS-1$
+
+ private static final String[] attr_names = {
+ TCFBreakpointsModel.ATTR_LINE, "line",
+ TCFBreakpointsModel.ATTR_ADDRESS, "address",
+ TCFBreakpointsModel.ATTR_FUNCTION, "location",
+ TCFBreakpointsModel.ATTR_EXPRESSION, "expression",
+ TCFBreakpointsModel.ATTR_CONDITION, "condition",
+ TCFBreakpointsModel.ATTR_CONTEXTNAMES, "scope (names)",
+ TCFBreakpointsModel.ATTR_CONTEXTIDS, "scope (IDs)",
+ TCFBreakpointsModel.ATTR_EXE_PATHS, "scope (modules)",
+ TCFBreakpointsModel.ATTR_STOP_GROUP, "stop group",
+ };
+
+ private final Collection<ILabelProviderListener> listeners = new HashSet<ILabelProviderListener>();
+ private HashMap<String,Object> attrs = new HashMap<String,Object>();
+
+ private static final TCFModelPresentation default_instance = new TCFModelPresentation();
+
+ public static TCFModelPresentation getDefault() {
+ return default_instance;
+ }
+
+ public void addListener(ILabelProviderListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeListener(ILabelProviderListener listener) {
+ listeners.remove(listener);
+ }
+
+ public void dispose() {
+ }
+
+ public void computeDetail(IValue value, IValueDetailListener listener) {
+ }
+
+ public Image getImage(Object element) {
+ ImageDescriptor descriptor = null;
+ if (element instanceof IBreakpoint) {
+ final IBreakpoint breakpoint = (IBreakpoint)element;
+ descriptor = ImageCache.getImageDescriptor(ImageCache.IMG_BREAKPOINT_DISABLED);
+ try {
+ if (breakpoint.isEnabled()) {
+ descriptor = new TCFTask<ImageDescriptor>() {
+ public void run() {
+ boolean installed = false;
+ boolean warning = false;
+ boolean error = false;
+ boolean moved = false;
+ ImageDescriptor d = ImageCache.getImageDescriptor(ImageCache.IMG_BREAKPOINT_ENABLED);
+ Map<TCFLaunch,Map<String,Object>> status = Activator.getAnnotationManager().getBreakpointStatus(breakpoint);
+ for (TCFLaunch launch : status.keySet()) {
+ Map<String,Object> map = status.get(launch);
+ if (map == null) continue;
+ if ((String)map.get(IBreakpoints.STATUS_ERROR) != null) error = true;
+ Object planted = map.get(IBreakpoints.STATUS_INSTANCES);
+ if (planted == null) continue;
+ @SuppressWarnings("unchecked")
+ Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)planted;
+ for (Map<String,Object> m : list) {
+ if (m.get(IBreakpoints.INSTANCE_ERROR) == null) {
+ installed = true;
+ if (!moved) {
+ TCFNodeExecContext ctx = null;
+ String ctx_id = (String)m.get(IBreakpoints.INSTANCE_CONTEXT);
+ BigInteger addr = JSON.toBigInteger((Number)m.get(IBreakpoints.INSTANCE_ADDRESS));
+ int line = breakpoint.getMarker().getAttribute(TCFBreakpointsModel.ATTR_LINE, 0);
+ if (ctx_id != null && addr != null && line > 0) {
+ TCFModel model = TCFModelManager.getModelManager().getModel(launch);
+ if (model != null) {
+ if (!model.createNode(ctx_id, this)) return;
+ TCFDataCache<TCFNodeExecContext> mem = model.searchMemoryContext(model.getNode(ctx_id));
+ if (mem != null) {
+ if (!mem.validate(this)) return;
+ ctx = mem.getData();
+ }
+ }
+ }
+ if (ctx != null) {
+ ILineNumbers.CodeArea area = null;
+ TCFDataCache<TCFSourceRef> line_cache = ctx.getLineInfo(addr);
+ if (line_cache != null) {
+ if (!line_cache.validate(this)) return;
+ TCFSourceRef line_data = line_cache.getData();
+ if (line_data != null && line_data.area != null) area = line_data.area;
+ }
+ if (area != null && area.start_line != line) moved = true;
+ }
+ }
+ }
+ else {
+ warning = true;
+ }
+ }
+ }
+ if (moved) d = ImageCache.addOverlay(d, ImageCache.IMG_BREAKPOINT_MOVED, 0, 0);
+ else if (installed) d = ImageCache.addOverlay(d, ImageCache.IMG_BREAKPOINT_INSTALLED, 0, 8);
+ if (warning) d = ImageCache.addOverlay(d, ImageCache.IMG_BREAKPOINT_WARNING, 9, 8);
+ if (error) d = ImageCache.addOverlay(d, ImageCache.IMG_BREAKPOINT_ERROR, 9, 8);
+ done(d);
+ }
+ }.getE();
+ }
+ String cond = breakpoint.getMarker().getAttribute(TCFBreakpointsModel.ATTR_CONDITION, null);
+ if (cond != null && cond.length() > 0) {
+ descriptor = ImageCache.addOverlay(descriptor, ImageCache.IMG_BREAKPOINT_CONDITIONAL);
+ }
+ }
+ catch (Throwable x) {
+ }
+ }
+ if (descriptor != null) return ImageCache.getImage(descriptor);
+ return null;
+ }
+
+ public String getText(Object element) {
+ String text = null;
+ if (element instanceof IBreakpoint) {
+ final IBreakpoint breakpoint = (IBreakpoint)element;
+ IMarker marker = breakpoint.getMarker();
+ if (marker == null) return null;
+ StringBuffer bf = new StringBuffer();
+ try {
+ Map<String,Object> m = marker.getAttributes();
+ String file = marker.getAttribute(TCFBreakpointsModel.ATTR_FILE, null);
+ if (file != null && file.length() > 0) {
+ IPath path = new Path(file);
+ if (path.isValidPath(file)) {
+ bf.append(isShowQualifiedNames() ? path.toOSString() : path.lastSegment());
+ bf.append(' ');
+ }
+ }
+ for (int i = 0; i < attr_names.length; i += 2) {
+ Object obj = m.get(attr_names[i]);
+ if (obj == null) continue;
+ String s = obj.toString();
+ if (s.length() == 0) continue;
+ bf.append('[');
+ bf.append(attr_names[i + 1]);
+ bf.append(": ");
+ bf.append(s);
+ bf.append(']');
+ }
+ if (bf.length() == 0) {
+ String id = marker.getAttribute(TCFBreakpointsModel.ATTR_ID, null);
+ if (id == null) id = Long.toString(marker.getId());
+ bf.append(id);
+ }
+ }
+ catch (Throwable x) {
+ return x.toString();
+ }
+ text = bf.toString();
+ String status = new TCFTask<String>() {
+ public void run() {
+ done(Activator.getAnnotationManager().getBreakpointStatusText(breakpoint));
+ }
+ }.getE();
+ if (status != null) text += " (" + status + ")";
+ }
+ return text;
+ }
+
+ protected boolean isShowQualifiedNames() {
+ Boolean show_qualified = (Boolean)attrs.get( DISPLAY_FULL_PATHS );
+ if (show_qualified == null) return false;
+ return show_qualified.booleanValue();
+ }
+
+ public void setAttribute(String attribute, Object value) {
+ if (value == null) attrs.remove(attribute);
+ else attrs.put(attribute, value);
+ }
+
+ public boolean isLabelProperty(Object element, String property) {
+ return true;
+ }
+
+ public String getEditorId(IEditorInput input, Object element) {
+ String id = null;
+ if (input != null) {
+ IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry();
+ IEditorDescriptor descriptor = registry.getDefaultEditor(input.getName());
+ if (descriptor != null) id = descriptor.getId();
+ }
+ return id;
+ }
+
+ public IEditorInput getEditorInput(Object element) {
+ if (element instanceof ILineBreakpoint) {
+ element = ((ILineBreakpoint)element).getMarker();
+ }
+ if (element instanceof IMarker) {
+ element = ((IMarker)element).getResource();
+ }
+ if (element instanceof IFile) {
+ return new FileEditorInput((IFile)element);
+ }
+ if (element instanceof IStorage) {
+ IPath fullPath = ((IStorage)element).getFullPath();
+ URI uri = URIUtil.toURI(fullPath);
+ if (uri != null) {
+ try {
+ return new FileStoreEditorInput(EFS.getStore(uri));
+ }
+ catch (CoreException e) {
+ Activator.log(e);
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java
new file mode 100644
index 000000000..84972913d
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java
@@ -0,0 +1,510 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.InternalVirtualTreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
+import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tcf.internal.debug.model.TCFLaunch;
+import org.eclipse.tcf.protocol.Protocol;
+
+/**
+ * A model proxy represents a model for a specific presentation context and
+ * fires deltas to notify listeners of changes in the model.
+ * Model proxy listeners are debuggers views.
+ */
+public class TCFModelProxy extends AbstractModelProxy implements IModelProxy, Runnable {
+
+ private static final TCFNode[] EMPTY_NODE_ARRAY = new TCFNode[0];
+
+ private final TCFModel model;
+ private final TCFLaunch launch;
+ private final Display display;
+ private final Map<TCFNode,Integer> node2flags = new HashMap<TCFNode,Integer>();
+ private final Map<TCFNode,TCFNode[]> node2children = new HashMap<TCFNode,TCFNode[]>();
+ private final Map<TCFNode,ModelDelta> node2delta = new HashMap<TCFNode,ModelDelta>();
+ private final Set<ModelDelta> content_deltas = new HashSet<ModelDelta>();
+ private final LinkedList<TCFNode> selection = new LinkedList<TCFNode>();
+
+ private boolean posted;
+ private boolean installed;
+ private boolean disposed;
+ private boolean realized;
+ private long last_update_time;
+
+ private final Runnable timer = new Runnable() {
+
+ public void run() {
+ posted = false;
+ long idle_time = System.currentTimeMillis() - last_update_time;
+ long min_idle_time = model.getMinViewUpdatesInterval();
+ if (model.getViewUpdatesThrottleEnabled()) {
+ int congestion = Protocol.getCongestionLevel() + 50;
+ if (congestion > 0) min_idle_time += congestion * 10;
+ }
+ if (model.getChannelThrottleEnabled()) {
+ int congestion = model.getChannel().getCongestion() + 50;
+ if (congestion > 0) min_idle_time += congestion * 10;
+ }
+ if (idle_time < min_idle_time - 5) {
+ Protocol.invokeLater(min_idle_time - idle_time, this);
+ posted = true;
+ }
+ else {
+ TCFModelProxy.this.run();
+ }
+ }
+ };
+
+ private class ViewerUpdate implements IViewerUpdate {
+
+ IStatus status;
+
+ public Object getElement() {
+ return null;
+ }
+
+ public TreePath getElementPath() {
+ return null;
+ }
+
+ public IPresentationContext getPresentationContext() {
+ return TCFModelProxy.this.getPresentationContext();
+ }
+
+ public Object getViewerInput() {
+ return TCFModelProxy.this.getInput();
+ }
+
+ public void cancel() {
+ }
+
+ public void done() {
+ }
+
+ public IStatus getStatus() {
+ return status;
+ }
+
+ public boolean isCanceled() {
+ return false;
+ }
+
+ public void setStatus(IStatus status) {
+ this.status = status;
+ }
+ }
+
+ private class ChildrenCountUpdate extends ViewerUpdate implements IChildrenCountUpdate {
+
+ int count;
+
+ public void setChildCount(int count) {
+ this.count = count;
+ }
+ }
+
+ private class ChildrenUpdate extends ViewerUpdate implements IChildrenUpdate {
+
+ int length;
+ TCFNode[] children;
+
+ void setLength(int length) {
+ this.length = length;
+ this.children = length == 0 ? EMPTY_NODE_ARRAY : new TCFNode[length];
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public int getOffset() {
+ return 0;
+ }
+
+ public void setChild(Object child, int offset) {
+ children[offset] = (TCFNode)child;
+ }
+ }
+
+ private final IViewerUpdateListener update_listener = new IViewerUpdateListener() {
+
+ public void viewerUpdatesBegin() {
+ if (!model.getWaitForViewsUpdateAfterStep()) return;
+ launch.addPendingClient(this);
+ }
+
+ public void viewerUpdatesComplete() {
+ launch.removePendingClient(this);
+ }
+
+ public void updateStarted(IViewerUpdate update) {
+ }
+
+ public void updateComplete(IViewerUpdate update) {
+ }
+ };
+
+ private final ChildrenCountUpdate children_count_update = new ChildrenCountUpdate();
+ private final ChildrenUpdate children_update = new ChildrenUpdate();
+
+ private TCFNode pending_node;
+
+ TCFModelProxy(TCFModel model) {
+ this.model = model;
+ launch = model.getLaunch();
+ display = model.getDisplay();
+ }
+
+ public void installed(Viewer viewer) {
+ if (isDisposed()) return;
+ super.installed(viewer);
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ assert !installed;
+ assert !disposed;
+ ((ITreeModelViewer)getViewer()).addViewerUpdateListener(update_listener);
+ model.onProxyInstalled(TCFModelProxy.this);
+ installed = true;
+ }
+ });
+ }
+
+ public void dispose() {
+ if (isDisposed()) return;
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ assert !disposed;
+ if (installed) {
+ model.onProxyDisposed(TCFModelProxy.this);
+ ((ITreeModelViewer)getViewer()).removeViewerUpdateListener(update_listener);
+ launch.removePendingClient(update_listener);
+ launch.removePendingClient(TCFModelProxy.this);
+ }
+ disposed = true;
+ }
+ });
+ super.dispose();
+ }
+
+ /**
+ * Add model change information (delta) to a buffer of pending deltas.
+ * Implementation will coalesce and post deltas to the view.
+ * @param node - a model node that changed.
+ * @param flags - flags that describe the change, see IModelDelta
+ */
+ void addDelta(TCFNode node, int flags) {
+ assert Protocol.isDispatchThread();
+ assert installed && !disposed;
+ if (flags == 0) return;
+ Integer delta = node2flags.get(node);
+ if (delta != null) flags |= delta.intValue();
+ node2flags.put(node, flags);
+ post();
+ }
+
+ /**
+ * Request node to be expanded in the view.
+ * @param node - a model node that will become expanded.
+ */
+ void expand(TCFNode node) {
+ Object input = getInput();
+ IPresentationContext ctx = getPresentationContext();
+ while (node != null && node != input) {
+ addDelta(node, IModelDelta.EXPAND);
+ node = node.getParent(ctx);
+ }
+ post();
+ }
+
+ /**
+ * Request view selection to be set to given node.
+ * @param node - a model node that will become new selection.
+ */
+ void setSelection(TCFNode node) {
+ if (selection.size() > 0 && selection.getLast() == node) return;
+ selection.add(node);
+ expand(node.getParent(getPresentationContext()));
+ }
+
+ /**
+ * Get current value of the view input.
+ * @return view input object.
+ */
+ Object getInput() {
+ return getViewer().getInput();
+ }
+
+ public void post() {
+ assert Protocol.isDispatchThread();
+ assert installed && !disposed;
+ if (!posted) {
+ long idle_time = System.currentTimeMillis() - last_update_time;
+ Protocol.invokeLater(model.getMinViewUpdatesInterval() - idle_time, timer);
+ if (model.getWaitForViewsUpdateAfterStep()) launch.addPendingClient(this);
+ posted = true;
+ }
+ }
+
+ private TCFNode[] getNodeChildren(TCFNode node) {
+ TCFNode[] res = node2children.get(node);
+ if (res == null) {
+ if (node.isDisposed()) {
+ res = EMPTY_NODE_ARRAY;
+ }
+ else if (!node.getLockedData(children_count_update, null)) {
+ pending_node = node;
+ res = EMPTY_NODE_ARRAY;
+ }
+ else {
+ children_update.setLength(children_count_update.count);
+ if (!node.getLockedData(children_update, null)) {
+ assert false;
+ pending_node = node;
+ res = EMPTY_NODE_ARRAY;
+ }
+ else {
+ res = children_update.children;
+ }
+ }
+ node2children.put(node, res);
+ }
+ return res;
+ }
+
+ private int getNodeIndex(TCFNode node) {
+ TCFNode p = node.getParent(getPresentationContext());
+ if (p == null) return -1;
+ TCFNode[] arr = getNodeChildren(p);
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == node) return i;
+ }
+ return -1;
+ }
+
+ private ModelDelta makeDelta(ModelDelta root, TCFNode node, TCFNode selection) {
+ ModelDelta delta = node2delta.get(node);
+ if (delta == null) {
+ if (node == root.getElement()) {
+ delta = root;
+ }
+ else {
+ int flags = 0;
+ Integer flags_obj = node2flags.get(node);
+ if (flags_obj != null) flags = flags_obj.intValue();
+ if ((flags & IModelDelta.REMOVED) != 0 && (flags & (IModelDelta.INSERTED|IModelDelta.ADDED)) != 0) return null;
+ if (node == selection) {
+ // Bug in Eclipse 3.6.1: SELECT delta has no effect without STATE
+ flags |= IModelDelta.SELECT | IModelDelta.STATE;
+ if (this.selection.size() <= 1) flags |= IModelDelta.REVEAL;
+ }
+ if (node.parent == null) {
+ // The node is TCF launch node
+ if (root.getElement() instanceof TCFNode) return null;
+ int children = -1;
+ if (selection != null && selection != node || (flags & IModelDelta.EXPAND) != 0) {
+ children = getNodeChildren(node).length;
+ }
+ delta = root.addNode(launch, -1, flags, children);
+ }
+ else {
+ TCFNode parent = node.getParent(getPresentationContext());
+ if (parent == null) return null;
+ ModelDelta up = makeDelta(root, parent, selection);
+ if (up == null) return null;
+ boolean content = content_deltas.contains(up);
+ if (content) {
+ assert selection == null;
+ flags &= ~(IModelDelta.ADDED | IModelDelta.REMOVED |
+ IModelDelta.REPLACED | IModelDelta.INSERTED |
+ IModelDelta.CONTENT | IModelDelta.STATE);
+ if (flags == 0) return null;
+ }
+ int index = -1;
+ int children = -1;
+ if (selection != null || (flags & IModelDelta.INSERTED) != 0 || (flags & IModelDelta.EXPAND) != 0) {
+ index = getNodeIndex(node);
+ }
+ if (selection != null && selection != node || (flags & IModelDelta.EXPAND) != 0) {
+ children = getNodeChildren(node).length;
+ }
+ delta = up.addNode(node, index, flags, children);
+ if (content) content_deltas.add(delta);
+ }
+ node2delta.put(node, delta);
+ }
+ }
+ int flags = delta.getFlags();
+ if ((flags & IModelDelta.REMOVED) != 0) return null;
+ //if ((flags & IModelDelta.CONTENT) != 0 && (flags & IModelDelta.EXPAND) == 0) return null;
+ return delta;
+ }
+
+ private void asyncExec(Runnable r) {
+ synchronized (Device.class) {
+ if (!display.isDisposed()) {
+ display.asyncExec(r);
+ }
+ }
+ }
+
+ private final Comparator<IModelDelta> delta_comparator = new Comparator<IModelDelta>() {
+ public int compare(IModelDelta o1, IModelDelta o2) {
+ int f1 = o1.getFlags();
+ int f2 = o2.getFlags();
+ if ((f1 & IModelDelta.REMOVED) != 0 && (f2 & IModelDelta.REMOVED) == 0) return -1;
+ if ((f1 & IModelDelta.REMOVED) == 0 && (f2 & IModelDelta.REMOVED) != 0) return +1;
+ if ((f1 & IModelDelta.ADDED) != 0 && (f2 & IModelDelta.ADDED) == 0) return -1;
+ if ((f1 & IModelDelta.ADDED) == 0 && (f2 & IModelDelta.ADDED) != 0) return +1;
+ if ((f1 & IModelDelta.INSERTED) != 0 && (f2 & IModelDelta.INSERTED) == 0) return -1;
+ if ((f1 & IModelDelta.INSERTED) == 0 && (f2 & IModelDelta.INSERTED) != 0) return +1;
+ int i1 = o1.getIndex();
+ int i2 = o2.getIndex();
+ if (i1 < i2) return -1;
+ if (i1 > i2) return +1;
+ return 0;
+ }
+ };
+
+ private void sortDeltaChildren(IModelDelta delta) {
+ IModelDelta arr[] = delta.getChildDeltas();
+ Arrays.sort(arr, delta_comparator);
+ for (IModelDelta d : arr) sortDeltaChildren(d);
+ }
+
+ private void postDelta(final ModelDelta root) {
+ assert pending_node == null;
+ if (root.getFlags() != 0 || root.getChildDeltas().length > 0) {
+ last_update_time = System.currentTimeMillis();
+ asyncExec(new Runnable() {
+ public void run() {
+ sortDeltaChildren(root);
+ fireModelChanged(root);
+ }
+ });
+ }
+ }
+
+ public void run() {
+ assert Protocol.isDispatchThread();
+ if (disposed) return;
+ if (node2flags.isEmpty() && selection.isEmpty()) return;
+ if (!realized) {
+ if (getPresentationContext().getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) {
+ // Wait until launch manager done creating our launch item in the Debug view.
+ // Deltas do NOT work without the launch item.
+ asyncExec(new Runnable() {
+ boolean found;
+ public void run() {
+ if (getViewer() instanceof InternalTreeModelViewer) {
+ found = ((InternalTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0;
+ } else if (getViewer() instanceof InternalVirtualTreeModelViewer) {
+ found = ((InternalVirtualTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0;
+ }
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ if (disposed) return;
+ if (found) realized = true;
+ else last_update_time = System.currentTimeMillis() + 20;
+ post();
+ }
+ });
+ }
+ });
+ return;
+ }
+ else {
+ realized = true;
+ }
+ }
+ Object input = getInput();
+ int flags = 0;
+ if (input instanceof TCFNode) {
+ // Optimize away STATE delta on a view input node
+ TCFNode node = (TCFNode)input;
+ Integer i = node2flags.get(node);
+ if (i != null) {
+ flags = i;
+ if ((flags & IModelDelta.STATE) != 0) {
+ flags &= ~IModelDelta.STATE;
+ if (flags == 0) {
+ node2flags.remove(node);
+ if (node2flags.isEmpty() && selection.isEmpty()) return;
+ }
+ else {
+ node2flags.put(node, flags);
+ }
+ }
+ }
+ }
+ pending_node = null;
+ node2children.clear();
+ if (flags != 0 || node2flags.size() > 0) {
+ node2delta.clear();
+ content_deltas.clear();
+ ModelDelta root = new ModelDelta(input, flags);
+ if ((flags & IModelDelta.CONTENT) != 0) content_deltas.add(root);
+ for (TCFNode node : node2flags.keySet()) makeDelta(root, node, null);
+ if (pending_node == null) {
+ node2flags.clear();
+ postDelta(root);
+ }
+ }
+ node2delta.clear();
+ content_deltas.clear();
+ if (pending_node == null) {
+ while (!selection.isEmpty()) {
+ TCFNode node = selection.getFirst();
+ if (!node.isDisposed()) {
+ ModelDelta root = new ModelDelta(input, IModelDelta.NO_CHANGE);
+ makeDelta(root, node, node);
+ node2delta.clear();
+ content_deltas.clear();
+ if (pending_node != null) break;
+ postDelta(root);
+ }
+ selection.remove(node);
+ }
+ }
+
+ if (pending_node == null) {
+ launch.removePendingClient(this);
+ }
+ else if (pending_node.getLockedData(children_count_update, this)) {
+ assert false;
+ Protocol.invokeLater(this);
+ }
+ node2children.clear();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelSelectionPolicy.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelSelectionPolicy.java
new file mode 100644
index 000000000..9f5242bb5
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelSelectionPolicy.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.tcf.internal.debug.model.TCFContextState;
+import org.eclipse.tcf.services.IRunControl;
+import org.eclipse.tcf.util.TCFDataCache;
+import org.eclipse.tcf.util.TCFTask;
+
+class TCFModelSelectionPolicy implements IModelSelectionPolicy {
+
+ private final TCFModel model;
+
+ TCFModelSelectionPolicy(TCFModel model) {
+ this.model = model;
+ }
+
+ public boolean contains(ISelection selection, IPresentationContext context) {
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection)selection;
+ Object e = ss.getFirstElement();
+ if (e instanceof TCFNode) {
+ TCFNode n = (TCFNode)e;
+ return !n.isDisposed() && n.model == model;
+ }
+ }
+ return false;
+ }
+
+ public boolean isSticky(ISelection selection, IPresentationContext context) {
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection)selection;
+ Object e = ss.getFirstElement();
+ if (e instanceof TCFNode) return getSuspendReason((TCFNode)e) != null;
+ }
+ return false;
+ }
+
+ private String getSuspendReason(final TCFNode node) {
+ return new TCFTask<String>() {
+ public void run() {
+ TCFNode n = node;
+ while (n != null && !n.isDisposed()) {
+ if (n instanceof TCFNodeExecContext) {
+ TCFDataCache<TCFContextState> cache = ((TCFNodeExecContext)n).getState();
+ if (!cache.validate(this)) return;
+ TCFContextState state = cache.getData();
+ if (state != null && state.is_suspended) {
+ if (state.suspend_reason == null) {
+ done(IRunControl.REASON_USER_REQUEST);
+ }
+ else {
+ done(state.suspend_reason);
+ }
+ return;
+ }
+ }
+ n = n.parent;
+ }
+ done(null);
+ }
+ }.getE();
+ }
+
+ public boolean overrides(ISelection existing, ISelection candidate, IPresentationContext context) {
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId())) {
+ if (existing instanceof IStructuredSelection && candidate instanceof IStructuredSelection) {
+ Object el_existing = ((IStructuredSelection)existing).getFirstElement();
+ Object el_candidate = ((IStructuredSelection)candidate).getFirstElement();
+ if (el_existing == null) return true;
+ if (el_existing == el_candidate) return true;
+ if (el_existing instanceof TCFNode && el_candidate instanceof TCFNode) {
+ if (el_existing instanceof TCFNodeStackFrame && el_candidate instanceof TCFNodeStackFrame) {
+ TCFNodeStackFrame curr = (TCFNodeStackFrame)el_existing;
+ TCFNodeStackFrame next = (TCFNodeStackFrame)el_candidate;
+ if (curr.parent == next.parent) return true;
+ }
+ if (el_existing instanceof TCFNodeStackFrame && el_candidate instanceof TCFNodeExecContext) {
+ TCFNodeStackFrame curr = (TCFNodeStackFrame)el_existing;
+ TCFNodeExecContext next = (TCFNodeExecContext)el_candidate;
+ if (curr.parent == next) return true;
+ }
+ String s1 = getSuspendReason((TCFNode)el_existing);
+ if (s1 == null) return true;
+ String s2 = getSuspendReason((TCFNode)el_candidate);
+ if (s2 == null) return false;
+ if (s2.equals(IRunControl.REASON_USER_REQUEST)) return false;
+ if (s2.equals(IRunControl.REASON_CONTAINER)) return false;
+ if (s1.equals(IRunControl.REASON_USER_REQUEST)) return true;
+ if (s1.equals(IRunControl.REASON_CONTAINER)) return true;
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public ISelection replaceInvalidSelection(ISelection existing, ISelection candidate) {
+ if (existing instanceof IStructuredSelection && candidate instanceof IStructuredSelection) {
+ Object el_existing = ((IStructuredSelection)existing).getFirstElement();
+ Object el_candidate = ((IStructuredSelection)candidate).getFirstElement();
+ if (el_candidate == null) {
+ if (el_existing == null) return new StructuredSelection(model.getLaunch());
+ if (el_existing instanceof TCFNode) {
+ TCFNode node = (TCFNode)el_existing;
+ if (node.parent == null || node.parent instanceof TCFNodeLaunch) {
+ return new StructuredSelection(model.getLaunch());
+ }
+ return new StructuredSelection(node.parent);
+ }
+ }
+ }
+ return candidate;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNode.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNode.java
new file mode 100644
index 000000000..ff5cf76e4
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNode.java
@@ -0,0 +1,471 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.LinkedList;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate;
+import org.eclipse.tcf.debug.ui.ITCFObject;
+import org.eclipse.tcf.internal.debug.model.TCFLaunch;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.util.TCFDataCache;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * TCFNode is base class for all TCF debug model elements.
+ */
+public abstract class TCFNode extends PlatformObject implements ITCFObject, Comparable<TCFNode> {
+
+ protected final String id;
+ protected final TCFNode parent;
+ protected final TCFModel model;
+ protected final TCFLaunch launch;
+ protected final IChannel channel;
+
+ private boolean disposed;
+
+ /**
+ * An extension of TCFDataCache class that is automatically disposed when the parent node is disposed.
+ */
+ protected abstract class TCFData<V> extends TCFDataCache<V> {
+
+ TCFData(IChannel channel) {
+ super(channel);
+ addDataCache(this);
+ }
+
+ @Override
+ public void dispose() {
+ removeDataCache(this);
+ super.dispose();
+ }
+ }
+
+ private LinkedList<TCFDataCache<?>> caches;
+
+ /**
+ * Constructor for a root node. There should be exactly one root in the model.
+ * @param model
+ */
+ protected TCFNode(TCFModel model) {
+ id = null;
+ parent = null;
+ launch = model.getLaunch();
+ channel = model.getChannel();
+ this.model = model;
+ }
+
+ /**
+ * Constructor for a node other then root. Node ID must be unique.
+ * @param parent - parent node.
+ * @param id - node ID.
+ */
+ protected TCFNode(TCFNode parent, String id) {
+ assert Protocol.isDispatchThread();
+ assert parent != null;
+ assert id != null;
+ assert !parent.disposed;
+ this.parent = parent;
+ this.id = id;
+ model = parent.model;
+ model.addNode(id, this);
+ launch = model.getLaunch();
+ channel = model.getChannel();
+ }
+
+ /**
+ * Register a data cache object that caches data for this node.
+ * @param c - a TCFData object.
+ */
+ final void addDataCache(TCFDataCache<?> c) {
+ if (caches == null) caches = new LinkedList<TCFDataCache<?>>();
+ caches.add(c);
+ }
+
+ /**
+ * Unregister a data cache object that caches children for this node.
+ * @param c - a TCFData object.
+ */
+ final void removeDataCache(TCFDataCache<?> c) {
+ if (caches != null) caches.remove(c);
+ }
+
+ /**
+ * Flush (reset) all node data caches.
+ */
+ void flushAllCaches() {
+ if (caches == null) return;
+ for (TCFDataCache<?> c : caches) c.reset();
+ }
+
+ /**
+ * Dispose this node and its children. The node is removed from the model.
+ */
+ void dispose() {
+ assert !disposed;
+ while (caches != null && caches.size() > 0) {
+ caches.getLast().dispose();
+ }
+ if (parent != null && parent.caches != null) {
+ for (TCFDataCache<?> c : parent.caches) {
+ if (c instanceof TCFChildren) ((TCFChildren)c).onNodeDisposed(id);
+ }
+ }
+ if (id != null) {
+ assert model.getNode(id) == this;
+ model.removeNode(id);
+ }
+ disposed = true;
+ }
+
+ /**
+ * Check if node is disposed.
+ * @return true if disposed.
+ */
+ public final boolean isDisposed() {
+ return disposed;
+ }
+
+ /**
+ * Get TCFModel that owns this node.
+ * @return TCFModel object
+ */
+ public TCFModel getModel() {
+ return model;
+ }
+
+ /**
+ * Get IChannel of TCFModel that owns this node.
+ * @return IChannel object
+ */
+ public IChannel getChannel() {
+ return channel;
+ }
+
+ /**
+ * Get TCF ID of the node.
+ * @return TCF ID
+ */
+ public String getID() {
+ return id;
+ }
+
+ /**
+ * Returns an object which is an instance of the given class
+ * associated with this object. Returns <code>null</code> if
+ * no such object can be found.
+ *
+ * @param adapter the class to adapt to
+ * @return the adapted object or <code>null</code>
+ * @see IAdaptable#getAdapter(Class)
+ */
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Object getAdapter(final Class adapter) {
+ if (adapter.isInstance(this)) return this;
+ if (adapter.isInstance(model)) return model;
+ Object o = model.getAdapter(adapter, TCFNode.this);
+ if (o != null) return o;
+ return Platform.getAdapterManager().loadAdapter(this, adapter.getName());
+ }
+
+ /**
+ * Get parent node.
+ * @return parent node or null if the node is a root
+ */
+ public final TCFNode getParent() {
+ return parent;
+ }
+
+ /**
+ * Get parent node in given presentation context.
+ * @param ctx - presentation context.
+ * @return parent node or null if the node is a root
+ */
+ public TCFNode getParent(IPresentationContext ctx) {
+ assert Protocol.isDispatchThread();
+ return parent;
+ }
+
+ /**
+ * Retrieve children count for a presentation context.
+ * @param update - children count update request.
+ */
+ final void update(final IChildrenCountUpdate update) {
+ new TCFRunnable(update) {
+ public void run() {
+ if (!done) {
+ if (!update.isCanceled()) {
+ if (!disposed && channel.getState() == IChannel.STATE_OPEN) {
+ if (!getData(update, this)) return;
+ }
+ else {
+ update.setChildCount(0);
+ }
+ update.setStatus(Status.OK_STATUS);
+ }
+ done();
+ }
+ }
+ };
+ }
+
+ /**
+ * Retrieve children for a presentation context.
+ * @param update - children update request.
+ */
+ final void update(final IChildrenUpdate update) {
+ new TCFRunnable(update) {
+ public void run() {
+ if (!done) {
+ if (!update.isCanceled()) {
+ if (!disposed && channel.getState() == IChannel.STATE_OPEN) {
+ if (!getLockedData(update, this)) return;
+ }
+ update.setStatus(Status.OK_STATUS);
+ }
+ done();
+ }
+ }
+ };
+ }
+
+ /**
+ * Check if the node has children in a presentation context.
+ * @param update - "has children" update request.
+ */
+ final void update(final IHasChildrenUpdate update) {
+ new TCFRunnable(update) {
+ public void run() {
+ if (!done) {
+ if (!update.isCanceled()) {
+ if (!disposed && channel.getState() == IChannel.STATE_OPEN) {
+ if (!getLockedData(update, this)) return;
+ }
+ else {
+ update.setHasChilren(false);
+ }
+ update.setStatus(Status.OK_STATUS);
+ }
+ done();
+ }
+ }
+ };
+ }
+
+ /**
+ * Retrieve node label for a presentation context.
+ * @param update - label update request.
+ */
+ final void update(final ILabelUpdate update) {
+ new TCFRunnable(update) {
+ public void run() {
+ if (!done) {
+ if (!update.isCanceled()) {
+ if (!disposed && channel.getState() == IChannel.STATE_OPEN) {
+ if (!getLockedData(update, this)) return;
+ }
+ else {
+ update.setLabel("...", 0);
+ }
+ update.setStatus(Status.OK_STATUS);
+ }
+ done();
+ }
+ }
+ };
+ }
+
+ /**
+ * Retrieve viewer input object for a presentation context.
+ * Allows a view to translate the active debug context into an appropriate viewer input element.
+ * @param update - input update request.
+ */
+ final void update(final IViewerInputUpdate update) {
+ new TCFRunnable(update) {
+ public void run() {
+ if (!done) {
+ if (!update.isCanceled()) {
+ if (!disposed && channel.getState() == IChannel.STATE_OPEN) {
+ if (!getData(update, this)) return;
+ }
+ else {
+ update.setInputElement(TCFNode.this);
+ }
+ update.setStatus(Status.OK_STATUS);
+ }
+ done();
+ }
+ }
+ };
+ }
+
+ /**
+ * Retrieve children count for a presentation context.
+ * If the context is locked, return snapshot data.
+ * Otherwise return live data from the target.
+ * The method is always called on TCF dispatch thread.
+ * @param update - children count update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ final boolean getLockedData(IChildrenCountUpdate update, Runnable done) {
+ TCFSnapshot snapshot = model.getSnapshot(update.getPresentationContext());
+ if (snapshot != null) return snapshot.getData(update, this, done);
+ return getData(update, done);
+ }
+
+ /**
+ * Retrieve children for a presentation context.
+ * If the context is locked, return snapshot data.
+ * Otherwise return live data from the target.
+ * The method is always called on TCF dispatch thread.
+ * @param update - children update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ final boolean getLockedData(IChildrenUpdate update, Runnable done) {
+ TCFSnapshot snapshot = model.getSnapshot(update.getPresentationContext());
+ if (snapshot != null) return snapshot.getData(update, this, done);
+ return getData(update, done);
+ }
+
+ /**
+ * Check if the node has children in a presentation context.
+ * If the context is locked, return snapshot data.
+ * Otherwise return live data from the target.
+ * The method is always called on TCF dispatch thread.
+ * @param update - "has children" update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ final boolean getLockedData(IHasChildrenUpdate update, Runnable done) {
+ TCFSnapshot snapshot = model.getSnapshot(update.getPresentationContext());
+ if (snapshot != null) return snapshot.getData(update, this, done);
+ return getData(update, done);
+ }
+
+ /**
+ * Retrieve node label for a presentation context.
+ * If the context is locked, return snapshot data.
+ * Otherwise return live data from the target.
+ * The method is always called on TCF dispatch thread.
+ * @param update - label update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ final boolean getLockedData(ILabelUpdate update, Runnable done) {
+ TCFSnapshot snapshot = model.getSnapshot(update.getPresentationContext());
+ if (snapshot != null) return snapshot.getData(update, this, done);
+ return getData(update, done);
+ }
+
+ /**
+ * Retrieve children count for a presentation context.
+ * The method is always called on TCF dispatch thread.
+ * @param update - children count update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ protected boolean getData(IChildrenCountUpdate update, Runnable done) {
+ update.setChildCount(0);
+ return true;
+ }
+
+ /**
+ * Retrieve children for a presentation context.
+ * The method is always called on TCF dispatch thread.
+ * @param update - children update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ protected boolean getData(IChildrenUpdate update, Runnable done) {
+ return true;
+ }
+
+ /**
+ * Check if the node has children in a presentation context.
+ * The method is always called on TCF dispatch thread.
+ * @param update - "has children" update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ protected boolean getData(IHasChildrenUpdate update, Runnable done) {
+ update.setHasChilren(false);
+ return true;
+ }
+
+ /**
+ * Retrieve node label for a presentation context.
+ * The method is always called on TCF dispatch thread.
+ * @param update - label update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ protected boolean getData(ILabelUpdate update, Runnable done) {
+ update.setLabel(id, 0);
+ return true;
+ }
+
+ /**
+ * Retrieve viewer input object for a presentation context.
+ * Allows a view to translate the active debug context into an appropriate viewer input element.
+ * The method is always called on TCF dispatch thread.
+ * @param update - view input update request.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ protected boolean getData(IViewerInputUpdate update, Runnable done) {
+ update.setInputElement(this);
+ return true;
+ }
+
+ /*--------------------------------------------------------------------------------------*/
+ /* Misc */
+
+ public void refresh(IWorkbenchPart part) {
+ model.flushAllCaches();
+ for (TCFModelProxy p : model.getModelProxies()) {
+ if (p.getPresentationContext().getPart() != part) continue;
+ p.addDelta(this, IModelDelta.STATE | IModelDelta.CONTENT);
+ }
+ }
+
+ public int compareTo(TCFNode n) {
+ return id.compareTo(n.id);
+ }
+
+ public String toString() {
+ String s = "[" + Integer.toHexString(hashCode()) + "] " + id;
+ if (disposed) s += ", disposed";
+ return s;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeArrayPartition.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeArrayPartition.java
new file mode 100644
index 000000000..e3a12d201
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeArrayPartition.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+
+public class TCFNodeArrayPartition extends TCFNode {
+
+ private final int offs;
+ private final int size;
+ private final TCFChildrenSubExpressions children;
+
+ TCFNodeArrayPartition(TCFNode parent, int level, int offs, int size) {
+ super(parent, "AP" + level + "." + offs + "." + parent.id);
+ this.offs = offs;
+ this.size = size;
+ children = new TCFChildrenSubExpressions(this, level, offs, size);
+ }
+
+ int getOffset() {
+ return offs;
+ }
+
+ int getSize() {
+ return size;
+ }
+
+ @Override
+ protected boolean getData(IChildrenCountUpdate result, Runnable done) {
+ if (!children.validate(done)) return false;
+ result.setChildCount(children.size());
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IChildrenUpdate result, Runnable done) {
+ return children.getData(result, done);
+ }
+
+ @Override
+ protected boolean getData(IHasChildrenUpdate result, Runnable done) {
+ result.setHasChilren(true);
+ return true;
+ }
+
+ @Override
+ protected boolean getData(ILabelUpdate result, Runnable done) {
+ result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_ARRAY_PARTITION), 0);
+ String[] cols = result.getColumnIds();
+ String name = "[" + offs + ".." + (offs + size - 1) + "]";
+ if (cols == null || cols.length <= 1) {
+ result.setLabel(name, 0);
+ }
+ else {
+ for (int i = 0; i < cols.length; i++) {
+ String c = cols[i];
+ if (c.equals(TCFColumnPresentationExpression.COL_NAME)) {
+ result.setLabel(name, i);
+ }
+ else {
+ result.setLabel("", i);
+ }
+ }
+ }
+ return true;
+ }
+
+ void onSuspended() {
+ children.onSuspended();
+ }
+
+ void onValueChanged() {
+ children.onValueChanged();
+ }
+
+ void onRegisterValueChanged() {
+ children.onRegisterValueChanged();
+ }
+
+ void onMemoryChanged() {
+ children.onMemoryChanged();
+ }
+
+ void onMemoryMapChanged() {
+ children.onMemoryMapChanged();
+ }
+
+ @Override
+ public int compareTo(TCFNode n) {
+ TCFNodeArrayPartition p = (TCFNodeArrayPartition)n;
+ if (offs < p.offs) return -1;
+ if (offs > p.offs) return +1;
+ return 0;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExecContext.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExecContext.java
new file mode 100644
index 000000000..29836cf61
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExecContext.java
@@ -0,0 +1,1541 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.debug.ui.memory.IMemoryRenderingSite;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.tcf.internal.debug.model.TCFContextState;
+import org.eclipse.tcf.internal.debug.model.TCFFunctionRef;
+import org.eclipse.tcf.internal.debug.model.TCFSourceRef;
+import org.eclipse.tcf.internal.debug.model.TCFSymFileRef;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+import org.eclipse.tcf.protocol.IErrorReport;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.JSON;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.ILineNumbers;
+import org.eclipse.tcf.services.IMemory;
+import org.eclipse.tcf.services.IMemoryMap;
+import org.eclipse.tcf.services.IProcesses;
+import org.eclipse.tcf.services.IRunControl;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.util.TCFDataCache;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class TCFNodeExecContext extends TCFNode implements ISymbolOwner {
+
+ private final TCFChildrenExecContext children_exec;
+ private final TCFChildrenStackTrace children_stack;
+ private final TCFChildrenRegisters children_regs;
+ private final TCFChildrenExpressions children_exps;
+ private final TCFChildrenHoverExpressions children_hover_exps;
+ private final TCFChildrenLogExpressions children_log_exps;
+ private final TCFChildrenModules children_modules;
+
+ private final TCFData<IMemory.MemoryContext> mem_context;
+ private final TCFData<IRunControl.RunControlContext> run_context;
+ private final TCFData<MemoryRegion[]> memory_map;
+ private final TCFData<IProcesses.ProcessContext> prs_context;
+ private final TCFData<TCFContextState> state;
+ private final TCFData<BigInteger> address; // Current PC as BigInteger
+ private final TCFData<Collection<Map<String,Object>>> signal_list;
+ private final TCFData<SignalMask[]> signal_mask;
+ private final TCFData<TCFNodeExecContext> memory_node;
+ private final TCFData<TCFNodeExecContext> symbols_node;
+ private final TCFData<String> full_name;
+
+ private LinkedHashMap<BigInteger,TCFDataCache<TCFSymFileRef>> syms_info_lookup_cache;
+ private LinkedHashMap<BigInteger,TCFDataCache<TCFSourceRef>> line_info_lookup_cache;
+ private LinkedHashMap<BigInteger,TCFDataCache<TCFFunctionRef>> func_info_lookup_cache;
+ private LookupCacheTimer lookup_cache_timer;
+
+ private int mem_seq_no;
+ private int exe_seq_no;
+
+ /*
+ * LookupCacheTimer is executed periodically to dispose least-recently
+ * accessed entries in line_info_lookup_cache and func_info_lookup_cache.
+ * The timer disposes itself when both caches become empty.
+ */
+ private class LookupCacheTimer implements Runnable {
+
+ LookupCacheTimer() {
+ Protocol.invokeLater(4000, this);
+ }
+
+ public void run() {
+ if (isDisposed()) return;
+ if (syms_info_lookup_cache != null) {
+ BigInteger addr = syms_info_lookup_cache.keySet().iterator().next();
+ TCFDataCache<?> cache = syms_info_lookup_cache.get(addr);
+ if (!cache.isPending()) {
+ syms_info_lookup_cache.remove(addr).dispose();
+ if (syms_info_lookup_cache.size() == 0) syms_info_lookup_cache = null;
+ }
+ }
+ if (line_info_lookup_cache != null) {
+ BigInteger addr = line_info_lookup_cache.keySet().iterator().next();
+ TCFDataCache<?> cache = line_info_lookup_cache.get(addr);
+ if (!cache.isPending()) {
+ line_info_lookup_cache.remove(addr).dispose();
+ if (line_info_lookup_cache.size() == 0) line_info_lookup_cache = null;
+ }
+ }
+ if (func_info_lookup_cache != null) {
+ BigInteger addr = func_info_lookup_cache.keySet().iterator().next();
+ TCFDataCache<?> cache = func_info_lookup_cache.get(addr);
+ if (!cache.isPending()) {
+ func_info_lookup_cache.remove(addr).dispose();
+ if (func_info_lookup_cache.size() == 0) func_info_lookup_cache = null;
+ }
+ }
+ if (syms_info_lookup_cache == null && line_info_lookup_cache == null && func_info_lookup_cache == null) {
+ lookup_cache_timer = null;
+ }
+ else {
+ Protocol.invokeLater(2500, this);
+ }
+ }
+ }
+
+ public static class ChildrenStateInfo {
+ public boolean running;
+ public boolean suspended;
+ public boolean not_active;
+ public boolean breakpoint;
+ }
+
+ private final Map<String,TCFNodeSymbol> symbols = new HashMap<String,TCFNodeSymbol>();
+
+ private int resumed_cnt;
+ private boolean resume_pending;
+ private boolean resumed_by_action;
+ private TCFNode[] last_stack_trace;
+ private TCFNode[] last_children_list;
+ private String last_label;
+ private ImageDescriptor last_image;
+ private ChildrenStateInfo last_children_state_info;
+ private boolean delayed_children_list_delta;
+
+ /**
+ * Wrapper class for IMemoryMap.MemoryRegion.
+ * The class help to search memory region by address by
+ * providing contains() method.
+ */
+ public static class MemoryRegion {
+
+ private final BigInteger addr_start;
+ private final BigInteger addr_end;
+
+ public final IMemoryMap.MemoryRegion region;
+
+ private MemoryRegion(IMemoryMap.MemoryRegion region) {
+ this.region = region;
+ Number addr = region.getAddress();
+ Number size = region.getSize();
+ if (addr == null || size == null) {
+ addr_start = null;
+ addr_end = null;
+ }
+ else {
+ addr_start = JSON.toBigInteger(addr);
+ addr_end = addr_start.add(JSON.toBigInteger(size));
+ }
+ }
+
+ public boolean contains(BigInteger addr) {
+ return
+ addr_start != null && addr_end != null &&
+ addr_start.compareTo(addr) <= 0 &&
+ addr_end.compareTo(addr) > 0;
+ }
+
+ @Override
+ public String toString() {
+ return region.getProperties().toString();
+ }
+ }
+
+ public static class SignalMask {
+
+ protected Map<String,Object> props;
+ protected boolean dont_stop;
+ protected boolean dont_pass;
+ protected boolean pending;
+
+ public Number getIndex() {
+ return (Number)props.get(IProcesses.SIG_INDEX);
+ }
+
+ public Number getCode() {
+ return (Number)props.get(IProcesses.SIG_CODE);
+ }
+
+ public Map<String,Object> getProperties() {
+ return props;
+ }
+
+ public boolean isDontStop() {
+ return dont_stop;
+ }
+
+ public boolean isDontPass() {
+ return dont_pass;
+ }
+
+ public boolean isPending() {
+ return pending;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer bf = new StringBuffer();
+ bf.append("[attrs=");
+ bf.append(props.toString());
+ if (dont_stop) bf.append(",don't stop");
+ if (dont_pass) bf.append(",don't pass");
+ if (pending) bf.append(",pending");
+ bf.append(']');
+ return bf.toString();
+ }
+ }
+
+ TCFNodeExecContext(TCFNode parent, final String id) {
+ super(parent, id);
+ children_exec = new TCFChildrenExecContext(this);
+ children_stack = new TCFChildrenStackTrace(this);
+ children_regs = new TCFChildrenRegisters(this);
+ children_exps = new TCFChildrenExpressions(this);
+ children_hover_exps = new TCFChildrenHoverExpressions(this);
+ children_log_exps = new TCFChildrenLogExpressions(this);
+ children_modules = new TCFChildrenModules(this);
+ mem_context = new TCFData<IMemory.MemoryContext>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ assert command == null;
+ IMemory mem = launch.getService(IMemory.class);
+ if (mem == null) {
+ set(null, null, null);
+ return true;
+ }
+ command = mem.getContext(id, new IMemory.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IMemory.MemoryContext context) {
+ set(token, error, context);
+ }
+ });
+ return false;
+ }
+ };
+ run_context = new TCFData<IRunControl.RunControlContext>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ assert command == null;
+ IRunControl run = launch.getService(IRunControl.class);
+ if (run == null) {
+ set(null, null, null);
+ return true;
+ }
+ command = run.getContext(id, new IRunControl.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext context) {
+ if (context != null) model.updateContextMap(id, context);
+ set(token, error, context);
+ }
+ });
+ return false;
+ }
+ };
+ prs_context = new TCFData<IProcesses.ProcessContext>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ assert command == null;
+ IProcesses prs = launch.getService(IProcesses.class);
+ if (prs == null) {
+ set(null, null, null);
+ return true;
+ }
+ command = prs.getContext(id, new IProcesses.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IProcesses.ProcessContext context) {
+ set(token, error, context);
+ }
+ });
+ return false;
+ }
+ };
+ memory_map = new TCFData<MemoryRegion[]>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ assert command == null;
+ IMemoryMap mmap = launch.getService(IMemoryMap.class);
+ if (mmap == null) {
+ set(null, null, null);
+ return true;
+ }
+ command = mmap.get(id, new IMemoryMap.DoneGet() {
+ public void doneGet(IToken token, Exception error, IMemoryMap.MemoryRegion[] map) {
+ MemoryRegion[] arr = null;
+ if (map != null) {
+ int i = 0;
+ arr = new MemoryRegion[map.length];
+ for (IMemoryMap.MemoryRegion r : map) arr[i++] = new MemoryRegion(r);
+ }
+ set(token, error, arr);
+ }
+ });
+ return false;
+ }
+ };
+ state = new TCFData<TCFContextState>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ assert command == null;
+ if (!run_context.validate(this)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx == null || !ctx.hasState()) {
+ set(null, null, null);
+ return true;
+ }
+ command = ctx.getState(new IRunControl.DoneGetState() {
+ public void doneGetState(IToken token, Exception error, boolean suspended, String pc, String reason, Map<String,Object> params) {
+ TCFContextState s = new TCFContextState();
+ s.is_suspended = suspended;
+ s.suspend_pc = pc;
+ s.suspend_reason = reason;
+ s.suspend_params = params;
+ set(token, error, s);
+ }
+ });
+ return false;
+ }
+ };
+ address = new TCFData<BigInteger>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!run_context.validate(this)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx == null || !ctx.hasState()) {
+ set(null, run_context.getError(), null);
+ return true;
+ }
+ if (!state.validate(this)) return false;
+ TCFContextState s = state.getData();
+ if (s == null) {
+ set(null, state.getError(), null);
+ return true;
+ }
+ if (s.suspend_pc == null) {
+ set(null, null, null);
+ return true;
+ }
+ set(null, null, new BigInteger(s.suspend_pc));
+ return true;
+ }
+ };
+ signal_list = new TCFData<Collection<Map<String,Object>>>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ IProcesses prs = channel.getRemoteService(IProcesses.class);
+ if (prs == null) {
+ set(null, null, null);
+ return true;
+ }
+ command = prs.getSignalList(id, new IProcesses.DoneGetSignalList() {
+ public void doneGetSignalList(IToken token, Exception error, Collection<Map<String, Object>> list) {
+ set(token, error, list);
+ }
+ });
+ return false;
+ }
+ };
+ signal_mask = new TCFData<SignalMask[]>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!signal_list.validate(this)) return false;
+ IProcesses prs = channel.getRemoteService(IProcesses.class);
+ final Collection<Map<String,Object>> sigs = signal_list.getData();
+ if (prs == null || sigs == null) {
+ set(null, signal_list.getError(), null);
+ return true;
+ }
+ command = prs.getSignalMask(id, new IProcesses.DoneGetSignalMask() {
+ public void doneGetSignalMask(IToken token, Exception error, int dont_stop, int dont_pass, int pending) {
+ int n = 0;
+ SignalMask[] list = new SignalMask[sigs.size()];
+ for (Map<String,Object> m : sigs) {
+ SignalMask s = list[n++] = new SignalMask();
+ s.props = m;
+ int mask = 1 << s.getIndex().intValue();
+ s.dont_stop = (dont_stop & mask) != 0;
+ s.dont_pass = (dont_pass & mask) != 0;
+ s.pending = (pending & mask) != 0;
+ }
+ set(token, error, list);
+ }
+ });
+ return false;
+ }
+ };
+ memory_node = new TCFData<TCFNodeExecContext>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ String mem_id = null;
+ if (!run_context.validate(this)) return false;
+ Throwable err = run_context.getError();
+ if (err == null) {
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null) mem_id = ctx.getProcessID();
+ }
+ if (err != null) {
+ set(null, err, null);
+ }
+ else if (mem_id == null) {
+ set(null, new Exception("Context does not provide memory access"), null);
+ }
+ else {
+ if (!model.createNode(mem_id, this)) return false;
+ if (!isValid()) set(null, null, (TCFNodeExecContext)model.getNode(mem_id));
+ }
+ return true;
+ }
+ };
+ symbols_node = new TCFData<TCFNodeExecContext>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ String syms_id = null;
+ if (!run_context.validate(this)) return false;
+ Throwable err = run_context.getError();
+ if (err == null) {
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null) {
+ syms_id = ctx.getSymbolsGroup();
+ if (syms_id == null) syms_id = ctx.getProcessID();
+ }
+ }
+ if (err != null) {
+ set(null, err, null);
+ }
+ else if (syms_id == null) {
+ set(null, new Exception("Context does not support symbol groups"), null);
+ }
+ else {
+ if (!model.createNode(syms_id, this)) return false;
+ if (!isValid()) set(null, null, (TCFNodeExecContext)model.getNode(syms_id));
+ }
+ return true;
+ }
+ };
+ full_name = new TCFData<String>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!run_context.validate(this)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ String res = null;
+ if (ctx != null) {
+ res = ctx.getName();
+ if (res == null) {
+ res = ctx.getID();
+ }
+ else {
+ // Add ancestor names
+ TCFNodeExecContext p = TCFNodeExecContext.this;
+ ArrayList<String> lst = new ArrayList<String>();
+ lst.add(res);
+ while (p.parent instanceof TCFNodeExecContext) {
+ p = (TCFNodeExecContext)p.parent;
+ TCFDataCache<IRunControl.RunControlContext> run_ctx_cache = p.run_context;
+ if (!run_ctx_cache.validate(this)) return false;
+ IRunControl.RunControlContext run_ctx_data = run_ctx_cache.getData();
+ String name = null;
+ if (run_ctx_data != null) name = run_ctx_data.getName();
+ if (name == null) name = "";
+ lst.add(name);
+ }
+ StringBuffer bf = new StringBuffer();
+ for (int i = lst.size(); i > 0; i--) {
+ bf.append('/');
+ bf.append(lst.get(i - 1));
+ }
+ res = bf.toString();
+ }
+ }
+ set(null, null, res);
+ return true;
+ }
+ };
+ }
+
+ @Override
+ void dispose() {
+ assert !isDisposed();
+ ArrayList<TCFNodeSymbol> l = new ArrayList<TCFNodeSymbol>(symbols.values());
+ for (TCFNodeSymbol s : l) s.dispose();
+ assert symbols.size() == 0;
+ super.dispose();
+ }
+
+ void setMemSeqNo(int no) {
+ mem_seq_no = no;
+ }
+
+ void setExeSeqNo(int no) {
+ exe_seq_no = no;
+ }
+
+ TCFChildren getHoverExpressionCache(String expression) {
+ children_hover_exps.setExpression(expression);
+ return children_hover_exps;
+ }
+
+ public TCFChildrenLogExpressions getLogExpressionCache() {
+ return children_log_exps;
+ }
+
+ void setRunContext(IRunControl.RunControlContext ctx) {
+ run_context.reset(ctx);
+ }
+
+ void setProcessContext(IProcesses.ProcessContext ctx) {
+ prs_context.reset(ctx);
+ }
+
+ void setMemoryContext(IMemory.MemoryContext ctx) {
+ mem_context.reset(ctx);
+ }
+
+ public TCFDataCache<TCFNodeExecContext> getSymbolsNode() {
+ return symbols_node;
+ }
+
+ public TCFDataCache<TCFNodeExecContext> getMemoryNode() {
+ return memory_node;
+ }
+
+ public TCFDataCache<MemoryRegion[]> getMemoryMap() {
+ return memory_map;
+ }
+
+ public TCFDataCache<Collection<Map<String,Object>>> getSignalList() {
+ return signal_list;
+ }
+
+ public TCFDataCache<SignalMask[]> getSignalMask() {
+ return signal_mask;
+ }
+
+ public TCFDataCache<TCFSymFileRef> getSymFileInfo(final BigInteger addr) {
+ if (isDisposed()) return null;
+ TCFDataCache<TCFSymFileRef> ref_cache;
+ if (syms_info_lookup_cache != null) {
+ ref_cache = syms_info_lookup_cache.get(addr);
+ if (ref_cache != null) return ref_cache;
+ }
+ final ISymbols syms = launch.getService(ISymbols.class);
+ if (syms == null) return null;
+ if (syms_info_lookup_cache == null) {
+ syms_info_lookup_cache = new LinkedHashMap<BigInteger,TCFDataCache<TCFSymFileRef>>(11, 0.75f, true);
+ if (lookup_cache_timer == null) lookup_cache_timer = new LookupCacheTimer();
+ }
+ syms_info_lookup_cache.put(addr, ref_cache = new TCFData<TCFSymFileRef>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!memory_node.validate(this)) return false;
+ IMemory.MemoryContext mem_data = null;
+ TCFNodeExecContext mem = memory_node.getData();
+ if (mem != null) {
+ TCFDataCache<IMemory.MemoryContext> mem_cache = mem.mem_context;
+ if (!mem_cache.validate(this)) return false;
+ mem_data = mem_cache.getData();
+ }
+ final TCFSymFileRef ref_data = new TCFSymFileRef();
+ if (mem_data != null) {
+ ref_data.context_id = mem_data.getID();
+ ref_data.address_size = mem_data.getAddressSize();
+ }
+ command = syms.getSymFileInfo(ref_data.context_id, addr, new ISymbols.DoneGetSymFileInfo() {
+ public void doneGetSymFileInfo(IToken token, Exception error, Map<String,Object> props) {
+ ref_data.address = addr;
+ ref_data.error = error;
+ ref_data.props = props;
+ set(token, null, ref_data);
+ }
+ });
+ return false;
+ }
+ });
+ return ref_cache;
+ }
+
+ public TCFDataCache<TCFSourceRef> getLineInfo(final BigInteger addr) {
+ if (isDisposed()) return null;
+ TCFDataCache<TCFSourceRef> ref_cache;
+ if (line_info_lookup_cache != null) {
+ ref_cache = line_info_lookup_cache.get(addr);
+ if (ref_cache != null) return ref_cache;
+ }
+ final ILineNumbers ln = launch.getService(ILineNumbers.class);
+ if (ln == null) return null;
+ final BigInteger n0 = addr;
+ final BigInteger n1 = n0.add(BigInteger.valueOf(1));
+ if (line_info_lookup_cache == null) {
+ line_info_lookup_cache = new LinkedHashMap<BigInteger,TCFDataCache<TCFSourceRef>>(11, 0.75f, true);
+ if (lookup_cache_timer == null) lookup_cache_timer = new LookupCacheTimer();
+ }
+ line_info_lookup_cache.put(addr, ref_cache = new TCFData<TCFSourceRef>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!memory_node.validate(this)) return false;
+ IMemory.MemoryContext mem_data = null;
+ TCFNodeExecContext mem = memory_node.getData();
+ if (mem != null) {
+ TCFDataCache<IMemory.MemoryContext> mem_cache = mem.mem_context;
+ if (!mem_cache.validate(this)) return false;
+ mem_data = mem_cache.getData();
+ }
+ final TCFSourceRef ref_data = new TCFSourceRef();
+ if (mem_data != null) {
+ ref_data.context_id = mem_data.getID();
+ ref_data.address_size = mem_data.getAddressSize();
+ }
+ command = ln.mapToSource(id, n0, n1, new ILineNumbers.DoneMapToSource() {
+ public void doneMapToSource(IToken token, Exception error, ILineNumbers.CodeArea[] areas) {
+ ref_data.address = addr;
+ if (error == null && areas != null && areas.length > 0) {
+ for (ILineNumbers.CodeArea area : areas) {
+ BigInteger a0 = JSON.toBigInteger(area.start_address);
+ BigInteger a1 = JSON.toBigInteger(area.end_address);
+ if (n0.compareTo(a0) >= 0 && n0.compareTo(a1) < 0) {
+ if (ref_data.area == null || area.start_line < ref_data.area.start_line) {
+ if (area.start_address != a0 || area.end_address != a1) {
+ area = new ILineNumbers.CodeArea(area.directory, area.file,
+ area.start_line, area.start_column,
+ area.end_line, area.end_column,
+ a0, a1, area.isa,
+ area.is_statement, area.basic_block,
+ area.prologue_end, area.epilogue_begin);
+ }
+ ref_data.area = area;
+ }
+ }
+ }
+ }
+ ref_data.error = error;
+ set(token, null, ref_data);
+ }
+ });
+ return false;
+ }
+ });
+ return ref_cache;
+ }
+
+ public TCFDataCache<TCFFunctionRef> getFuncInfo(final BigInteger addr) {
+ if (isDisposed()) return null;
+ TCFDataCache<TCFFunctionRef> ref_cache;
+ if (func_info_lookup_cache != null) {
+ ref_cache = func_info_lookup_cache.get(addr);
+ if (ref_cache != null) return ref_cache;
+ }
+ final ISymbols syms = launch.getService(ISymbols.class);
+ if (syms == null) return null;
+ if (func_info_lookup_cache == null) {
+ func_info_lookup_cache = new LinkedHashMap<BigInteger,TCFDataCache<TCFFunctionRef>>(11, 0.75f, true);
+ if (lookup_cache_timer == null) lookup_cache_timer = new LookupCacheTimer();
+ }
+ func_info_lookup_cache.put(addr, ref_cache = new TCFData<TCFFunctionRef>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!memory_node.validate(this)) return false;
+ IMemory.MemoryContext mem_data = null;
+ TCFNodeExecContext mem = memory_node.getData();
+ if (mem != null) {
+ TCFDataCache<IMemory.MemoryContext> mem_cache = mem.mem_context;
+ if (!mem_cache.validate(this)) return false;
+ mem_data = mem_cache.getData();
+ }
+ final TCFFunctionRef ref_data = new TCFFunctionRef();
+ if (mem_data != null) {
+ ref_data.context_id = mem_data.getID();
+ ref_data.address_size = mem_data.getAddressSize();
+ }
+ command = syms.findByAddr(id, addr, new ISymbols.DoneFind() {
+ public void doneFind(IToken token, Exception error, String symbol_id) {
+ ref_data.address = addr;
+ ref_data.error = error;
+ ref_data.symbol_id = symbol_id;
+ set(token, null, ref_data);
+ }
+ });
+ return false;
+ }
+ });
+ return ref_cache;
+ }
+
+ private void clearLookupCaches() {
+ if (syms_info_lookup_cache != null) {
+ Iterator<TCFDataCache<TCFSymFileRef>> i = syms_info_lookup_cache.values().iterator();
+ while (i.hasNext()) {
+ TCFDataCache<TCFSymFileRef> cache = i.next();
+ if (cache.isPending()) continue;
+ cache.dispose();
+ i.remove();
+ }
+ if (syms_info_lookup_cache.size() == 0) syms_info_lookup_cache = null;
+ }
+ if (line_info_lookup_cache != null) {
+ Iterator<TCFDataCache<TCFSourceRef>> i = line_info_lookup_cache.values().iterator();
+ while (i.hasNext()) {
+ TCFDataCache<TCFSourceRef> cache = i.next();
+ if (cache.isPending()) continue;
+ cache.dispose();
+ i.remove();
+ }
+ if (line_info_lookup_cache.size() == 0) line_info_lookup_cache = null;
+ }
+ if (func_info_lookup_cache != null) {
+ Iterator<TCFDataCache<TCFFunctionRef>> i = func_info_lookup_cache.values().iterator();
+ while (i.hasNext()) {
+ TCFDataCache<TCFFunctionRef> cache = i.next();
+ if (cache.isPending()) continue;
+ cache.dispose();
+ i.remove();
+ }
+ if (func_info_lookup_cache.size() == 0) func_info_lookup_cache = null;
+ }
+ }
+
+ @Override
+ public TCFNode getParent(IPresentationContext ctx) {
+ assert Protocol.isDispatchThread();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(ctx.getId())) {
+ Set<String> ids = launch.getContextFilter();
+ if (ids != null) {
+ if (ids.contains(id)) return model.getRootNode();
+ if (parent instanceof TCFNodeLaunch) return null;
+ }
+ }
+ return parent;
+ }
+
+ public TCFDataCache<IRunControl.RunControlContext> getRunContext() {
+ return run_context;
+ }
+
+ public TCFDataCache<IProcesses.ProcessContext> getProcessContext() {
+ return prs_context;
+ }
+
+ public TCFDataCache<IMemory.MemoryContext> getMemoryContext() {
+ return mem_context;
+ }
+
+ public TCFDataCache<BigInteger> getAddress() {
+ return address;
+ }
+
+ public TCFDataCache<TCFContextState> getState() {
+ return state;
+ }
+
+ public TCFChildrenStackTrace getStackTrace() {
+ return children_stack;
+ }
+
+ public TCFChildren getRegisters() {
+ return children_regs;
+ }
+
+ public TCFChildren getModules() {
+ return children_modules;
+ }
+
+ public TCFChildren getChildren() {
+ return children_exec;
+ }
+
+ public TCFNodeStackFrame getLastTopFrame() {
+ if (!resume_pending) return null;
+ if (last_stack_trace == null || last_stack_trace.length == 0) return null;
+ return (TCFNodeStackFrame)last_stack_trace[0];
+ }
+
+ public TCFNodeStackFrame getViewBottomFrame() {
+ if (last_stack_trace == null || last_stack_trace.length == 0) return null;
+ return (TCFNodeStackFrame)last_stack_trace[last_stack_trace.length - 1];
+ }
+
+ /**
+ * Get context full name - including all ancestor names.
+ * Return context ID if the context does not have a name.
+ * @return cache item with the context full name.
+ */
+ public TCFDataCache<String> getFullName() {
+ return full_name;
+ }
+
+ public void addSymbol(TCFNodeSymbol s) {
+ assert symbols.get(s.id) == null;
+ symbols.put(s.id, s);
+ }
+
+ public void removeSymbol(TCFNodeSymbol s) {
+ assert symbols.get(s.id) == s;
+ symbols.remove(s.id);
+ }
+
+ /**
+ * Return true if this context cannot be accessed because it is not active.
+ * Not active means the target is suspended but this context is not one that is
+ * currently scheduled to run on a target CPU.
+ * Some debuggers don't support access to register values and other properties of such contexts.
+ */
+ public boolean isNotActive() {
+ TCFContextState state_data = state.getData();
+ if (state_data != null && state_data.suspend_params != null) {
+ @SuppressWarnings("unchecked")
+ Map<String,Object> attrs = (Map<String,Object>)state_data.suspend_params.get(IRunControl.STATE_PC_ERROR);
+ if (attrs != null) {
+ Number n = (Number)attrs.get(IErrorReport.ERROR_CODE);
+ if (n != null) return n.intValue() == IErrorReport.TCF_ERROR_NOT_ACTIVE;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean getData(IChildrenCountUpdate result, Runnable done) {
+ TCFChildren children = null;
+ String view_id = result.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) {
+ if (resume_pending && last_stack_trace != null) {
+ result.setChildCount(last_stack_trace.length);
+ return true;
+ }
+ if (!state.validate(done)) return false;
+ if (isNotActive()) {
+ last_stack_trace = new TCFNode[0];
+ result.setChildCount(0);
+ return true;
+ }
+ TCFContextState state_data = state.getData();
+ if (state_data != null && !state_data.is_suspended) {
+ result.setChildCount(0);
+ return true;
+ }
+ children = children_stack;
+ }
+ else {
+ if (!model.getAutoChildrenListUpdates() && last_children_list != null) {
+ result.setChildCount(last_children_list.length);
+ return true;
+ }
+ children = children_exec;
+ }
+ }
+ else if (IDebugUIConstants.ID_REGISTER_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_regs;
+ }
+ else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_exps;
+ }
+ else if (TCFModel.ID_EXPRESSION_HOVER.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_hover_exps;
+ }
+ else if (IDebugUIConstants.ID_MODULE_VIEW.equals(view_id)) {
+ if (!mem_context.validate(done)) return false;
+ IMemory.MemoryContext ctx = mem_context.getData();
+ if (ctx != null) children = children_modules;
+ }
+ if (children != null) {
+ if (!children.validate(done)) return false;
+ if (children == children_stack) last_stack_trace = children_stack.toArray();
+ if (children == children_exec) last_children_list = children_exec.toArray();
+ result.setChildCount(children.size());
+ }
+ else {
+ result.setChildCount(0);
+ }
+ return true;
+ }
+
+ private void setResultChildren(IChildrenUpdate result, TCFNode[] arr) {
+ int offset = 0;
+ int r_offset = result.getOffset();
+ int r_length = result.getLength();
+ for (TCFNode n : arr) {
+ if (offset >= r_offset && offset < r_offset + r_length) {
+ result.setChild(n, offset);
+ }
+ offset++;
+ }
+ }
+
+ @Override
+ protected boolean getData(IChildrenUpdate result, Runnable done) {
+ TCFChildren children = null;
+ String view_id = result.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) {
+ if (resume_pending && last_stack_trace != null) {
+ setResultChildren(result, last_stack_trace);
+ return true;
+ }
+ if (!state.validate(done)) return false;
+ if (isNotActive()) {
+ last_stack_trace = new TCFNode[0];
+ return true;
+ }
+ TCFContextState state_data = state.getData();
+ if (state_data != null && !state_data.is_suspended) {
+ return true;
+ }
+ children = children_stack;
+ }
+ else {
+ if (!model.getAutoChildrenListUpdates() && last_children_list != null) {
+ setResultChildren(result, last_children_list);
+ return true;
+ }
+ children = children_exec;
+ }
+ }
+ else if (IDebugUIConstants.ID_REGISTER_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_regs;
+ }
+ else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_exps;
+ }
+ else if (TCFModel.ID_EXPRESSION_HOVER.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_hover_exps;
+ }
+ else if (IDebugUIConstants.ID_MODULE_VIEW.equals(view_id)) {
+ if (!mem_context.validate(done)) return false;
+ IMemory.MemoryContext ctx = mem_context.getData();
+ if (ctx != null) children = children_modules;
+ }
+ if (children == null) return true;
+ if (children == children_stack) {
+ if (!children.validate(done)) return false;
+ last_stack_trace = children_stack.toArray();
+ }
+ if (children == children_exec) {
+ if (!children.validate(done)) return false;
+ last_children_list = children_exec.toArray();
+ }
+ return children.getData(result, done);
+ }
+
+ @Override
+ protected boolean getData(IHasChildrenUpdate result, Runnable done) {
+ TCFChildren children = null;
+ String view_id = result.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) {
+ if (resume_pending && last_stack_trace != null) {
+ result.setHasChilren(last_stack_trace.length > 0);
+ return true;
+ }
+ if (!state.validate(done)) return false;
+ if (isNotActive()) {
+ last_stack_trace = new TCFNode[0];
+ result.setHasChilren(false);
+ return true;
+ }
+ TCFContextState state_data = state.getData();
+ if (state_data != null) {
+ result.setHasChilren(state_data.is_suspended);
+ return true;
+ }
+ children = children_stack;
+ }
+ else {
+ if (!model.getAutoChildrenListUpdates() && last_children_list != null) {
+ result.setHasChilren(last_children_list.length > 0);
+ return true;
+ }
+ children = children_exec;
+ }
+ }
+ else if (IDebugUIConstants.ID_REGISTER_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_regs;
+ }
+ else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_exps;
+ }
+ else if (TCFModel.ID_EXPRESSION_HOVER.equals(view_id)) {
+ if (!run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && ctx.hasState()) children = children_hover_exps;
+ }
+ else if (IDebugUIConstants.ID_MODULE_VIEW.equals(view_id)) {
+ if (!mem_context.validate(done)) return false;
+ IMemory.MemoryContext ctx = mem_context.getData();
+ if (ctx != null) children = children_modules;
+ }
+ if (children != null) {
+ if (!children.validate(done)) return false;
+ if (children == children_stack) last_stack_trace = children_stack.toArray();
+ if (children == children_exec) last_children_list = children_exec.toArray();
+ result.setHasChilren(children.size() > 0);
+ }
+ else {
+ result.setHasChilren(false);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean getData(ILabelUpdate result, Runnable done) {
+ result.getViewerInput();
+ if (!run_context.validate(done)) return false;
+ String image_name = null;
+ boolean suspended_by_bp = false;
+ StringBuffer label = new StringBuffer();
+ Throwable error = run_context.getError();
+ if (error != null) {
+ result.setForeground(new RGB(255, 0, 0), 0);
+ label.append(id);
+ label.append(": ");
+ label.append(TCFModel.getErrorMessage(error, false));
+ }
+ else {
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx == null) {
+ label.append(id);
+ }
+ else {
+ String nm = ctx.getName();
+ if (nm == null && !ctx.hasState()) {
+ String prs = ctx.getProcessID();
+ if (prs != null) {
+ if (!prs_context.validate(done)) return false;
+ IProcesses.ProcessContext pctx = prs_context.getData();
+ if (pctx != null) nm = pctx.getName();
+ }
+ }
+ label.append(nm != null ? nm : id);
+ if (ctx.hasState() && !TCFModel.ID_PINNED_VIEW.equals(result.getPresentationContext().getId())) {
+ // Thread
+ if (resume_pending && resumed_by_action || model.getActiveAction(id) != null) {
+ image_name = ImageCache.IMG_THREAD_RUNNNIG;
+ if (resume_pending && last_label != null) {
+ result.setImageDescriptor(ImageCache.getImageDescriptor(image_name), 0);
+ result.setLabel(last_label, 0);
+ return true;
+ }
+ label.append(" (Running)");
+ }
+ else if (resume_pending && last_label != null && last_image != null) {
+ result.setImageDescriptor(last_image, 0);
+ result.setLabel(last_label, 0);
+ return true;
+ }
+ else {
+ if (!state.validate(done)) return false;
+ TCFContextState state_data = state.getData();
+ if (isNotActive()) {
+ image_name = ImageCache.IMG_THREAD_NOT_ACTIVE;
+ label.append(" (Not active)");
+ if (state_data.suspend_reason != null && !state_data.suspend_reason.equals(IRunControl.REASON_USER_REQUEST)) {
+ label.append(" - ");
+ label.append(state_data.suspend_reason);
+ }
+ }
+ else {
+ if (state_data == null) image_name = ImageCache.IMG_THREAD_UNKNOWN_STATE;
+ else if (state_data.is_suspended) image_name = ImageCache.IMG_THREAD_SUSPENDED;
+ else image_name = ImageCache.IMG_THREAD_RUNNNIG;
+ if (state_data != null) {
+ if (!state_data.is_suspended) {
+ label.append(" (Running)");
+ }
+ else {
+ String s = null;
+ String r = model.getContextActionResult(id);
+ if (r == null) {
+ r = state_data.suspend_reason;
+ if (state_data.suspend_params != null) {
+ s = (String)state_data.suspend_params.get(IRunControl.STATE_SIGNAL_DESCRIPTION);
+ if (s == null) s = (String)state_data.suspend_params.get(IRunControl.STATE_SIGNAL_NAME);
+ }
+ }
+ suspended_by_bp = IRunControl.REASON_BREAKPOINT.equals(r);
+ if (r == null) r = "Suspended";
+ if (s != null) r += ": " + s;
+ label.append(" (");
+ label.append(r);
+ if (state_data.suspend_params != null) {
+ String prs = (String)state_data.suspend_params.get("Context");
+ if (prs != null) {
+ label.append(", ");
+ label.append(prs);
+ }
+ String cpu = (String)state_data.suspend_params.get("CPU");
+ if (cpu != null) {
+ label.append(", ");
+ label.append(cpu);
+ }
+ }
+ label.append(")");
+ }
+ }
+ }
+ }
+ last_children_state_info = null;
+ }
+ else {
+ // Thread container (process)
+ ChildrenStateInfo i = new ChildrenStateInfo();
+ if (!hasSuspendedChildren(i, done)) return false;
+ if (i.suspended) image_name = ImageCache.IMG_PROCESS_SUSPENDED;
+ else image_name = ImageCache.IMG_PROCESS_RUNNING;
+ suspended_by_bp = i.breakpoint;
+ last_children_state_info = i;
+ }
+ }
+ }
+ last_image = ImageCache.getImageDescriptor(image_name);
+ if (suspended_by_bp) last_image = ImageCache.addOverlay(last_image, ImageCache.IMG_BREAKPOINT_OVERLAY);
+ result.setImageDescriptor(last_image, 0);
+ result.setLabel(last_label = label.toString(), 0);
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IViewerInputUpdate result, Runnable done) {
+ result.setInputElement(this);
+ String view_id = result.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(view_id)) {
+ if (!children_stack.validate(done)) return false;
+ TCFNodeStackFrame frame = children_stack.getTopFrame();
+ if (frame != null) result.setInputElement(frame);
+ }
+ else if (IDebugUIConstants.ID_MODULE_VIEW.equals(view_id)) {
+ // TODO: need to post view input delta when memory context changes
+ TCFDataCache<TCFNodeExecContext> mem = model.searchMemoryContext(this);
+ if (mem == null) return true;
+ if (!mem.validate(done)) return false;
+ if (mem.getData() == null) return true;
+ result.setInputElement(mem.getData());
+ }
+ return true;
+ }
+
+ @Override
+ public void refresh(IWorkbenchPart part) {
+ if (part instanceof IMemoryRenderingSite) {
+ model.onMemoryChanged(id, false, false);
+ }
+ else {
+ last_children_list = null;
+ last_children_state_info = null;
+ last_stack_trace = null;
+ last_label = null;
+ last_image = null;
+ super.refresh(part);
+ }
+ }
+
+ void postAllChangedDelta() {
+ postContentChangedDelta();
+ postStateChangedDelta();
+ }
+
+ void postContextAddedDelta() {
+ if (parent instanceof TCFNodeExecContext) {
+ TCFNodeExecContext exe = (TCFNodeExecContext)parent;
+ ChildrenStateInfo info = exe.last_children_state_info;
+ if (info != null) {
+ if (!model.getAutoChildrenListUpdates()) {
+ // Manual manual updates.
+ return;
+ }
+ if (!info.suspended && !info.not_active && model.getDelayChildrenListUpdates()) {
+ // Delay content update until a child is suspended.
+ exe.delayed_children_list_delta = true;
+ return;
+ }
+ }
+ }
+ for (TCFModelProxy p : model.getModelProxies()) {
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(p.getPresentationContext().getId())) {
+ /* Note: should use IModelDelta.INSERTED but it is broken in Eclipse 3.6 */
+ p.addDelta(this, IModelDelta.ADDED);
+ }
+ }
+ }
+
+ private void postContextRemovedDelta() {
+ if (parent instanceof TCFNodeExecContext) {
+ TCFNodeExecContext exe = (TCFNodeExecContext)parent;
+ ChildrenStateInfo info = exe.last_children_state_info;
+ if (info != null) {
+ if (!model.getAutoChildrenListUpdates()) {
+ // Manual manual updates.
+ return;
+ }
+ if (!info.suspended && !info.not_active && model.getDelayChildrenListUpdates()) {
+ // Delay content update until a child is suspended.
+ exe.delayed_children_list_delta = true;
+ return;
+ }
+ }
+ }
+ for (TCFModelProxy p : model.getModelProxies()) {
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(p.getPresentationContext().getId())) {
+ p.addDelta(this, IModelDelta.REMOVED);
+ }
+ }
+ }
+
+ private void postContentChangedDelta() {
+ delayed_children_list_delta = false;
+ for (TCFModelProxy p : model.getModelProxies()) {
+ int flags = 0;
+ String view_id = p.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id) &&
+ (launch.getContextActionsCount(id) == 0 ||
+ !model.getDelayStackUpdateUtilLastStep())) {
+ flags |= IModelDelta.CONTENT;
+ }
+ if (IDebugUIConstants.ID_REGISTER_VIEW.equals(view_id) ||
+ IDebugUIConstants.ID_EXPRESSION_VIEW.equals(view_id) ||
+ TCFModel.ID_EXPRESSION_HOVER.equals(view_id)) {
+ if (p.getInput() == this) flags |= IModelDelta.CONTENT;
+ }
+ if (flags == 0) continue;
+ p.addDelta(this, flags);
+ }
+ }
+
+ private void postAllAndParentsChangedDelta() {
+ postContentChangedDelta();
+ TCFNode n = this;
+ while (n instanceof TCFNodeExecContext) {
+ TCFNodeExecContext e = (TCFNodeExecContext)n;
+ if (e.delayed_children_list_delta) e.postContentChangedDelta();
+ e.postStateChangedDelta();
+ n = n.parent;
+ }
+ }
+
+ public void postStateChangedDelta() {
+ for (TCFModelProxy p : model.getModelProxies()) {
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(p.getPresentationContext().getId())) {
+ p.addDelta(this, IModelDelta.STATE);
+ }
+ }
+ }
+
+ private void postModulesChangedDelta() {
+ for (TCFModelProxy p : model.getModelProxies()) {
+ if (IDebugUIConstants.ID_MODULE_VIEW.equals(p.getPresentationContext().getId())) {
+ p.addDelta(this, IModelDelta.CONTENT);
+ }
+ }
+ }
+
+ private void postStackChangedDelta() {
+ for (TCFModelProxy p : model.getModelProxies()) {
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(p.getPresentationContext().getId())) {
+ p.addDelta(this, IModelDelta.CONTENT);
+ }
+ }
+ }
+
+ void onContextAdded(IRunControl.RunControlContext context) {
+ children_exec.onContextAdded(context);
+ }
+
+ void onContextChanged(IRunControl.RunControlContext context) {
+ assert !isDisposed();
+ full_name.reset();
+ run_context.reset(context);
+ symbols_node.reset();
+ memory_node.reset();
+ signal_mask.reset();
+ if (state.isValid()) {
+ TCFContextState s = state.getData();
+ if (s == null || s.is_suspended) state.reset();
+ }
+ children_stack.reset();
+ children_stack.onSourceMappingChange();
+ children_regs.reset();
+ children_exec.onAncestorContextChanged();
+ for (TCFNodeSymbol s : symbols.values()) s.onMemoryMapChanged();
+ postAllChangedDelta();
+ }
+
+ void onAncestorContextChanged() {
+ full_name.reset();
+ }
+
+ void onContextAdded(IMemory.MemoryContext context) {
+ children_exec.onContextAdded(context);
+ }
+
+ void onContextChanged(IMemory.MemoryContext context) {
+ assert !isDisposed();
+ clearLookupCaches();
+ mem_context.reset(context);
+ for (TCFNodeSymbol s : symbols.values()) s.onMemoryMapChanged();
+ postAllChangedDelta();
+ }
+
+ void onContextRemoved() {
+ assert !isDisposed();
+ resumed_cnt++;
+ resume_pending = false;
+ resumed_by_action = false;
+ dispose();
+ postContextRemovedDelta();
+ launch.removeContextActions(id);
+ }
+
+ void onExpressionAddedOrRemoved() {
+ children_exps.cancel();
+ children_stack.onExpressionAddedOrRemoved();
+ }
+
+ void onContainerSuspended() {
+ assert !isDisposed();
+ if (run_context.isValid()) {
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && !ctx.hasState()) return;
+ }
+ onContextSuspended(null, null, null);
+ }
+
+ void onContainerResumed() {
+ assert !isDisposed();
+ if (run_context.isValid()) {
+ IRunControl.RunControlContext ctx = run_context.getData();
+ if (ctx != null && !ctx.hasState()) return;
+ }
+ onContextResumed();
+ }
+
+ void onContextSuspended(String pc, String reason, Map<String,Object> params) {
+ assert !isDisposed();
+ if (pc != null) {
+ TCFContextState s = new TCFContextState();
+ s.is_suspended = true;
+ s.suspend_pc = pc;
+ s.suspend_reason = reason;
+ s.suspend_params = params;
+ state.reset(s);
+ }
+ else {
+ state.reset();
+ }
+ address.reset();
+ signal_mask.reset();
+ children_stack.onSuspended();
+ children_regs.onSuspended();
+ children_exps.onSuspended();
+ children_hover_exps.onSuspended();
+ children_log_exps.onSuspended();
+ for (TCFNodeSymbol s : symbols.values()) s.onExeStateChange();
+ if (model.getActiveAction(id) == null) {
+ boolean update_now = pc != null || resumed_by_action;
+ resumed_cnt++;
+ resume_pending = false;
+ resumed_by_action = false;
+ if (update_now) {
+ children_stack.postAllChangedDelta();
+ postAllAndParentsChangedDelta();
+ }
+ else {
+ final int cnt = ++resumed_cnt;
+ Protocol.invokeLater(500, new Runnable() {
+ public void run() {
+ if (cnt != resumed_cnt) return;
+ if (isDisposed()) return;
+ children_stack.postAllChangedDelta();
+ postAllAndParentsChangedDelta();
+ }
+ });
+ }
+ }
+ }
+
+ void onContextResumed() {
+ assert !isDisposed();
+ state.reset(new TCFContextState());
+ if (!resume_pending) {
+ final int cnt = ++resumed_cnt;
+ resume_pending = true;
+ resumed_by_action = model.getActiveAction(id) != null;
+ if (resumed_by_action) postAllChangedDelta();
+ Protocol.invokeLater(400, new Runnable() {
+ public void run() {
+ if (cnt != resumed_cnt) return;
+ if (isDisposed()) return;
+ children_stack.onResumed();
+ resume_pending = false;
+ postAllAndParentsChangedDelta();
+ model.onContextRunning();
+ }
+ });
+ }
+ }
+
+ void onContextActionDone() {
+ if (!state.isValid() || state.getData() == null || state.getData().is_suspended) {
+ resumed_cnt++;
+ resume_pending = false;
+ resumed_by_action = false;
+ }
+ postAllChangedDelta();
+ children_stack.postAllChangedDelta();
+ }
+
+ void onContextException(String msg) {
+ }
+
+ void onMemoryChanged(Number[] addr, long[] size) {
+ assert !isDisposed();
+ children_stack.onMemoryChanged();
+ children_exps.onMemoryChanged();
+ children_hover_exps.onMemoryChanged();
+ children_log_exps.onMemoryChanged();
+ postContentChangedDelta();
+ }
+
+ void onMemoryMapChanged() {
+ clearLookupCaches();
+ memory_map.reset();
+ children_modules.onMemoryMapChanged();
+ children_stack.onMemoryMapChanged();
+ children_exps.onMemoryMapChanged();
+ children_hover_exps.onMemoryMapChanged();
+ children_log_exps.onMemoryMapChanged();
+ postModulesChangedDelta();
+ }
+
+ void onRegistersChanged() {
+ children_stack.onRegistersChanged();
+ postContentChangedDelta();
+ }
+
+ void onRegisterValueChanged() {
+ if (state.isValid()) {
+ TCFContextState s = state.getData();
+ if (s == null || s.is_suspended) state.reset();
+ }
+ address.reset();
+ children_stack.onRegisterValueChanged();
+ children_exps.onRegisterValueChanged();
+ children_hover_exps.onRegisterValueChanged();
+ children_log_exps.onRegisterValueChanged();
+ postContentChangedDelta();
+ }
+
+ void onPreferencesChanged() {
+ if (delayed_children_list_delta && !model.getDelayChildrenListUpdates() ||
+ model.getAutoChildrenListUpdates()) postContentChangedDelta();
+ children_stack.onPreferencesChanged();
+ postStackChangedDelta();
+ }
+
+ void riseTraceLimit() {
+ children_stack.riseTraceLimit();
+ postStackChangedDelta();
+ }
+
+ public boolean hasSuspendedChildren(ChildrenStateInfo info, Runnable done) {
+ if (!children_exec.validate(done)) return false;
+ Map<String,TCFNode> m = children_exec.getData();
+ if (m == null || m.size() == 0) return true;
+ for (TCFNode n : m.values()) {
+ if (!(n instanceof TCFNodeExecContext)) continue;
+ TCFNodeExecContext e = (TCFNodeExecContext)n;
+ if (!e.run_context.validate(done)) return false;
+ IRunControl.RunControlContext ctx = e.run_context.getData();
+ if (ctx != null && ctx.hasState()) {
+ TCFDataCache<TCFContextState> state_cache = e.getState();
+ if (!state_cache.validate(done)) return false;
+ TCFContextState state_data = state_cache.getData();
+ if (state_data != null) {
+ if (!state_data.is_suspended) {
+ info.running = true;
+ }
+ else if (e.isNotActive()) {
+ info.not_active = true;
+ }
+ else {
+ info.suspended = true;
+ String r = model.getContextActionResult(e.id);
+ if (r == null) r = state_data.suspend_reason;
+ if (IRunControl.REASON_BREAKPOINT.equals(r)) info.breakpoint = true;
+ }
+ }
+ }
+ else {
+ if (!e.hasSuspendedChildren(info, done)) return false;
+ }
+ if (info.breakpoint && info.running) break;
+ }
+ return true;
+ }
+
+ @Override
+ public int compareTo(TCFNode n) {
+ if (n instanceof TCFNodeExecContext) {
+ TCFNodeExecContext f = (TCFNodeExecContext)n;
+ if (mem_seq_no < f.mem_seq_no) return -1;
+ if (mem_seq_no > f.mem_seq_no) return +1;
+ if (exe_seq_no < f.exe_seq_no) return -1;
+ if (exe_seq_no > f.exe_seq_no) return +1;
+ }
+ return id.compareTo(n.id);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExpression.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExpression.java
new file mode 100644
index 000000000..a83c10642
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExpression.java
@@ -0,0 +1,1618 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IExpressionManager;
+import org.eclipse.debug.core.model.IExpression;
+import org.eclipse.debug.core.model.IWatchExpression;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ICellModifier;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tcf.internal.debug.model.TCFContextState;
+import org.eclipse.tcf.internal.debug.ui.Activator;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.JSON;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IExpressions;
+import org.eclipse.tcf.services.IMemory;
+import org.eclipse.tcf.services.IMemory.MemoryError;
+import org.eclipse.tcf.services.IRegisters;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.util.TCFDataCache;
+import org.eclipse.tcf.util.TCFTask;
+
+public class TCFNodeExpression extends TCFNode implements IElementEditor, ICastToType, IWatchInExpressions, IDetailsProvider {
+
+ // TODO: User commands: Add Global Variables, Remove Global Variables
+ // TODO: enable Change Value user command
+
+ private final String script;
+ private final int index;
+ private final boolean deref;
+ private final String field_id;
+ private final String reg_id;
+ private final TCFData<IExpressions.Expression> var_expression;
+ private final TCFData<String> base_text;
+ private final TCFData<Expression> expression;
+ private final TCFData<IExpressions.Value> value;
+ private final TCFData<ISymbols.Symbol> type;
+ private final TCFData<String> type_name;
+ private final TCFData<String> string;
+ private final TCFData<String> expression_text;
+ private final TCFChildrenSubExpressions children;
+ private int sort_pos;
+ private boolean enabled = true;
+ private IExpressions.Value prev_value;
+ private IExpressions.Value next_value;
+
+ private static final RGB
+ rgb_error = new RGB(192, 0, 0),
+ rgb_highlight = new RGB(255, 255, 128),
+ rgb_disabled = new RGB(127, 127, 127);
+
+ private static int expr_cnt;
+
+ private class Expression {
+
+ final IExpressions.Expression expression;
+ boolean must_be_disposed;
+
+ Expression(IExpressions.Expression expression) {
+ assert expression != null;
+ this.expression = expression;
+ }
+
+ void dispose() {
+ if (!must_be_disposed) return;
+ if (channel.getState() == IChannel.STATE_OPEN) {
+ IExpressions exps = channel.getRemoteService(IExpressions.class);
+ exps.dispose(expression.getID(), new IExpressions.DoneDispose() {
+ public void doneDispose(IToken token, Exception error) {
+ if (error == null) return;
+ if (channel.getState() != IChannel.STATE_OPEN) return;
+ Activator.log("Error disposing remote expression evaluator", error);
+ }
+ });
+ }
+ must_be_disposed = false;
+ }
+ }
+
+ TCFNodeExpression(final TCFNode parent, final String script,
+ final String field_id, final String var_id, final String reg_id,
+ final int index, final boolean deref) {
+ super(parent, var_id != null ? var_id : "Expr" + expr_cnt++);
+ assert script != null || field_id != null || var_id != null || reg_id != null || index >= 0;
+ this.script = script;
+ this.field_id = field_id;
+ this.reg_id = reg_id;
+ this.index = index;
+ this.deref = deref;
+ var_expression = new TCFData<IExpressions.Expression>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ IExpressions exps = launch.getService(IExpressions.class);
+ if (exps == null || var_id == null) {
+ set(null, null, null);
+ return true;
+ }
+ command = exps.getContext(var_id, new IExpressions.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IExpressions.Expression context) {
+ set(token, error, context);
+ }
+ });
+ return false;
+ }
+ };
+ base_text = new TCFData<String>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ /* Compute expression script, not including type cast */
+ if (script != null) {
+ set(null, null, script);
+ return true;
+ }
+ if (var_id != null) {
+ if (!var_expression.validate(this)) return false;
+ Throwable err = null;
+ String exp = null;
+ if (var_expression.getData() == null) {
+ err = var_expression.getError();
+ }
+ else {
+ exp = var_expression.getData().getExpression();
+ if (exp == null) err = new Exception("Missing 'Expression' property");
+ }
+ set(null, err, exp);
+ return true;
+ }
+ if (reg_id != null) {
+ set(null, null, "${" + reg_id + "}");
+ return true;
+ }
+ TCFNode n = parent;
+ while (n instanceof TCFNodeArrayPartition) n = n.parent;
+ TCFDataCache<String> t = ((TCFNodeExpression)n).base_text;
+ if (!t.validate(this)) return false;
+ String e = t.getData();
+ if (e == null) {
+ set(null, t.getError(), null);
+ return true;
+ }
+ String cast = model.getCastToType(n.id);
+ if (cast != null) e = "(" + cast + ")(" + e + ")";
+ if (field_id != null) {
+ e = "(" + e + ")" + (deref ? "->" : ".") + "${" + field_id + "}";
+ }
+ else if (index == 0) {
+ e = "*(" + e + ")";
+ }
+ else if (index > 0) {
+ e = "(" + e + ")[" + index + "]";
+ }
+ set(null, null, e);
+ return true;
+ }
+ };
+ expression_text = new TCFData<String>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ /* Compute human readable expression script,
+ * including type cast, and using variable names instead of IDs */
+ String expr_text = null;
+ if (script != null) {
+ expr_text = script;
+ }
+ else if (var_id != null) {
+ if (!var_expression.validate(this)) return false;
+ IExpressions.Expression e = var_expression.getData();
+ if (e != null) {
+ TCFDataCache<ISymbols.Symbol> var = model.getSymbolInfoCache(e.getSymbolID());
+ if (var != null) {
+ if (!var.validate(this)) return false;
+ if (var.getData() != null) expr_text = var.getData().getName();
+ }
+ }
+ }
+ else if (reg_id != null) {
+ if (!model.createNode(reg_id, this)) return false;
+ if (isValid()) return true;
+ TCFNodeRegister reg_node = (TCFNodeRegister)model.getNode(reg_id);
+ for (;;) {
+ TCFDataCache<IRegisters.RegistersContext> ctx_cache = reg_node.getContext();
+ if (!ctx_cache.validate(this)) return false;
+ IRegisters.RegistersContext ctx_data = ctx_cache.getData();
+ if (ctx_data == null) {
+ set(null, ctx_cache.getError(), null);
+ return true;
+ }
+ expr_text = expr_text == null ? ctx_data.getName() : ctx_data.getName() + "." + expr_text;
+ if (!(reg_node.parent instanceof TCFNodeRegister)) break;
+ reg_node = (TCFNodeRegister)reg_node.parent;
+ }
+ expr_text = "$" + expr_text;
+ }
+ else {
+ TCFDataCache<?> pending = null;
+ TCFDataCache<ISymbols.Symbol> field = model.getSymbolInfoCache(field_id);
+ if (field != null && !field.validate()) pending = field;
+ if (!base_text.validate()) pending = base_text;
+ if (pending != null) {
+ pending.wait(this);
+ return false;
+ }
+ String parent_text = "";
+ TCFNode n = parent;
+ while (n instanceof TCFNodeArrayPartition) n = n.parent;
+ TCFDataCache<String> parent_text_cache = ((TCFNodeExpression)n).expression_text;
+ if (!parent_text_cache.validate(this)) return false;
+ if (parent_text_cache.getData() != null) {
+ parent_text = parent_text_cache.getData();
+ // surround with parentheses if not a simple identifier
+ if (!parent_text.matches("\\w*")) {
+ parent_text = '(' + parent_text + ')';
+ }
+ }
+ if (index >= 0) {
+ if (index == 0 && deref) {
+ expr_text = "*" + parent_text;
+ }
+ else {
+ expr_text = parent_text + "[" + index + "]";
+ }
+ }
+ if (expr_text == null && field != null && field.getData() != null) {
+ expr_text = parent_text + (deref ? "->" : ".") + field.getData().getName();
+ }
+ if (expr_text == null && base_text.getData() != null) expr_text = base_text.getData();
+ }
+ if (expr_text != null) {
+ String cast = model.getCastToType(id);
+ if (cast != null) expr_text = "(" + cast + ")(" + expr_text + ")";
+ }
+ set(null, null, expr_text);
+ return true;
+ }
+ };
+ expression = new TCFData<Expression>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ IExpressions exps = launch.getService(IExpressions.class);
+ if (exps == null) {
+ set(null, null, null);
+ return true;
+ }
+ String cast = model.getCastToType(id);
+ if (var_id != null && cast == null) {
+ if (!var_expression.validate(this)) return false;
+ Expression exp = null;
+ if (var_expression.getData() != null) exp = new Expression(var_expression.getData());
+ set(null, var_expression.getError(), exp);
+ return true;
+ }
+ if (!base_text.validate(this)) return false;
+ String e = base_text.getData();
+ if (e == null) {
+ set(null, base_text.getError(), null);
+ return true;
+ }
+ if (cast != null) e = "(" + cast + ")(" + e + ")";
+ TCFNode n = getRootExpression().parent;
+ if (n instanceof TCFNodeStackFrame && ((TCFNodeStackFrame)n).isEmulated()) n = n.parent;
+ command = exps.create(n.id, null, e, new IExpressions.DoneCreate() {
+ public void doneCreate(IToken token, Exception error, IExpressions.Expression context) {
+ Expression e = null;
+ if (context != null) {
+ e = new Expression(context);
+ e.must_be_disposed = true;
+ }
+ if (!isDisposed()) set(token, error, e);
+ else if (e != null) e.dispose();
+ }
+ });
+ return false;
+ }
+ @Override
+ public void reset() {
+ if (isValid() && getData() != null) getData().dispose();
+ super.reset();
+ }
+ @Override
+ public void dispose() {
+ if (isValid() && getData() != null) getData().dispose();
+ super.dispose();
+ }
+ };
+ value = new TCFData<IExpressions.Value>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ Boolean b = usePrevValue(this);
+ if (b == null) return false;
+ if (b) {
+ set(null, null, prev_value);
+ return true;
+ }
+ if (!expression.validate(this)) return false;
+ final Expression exp = expression.getData();
+ if (exp == null) {
+ set(null, expression.getError(), null);
+ return true;
+ }
+ IExpressions exps = launch.getService(IExpressions.class);
+ command = exps.evaluate(exp.expression.getID(), new IExpressions.DoneEvaluate() {
+ public void doneEvaluate(IToken token, Exception error, IExpressions.Value value) {
+ if (error != null) {
+ Boolean b = usePrevValue(null);
+ if (b != null && b) {
+ set(token, null, prev_value);
+ return;
+ }
+ }
+ set(token, error, value);
+ }
+ });
+ return false;
+ }
+ public void reset() {
+ super.reset();
+ }
+ };
+ type = new TCFData<ISymbols.Symbol>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ String type_id = null;
+ if (!value.validate(this)) return false;
+ IExpressions.Value val = value.getData();
+ if (val != null) type_id = val.getTypeID();
+ if (type_id == null) {
+ if (!expression.validate(this)) return false;
+ Expression exp = expression.getData();
+ if (exp != null) type_id = exp.expression.getTypeID();
+ }
+ if (type_id == null) {
+ set(null, value.getError(), null);
+ return true;
+ }
+ TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(type_id);
+ if (type_cache == null) {
+ set(null, null, null);
+ return true;
+ }
+ if (!type_cache.validate(this)) return false;
+ set(null, type_cache.getError(), type_cache.getData());
+ return true;
+ }
+ };
+ string = new TCFData<String>(channel) {
+ IMemory.MemoryContext mem;
+ ISymbols.Symbol base_type_data;
+ BigInteger addr;
+ byte[] buf;
+ int size;
+ int offs;
+ @Override
+ protected boolean startDataRetrieval() {
+ if (addr != null) {
+ if (mem == null) {
+ TCFDataCache<TCFNodeExecContext> mem_node_cache = model.searchMemoryContext(parent);
+ if (mem_node_cache == null) {
+ set(null, new Exception("Context does not provide memory access"), null);
+ return true;
+ }
+ if (!mem_node_cache.validate(this)) return false;
+ if (mem_node_cache.getError() != null) {
+ set(null, mem_node_cache.getError(), null);
+ return true;
+ }
+ TCFNodeExecContext mem_node = mem_node_cache.getData();
+ if (mem_node == null) {
+ set(null, new Exception("Context does not provide memory access"), null);
+ return true;
+ }
+ TCFDataCache<IMemory.MemoryContext> mem_ctx_cache = mem_node.getMemoryContext();
+ if (!mem_ctx_cache.validate(this)) return false;
+ if (mem_ctx_cache.getError() != null) {
+ set(null, mem_ctx_cache.getError(), null);
+ return true;
+ }
+ mem = mem_ctx_cache.getData();
+ if (mem == null) {
+ set(null, new Exception("Context does not provide memory access"), null);
+ return true;
+ }
+ }
+ if (size == 0) {
+ // data is ASCII string
+ if (buf == null) buf = new byte[256];
+ if (offs >= buf.length) {
+ byte[] tmp = new byte[buf.length * 2];
+ System.arraycopy(buf, 0, tmp, 0, buf.length);
+ buf = tmp;
+ }
+ command = mem.get(addr.add(BigInteger.valueOf(offs)), 1, buf, offs, 1, 0, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ if (error != null) {
+ set(command, error, null);
+ }
+ else if (buf[offs] == 0 || offs >= 2048) {
+ set(command, null, toASCIIString(buf, 0, offs, '"'));
+ }
+ else if (command == token) {
+ command = null;
+ offs++;
+ run();
+ }
+ }
+ });
+ return false;
+ }
+ // data is a struct
+ if (offs != size) {
+ if (buf == null || buf.length < size) buf = new byte[size];
+ command = mem.get(addr, 1, buf, 0, size, 0, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ if (error != null) {
+ set(command, error, null);
+ }
+ else if (command == token) {
+ command = null;
+ offs = size;
+ run();
+ }
+ }
+ });
+ return false;
+ }
+ StyledStringBuffer bf = new StyledStringBuffer();
+ bf.append('{');
+ if (!appendCompositeValueText(bf, 1, base_type_data, buf, 0, size, mem.isBigEndian(), this)) return false;
+ bf.append('}');
+ set(null, null, bf.toString());
+ return true;
+ }
+ if (!type.validate(this)) return false;
+ ISymbols.Symbol type_data = type.getData();
+ if (type_data != null) {
+ switch (type_data.getTypeClass()) {
+ case pointer:
+ case array:
+ TCFDataCache<ISymbols.Symbol> base_type_cahce = model.getSymbolInfoCache(type_data.getBaseTypeID());
+ if (base_type_cahce != null) {
+ if (!base_type_cahce.validate(this)) return false;
+ base_type_data = base_type_cahce.getData();
+ if (base_type_data != null) {
+ offs = 0;
+ size = base_type_data.getSize();
+ switch (base_type_data.getTypeClass()) {
+ case integer:
+ case cardinal:
+ if (base_type_data.getSize() != 1) break;
+ size = 0; // read until character = 0
+ case composite:
+ if (base_type_data.getSize() == 0) break;
+ if (type_data.getTypeClass() == ISymbols.TypeClass.array &&
+ base_type_data.getTypeClass() == ISymbols.TypeClass.composite) break;
+ if (!value.validate(this)) return false;
+ IExpressions.Value v = value.getData();
+ if (v != null) {
+ byte[] data = v.getValue();
+ if (type_data.getTypeClass() == ISymbols.TypeClass.array) {
+ set(null, null, toASCIIString(data, 0, data.length, '"'));
+ return true;
+ }
+ BigInteger a = TCFNumberFormat.toBigInteger(data, 0, data.length, v.isBigEndian(), false);
+ if (!a.equals(BigInteger.valueOf(0))) {
+ addr = a;
+ Protocol.invokeLater(this);
+ return false;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case integer:
+ case cardinal:
+ if (type_data.getSize() == 1) {
+ if (!value.validate(this)) return false;
+ IExpressions.Value v = value.getData();
+ if (v != null) {
+ byte[] data = v.getValue();
+ set(null, null, toASCIIString(data, 0, data.length, '\''));
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ set(null, null, null);
+ return true;
+ }
+ @Override
+ public void reset() {
+ super.reset();
+ mem = null;
+ addr = null;
+ }
+ };
+ type_name = new TCFData<String>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!type.validate(this)) return false;
+ if (type.getData() == null) {
+ if (!value.validate(this)) return false;
+ IExpressions.Value val = value.getData();
+ if (val != null && val.getValue() != null) {
+ String s = getTypeName(val.getTypeClass(), val.getValue().length);
+ if (s != null) {
+ set(null, null, s);
+ return true;
+ }
+ }
+ }
+ StringBuffer bf = new StringBuffer();
+ if (!getTypeName(bf, type, this)) return false;
+ set(null, null, bf.toString());
+ return true;
+ }
+ };
+ children = new TCFChildrenSubExpressions(this, 0, 0, 0);
+ }
+
+ private TCFNodeExpression getRootExpression() {
+ TCFNode n = this;
+ while (n.parent instanceof TCFNodeExpression || n.parent instanceof TCFNodeArrayPartition) n = n.parent;
+ return (TCFNodeExpression)n;
+ }
+
+ private void postAllChangedDelta() {
+ TCFNodeExpression n = getRootExpression();
+ for (TCFModelProxy p : model.getModelProxies()) {
+ String id = p.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id) && n.script != null ||
+ TCFModel.ID_EXPRESSION_HOVER.equals(id) && n.script != null ||
+ IDebugUIConstants.ID_VARIABLE_VIEW.equals(id) && n.script == null) {
+ p.addDelta(this, IModelDelta.STATE | IModelDelta.CONTENT);
+ }
+ }
+ }
+
+ void onSuspended() {
+ prev_value = next_value;
+ if (expression.isValid() && expression.getError() != null) expression.reset();
+ value.reset();
+ type.reset();
+ type_name.reset();
+ string.reset();
+ children.onSuspended();
+ // No need to post delta: parent posted CONTENT
+ }
+
+ void onRegisterValueChanged() {
+ value.reset();
+ type.reset();
+ type_name.reset();
+ string.reset();
+ children.onRegisterValueChanged();
+ postAllChangedDelta();
+ }
+
+ void onMemoryChanged() {
+ value.reset();
+ type.reset();
+ type_name.reset();
+ string.reset();
+ children.onMemoryChanged();
+ if (parent instanceof TCFNodeExpression) return;
+ if (parent instanceof TCFNodeArrayPartition) return;
+ postAllChangedDelta();
+ }
+
+ void onMemoryMapChanged() {
+ value.reset();
+ type.reset();
+ type_name.reset();
+ string.reset();
+ children.onMemoryMapChanged();
+ if (parent instanceof TCFNodeExpression) return;
+ if (parent instanceof TCFNodeArrayPartition) return;
+ postAllChangedDelta();
+ }
+
+ void onValueChanged() {
+ value.reset();
+ type.reset();
+ type_name.reset();
+ string.reset();
+ children.onValueChanged();
+ postAllChangedDelta();
+ }
+
+ public void onCastToTypeChanged() {
+ expression.cancel();
+ value.cancel();
+ type.cancel();
+ type_name.cancel();
+ string.cancel();
+ expression_text.cancel();
+ children.onCastToTypeChanged();
+ postAllChangedDelta();
+ }
+
+ public String getScript() {
+ return script;
+ }
+
+ String getFieldID() {
+ return field_id;
+ }
+
+ String getRegisterID() {
+ return reg_id;
+ }
+
+ int getIndex() {
+ return index;
+ }
+
+ boolean isDeref() {
+ return deref;
+ }
+
+ void setSortPosition(int sort_pos) {
+ this.sort_pos = sort_pos;
+ }
+
+ void setEnabled(boolean enabled) {
+ if (this.enabled == enabled) return;
+ this.enabled = enabled;
+ postAllChangedDelta();
+ }
+
+ /**
+ * Get expression properties cache.
+ * The cache is empty is the expression does not represent a variable.
+ * @return The expression properties cache.
+ */
+ public TCFDataCache<IExpressions.Expression> getVariable() {
+ return var_expression;
+ }
+
+ /**
+ * Get expression value cache.
+ * @return The expression value cache.
+ */
+ public TCFDataCache<IExpressions.Value> getValue() {
+ return value;
+ }
+
+ /**
+ * Get expression type cache.
+ * @return The expression type cache.
+ */
+ public TCFDataCache<ISymbols.Symbol> getType() {
+ return type;
+ }
+
+ /**
+ * Get human readable expression script,
+ * including type cast, and using variable names instead of IDs.
+ */
+ public TCFDataCache<String> getExpressionText() {
+ return expression_text;
+ }
+
+ private Boolean usePrevValue(Runnable done) {
+ // Check if view should show old value.
+ // Old value is shown if context is running or
+ // stack trace does not contain expression parent frame.
+ // Return null if waiting for cache update.
+ if (prev_value == null) return false;
+ if (parent instanceof TCFNodeStackFrame) {
+ TCFNodeExecContext exe = (TCFNodeExecContext)parent.parent;
+ TCFDataCache<TCFContextState> state_cache = exe.getState();
+ if (!state_cache.validate(done)) return null;
+ TCFContextState state = state_cache.getData();
+ if (state == null || !state.is_suspended) return true;
+ TCFChildrenStackTrace stack_trace_cache = exe.getStackTrace();
+ if (!stack_trace_cache.validate(done)) return null;
+ if (stack_trace_cache.getData().get(parent.id) == null) return true;
+ }
+ else if (parent instanceof TCFNodeExecContext) {
+ TCFNodeExecContext exe = (TCFNodeExecContext)parent;
+ TCFDataCache<TCFContextState> state_cache = exe.getState();
+ if (!state_cache.validate(done)) return null;
+ TCFContextState state = state_cache.getData();
+ if (state == null || !state.is_suspended) return true;
+ }
+ return false;
+ }
+
+ private String getTypeName(ISymbols.TypeClass type_class, int size) {
+ String s = null;
+ switch (type_class) {
+ case integer:
+ if (size == 0) s = "<Void>";
+ else s = "<Integer-" + (size * 8) + ">";
+ break;
+ case cardinal:
+ if (size == 0) s = "<Void>";
+ else s = "<Unsigned-" + (size * 8) + ">";
+ break;
+ case real:
+ if (size == 0) s = "<Void>";
+ else s = "<Float-" + (size * 8) + ">";
+ break;
+ }
+ return s;
+ }
+
+ private boolean getTypeName(StringBuffer bf, TCFDataCache<ISymbols.Symbol> type_cache, Runnable done) {
+ String name = null;
+ for (;;) {
+ String s = null;
+ boolean get_base_type = false;
+ if (!type_cache.validate(done)) return false;
+ ISymbols.Symbol type_symbol = type_cache.getData();
+ if (type_symbol != null) {
+ int flags = type_symbol.getFlags();
+ s = type_symbol.getName();
+ if (s != null && type_symbol.getTypeClass() == ISymbols.TypeClass.composite) {
+ if ((flags & ISymbols.SYM_FLAG_UNION_TYPE) != 0) s = "union " + s;
+ else if ((flags & ISymbols.SYM_FLAG_CLASS_TYPE) != 0) s = "class " + s;
+ else if ((flags & ISymbols.SYM_FLAG_INTERFACE_TYPE) != 0) s = "interface " + s;
+ else s = "struct " + s;
+ }
+ if (s == null) s = getTypeName(type_symbol.getTypeClass(), type_symbol.getSize());
+ if (s == null) {
+ switch (type_symbol.getTypeClass()) {
+ case pointer:
+ s = "*";
+ if ((flags & ISymbols.SYM_FLAG_REFERENCE) != 0) s = "&";
+ get_base_type = true;
+ break;
+ case member_pointer:
+ {
+ String id = type_symbol.getContainerID();
+ if (id != null) {
+ TCFDataCache<ISymbols.Symbol> cls_cache = model.getSymbolInfoCache(id);
+ if (!cls_cache.validate(done)) return false;
+ ISymbols.Symbol cls_data = cls_cache.getData();
+ if (cls_data != null) {
+ String cls_name = cls_data.getName();
+ if (cls_name != null) s = cls_name + "::*";
+ }
+ }
+ if (s == null) s = "::*";
+ }
+ get_base_type = true;
+ break;
+ case array:
+ s = "[" + type_symbol.getLength() + "]";
+ get_base_type = true;
+ break;
+ case composite:
+ s = "<Structure>";
+ break;
+ case function:
+ {
+ TCFDataCache<String[]> children_cache = model.getSymbolChildrenCache(type_symbol.getID());
+ if (!children_cache.validate(done)) return false;
+ if (children_cache.getError() == null) {
+ String[] children = children_cache.getData();
+ if (children != null) {
+ StringBuffer args = new StringBuffer();
+ if (name != null) {
+ args.append('(');
+ args.append(name);
+ args.append(')');
+ name = null;
+ }
+ args.append('(');
+ for (String id : children) {
+ if (id != children[0]) args.append(',');
+ if (!getTypeName(args, model.getSymbolInfoCache(id), done)) return false;
+ }
+ args.append(')');
+ s = args.toString();
+ get_base_type = true;
+ break;
+ }
+ }
+ }
+ s = "<Function>";
+ break;
+ }
+ }
+ if (s != null) {
+ if ((flags & ISymbols.SYM_FLAG_VOLATILE_TYPE) != 0) s = "volatile " + s;
+ if ((flags & ISymbols.SYM_FLAG_CONST_TYPE) != 0) s = "const " + s;
+ }
+ }
+ if (s == null) {
+ name = "N/A";
+ break;
+ }
+ if (name == null) name = s;
+ else if (!get_base_type) name = s + " " + name;
+ else name = s + name;
+
+ if (!get_base_type) break;
+
+ type_cache = model.getSymbolInfoCache(type_symbol.getBaseTypeID());
+ if (type_cache == null) {
+ name = "N/A";
+ break;
+ }
+ }
+ bf.append(name);
+ return true;
+ }
+
+ private String toASCIIString(byte[] data, int offs, int size, char quote_char) {
+ StringBuffer bf = new StringBuffer();
+ bf.append(quote_char);
+ for (int i = 0; i < size; i++) {
+ int ch = data[offs + i] & 0xff;
+ if (ch >= ' ' && ch < 0x7f) {
+ bf.append((char)ch);
+ }
+ else {
+ switch (ch) {
+ case '\r': bf.append("\\r"); break;
+ case '\n': bf.append("\\n"); break;
+ case '\b': bf.append("\\b"); break;
+ case '\t': bf.append("\\t"); break;
+ case '\f': bf.append("\\f"); break;
+ default:
+ bf.append('\\');
+ bf.append((char)('0' + ch / 64));
+ bf.append((char)('0' + ch / 8 % 8));
+ bf.append((char)('0' + ch % 8));
+ }
+ }
+ }
+ if (data.length <= offs + size || data[offs + size] == 0) bf.append(quote_char);
+ else bf.append("...");
+ return bf.toString();
+ }
+
+ private String toNumberString(int radix, ISymbols.TypeClass t, byte[] data, int offs, int size, boolean big_endian) {
+ if (size <= 0 || size > 16) return "";
+ if (radix != 16) {
+ switch (t) {
+ case array:
+ case composite:
+ return "";
+ }
+ }
+ String s = null;
+ if (data == null) s = "N/A";
+ if (s == null && radix == 10) {
+ if (t != null) {
+ switch (t) {
+ case integer:
+ s = TCFNumberFormat.toBigInteger(data, offs, size, big_endian, true).toString();
+ break;
+ case real:
+ s = TCFNumberFormat.toFPString(data, offs, size, big_endian);
+ break;
+ }
+ }
+ }
+ if (s == null) {
+ s = TCFNumberFormat.toBigInteger(data, offs, size, big_endian, false).toString(radix);
+ switch (radix) {
+ case 8:
+ if (!s.startsWith("0")) s = "0" + s;
+ break;
+ case 16:
+ if (s.length() < size * 2) {
+ StringBuffer bf = new StringBuffer();
+ while (bf.length() + s.length() < size * 2) bf.append('0');
+ bf.append(s);
+ s = bf.toString();
+ }
+ break;
+ }
+ }
+ if (s == null) s = "N/A";
+ return s;
+ }
+
+ private String toNumberString(int radix) {
+ String s = null;
+ IExpressions.Value val = value.getData();
+ if (val != null) {
+ byte[] data = val.getValue();
+ if (data != null) {
+ ISymbols.TypeClass t = val.getTypeClass();
+ if (t == ISymbols.TypeClass.unknown && type.getData() != null) t = type.getData().getTypeClass();
+ s = toNumberString(radix, t, data, 0, data.length, val.isBigEndian());
+ }
+ }
+ if (s == null) s = "N/A";
+ return s;
+ }
+
+ private void setLabel(ILabelUpdate result, String name, int col, int radix) {
+ String s = toNumberString(radix);
+ if (name == null) {
+ result.setLabel(s, col);
+ }
+ else {
+ result.setLabel(name + " = " + s, col);
+ }
+ }
+
+ private boolean isValueChanged(IExpressions.Value x, IExpressions.Value y) {
+ if (x == null || y == null) return false;
+ byte[] xb = x.getValue();
+ byte[] yb = y.getValue();
+ if (xb == null || yb == null) return false;
+ if (xb.length != yb.length) return true;
+ for (int i = 0; i < xb.length; i++) {
+ if (xb[i] != yb[i]) return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean getData(ILabelUpdate result, Runnable done) {
+ if (enabled || script == null) {
+ TCFDataCache<ISymbols.Symbol> field = model.getSymbolInfoCache(field_id);
+ TCFDataCache<?> pending = null;
+ if (field != null && !field.validate()) pending = field;
+ if (reg_id != null && !expression_text.validate(done)) pending = expression_text;
+ if (!var_expression.validate()) pending = var_expression;
+ if (!base_text.validate()) pending = base_text;
+ if (!value.validate()) pending = value;
+ if (!type.validate()) pending = type;
+ if (pending != null) {
+ pending.wait(done);
+ return false;
+ }
+ String name = null;
+ if (index >= 0) {
+ if (index == 0 && deref) {
+ name = "*";
+ }
+ else {
+ name = "[" + index + "]";
+ }
+ }
+ if (name == null && field != null && field.getData() != null) name = field.getData().getName();
+ if (name == null && reg_id != null && expression_text.getData() != null) name = expression_text.getData();
+ if (name == null && var_expression.getData() != null) {
+ TCFDataCache<ISymbols.Symbol> var = model.getSymbolInfoCache(var_expression.getData().getSymbolID());
+ if (var != null) {
+ if (!var.validate(done)) return false;
+ ISymbols.Symbol var_data = var.getData();
+ if (var_data != null) {
+ name = var_data.getName();
+ if (name == null && var_data.getFlag(ISymbols.SYM_FLAG_VARARG)) name = "<VarArg>";
+ if (name == null) name = "<" + var_data.getID() + ">";
+ }
+ }
+ }
+ if (name == null && base_text.getData() != null) name = base_text.getData();
+ if (name != null) {
+ String cast = model.getCastToType(id);
+ if (cast != null) name = "(" + cast + ")(" + name + ")";
+ }
+ Throwable error = base_text.getError();
+ if (error == null) error = value.getError();
+ String[] cols = result.getColumnIds();
+ if (error != null) {
+ if (cols == null || cols.length <= 1) {
+ result.setForeground(rgb_error, 0);
+ result.setLabel(name + ": N/A", 0);
+ }
+ else {
+ for (int i = 0; i < cols.length; i++) {
+ String c = cols[i];
+ if (c.equals(TCFColumnPresentationExpression.COL_NAME)) {
+ result.setLabel(name, i);
+ }
+ else if (c.equals(TCFColumnPresentationExpression.COL_TYPE)) {
+ if (!type_name.validate(done)) return false;
+ result.setLabel(type_name.getData(), i);
+ }
+ else {
+ result.setForeground(rgb_error, i);
+ result.setLabel("N/A", i);
+ }
+ }
+ }
+ }
+ else {
+ if (cols == null) {
+ setLabel(result, name, 0, 16);
+ }
+ else {
+ for (int i = 0; i < cols.length; i++) {
+ String c = cols[i];
+ if (c.equals(TCFColumnPresentationExpression.COL_NAME)) {
+ result.setLabel(name, i);
+ }
+ else if (c.equals(TCFColumnPresentationExpression.COL_TYPE)) {
+ if (!type_name.validate(done)) return false;
+ result.setLabel(type_name.getData(), i);
+ }
+ else if (c.equals(TCFColumnPresentationExpression.COL_HEX_VALUE)) {
+ setLabel(result, null, i, 16);
+ }
+ else if (c.equals(TCFColumnPresentationExpression.COL_DEC_VALUE)) {
+ setLabel(result, null, i, 10);
+ }
+ }
+ }
+ }
+ next_value = value.getData();
+ if (isValueChanged(prev_value, next_value)) {
+ result.setBackground(rgb_highlight, 0);
+ if (cols != null) {
+ for (int i = 1; i < cols.length; i++) {
+ result.setBackground(rgb_highlight, i);
+ }
+ }
+ }
+ ISymbols.TypeClass type_class = ISymbols.TypeClass.unknown;
+ ISymbols.Symbol type_symbol = type.getData();
+ if (type_symbol != null) {
+ type_class = type_symbol.getTypeClass();
+ }
+ switch (type_class) {
+ case pointer:
+ result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE_POINTER), 0);
+ break;
+ case composite:
+ case array:
+ result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE_AGGREGATE), 0);
+ break;
+ default:
+ result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE), 0);
+ }
+ }
+ else {
+ String[] cols = result.getColumnIds();
+ if (cols == null || cols.length <= 1) {
+ result.setForeground(rgb_disabled, 0);
+ result.setLabel(script, 0);
+ }
+ else {
+ for (int i = 0; i < cols.length; i++) {
+ String c = cols[i];
+ if (c.equals(TCFColumnPresentationExpression.COL_NAME)) {
+ result.setForeground(rgb_disabled, i);
+ result.setLabel(script, i);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private void appendErrorText(StringBuffer bf, Throwable error) {
+ if (error == null) return;
+ bf.append("Exception: ");
+ bf.append(TCFModel.getErrorMessage(error, true));
+ }
+
+ private boolean appendArrayValueText(StyledStringBuffer bf, int level, ISymbols.Symbol type,
+ byte[] data, int offs, int size, boolean big_endian, Runnable done) {
+ assert offs + size <= data.length;
+ int length = type.getLength();
+ bf.append('[');
+ if (length > 0) {
+ int elem_size = size / length;
+ for (int n = 0; n < length; n++) {
+ if (n >= 100) {
+ bf.append("...");
+ break;
+ }
+ if (n > 0) bf.append(", ");
+ if (!appendValueText(bf, level + 1, type.getBaseTypeID(),
+ data, offs + n * elem_size, elem_size, big_endian, done)) return false;
+ }
+ }
+ bf.append(']');
+ return true;
+ }
+
+ private boolean appendCompositeValueText(StyledStringBuffer bf, int level, ISymbols.Symbol type,
+ byte[] data, int offs, int size, boolean big_endian, Runnable done) {
+ TCFDataCache<String[]> children_cache = model.getSymbolChildrenCache(type.getID());
+ if (children_cache == null) {
+ bf.append("...");
+ return true;
+ }
+ if (!children_cache.validate(done)) return false;
+ String[] children_data = children_cache.getData();
+ if (children_data == null) {
+ bf.append("...");
+ return true;
+ }
+ int cnt = 0;
+ TCFDataCache<?> pending = null;
+ for (String id : children_data) {
+ TCFDataCache<ISymbols.Symbol> field_cache = model.getSymbolInfoCache(id);
+ if (!field_cache.validate()) {
+ pending = field_cache;
+ continue;
+ }
+ ISymbols.Symbol field_data = field_cache.getData();
+ if (field_data == null) continue;
+ if (field_data.getSymbolClass() != ISymbols.SymbolClass.reference) continue;
+ String name = field_data.getName();
+ int f_offs = field_data.getOffset();
+ int f_size = field_data.getSize();
+ if (name == null) {
+ // Super-class members
+ if (offs + f_offs + f_size > data.length) {
+ continue;
+ }
+ else {
+ StyledStringBuffer bf1 = new StyledStringBuffer();
+ if (!appendCompositeValueText(bf1, level, field_data, data,
+ offs + f_offs, f_size, big_endian, done)) return false;
+ if (bf1.length() > 0) {
+ if (cnt > 0) bf.append(", ");
+ bf.append(bf1);
+ cnt++;
+ }
+ }
+ }
+ else {
+ if (cnt > 0) bf.append(", ");
+ bf.append(name);
+ bf.append('=');
+ if (offs + f_offs + f_size > data.length) {
+ bf.append('?');
+ }
+ else {
+ if (!appendValueText(bf, level + 1, field_data.getTypeID(),
+ data, offs + f_offs, f_size, big_endian, done)) return false;
+ }
+ cnt++;
+ }
+ }
+ if (pending == null) return true;
+ pending.wait(done);
+ return false;
+ }
+
+ private boolean appendValueText(StyledStringBuffer bf, int level, String type_id,
+ byte[] data, int offs, int size, boolean big_endian, Runnable done) {
+ if (data == null) return true;
+ ISymbols.Symbol type_data = null;
+ if (type_id != null) {
+ TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(type_id);
+ if (!type_cache.validate(done)) return false;
+ type_data = type_cache.getData();
+ }
+ if (type_data == null) {
+ ISymbols.TypeClass type_class = ISymbols.TypeClass.unknown;
+ if (!value.validate(done)) return false;
+ if (value.getData() != null) type_class = value.getData().getTypeClass();
+ if (level == 0) {
+ assert offs == 0;
+ assert size == data.length;
+ String s = getTypeName(type_class, size);
+ if (s == null) s = "not available";
+ bf.append("Type: ", SWT.BOLD);
+ bf.append(s);
+ bf.append('\n');
+ bf.append("Size: ", SWT.BOLD);
+ bf.append(size);
+ bf.append(size == 1 ? " byte\n" : " bytes\n");
+ if (size > 0) {
+ if (type_class == ISymbols.TypeClass.integer || type_class == ISymbols.TypeClass.real) {
+ bf.append("Dec: ", SWT.BOLD);
+ bf.append(toNumberString(10, type_class, data, offs, size, big_endian));
+ bf.append("\n");
+ }
+ bf.append("Hex: ", SWT.BOLD);
+ bf.append(toNumberString(16, type_class, data, offs, size, big_endian));
+ bf.append("\n");
+ }
+ }
+ else if (type_class == ISymbols.TypeClass.integer || type_class == ISymbols.TypeClass.real) {
+ bf.append(toNumberString(10, type_class, data, offs, size, big_endian));
+ }
+ else {
+ bf.append(toNumberString(16, type_class, data, offs, size, big_endian));
+ }
+ return true;
+ }
+ if (level == 0) {
+ if (!string.validate(done)) return false;
+ Throwable e = string.getError();
+ String s = string.getData();
+ if (s != null) {
+ bf.append(s);
+ bf.append("\n");
+ }
+ else if (e != null) {
+ bf.append("Cannot read pointed value: ", SWT.BOLD, null, rgb_error);
+ bf.append(TCFModel.getErrorMessage(e, true), SWT.ITALIC, null, rgb_error);
+ }
+ }
+ if (type_data.getSize() > 0) {
+ ISymbols.TypeClass type_class = type_data.getTypeClass();
+ switch (type_class) {
+ case enumeration:
+ case integer:
+ case cardinal:
+ case real:
+ if (level == 0) {
+ bf.append("Dec: ", SWT.BOLD);
+ bf.append(toNumberString(10, type_class, data, offs, size, big_endian));
+ bf.append("\n");
+ bf.append("Oct: ", SWT.BOLD);
+ bf.append(toNumberString(8, type_class, data, offs, size, big_endian));
+ bf.append("\n");
+ bf.append("Hex: ", SWT.BOLD);
+ bf.append(toNumberString(16, type_class, data, offs, size, big_endian));
+ bf.append("\n");
+ }
+ else if (type_data.getTypeClass() == ISymbols.TypeClass.cardinal) {
+ bf.append("0x");
+ bf.append(toNumberString(16, type_class, data, offs, size, big_endian));
+ }
+ else {
+ bf.append(toNumberString(10, type_class, data, offs, size, big_endian));
+ }
+ break;
+ case pointer:
+ case function:
+ case member_pointer:
+ if (level == 0) {
+ bf.append("Oct: ", SWT.BOLD);
+ bf.append(toNumberString(8, type_class, data, offs, size, big_endian));
+ bf.append("\n");
+ bf.append("Hex: ", SWT.BOLD);
+ bf.append(toNumberString(16, type_class, data, offs, size, big_endian));
+ bf.append("\n");
+ }
+ else {
+ bf.append("0x");
+ bf.append(toNumberString(16, type_class, data, offs, size, big_endian));
+ }
+ break;
+ case array:
+ if (!appendArrayValueText(bf, level, type_data, data, offs, size, big_endian, done)) return false;
+ if (level == 0) bf.append("\n");
+ break;
+ case composite:
+ bf.append('{');
+ if (!appendCompositeValueText(bf, level, type_data, data, offs, size, big_endian, done)) return false;
+ bf.append('}');
+ if (level == 0) bf.append("\n");
+ break;
+ }
+ }
+ if (level == 0) {
+ if (!type_name.validate(done)) return false;
+ String nm = type_name.getData();
+ if (nm != null) {
+ bf.append("Type: ", SWT.BOLD);
+ bf.append(nm);
+ bf.append("\n");
+ }
+ bf.append("Size: ", SWT.BOLD);
+ bf.append(type_data.getSize());
+ bf.append(type_data.getSize() == 1 ? " byte\n" : " bytes\n");
+ }
+ return true;
+ }
+
+ private String getRegisterName(String reg_id, Runnable done) {
+ String name = reg_id;
+ TCFDataCache<?> pending = null;
+ TCFNodeRegister reg_node = null;
+ LinkedList<TCFChildren> queue = new LinkedList<TCFChildren>();
+ TCFNode n = parent;
+ while (n != null) {
+ if (n instanceof TCFNodeStackFrame) {
+ queue.add(((TCFNodeStackFrame)n).getRegisters());
+ }
+ if (n instanceof TCFNodeExecContext) {
+ queue.add(((TCFNodeExecContext)n).getRegisters());
+ break;
+ }
+ n = n.parent;
+ }
+ while (!queue.isEmpty()) {
+ TCFChildren reg_list = queue.removeFirst();
+ if (!reg_list.validate()) {
+ pending = reg_list;
+ }
+ else {
+ Map<String,TCFNode> reg_map = reg_list.getData();
+ if (reg_map != null) {
+ reg_node = (TCFNodeRegister)reg_map.get(reg_id);
+ if (reg_node != null) break;
+ for (TCFNode node : reg_map.values()) {
+ queue.add(((TCFNodeRegister)node).getChildren());
+ }
+ }
+ }
+ }
+ if (pending != null) {
+ pending.wait(done);
+ return null;
+ }
+ if (reg_node != null) {
+ TCFDataCache<IRegisters.RegistersContext> reg_ctx_cache = reg_node.getContext();
+ if (!reg_ctx_cache.validate(done)) return null;
+ IRegisters.RegistersContext reg_ctx_data = reg_ctx_cache.getData();
+ if (reg_ctx_data != null && reg_ctx_data.getName() != null) name = reg_ctx_data.getName();
+ }
+ return name;
+ }
+
+ public boolean getDetailText(StyledStringBuffer bf, Runnable done) {
+ if (!enabled) {
+ bf.append("Disabled");
+ return true;
+ }
+ if (!expression.validate(done)) return false;
+ if (!value.validate(done)) return false;
+ int pos = bf.length();
+ appendErrorText(bf.getStringBuffer(), expression.getError());
+ if (bf.length() == pos) appendErrorText(bf.getStringBuffer(), value.getError());
+ if (bf.length() > pos) {
+ bf.append(pos, SWT.ITALIC, null, rgb_error);
+ }
+ else {
+ IExpressions.Value v = value.getData();
+ if (v != null) {
+ byte[] data = v.getValue();
+ if (data != null) {
+ boolean big_endian = v.isBigEndian();
+ if (!appendValueText(bf, 0, v.getTypeID(),
+ data, 0, data.length, big_endian, done)) return false;
+ }
+ String reg_id = v.getRegisterID();
+ if (reg_id != null) {
+ String nm = getRegisterName(reg_id, done);
+ if (nm == null) return false;
+ bf.append("Register: ", SWT.BOLD);
+ bf.append(nm);
+ bf.append('\n');
+ }
+ Number addr = v.getAddress();
+ if (addr != null) {
+ BigInteger i = JSON.toBigInteger(addr);
+ bf.append("Address: ", SWT.BOLD);
+ bf.append("0x");
+ bf.append(i.toString(16));
+ bf.append('\n');
+ }
+ }
+ }
+ return true;
+ }
+
+ public String getValueText(boolean add_error_text, Runnable done) {
+ if (!expression.validate(done)) return null;
+ if (!value.validate(done)) return null;
+ StyledStringBuffer bf = new StyledStringBuffer();
+ IExpressions.Value v = value.getData();
+ if (v != null) {
+ byte[] data = v.getValue();
+ if (data != null) {
+ boolean big_endian = v.isBigEndian();
+ if (!appendValueText(bf, 1, v.getTypeID(),
+ data, 0, data.length, big_endian, done)) return null;
+ }
+ }
+ if (add_error_text) {
+ if (bf.length() == 0 && expression.getError() != null) {
+ bf.append(TCFModel.getErrorMessage(expression.getError(), false));
+ }
+ if (bf.length() == 0 && value.getError() != null) {
+ bf.append(TCFModel.getErrorMessage(value.getError(), false));
+ }
+ }
+ return bf.toString();
+ }
+
+ @Override
+ protected boolean getData(IChildrenCountUpdate result, Runnable done) {
+ if (enabled) {
+ if (!children.validate(done)) return false;
+ result.setChildCount(children.size());
+ }
+ else {
+ result.setChildCount(0);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IChildrenUpdate result, Runnable done) {
+ if (!enabled) return true;
+ return children.getData(result, done);
+ }
+
+ @Override
+ protected boolean getData(IHasChildrenUpdate result, Runnable done) {
+ if (enabled) {
+ if (!children.validate(done)) return false;
+ result.setHasChilren(children.size() > 0);
+ }
+ else {
+ result.setHasChilren(false);
+ }
+ return true;
+ }
+
+ @Override
+ public int compareTo(TCFNode n) {
+ TCFNodeExpression e = (TCFNodeExpression)n;
+ if (sort_pos < e.sort_pos) return -1;
+ if (sort_pos > e.sort_pos) return +1;
+ return 0;
+ }
+
+ public CellEditor getCellEditor(IPresentationContext context, String column_id, Object element, Composite parent) {
+ assert element == this;
+ if (TCFColumnPresentationExpression.COL_NAME.equals(column_id) && script != null) {
+ return new TextCellEditor(parent);
+ }
+ if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(column_id)) {
+ return new TextCellEditor(parent);
+ }
+ if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(column_id)) {
+ return new TextCellEditor(parent);
+ }
+ return null;
+ }
+
+ private static final ICellModifier cell_modifier = new ICellModifier() {
+
+ public boolean canModify(Object element, final String property) {
+ final TCFNodeExpression node = (TCFNodeExpression)element;
+ return new TCFTask<Boolean>() {
+ public void run() {
+ if (TCFColumnPresentationExpression.COL_NAME.equals(property)) {
+ done(node.script != null);
+ return;
+ }
+ if (node.enabled) {
+ if (!node.expression.validate(this)) return;
+ if (node.expression.getData() != null && node.expression.getData().expression.canAssign()) {
+ if (!node.value.validate(this)) return;
+ if (!node.type.validate(this)) return;
+ if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) {
+ done(TCFNumberFormat.isValidHexNumber(node.toNumberString(16)) == null);
+ return;
+ }
+ if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) {
+ done(TCFNumberFormat.isValidDecNumber(true, node.toNumberString(10)) == null);
+ return;
+ }
+ }
+ }
+ done(Boolean.FALSE);
+ }
+ }.getE();
+ }
+
+ public Object getValue(Object element, final String property) {
+ final TCFNodeExpression node = (TCFNodeExpression)element;
+ return new TCFTask<String>() {
+ public void run() {
+ if (TCFColumnPresentationExpression.COL_NAME.equals(property)) {
+ done(node.script);
+ return;
+ }
+ if (!node.value.validate(this)) return;
+ if (node.value.getData() != null) {
+ if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) {
+ done(node.toNumberString(16));
+ return;
+ }
+ if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) {
+ done(node.toNumberString(10));
+ return;
+ }
+ }
+ done(null);
+ }
+ }.getE();
+ }
+
+ public void modify(Object element, final String property, final Object value) {
+ if (value == null) return;
+ final TCFNodeExpression node = (TCFNodeExpression)element;
+ new TCFTask<Boolean>() {
+ public void run() {
+ try {
+ if (TCFColumnPresentationExpression.COL_NAME.equals(property)) {
+ if (!node.script.equals(value)) {
+ IExpressionManager m = DebugPlugin.getDefault().getExpressionManager();
+ for (final IExpression e : m.getExpressions()) {
+ if (node.script.equals(e.getExpressionText())) m.removeExpression(e);
+ }
+ IExpression e = m.newWatchExpression((String)value);
+ m.addExpression(e);
+ }
+ done(Boolean.TRUE);
+ return;
+ }
+ if (!node.expression.validate(this)) return;
+ if (node.expression.getData() != null) {
+ IExpressions.Expression exp = node.expression.getData().expression;
+ if (exp.canAssign()) {
+ byte[] bf = null;
+ int size = exp.getSize();
+ boolean is_float = false;
+ boolean big_endian = false;
+ boolean signed = false;
+ if (!node.value.validate(this)) return;
+ IExpressions.Value eval = node.value.getData();
+ if (eval != null) {
+ switch(eval.getTypeClass()) {
+ case real:
+ is_float = true;
+ case integer:
+ signed = true;
+ break;
+ }
+ big_endian = eval.isBigEndian();
+ size = eval.getValue().length;
+ }
+ String input = (String)value;
+ String error = null;
+ if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) {
+ error = TCFNumberFormat.isValidHexNumber(input);
+ if (error == null) bf = TCFNumberFormat.toByteArray(input, 16, false, size, signed, big_endian);
+ }
+ else if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) {
+ error = TCFNumberFormat.isValidDecNumber(is_float, input);
+ if (error == null) bf = TCFNumberFormat.toByteArray(input, 10, is_float, size, signed, big_endian);
+ }
+ if (error != null) throw new Exception("Invalid value: " + value, new Exception(error));
+ if (bf != null) {
+ IExpressions exps = node.launch.getService(IExpressions.class);
+ exps.assign(exp.getID(), bf, new IExpressions.DoneAssign() {
+ public void doneAssign(IToken token, Exception error) {
+ node.getRootExpression().onValueChanged();
+ if (error != null) {
+ node.model.showMessageBox("Cannot modify element value", error);
+ done(Boolean.FALSE);
+ }
+ else {
+ done(Boolean.TRUE);
+ }
+ }
+ });
+ return;
+ }
+ }
+ }
+ done(Boolean.FALSE);
+ }
+ catch (Throwable x) {
+ node.model.showMessageBox("Cannot modify element value", x);
+ done(Boolean.FALSE);
+ }
+ }
+ }.getE();
+ }
+ };
+
+ public ICellModifier getCellModifier(IPresentationContext context, Object element) {
+ assert element == this;
+ return cell_modifier;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (script != null) {
+ if (adapter == IExpression.class) {
+ IExpressionManager m = DebugPlugin.getDefault().getExpressionManager();
+ for (final IExpression e : m.getExpressions()) {
+ if (script.equals(e.getExpressionText())) return e;
+ }
+ }
+ if (adapter == IWatchExpression.class) {
+ IExpressionManager m = DebugPlugin.getDefault().getExpressionManager();
+ for (final IExpression e : m.getExpressions()) {
+ if (e instanceof IWatchExpression && script.equals(e.getExpressionText())) return e;
+ }
+ }
+ }
+ return super.getAdapter(adapter);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeLaunch.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeLaunch.java
new file mode 100644
index 000000000..f5060c61c
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeLaunch.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.tcf.services.IMemory;
+import org.eclipse.tcf.services.IRunControl;
+
+
+public class TCFNodeLaunch extends TCFNode implements ISymbolOwner {
+
+ private final TCFChildrenExecContext children;
+ private final TCFChildren filtered_children;
+ private final Map<String,TCFNodeSymbol> symbols = new HashMap<String,TCFNodeSymbol>();
+
+ TCFNodeLaunch(final TCFModel model) {
+ super(model);
+ children = new TCFChildrenExecContext(this);
+ filtered_children = new TCFChildren(this) {
+ @Override
+ protected boolean startDataRetrieval() {
+ Set<String> filter = launch.getContextFilter();
+ if (filter == null) {
+ if (!children.validate(this)) return false;
+ set(null, children.getError(), children.getData());
+ return true;
+ }
+ Map<String,TCFNode> nodes = new HashMap<String,TCFNode>();
+ for (String id : filter) {
+ if (!model.createNode(id, this)) return false;
+ if (isValid()) {
+ // Ignore invalid IDs
+ reset();
+ }
+ else {
+ nodes.put(id, model.getNode(id));
+ }
+ }
+ set(null, null, nodes);
+ return true;
+ }
+ @Override
+ public void dispose() {
+ getNodes().clear();
+ super.dispose();
+ }
+ };
+ }
+
+ @Override
+ void dispose() {
+ ArrayList<TCFNodeSymbol> l = new ArrayList<TCFNodeSymbol>(symbols.values());
+ for (TCFNodeSymbol s : l) s.dispose();
+ assert symbols.size() == 0;
+ super.dispose();
+ }
+
+ @Override
+ protected boolean getData(IChildrenCountUpdate result, Runnable done) {
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(result.getPresentationContext().getId())) {
+ if (!filtered_children.validate(done)) return false;
+ result.setChildCount(filtered_children.size());
+ }
+ else {
+ result.setChildCount(0);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IChildrenUpdate result, Runnable done) {
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(result.getPresentationContext().getId())) {
+ return filtered_children.getData(result, done);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IHasChildrenUpdate result, Runnable done) {
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(result.getPresentationContext().getId())) {
+ if (!filtered_children.validate(done)) return false;
+ result.setHasChilren(filtered_children.size() > 0);
+ }
+ else {
+ result.setHasChilren(false);
+ }
+ return true;
+ }
+
+ void onContextAdded(IRunControl.RunControlContext context) {
+ children.onContextAdded(context);
+ }
+
+ void onContextAdded(IMemory.MemoryContext context) {
+ children.onContextAdded(context);
+ }
+
+ void onAnyContextSuspendedOrChanged() {
+ for (TCFNodeSymbol s : symbols.values()) s.onMemoryMapChanged();
+ }
+
+ void onAnyContextAddedOrRemoved() {
+ filtered_children.reset();
+ }
+
+ public void addSymbol(TCFNodeSymbol s) {
+ assert symbols.get(s.id) == null;
+ symbols.put(s.id, s);
+ }
+
+ public void removeSymbol(TCFNodeSymbol s) {
+ assert symbols.get(s.id) == s;
+ symbols.remove(s.id);
+ }
+
+ public TCFChildren getChildren() {
+ return children;
+ }
+
+ public TCFChildren getFilteredChildren() {
+ return filtered_children;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeModule.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeModule.java
new file mode 100644
index 000000000..dd7d32d24
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeModule.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigInteger;
+import java.util.Map;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.tcf.core.ErrorReport;
+import org.eclipse.tcf.internal.debug.model.TCFSymFileRef;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+import org.eclipse.tcf.protocol.JSON;
+import org.eclipse.tcf.services.IMemoryMap;
+import org.eclipse.tcf.util.TCFDataCache;
+
+/**
+ * A node representing a memory region (module).
+ */
+public class TCFNodeModule extends TCFNode implements IDetailsProvider {
+
+ private IMemoryMap.MemoryRegion region;
+
+ private static final RGB
+ rgb_error = new RGB(192, 0, 0);
+
+ protected TCFNodeModule(TCFNode parent, String id) {
+ super(parent, id);
+ }
+
+ void setRegion(IMemoryMap.MemoryRegion region) {
+ this.region = region;
+ }
+
+ @Override
+ protected boolean getData(ILabelUpdate update, Runnable done) {
+ String[] col_ids = update.getColumnIds();
+ if (col_ids == null) {
+ update.setLabel(region.getFileName(), 0);
+ }
+ else {
+ for (int i=0; i < col_ids.length; ++i) {
+ String col_id = col_ids[i];
+ if (TCFColumnPresentationModules.COL_NAME.equals(col_id)) {
+ update.setLabel(region.getFileName(), i);
+ }
+ else if (TCFColumnPresentationModules.COL_ADDRESS.equals(col_id)) {
+ update.setLabel(toHexString(region.getAddress()), i);
+ }
+ else if (TCFColumnPresentationModules.COL_SIZE.equals(col_id)) {
+ update.setLabel(toHexString(region.getSize()), i);
+ }
+ else if (TCFColumnPresentationModules.COL_FLAGS.equals(col_id)) {
+ update.setLabel(getFlagsLabel(region.getFlags()), i);
+ }
+ else if (TCFColumnPresentationModules.COL_OFFSET.equals(col_id)) {
+ update.setLabel(toHexString(region.getOffset()), i);
+ }
+ else if (TCFColumnPresentationModules.COL_SECTION.equals(col_id)) {
+ String sectionName = region.getSectionName();
+ update.setLabel(sectionName != null ? sectionName : "", i);
+ }
+ }
+ }
+ update.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_MEMORY_MAP), 0);
+ return true;
+ }
+
+ public boolean getDetailText(StyledStringBuffer bf, Runnable done) {
+ String file_name = region.getFileName();
+ if (file_name != null) {
+ bf.append("File name: ", SWT.BOLD).append(file_name).append('\n');
+ TCFNodeExecContext exe = (TCFNodeExecContext)parent;
+ TCFDataCache<TCFSymFileRef> sym_cache = exe.getSymFileInfo(JSON.toBigInteger(region.getAddress()));
+ if (sym_cache != null) {
+ if (!sym_cache.validate(done)) return false;
+ TCFSymFileRef sym_data = sym_cache.getData();
+ if (sym_data != null) {
+ if (sym_data.props != null) {
+ String sym_file_name = (String)sym_data.props.get("FileName");
+ if (sym_file_name != null) bf.append("Symbol file name: ", SWT.BOLD).append(sym_file_name).append('\n');
+ @SuppressWarnings("unchecked")
+ Map<String,Object> map = (Map<String,Object>)sym_data.props.get("FileError");
+ if (map != null) {
+ String msg = TCFModel.getErrorMessage(new ErrorReport("", map), false);
+ bf.append("Symbol file error: ", SWT.BOLD).append(msg, SWT.ITALIC, null, rgb_error).append('\n');
+ }
+ }
+ if (sym_data.error != null) bf.append("Symbol file error: ", SWT.BOLD).append(
+ TCFModel.getErrorMessage(sym_data.error, false),
+ SWT.ITALIC, null, rgb_error).append('\n');
+ }
+ }
+ String section = region.getSectionName();
+ if (section != null) bf.append("File section: ", SWT.BOLD).append(section).append('\n');
+ else bf.append("File offset: ", SWT.BOLD).append(toHexString(region.getOffset())).append('\n');
+ }
+ bf.append("Address: ", SWT.BOLD).append(toHexString(region.getAddress())).append('\n');
+ bf.append("Size: ", SWT.BOLD).append(toHexString(region.getSize())).append('\n');
+ bf.append("Flags: ", SWT.BOLD).append(getFlagsLabel(region.getFlags())).append('\n');
+ return true;
+ }
+
+ private String toHexString(Number address) {
+ if (address == null) return "";
+ BigInteger addr = JSON.toBigInteger(address);
+ String s = addr.toString(16);
+ int sz = s.length() <= 8 ? 8 : 16;
+ int l = sz - s.length();
+ if (l < 0) l = 0;
+ if (l > 16) l = 16;
+ return "0x0000000000000000".substring(0, 2 + l) + s;
+ }
+
+ private String getFlagsLabel(int flags) {
+ StringBuilder flagsLabel = new StringBuilder(3);
+ if ((flags & IMemoryMap.FLAG_READ) != 0) flagsLabel.append('r');
+ else flagsLabel.append('-');
+ if ((flags & IMemoryMap.FLAG_WRITE) != 0) flagsLabel.append('w');
+ else flagsLabel.append('-');
+ if ((flags & IMemoryMap.FLAG_EXECUTE) != 0) flagsLabel.append('x');
+ else flagsLabel.append('-');
+ return flagsLabel.toString();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeRegister.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeRegister.java
new file mode 100644
index 000000000..2f635e6e4
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeRegister.java
@@ -0,0 +1,631 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ICellModifier;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tcf.internal.debug.model.TCFContextState;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IRegisters;
+import org.eclipse.tcf.util.TCFDataCache;
+import org.eclipse.tcf.util.TCFTask;
+
+
+public class TCFNodeRegister extends TCFNode implements IElementEditor, IWatchInExpressions, IDetailsProvider {
+
+ private final TCFChildrenRegisters children;
+ private final TCFData<IRegisters.RegistersContext> context;
+ private final TCFData<String> expression_text;
+ private final TCFData<byte[]> value;
+ private final boolean is_stack_frame_register;
+
+ private byte[] prev_value;
+ private byte[] next_value;
+
+ private static final RGB
+ rgb_error = new RGB(192, 0, 0),
+ rgb_highlight = new RGB(255, 255, 128);
+
+ private int index;
+
+ TCFNodeRegister(TCFNode parent, final String id) {
+ super(parent, id);
+ if (parent instanceof TCFNodeStackFrame) is_stack_frame_register = true;
+ else if (parent instanceof TCFNodeRegister) is_stack_frame_register = ((TCFNodeRegister)parent).is_stack_frame_register;
+ else is_stack_frame_register = false;
+ children = new TCFChildrenRegisters(this);
+ context = new TCFData<IRegisters.RegistersContext>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ IRegisters regs = launch.getService(IRegisters.class);
+ command = regs.getContext(id, new IRegisters.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) {
+ set(token, error, context);
+ }
+ });
+ return false;
+ }
+ };
+ expression_text = new TCFData<String>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ Throwable err = null;
+ TCFNodeRegister n = TCFNodeRegister.this;
+ ArrayList<String> names = new ArrayList<String>();
+ for (;;) {
+ if (!n.context.validate(this)) return false;
+ IRegisters.RegistersContext ctx = n.context.getData();
+ if (ctx == null) {
+ err = n.context.getError();
+ break;
+ }
+ String s = ctx.getName();
+ if (s == null) break;
+ names.add(s);
+ if (!(n.parent instanceof TCFNodeRegister)) break;
+ n = (TCFNodeRegister)n.parent;
+ }
+ if (names.size() == 0 || err != null) {
+ set(null, err, null);
+ }
+ else {
+ StringBuffer bf = new StringBuffer();
+ boolean first = true;
+ int m = names.size();
+ while (m > 0) {
+ String s = names.get(--m);
+ boolean need_quotes = false;
+ int l = s.length();
+ for (int i = 0; i < l; i++) {
+ char ch = s.charAt(i);
+ if (ch >= 'A' && ch <= 'Z') continue;
+ if (ch >= 'a' && ch <= 'z') continue;
+ if (ch >= '0' && ch <= '9') continue;
+ need_quotes = true;
+ break;
+ }
+ if (!first) bf.append('.');
+ if (need_quotes) bf.append("$\"");
+ if (first) bf.append('$');
+ bf.append(s);
+ if (need_quotes) bf.append('"');
+ first = false;
+ }
+ set(null, null, bf.toString());
+ }
+ return true;
+ }
+ };
+ value = new TCFData<byte[]>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ Boolean b = usePrevValue(this);
+ if (b == null) return false;
+ if (b) {
+ set(null, null, prev_value);
+ return true;
+ }
+ if (!context.validate(this)) return false;
+ IRegisters.RegistersContext ctx = context.getData();
+ if (ctx == null || ctx.getSize() <= 0) {
+ set(null, null, null);
+ return true;
+ }
+ command = ctx.get(new IRegisters.DoneGet() {
+ public void doneGet(IToken token, Exception error, byte[] value) {
+ if (error != null) {
+ Boolean b = usePrevValue(null);
+ if (b != null && b) {
+ set(token, null, prev_value);
+ return;
+ }
+ }
+ set(token, error, value);
+ }
+ });
+ return false;
+ }
+ };
+ }
+
+ public TCFDataCache<IRegisters.RegistersContext> getContext() {
+ return context;
+ }
+
+ public TCFDataCache<byte[]> getValue() {
+ return value;
+ }
+
+ public TCFChildren getChildren() {
+ return children;
+ }
+
+ public TCFDataCache<String> getExpressionText() {
+ return expression_text;
+ }
+
+ void setIndex(int index) {
+ this.index = index;
+ }
+
+ private Boolean usePrevValue(Runnable done) {
+ // Check if view should show old value.
+ // Old value is shown if context is running or
+ // stack trace does not contain expression parent frame.
+ // Return null if waiting for cache update.
+ if (prev_value == null) return false;
+ if (parent instanceof TCFNodeStackFrame) {
+ TCFNodeExecContext exe = (TCFNodeExecContext)parent.parent;
+ TCFDataCache<TCFContextState> state_cache = exe.getState();
+ if (!state_cache.validate(done)) return null;
+ TCFContextState state = state_cache.getData();
+ if (state == null || !state.is_suspended) return true;
+ TCFChildrenStackTrace stack_trace_cache = exe.getStackTrace();
+ if (!stack_trace_cache.validate(done)) return null;
+ if (stack_trace_cache.getData().get(parent.id) == null) return true;
+ }
+ else if (parent instanceof TCFNodeExecContext) {
+ TCFNodeExecContext exe = (TCFNodeExecContext)parent;
+ TCFDataCache<TCFContextState> state_cache = exe.getState();
+ if (!state_cache.validate(done)) return null;
+ TCFContextState state = state_cache.getData();
+ if (state == null || !state.is_suspended) return true;
+ }
+ return false;
+ }
+
+ private void appendErrorText(StringBuffer bf, Throwable error) {
+ if (error == null) return;
+ bf.append("Exception: ");
+ bf.append(TCFModel.getErrorMessage(error, true));
+ }
+
+ public boolean getDetailText(StyledStringBuffer bf, Runnable done) {
+ if (!context.validate(done)) return false;
+ if (!value.validate(done)) return false;
+ int pos = bf.length();
+ appendErrorText(bf.getStringBuffer(), context.getError());
+ if (bf.length() == 0) appendErrorText(bf.getStringBuffer(), value.getError());
+ if (bf.length() > pos) {
+ bf.append(pos, 0, null, rgb_error);
+ }
+ else {
+ IRegisters.RegistersContext ctx = context.getData();
+ if (ctx != null) {
+ if (ctx.getDescription() != null) {
+ bf.append(ctx.getDescription());
+ bf.append('\n');
+ }
+ int l = bf.length();
+ if (ctx.isReadable()) {
+ bf.append("readable");
+ }
+ if (ctx.isReadOnce()) {
+ if (l < bf.length()) bf.append(", ");
+ bf.append("read once");
+ }
+ if (ctx.isWriteable()) {
+ if (l < bf.length()) bf.append(", ");
+ bf.append("writable");
+ }
+ if (ctx.isWriteOnce()) {
+ if (l < bf.length()) bf.append(", ");
+ bf.append("write once");
+ }
+ if (ctx.hasSideEffects()) {
+ if (l < bf.length()) bf.append(", ");
+ bf.append("side effects");
+ }
+ if (l < bf.length()) bf.append('\n');
+ }
+ byte[] v = value.getData();
+ if (v != null) {
+ bf.append("Hex: ", SWT.BOLD);
+ bf.append(toNumberString(16));
+ bf.append(", ");
+ bf.append("Dec: ", SWT.BOLD);
+ bf.append(toNumberString(10));
+ bf.append(", ");
+ bf.append("Oct: ", SWT.BOLD);
+ bf.append(toNumberString(8));
+ bf.append('\n');
+ bf.append("Bin: ", SWT.BOLD);
+ bf.append(toNumberString(2));
+ bf.append('\n');
+ }
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IHasChildrenUpdate result, Runnable done) {
+ if (!children.validate(done)) return false;
+ result.setHasChilren(children.size() > 0);
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IChildrenCountUpdate result, Runnable done) {
+ if (!children.validate(done)) return false;
+ result.setChildCount(children.size());
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IChildrenUpdate result, Runnable done) {
+ return children.getData(result, done);
+ }
+
+ @Override
+ protected boolean getData(ILabelUpdate result, Runnable done) {
+ TCFDataCache<?> pending = null;
+ if (!context.validate()) pending = context;
+ if (!value.validate()) pending = value;
+ if (pending != null) {
+ pending.wait(done);
+ return false;
+ }
+ String[] cols = result.getColumnIds();
+ if (cols == null) {
+ setLabel(result, -1, 16);
+ }
+ else {
+ IRegisters.RegistersContext ctx = context.getData();
+ for (int i = 0; i < cols.length; i++) {
+ String c = cols[i];
+ if (ctx == null) {
+ result.setForeground(rgb_error, i);
+ result.setLabel("N/A", i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_NAME)) {
+ result.setLabel(ctx.getName(), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_HEX_VALUE)) {
+ setLabel(result, i, 16);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_DEC_VALUE)) {
+ setLabel(result, i, 10);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_DESCRIPTION)) {
+ result.setLabel(ctx.getDescription(), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_READBLE)) {
+ result.setLabel(bool(ctx.isReadable()), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_READ_ONCE)) {
+ result.setLabel(bool(ctx.isReadOnce()), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_WRITEABLE)) {
+ result.setLabel(bool(ctx.isWriteable()), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_WRITE_ONCE)) {
+ result.setLabel(bool(ctx.isWriteOnce()), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_SIDE_EFFECTS)) {
+ result.setLabel(bool(ctx.hasSideEffects()), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_VOLATILE)) {
+ result.setLabel(bool(ctx.isVolatile()), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_FLOAT)) {
+ result.setLabel(bool(ctx.isFloat()), i);
+ }
+ else if (c.equals(TCFColumnPresentationRegister.COL_MNEMONIC)) {
+ result.setLabel(getMnemonic(ctx), i);
+ }
+ }
+ }
+ boolean changed = false;
+ next_value = value.getData();
+ if (prev_value != null && next_value != null) {
+ if (prev_value.length != next_value.length) {
+ changed = true;
+ }
+ else {
+ for (int i = 0; i < prev_value.length; i++) {
+ if (prev_value[i] != next_value[i]) changed = true;
+ }
+ }
+ }
+ if (changed) {
+ result.setBackground(rgb_highlight, 0);
+ if (cols != null) {
+ for (int i = 1; i < cols.length; i++) {
+ result.setBackground(rgb_highlight, i);
+ }
+ }
+ }
+ result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_REGISTER), 0);
+ return true;
+ }
+
+ private void setLabel(ILabelUpdate result, int col, int radix) {
+ IRegisters.RegistersContext ctx = context.getData();
+ Throwable error = context.getError();
+ if (error == null) error = value.getError();
+ byte[] data = value.getData();
+ if (error != null || ctx == null) {
+ result.setForeground(rgb_error, col);
+ result.setLabel("N/A", col);
+ }
+ else if (data != null) {
+ String s = toNumberString(radix);
+ if (col >= 0) {
+ result.setLabel(s, col);
+ }
+ else {
+ result.setLabel(ctx.getName() + " = " + s, 0);
+ }
+ }
+ }
+
+ private String toNumberString(int radix) {
+ IRegisters.RegistersContext ctx = context.getData();
+ byte[] data = value.getData();
+ if (ctx == null || data == null) return "N/A";
+ if (radix == 2) {
+ StringBuffer bf = new StringBuffer();
+ int i = data.length * 8;
+ while (i > 0) {
+ if (i % 4 == 0 && bf.length() > 0) bf.append(',');
+ i--;
+ int j = i / 8;
+ if (ctx.isBigEndian()) j = data.length - j - 1;
+ if ((data[j] & (1 << (i % 8))) != 0) {
+ bf.append('1');
+ }
+ else {
+ bf.append('0');
+ }
+ }
+ return bf.toString();
+ }
+ if (radix == 10 && ctx.isFloat()) {
+ String s = TCFNumberFormat.toFPString(data, 0, data.length, ctx.isBigEndian());
+ if (s != null) return s;
+ }
+ BigInteger b = TCFNumberFormat.toBigInteger(data, 0, data.length, ctx.isBigEndian(), false);
+ String s = b.toString(radix);
+ switch (radix) {
+ case 8:
+ if (!s.startsWith("0")) s = "0" + s;
+ break;
+ case 16:
+ if (s.length() < data.length * 2) {
+ StringBuffer bf = new StringBuffer();
+ while (bf.length() + s.length() < data.length * 2) bf.append('0');
+ bf.append(s);
+ s = bf.toString();
+ }
+ break;
+ }
+ return s;
+ }
+
+ private String bool(boolean b) {
+ return b ? "yes" : "no";
+ }
+
+ private String getMnemonic(IRegisters.RegistersContext ctx) {
+ if (value.getData() != null) {
+ IRegisters.NamedValue[] arr = ctx.getNamedValues();
+ if (arr != null) {
+ for (IRegisters.NamedValue n : arr) {
+ if (Arrays.equals(n.getValue(), value.getData())) return n.getName();
+ }
+ }
+ }
+ return "";
+ }
+
+ private void postStateChangedDelta() {
+ for (TCFModelProxy p : model.getModelProxies()) {
+ if (!IDebugUIConstants.ID_REGISTER_VIEW.equals(p.getPresentationContext().getId())) continue;
+ p.addDelta(this, IModelDelta.STATE);
+ }
+ }
+
+ void onValueChanged() {
+ prev_value = next_value;
+ value.reset();
+ TCFNode n = parent;
+ while (n != null) {
+ if (n instanceof TCFNodeExecContext) {
+ ((TCFNodeExecContext)n).onRegisterValueChanged();
+ break;
+ }
+ else if (n instanceof TCFNodeRegister) {
+ TCFNodeRegister r = (TCFNodeRegister)n;
+ if (r.value.isValid() && r.value.getData() != null) {
+ r.value.reset();
+ r.postStateChangedDelta();
+ }
+ }
+ n = n.parent;
+ }
+ children.onParentValueChanged();
+ postStateChangedDelta();
+ }
+
+ void onParentValueChanged() {
+ value.reset();
+ children.onParentValueChanged();
+ postStateChangedDelta();
+ }
+
+ void onSuspended() {
+ prev_value = next_value;
+ value.reset();
+ children.onSuspended();
+ // Unlike thread registers, stack frame register list must be retrieved on every suspend
+ if (is_stack_frame_register) children.reset();
+ // No need to post delta: parent posted CONTENT
+ }
+
+ void onRegistersChanged() {
+ children.onRegistersChanged();
+ expression_text.reset();
+ context.reset();
+ value.reset();
+ // No need to post delta: parent posted CONTENT
+ }
+
+ public CellEditor getCellEditor(IPresentationContext context, String column_id, Object element, Composite parent) {
+ assert element == this;
+ if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(column_id)) {
+ return new TextCellEditor(parent);
+ }
+ if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(column_id)) {
+ return new TextCellEditor(parent);
+ }
+ return null;
+ }
+
+ private static final ICellModifier cell_modifier = new ICellModifier() {
+
+ public boolean canModify(Object element, final String property) {
+ final TCFNodeRegister node = (TCFNodeRegister)element;
+ return new TCFTask<Boolean>() {
+ public void run() {
+ if (!node.context.validate(this)) return;
+ IRegisters.RegistersContext ctx = node.context.getData();
+ if (ctx != null && ctx.isWriteable()) {
+ if (!ctx.isReadable()) {
+ done(Boolean.TRUE);
+ return;
+ }
+ if (!node.value.validate(this)) return;
+ if (node.value.getError() == null) {
+ if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) {
+ done(TCFNumberFormat.isValidHexNumber(node.toNumberString(16)) == null);
+ return;
+ }
+ if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) {
+ done(TCFNumberFormat.isValidDecNumber(true, node.toNumberString(10)) == null);
+ return;
+ }
+ }
+ }
+ done(Boolean.FALSE);
+ }
+ }.getE();
+ }
+
+ public Object getValue(Object element, final String property) {
+ final TCFNodeRegister node = (TCFNodeRegister)element;
+ return new TCFTask<String>() {
+ public void run() {
+ if (!node.context.validate(this)) return;
+ IRegisters.RegistersContext ctx = node.context.getData();
+ if (!ctx.isReadable()) {
+ done("0");
+ return;
+ }
+ if (!node.value.validate(this)) return;
+ if (node.value.getError() == null) {
+ if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) {
+ done(node.toNumberString(16));
+ return;
+ }
+ if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) {
+ done(node.toNumberString(10));
+ return;
+ }
+ }
+ done(null);
+ }
+ }.getE();
+ }
+
+ public void modify(Object element, final String property, final Object value) {
+ if (value == null) return;
+ final TCFNodeRegister node = (TCFNodeRegister)element;
+ new TCFTask<Boolean>() {
+ public void run() {
+ try {
+ if (!node.context.validate(this)) return;
+ IRegisters.RegistersContext ctx = node.context.getData();
+ if (ctx != null && ctx.isWriteable()) {
+ byte[] bf = null;
+ boolean is_float = ctx.isFloat();
+ int size = ctx.getSize();
+ boolean big_endian = ctx.isBigEndian();
+ String input = (String)value;
+ String error = null;
+ if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) {
+ error = TCFNumberFormat.isValidHexNumber(input);
+ if (error == null) bf = TCFNumberFormat.toByteArray(input, 16, false, size, false, big_endian);
+ }
+ else if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) {
+ error = TCFNumberFormat.isValidDecNumber(is_float, input);
+ if (error == null) bf = TCFNumberFormat.toByteArray(input, 10, is_float, size, is_float, big_endian);
+ }
+ if (error != null) throw new Exception("Invalid value: " + value, new Exception(error));
+ if (bf != null) {
+ ctx.set(bf, new IRegisters.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ if (error != null) {
+ node.model.showMessageBox("Cannot modify register value", error);
+ done(Boolean.FALSE);
+ }
+ else {
+ node.value.reset();
+ node.postStateChangedDelta();
+ done(Boolean.TRUE);
+ }
+ }
+ });
+ return;
+ }
+ }
+ done(Boolean.FALSE);
+ }
+ catch (Throwable x) {
+ node.model.showMessageBox("Cannot modify register value", x);
+ done(Boolean.FALSE);
+ }
+ }
+ }.getE();
+ }
+ };
+
+ public ICellModifier getCellModifier(IPresentationContext context, Object element) {
+ assert element == this;
+ return cell_modifier;
+ }
+
+ @Override
+ public int compareTo(TCFNode n) {
+ if (n instanceof TCFNodeRegister) {
+ TCFNodeRegister r = (TCFNodeRegister)n;
+ if (index < r.index) return -1;
+ if (index > r.index) return +1;
+ }
+ return id.compareTo(n.id);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java
new file mode 100644
index 000000000..5a8d7fb51
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java
@@ -0,0 +1,541 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigInteger;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.tcf.internal.debug.model.TCFContextState;
+import org.eclipse.tcf.internal.debug.model.TCFFunctionRef;
+import org.eclipse.tcf.internal.debug.model.TCFSourceRef;
+import org.eclipse.tcf.internal.debug.ui.ImageCache;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.JSON;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IExpressions;
+import org.eclipse.tcf.services.IStackTrace;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.util.TCFDataCache;
+
+public class TCFNodeStackFrame extends TCFNode {
+
+ private int frame_no;
+ private boolean trace_limit;
+ private final boolean emulated;
+ private final TCFChildrenRegisters children_regs;
+ private final TCFChildrenLocalVariables children_vars;
+ private final TCFChildrenExpressions children_exps;
+ private final TCFChildrenHoverExpressions children_hover_exps;
+ private final TCFData<IStackTrace.StackTraceContext> stack_trace_context;
+ private final TCFData<TCFSourceRef> line_info;
+ private final TCFData<TCFFunctionRef> func_info;
+ private final TCFData<BigInteger> address;
+
+ TCFNodeStackFrame(final TCFNodeExecContext parent, final String id, final boolean emulated) {
+ super(parent, id);
+ this.emulated = emulated;
+ children_regs = new TCFChildrenRegisters(this);
+ children_vars = new TCFChildrenLocalVariables(this);
+ children_exps = new TCFChildrenExpressions(this);
+ children_hover_exps = new TCFChildrenHoverExpressions(this);
+ stack_trace_context = new TCFData<IStackTrace.StackTraceContext>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ assert command == null;
+ if (emulated) {
+ set(null, null, null);
+ return true;
+ }
+ TCFDataCache<TCFContextState> parent_state_cache = parent.getState();
+ if (!parent_state_cache.validate(this)) return false;
+ TCFContextState parent_state_data = parent_state_cache.getData();
+ if (parent_state_data == null || !parent_state_data.is_suspended) {
+ set(null, null, null);
+ return true;
+ }
+ TCFChildrenStackTrace stack_trace_cache = parent.getStackTrace();
+ if (!stack_trace_cache.validate(this)) return false;
+ if (frame_no < 0) {
+ set(null, null, null);
+ return true;
+ }
+ IStackTrace st = launch.getService(IStackTrace.class);
+ if (st == null) {
+ assert frame_no == 0;
+ set(null, null, null);
+ return true;
+ }
+ command = st.getContext(new String[]{ id }, new IStackTrace.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) {
+ set(token, error, context == null || context.length == 0 ? null : context[0]);
+ }
+ });
+ return false;
+ }
+ };
+ line_info = new TCFData<TCFSourceRef>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!stack_trace_context.validate(this)) return false;
+ if (!address.validate(this)) return false;
+ BigInteger n = address.getData();
+ if (n == null) {
+ set(null, address.getError(), null);
+ return true;
+ }
+ if (frame_no > 0) n = n.subtract(BigInteger.valueOf(1));
+ TCFDataCache<TCFNodeExecContext> mem_cache = ((TCFNodeExecContext)parent).getMemoryNode();
+ if (!mem_cache.validate(this)) return false;
+ if (mem_cache.getError() != null || mem_cache.getData() == null) {
+ set(null, mem_cache.getError(), null);
+ return true;
+ }
+ TCFDataCache<TCFSourceRef> info_cache = mem_cache.getData().getLineInfo(n);
+ if (info_cache == null) {
+ set(null, null, null);
+ return true;
+ }
+ if (!info_cache.validate(this)) return false;
+ set(null, info_cache.getError(), info_cache.getData());
+ return true;
+ }
+ };
+ func_info = new TCFData<TCFFunctionRef>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!address.validate(this)) return false;
+ BigInteger n = address.getData();
+ if (n == null) {
+ set(null, address.getError(), null);
+ return true;
+ }
+ TCFDataCache<TCFNodeExecContext> mem_cache = ((TCFNodeExecContext)parent).getMemoryNode();
+ if (!mem_cache.validate(this)) return false;
+ if (mem_cache.getError() != null || mem_cache.getData() == null) {
+ set(null, mem_cache.getError(), null);
+ return true;
+ }
+ TCFDataCache<TCFFunctionRef> info_cache = mem_cache.getData().getFuncInfo(n);
+ if (info_cache == null) {
+ set(null, null, null);
+ return true;
+ }
+ if (!info_cache.validate(this)) return false;
+ set(null, info_cache.getError(), info_cache.getData());
+ return true;
+ }
+ };
+ address = new TCFData<BigInteger>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ if (!stack_trace_context.validate(this)) return false;
+ IStackTrace.StackTraceContext ctx = stack_trace_context.getData();
+ if (ctx != null) {
+ Number n = ctx.getInstructionAddress();
+ if (n instanceof BigInteger) {
+ set(null, null, (BigInteger)n);
+ return true;
+ }
+ if (n != null) {
+ set(null, null, JSON.toBigInteger(n));
+ return true;
+ }
+ }
+ if (frame_no == 0) {
+ TCFDataCache<BigInteger> addr_cache = parent.getAddress();
+ if (!addr_cache.validate(this)) return false;
+ set(null, addr_cache.getError(), addr_cache.getData());
+ return true;
+ }
+ set(null, stack_trace_context.getError(), null);
+ return true;
+ }
+ };
+ }
+
+ /**
+ * Get frame position in the parent's stack trace.
+ * Top frame position is 0.
+ * @return frame position or -1 if the frame is not part of the trace.
+ */
+ public int getFrameNo() {
+ assert Protocol.isDispatchThread();
+ return frame_no;
+ }
+
+ void setFrameNo(int frame_no) {
+ this.frame_no = frame_no;
+ }
+
+ void setTraceLimit(boolean trace_limit) {
+ this.trace_limit = trace_limit;
+ }
+
+ TCFChildren getHoverExpressionCache(String expression) {
+ children_hover_exps.setExpression(expression);
+ return children_hover_exps;
+ }
+
+ public TCFDataCache<TCFSourceRef> getLineInfo() {
+ return line_info;
+ }
+
+ public TCFDataCache<IStackTrace.StackTraceContext> getStackTraceContext() {
+ return stack_trace_context;
+ }
+
+ public TCFDataCache<BigInteger> getAddress() {
+ return address;
+ }
+
+ TCFChildren getRegisters() {
+ return children_regs;
+ }
+
+ public BigInteger getReturnAddress() {
+ assert Protocol.isDispatchThread();
+ if (!stack_trace_context.isValid()) return null;
+ IStackTrace.StackTraceContext ctx = stack_trace_context.getData();
+ if (ctx != null) return JSON.toBigInteger(ctx.getReturnAddress());
+ return null;
+ }
+
+ public boolean isEmulated() {
+ return emulated;
+ }
+
+ boolean isTraceLimit() {
+ return trace_limit && ((TCFNodeExecContext)parent).getViewBottomFrame() == this;
+ }
+
+ void riseTraceLimit() {
+ ((TCFNodeExecContext)parent).riseTraceLimit();
+ }
+
+ private TCFChildren getChildren(IPresentationContext ctx) {
+ String id = ctx.getId();
+ if (IDebugUIConstants.ID_REGISTER_VIEW.equals(id)) return children_regs;
+ if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(id)) return children_vars;
+ if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id)) return children_exps;
+ if (TCFModel.ID_EXPRESSION_HOVER.equals(id)) return children_hover_exps;
+ return null;
+ }
+
+ @Override
+ protected boolean getData(IHasChildrenUpdate result, Runnable done) {
+ TCFChildren c = getChildren(result.getPresentationContext());
+ if (c != null) {
+ if (!c.validate(done)) return false;
+ result.setHasChilren(c.size() > 0);
+ }
+ else {
+ result.setHasChilren(false);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IChildrenCountUpdate result, Runnable done) {
+ TCFChildren c = getChildren(result.getPresentationContext());
+ if (c != null) {
+ if (!c.validate(done)) return false;
+ result.setChildCount(c.size());
+ }
+ else {
+ result.setChildCount(0);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IChildrenUpdate result, Runnable done) {
+ TCFChildren children = getChildren(result.getPresentationContext());
+ if (children == null) return true;
+ return children.getData(result, done);
+ }
+
+ @Override
+ protected boolean getData(ILabelUpdate result, Runnable done) {
+ TCFChildrenStackTrace stack_trace_cache = ((TCFNodeExecContext)parent).getStackTrace();
+ if (!stack_trace_cache.validate(done)) return false;
+ if (stack_trace_cache.getData().get(id) == null) {
+ result.setLabel("", 0);
+ }
+ else if (isTraceLimit()) {
+ result.setLabel("<select to see more frames>", 0);
+ }
+ else {
+ boolean show_arg_names = model.getShowFunctionArgNames();
+ boolean show_arg_values = model.getShowFunctionArgValues();
+ TCFDataCache<TCFContextState> state_cache = ((TCFNodeExecContext)parent).getState();
+ TCFDataCache<TCFNodeExecContext> mem_cache = ((TCFNodeExecContext)parent).getMemoryNode();
+ TCFDataCache<?> pending = null;
+ if (!state_cache.validate()) pending = state_cache;
+ if (!mem_cache.validate()) pending = mem_cache;
+ if (!stack_trace_context.validate()) pending = stack_trace_context;
+ if (!address.validate()) pending = address;
+ if (!line_info.validate()) pending = line_info;
+ if (!func_info.validate()) pending = func_info;
+ if (show_arg_names || show_arg_values) {
+ if (!children_vars.validate()) {
+ pending = children_vars;
+ }
+ else {
+ for (TCFNode n : children_vars.toArray()) {
+ TCFNodeExpression e = (TCFNodeExpression)n;
+ if (!e.getVariable().validate()) pending = e.getVariable();
+ }
+ }
+ }
+ if (pending != null) {
+ pending.wait(done);
+ return false;
+ }
+ Throwable error = state_cache.getError();
+ if (error == null) error = stack_trace_cache.getError();
+ if (error == null) error = stack_trace_context.getError();
+ if (error == null) error = address.getError();
+ if (error == null) error = line_info.getError();
+ BigInteger addr = address.getData();
+ TCFSourceRef sref = line_info.getData();
+ TCFContextState state = state_cache.getData();
+ StringBuffer bf = new StringBuffer();
+ if (addr != null) {
+ bf.append(makeHexAddrString(sref != null ? sref.address_size : 0, addr));
+ TCFNodeExecContext mem_node = mem_cache.getData();
+ if (mem_node != null) {
+ TCFDataCache<TCFNodeExecContext.MemoryRegion[]> map_dc = mem_node.getMemoryMap();
+ if (!map_dc.validate(done)) return false;
+ TCFNodeExecContext.MemoryRegion[] map = map_dc.getData();
+ if (map != null) {
+ for (TCFNodeExecContext.MemoryRegion r : map) {
+ String fnm = r.region.getFileName();
+ if (fnm != null && r.contains(addr)) {
+ fnm = fnm.replace('\\', '/');
+ int x = fnm.lastIndexOf('/');
+ if (x >= 0) fnm = fnm.substring(x + 1);
+ bf.append(" [");
+ bf.append(fnm);
+ bf.append("]");
+ break;
+ }
+ }
+ }
+ }
+ }
+ TCFFunctionRef ref = func_info.getData();
+ if (ref != null && ref.symbol_id != null) {
+ TCFDataCache<ISymbols.Symbol> sym_cache = model.getSymbolInfoCache(ref.symbol_id);
+ if (!sym_cache.validate(done)) return false;
+ ISymbols.Symbol sym_data = sym_cache.getData();
+ if (sym_data != null && sym_data.getName() != null) {
+ bf.append(" ");
+ bf.append(sym_data.getName());
+ bf.append('(');
+ if (show_arg_names || show_arg_values) {
+ if (children_vars.getError() != null) {
+ bf.append('?');
+ }
+ else {
+ int cnt = 0;
+ for (TCFNode n : children_vars.toArray()) {
+ ISymbols.Symbol sym = null;
+ TCFNodeExpression expr_node = (TCFNodeExpression)n;
+ IExpressions.Expression expr_props = expr_node.getVariable().getData();
+ if (expr_props != null) {
+ TCFDataCache<ISymbols.Symbol> s = model.getSymbolInfoCache(expr_props.getSymbolID());
+ if (!s.validate(done)) return false;
+ sym = s.getData();
+ }
+ if (sym == null) continue;
+ if (!sym.getFlag(ISymbols.SYM_FLAG_PARAMETER)) continue;
+ if (cnt > 0) bf.append(',');
+ if (show_arg_names) {
+ String name = "?";
+ if (sym != null && sym.getName() != null) name = sym.getName();
+ bf.append(name);
+ if (show_arg_values) bf.append('=');
+ }
+ if (show_arg_values) {
+ String s = expr_node.getValueText(false, done);
+ if (s == null) return false;
+ bf.append(s.length() == 0 ? "?" : s);
+ }
+ cnt++;
+ }
+ }
+ }
+ bf.append(')');
+ }
+ }
+ if (sref != null && sref.area != null && sref.area.file != null) {
+ bf.append(": ");
+ int l = sref.area.file.length();
+ if (l > 32) {
+ bf.append("...");
+ bf.append(sref.area.file.substring(l - 32));
+ }
+ else {
+ bf.append(sref.area.file);
+ }
+ bf.append(", line ");
+ bf.append(sref.area.start_line);
+ }
+ if (error != null) {
+ if (state == null || state.is_suspended) {
+ result.setForeground(new RGB(255, 0, 0), 0);
+ if (bf.length() > 0) bf.append(": ");
+ bf.append(TCFModel.getErrorMessage(error, false));
+ }
+ else {
+ result.setLabel("...", 0);
+ }
+ }
+ if (bf.length() == 0) bf.append("...");
+ result.setLabel(bf.toString(), 0);
+ String image_name = state != null && state.is_suspended ?
+ ImageCache.IMG_STACK_FRAME_SUSPENDED :
+ ImageCache.IMG_STACK_FRAME_RUNNING;
+ result.setImageDescriptor(ImageCache.getImageDescriptor(image_name), 0);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean getData(IViewerInputUpdate result, Runnable done) {
+ result.setInputElement(this);
+ String id = result.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_REGISTER_VIEW.equals(id) || IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id)) {
+ TCFNodeExecContext exe = (TCFNodeExecContext)parent;
+ TCFChildrenStackTrace stack_trace_cache = exe.getStackTrace();
+ if (!stack_trace_cache.validate(done)) return false;
+ if (stack_trace_cache.getTopFrame() == this) result.setInputElement(exe);
+ }
+ else if (IDebugUIConstants.ID_MODULE_VIEW.equals(id)) {
+ // TODO: need to post view input delta when memory context changes
+ TCFDataCache<TCFNodeExecContext> mem = model.searchMemoryContext(this);
+ if (mem == null) return true;
+ if (!mem.validate(done)) return false;
+ if (mem.getData() == null) return true;
+ result.setInputElement(mem.getData());
+ }
+ return true;
+ }
+
+ private String makeHexAddrString(int addr_size, BigInteger n) {
+ String s = n.toString(16);
+ int sz = (addr_size != 0 ? addr_size : 4) * 2;
+ int l = sz - s.length();
+ if (l < 0) l = 0;
+ if (l > 16) l = 16;
+ return "0x0000000000000000".substring(0, 2 + l) + s;
+ }
+
+ void postAllChangedDelta() {
+ for (TCFModelProxy p : model.getModelProxies()) {
+ int flags = 0;
+ String view_id = p.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id) &&
+ (launch.getContextActionsCount(parent.id) == 0 ||
+ !model.getDelayStackUpdateUtilLastStep())) {
+ flags |= IModelDelta.STATE;
+ }
+ if (getChildren(p.getPresentationContext()) != null && p.getInput() == this) flags |= IModelDelta.CONTENT;
+ if (flags == 0) continue;
+ p.addDelta(this, flags);
+ }
+ }
+
+ private void postStateChangedDelta() {
+ for (TCFModelProxy p : model.getModelProxies()) {
+ String id = p.getPresentationContext().getId();
+ if (IDebugUIConstants.ID_DEBUG_VIEW.equals(id)) {
+ p.addDelta(this, IModelDelta.STATE);
+ }
+ }
+ }
+
+ void onExpressionAddedOrRemoved() {
+ children_exps.cancel();
+ }
+
+ void onSourceMappingChange() {
+ line_info.reset();
+ postStateChangedDelta();
+ }
+
+ void onSuspended() {
+ stack_trace_context.cancel();
+ line_info.cancel();
+ func_info.cancel();
+ address.cancel();
+ children_regs.onSuspended();
+ // Unlike thread registers, stack frame register list must be retrieved on every suspend
+ children_regs.reset();
+ children_vars.onSuspended();
+ children_exps.onSuspended();
+ children_hover_exps.onSuspended();
+ // delta is posted by the parent node
+ }
+
+ void onMemoryMapChanged() {
+ line_info.reset();
+ func_info.reset();
+ children_vars.onMemoryMapChanged();
+ children_exps.onMemoryMapChanged();
+ postAllChangedDelta();
+ }
+
+ void onMemoryChanged() {
+ stack_trace_context.cancel();
+ line_info.cancel();
+ func_info.cancel();
+ address.cancel();
+ children_vars.onMemoryChanged();
+ children_exps.onMemoryChanged();
+ children_hover_exps.onMemoryChanged();
+ postStateChangedDelta();
+ }
+
+ void onRegistersChanged() {
+ children_regs.onRegistersChanged();
+ postAllChangedDelta();
+ }
+
+ void onRegisterValueChanged() {
+ stack_trace_context.cancel();
+ line_info.cancel();
+ func_info.cancel();
+ address.cancel();
+ children_vars.onRegisterValueChanged();
+ children_exps.onRegisterValueChanged();
+ children_hover_exps.onRegisterValueChanged();
+ postStateChangedDelta();
+ }
+
+ @Override
+ public int compareTo(TCFNode n) {
+ if (n instanceof TCFNodeStackFrame) {
+ TCFNodeStackFrame f = (TCFNodeStackFrame)n;
+ if (frame_no < f.frame_no) return -1;
+ if (frame_no > f.frame_no) return +1;
+ }
+ return id.compareTo(n.id);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeSymbol.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeSymbol.java
new file mode 100644
index 000000000..855ae0fc2
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeSymbol.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.util.TCFDataCache;
+
+public class TCFNodeSymbol extends TCFNode {
+
+ private final TCFData<ISymbols.Symbol> context;
+ private final TCFData<String[]> children;
+
+ private int update_policy;
+ private ISymbolOwner owner;
+
+ private TCFNodeSymbol prev;
+ private TCFNodeSymbol next;
+
+ private static final int MAX_SYMBOL_COUNT = 64;
+ private static TCFNodeSymbol sym_list;
+ private static int sym_count;
+ private static boolean gc_posted;
+
+ protected TCFNodeSymbol(final TCFNode parent, final String id) {
+ super(parent, id);
+ context = new TCFData<ISymbols.Symbol>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ ISymbols syms = launch.getService(ISymbols.class);
+ if (id == null || syms == null) {
+ set(null, null, null);
+ return true;
+ }
+ command = syms.getContext(id, new ISymbols.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, ISymbols.Symbol sym) {
+ set(token, error, sym);
+ if (error != null || sym == null) setUpdatePolicy(null, 0);
+ else setUpdatePolicy(sym.getOwnerID(), sym.getUpdatePolicy());
+ }
+ });
+ return false;
+ }
+ };
+ children = new TCFData<String[]>(channel) {
+ @Override
+ protected boolean startDataRetrieval() {
+ ISymbols syms = launch.getService(ISymbols.class);
+ if (id == null || syms == null) {
+ set(null, null, null);
+ return true;
+ }
+ command = syms.getChildren(id, new ISymbols.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] ids) {
+ set(token, error, ids);
+ }
+ });
+ return false;
+ }
+ };
+ setUpdatePolicy(null, 0);
+ if (sym_list == null) {
+ prev = next = this;
+ }
+ else {
+ prev = sym_list;
+ next = sym_list.next;
+ prev.next = next.prev = this;
+ }
+ sym_list = this;
+ if (!gc_posted) {
+ // Garbage collection: dispose unused symbols
+ gc_posted = true;
+ Protocol.invokeLater(5000, new Runnable() {
+ public void run() {
+ gc_posted = false;
+ int cnt = sym_count / 16;
+ while (sym_count > MAX_SYMBOL_COUNT) {
+ TCFNodeSymbol s = sym_list.next;
+ if (s.context.isPending()) break;
+ if (s.children.isPending()) break;
+ s.dispose();
+ if (cnt == 0) break;
+ cnt--;
+ }
+ if (sym_count > 0) {
+ gc_posted = true;
+ Protocol.invokeLater(5000, this);
+ }
+ }
+ });
+ }
+ sym_count++;
+ }
+
+ @Override
+ public void dispose() {
+ assert !isDisposed();
+ if (owner != null) {
+ owner.removeSymbol(this);
+ owner = null;
+ }
+ if (sym_list == this) sym_list = prev;
+ if (sym_list == this) {
+ sym_list = null;
+ }
+ else {
+ prev.next = next;
+ next.prev = prev;
+ }
+ prev = next = null;
+ sym_count--;
+ assert (sym_count == 0) == (sym_list == null);
+ super.dispose();
+ }
+
+ public TCFDataCache<ISymbols.Symbol> getContext() {
+ if (sym_list != this) {
+ prev.next = next;
+ next.prev = prev;
+ prev = sym_list;
+ next = sym_list.next;
+ prev.next = next.prev = this;
+ sym_list = this;
+ }
+ return context;
+ }
+
+ public TCFDataCache<String[]> getChildren() {
+ if (sym_list != this) {
+ prev.next = next;
+ next.prev = prev;
+ prev = sym_list;
+ next = sym_list.next;
+ prev.next = next.prev = this;
+ sym_list = this;
+ }
+ return children;
+ }
+
+ private void setUpdatePolicy(String id, int policy) {
+ update_policy = policy;
+ if (!isDisposed()) {
+ TCFNode n = model.getNode(id);
+ if (!(n instanceof ISymbolOwner)) n = parent;
+ if (n != owner) {
+ if (owner != null) owner.removeSymbol(this);
+ owner = (ISymbolOwner)n;
+ owner.addSymbol(this);
+ }
+ }
+ }
+
+ void onMemoryMapChanged() {
+ context.reset();
+ children.reset();
+ }
+
+ void onExeStateChange() {
+ if (update_policy == ISymbols.UPDATE_ON_MEMORY_MAP_CHANGES) return;
+ context.reset();
+ children.reset();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNumberFormat.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNumberFormat.java
new file mode 100644
index 000000000..fd9c63c3c
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNumberFormat.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+
+public class TCFNumberFormat {
+
+ public static String isValidHexNumber(String s) {
+ int l = s.length();
+ if (l == 0) return "Need at least one digit";
+ for (int i = 0; i < l; i++) {
+ char ch = s.charAt(i);
+ if (ch >= '0' && ch <= '9') continue;
+ if (ch >= 'A' && ch <= 'F') continue;
+ if (ch >= 'a' && ch <= 'f') continue;
+ return "Hex digit expected";
+ }
+ return null;
+ }
+
+ public static String isValidDecNumber(boolean fp, String s) {
+ int i = 0;
+ int l = s.length();
+ if (l == 0) return "Need at least one digit";
+ char ch = s.charAt(i++);
+ if (ch == '-' || ch == '+') {
+ if (i >= l) return "Need at least one digit";
+ ch = s.charAt(i++);
+ }
+ if (fp) {
+ String n = s.substring(i - 1);
+ if (n.equals("NaN")) return null;
+ if (n.equals("Infinity")) return null;
+ }
+ while (ch >= '0' && ch <= '9') {
+ if (i >= l) return null;
+ ch = s.charAt(i++);
+ }
+ if (fp) {
+ if (ch == '.') {
+ if (i >= l) return null;
+ ch = s.charAt(i++);
+ while (ch >= '0' && ch <= '9') {
+ if (i >= l) return null;
+ ch = s.charAt(i++);
+ }
+ }
+ if (ch == 'e' || ch == 'E') {
+ if (i >= l) return "Invalid exponent: need at least one digit";
+ ch = s.charAt(i++);
+ if (ch == '-' || ch == '+') {
+ if (i >= l) return "Invalid exponent: need at least one digit";
+ ch = s.charAt(i++);
+ }
+ while (ch >= '0' && ch <= '9') {
+ if (i >= l) return null;
+ ch = s.charAt(i++);
+ }
+ return "Invalid exponent: decimal digit expected";
+ }
+ }
+ return "Decimal digit expected";
+ }
+
+ public static byte[] toByteArray(String s, int radix, boolean fp, int size, boolean signed, boolean big_endian) throws Exception {
+ byte[] bf = null;
+ if (!fp) {
+ bf = new BigInteger(s, radix).toByteArray();
+ }
+ else if (size == 4) {
+ int n = Float.floatToIntBits(Float.parseFloat(s));
+ bf = new byte[size];
+ for (int i = 0; i < size; i++) {
+ bf[i] = (byte)((n >> ((size - 1 - i) * 8)) & 0xff);
+ }
+ }
+ else if (size == 8) {
+ long n = Double.doubleToLongBits(Double.parseDouble(s));
+ bf = new byte[size];
+ for (int i = 0; i < size; i++) {
+ bf[i] = (byte)((n >> ((size - 1 - i) * 8)) & 0xff);
+ }
+ }
+ else {
+ throw new Exception("Unsupported floating point format");
+ }
+ byte[] rs = new byte[size];
+ if (signed && rs.length > bf.length && (bf[0] & 0x80) != 0) {
+ // Sign extension
+ for (int i = 0; i < rs.length; i++) rs[i] = (byte)0xff;
+ }
+ for (int i = 0; i < bf.length; i++) {
+ // i == 0 -> least significant byte
+ byte b = bf[bf.length - i - 1];
+ int j = big_endian ? rs.length - i - 1 : i;
+ if (j >= 0 && j < rs.length) rs[j] = b;
+ }
+ return rs;
+ }
+
+ public static String toFPString(byte[] data, int offs, int size, boolean big_endian) {
+ assert offs + size <= data.length;
+ byte[] arr = new byte[size];
+ if (big_endian) {
+ System.arraycopy(data, offs, arr, 0, size);
+ }
+ else {
+ for (int i = 0; i < size; i++) {
+ arr[arr.length - i - 1] = data[offs + i];
+ }
+ }
+
+ boolean neg = (arr[0] & 0x80) != 0;
+ arr[0] &= 0x7f;
+
+ int precision = 0;
+ int exponent = 0;
+ boolean nan = false;
+ switch (size) {
+ case 2:
+ precision = 3;
+ exponent = (arr[0] & 0x7c) >> 2;
+ nan = exponent == 0x1f;
+ arr[0] &= 0x03;
+ if (exponent == 0) exponent = 1;
+ else arr[0] |= 0x04;
+ exponent -= 10; // Significand
+ exponent -= 15; // Exponent bias
+ break;
+ case 4:
+ precision = 7;
+ exponent = ((arr[0] & 0x7f) << 1) | ((arr[1] & 0x80) >> 7);
+ nan = exponent == 0xff;
+ arr[0] = 0;
+ arr[1] &= 0x7f;
+ if (exponent == 0) exponent = 1;
+ else arr[1] |= 0x80;
+ exponent -= 23; // Significand
+ exponent -= 127; // Exponent bias
+ break;
+ case 8:
+ precision = 16;
+ exponent = ((arr[0] & 0x7f) << 4) | ((arr[1] & 0xf0) >> 4);
+ nan = exponent == 0x7ff;
+ arr[0] = 0;
+ arr[1] &= 0x0f;
+ if (exponent == 0) exponent = 1;
+ else arr[1] |= 0x10;
+ exponent -= 52; // Significand
+ exponent -= 1023; // Exponent bias
+ break;
+ case 10:
+ case 16:
+ precision = 34;
+ exponent = ((arr[0] & 0x7f) << 8) | (arr[1] & 0xff);
+ nan = exponent == 0x7fff;
+ arr[0] = arr[1] = 0;
+ if (size == 10) {
+ exponent -= 63; // Significand
+ }
+ else {
+ if (exponent == 0) exponent = 1;
+ else arr[1] = 1;
+ exponent -= 112; // Significand
+ }
+ exponent -= 16383; // Exponent bias
+ break;
+ default:
+ return null;
+ }
+ if (nan) {
+ for (int i = 0; i < arr.length; i++) {
+ int n = arr[i] & 0xff;
+ if (size == 10 && i == 2) n &= 0x7f;
+ if (n != 0) return neg ? "-NaN" : "+NaN";
+ }
+ return neg ? "-Infinity" : "+Infinity";
+ }
+ BigDecimal a = new BigDecimal(new BigInteger(arr), 0);
+ if (a.signum() != 0 && exponent != 0) {
+ BigDecimal p = new BigDecimal(BigInteger.valueOf(2), 0);
+ if (exponent > 0) {
+ a = a.multiply(p.pow(exponent));
+ }
+ else {
+ BigDecimal b = p.pow(-exponent);
+ a = a.divide(b, b.precision(), RoundingMode.HALF_DOWN);
+ }
+ if (precision != 0 && a.precision() > precision) {
+ int scale = a.scale() - a.precision() + precision;
+ a = a.setScale(scale, RoundingMode.HALF_DOWN);
+ }
+ }
+ String s = a.toString();
+ if (neg) s = "-" + s;
+ return s;
+ }
+
+ public static BigInteger toBigInteger(byte[] data, int offs, int size, boolean big_endian, boolean sign_extension) {
+ assert offs + size <= data.length;
+ byte[] temp = null;
+ if (sign_extension) {
+ temp = new byte[size];
+ }
+ else {
+ temp = new byte[size + 1];
+ temp[0] = 0; // Extra byte to avoid sign extension by BigInteger
+ }
+ if (big_endian) {
+ System.arraycopy(data, offs, temp, sign_extension ? 0 : 1, size);
+ }
+ else {
+ for (int i = 0; i < size; i++) {
+ temp[temp.length - i - 1] = data[i + offs];
+ }
+ }
+ return new BigInteger(temp);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFRunnable.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFRunnable.java
new file mode 100644
index 000000000..b2ecb0ae2
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFRunnable.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import org.eclipse.debug.core.IRequest;
+import org.eclipse.tcf.protocol.Protocol;
+
+
+public abstract class TCFRunnable implements Runnable {
+
+ private final IRequest request;
+
+ protected boolean done;
+
+ public TCFRunnable(IRequest request) {
+ this.request = request;
+ Protocol.invokeLater(this);
+ }
+
+ public void done() {
+ assert !done;
+ done = true;
+ request.done();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFSnapshot.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFSnapshot.java
new file mode 100644
index 000000000..c5205f777
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFSnapshot.java
@@ -0,0 +1,353 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.internal.debug.ui.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.tcf.protocol.Protocol;
+
+/**
+ * TCFSnapshot is used to create snapshots of debug views presentation data.
+ * Such snapshots are used to implement various view update policies.
+ */
+class TCFSnapshot {
+
+ private final IPresentationContext ctx;
+
+ private final HashMap<TCFNode,PresentationData> cache = new HashMap<TCFNode,PresentationData>();
+
+ private final String[] columns;
+ private final RGB rgb_stalled = new RGB(128, 128, 128);
+
+ private boolean ignore_bg_color = true;
+
+ private class PresentationData implements IChildrenCountUpdate, IChildrenUpdate, ILabelUpdate, Runnable {
+
+ IViewerUpdate update;
+ Runnable done;
+ boolean canceled;
+ IStatus status;
+
+ String[] label;
+ FontData[] font_data;
+ ImageDescriptor[] image_desc;
+ RGB[] fg_color;
+ RGB[] bg_color;
+ boolean label_done;
+
+ TCFNode[] children;
+ boolean children_done;
+
+ boolean stalled;
+
+ private final ArrayList<Runnable> waiting_list = new ArrayList<Runnable>();
+
+ public IPresentationContext getPresentationContext() {
+ return ctx;
+ }
+
+ public Object getElement() {
+ return update.getElement();
+ }
+
+ public TreePath getElementPath() {
+ return update.getElementPath();
+ }
+
+ public Object getViewerInput() {
+ return update.getViewerInput();
+ }
+
+ public void setStatus(IStatus status) {
+ this.status = status;
+ }
+
+ public IStatus getStatus() {
+ return status;
+ }
+
+ public void done() {
+ assert false;
+ }
+
+ public void cancel() {
+ canceled = true;
+ }
+
+ public boolean isCanceled() {
+ return canceled;
+ }
+
+ public String[] getColumnIds() {
+ return columns;
+ }
+
+ public void setLabel(String text, int col) {
+ if (!label_done) {
+ if (label == null) {
+ int cnt = columns == null ? 1 : columns.length;
+ label = new String[cnt];
+ }
+ label[col] = text;
+ }
+ else {
+ if (col >= label.length) stalled = true;
+ else if (label[col] != text) {
+ if (label[col] == null || text == null || !text.equals(label[col])) {
+ stalled = true;
+ }
+ }
+ }
+ }
+
+ public void setFontData(FontData fnt, int col) {
+ if (!label_done) {
+ if (font_data == null) {
+ int cnt = columns == null ? 1 : columns.length;
+ font_data = new FontData[cnt];
+ }
+ font_data[col] = fnt;
+ }
+ }
+
+ public void setImageDescriptor(ImageDescriptor image, int col) {
+ if (!label_done) {
+ if (image_desc == null) {
+ int cnt = columns == null ? 1 : columns.length;
+ image_desc = new ImageDescriptor[cnt];
+ }
+ image_desc[col] = image;
+ }
+ }
+
+ public void setForeground(RGB rgb, int col) {
+ if (!label_done) {
+ if (fg_color == null) {
+ int cnt = columns == null ? 1 : columns.length;
+ fg_color = new RGB[cnt];
+ }
+ fg_color[col] = rgb;
+ }
+ }
+
+ public void setBackground(RGB rgb, int col) {
+ if (!label_done) {
+ if (bg_color == null) {
+ int cnt = columns == null ? 1 : columns.length;
+ bg_color = new RGB[cnt];
+ }
+ bg_color[col] = rgb;
+ }
+ }
+
+ public int getOffset() {
+ return 0;
+ }
+
+ public int getLength() {
+ return children.length;
+ }
+
+ public void setChild(Object child, int offset) {
+ if (!children_done) {
+ children[offset] = (TCFNode)child;
+ }
+ }
+
+ public void setChildCount(int cnt) {
+ if (!children_done) {
+ children = new TCFNode[cnt];
+ }
+ }
+
+ public void run() {
+ Runnable d = done;
+ update = null;
+ done = null;
+ for (Runnable r : waiting_list) Protocol.invokeLater(r);
+ waiting_list.clear();
+ d.run();
+ }
+ }
+
+ private PresentationData data;
+
+ TCFSnapshot(IPresentationContext ctx) {
+ this.ctx = ctx;
+ columns = ctx.getColumns();
+ }
+
+ void dispose() {
+ for (PresentationData d : cache.values()) {
+ for (Runnable r : d.waiting_list) Protocol.invokeLater(r);
+ }
+ cache.clear();
+ }
+
+ /**
+ * Retrieve children count for a presentation context.
+ * The method is always called on TCF dispatch thread.
+ * @param update - children count update request.
+ * @param node - debug model node.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ public boolean getData(IChildrenCountUpdate update, TCFNode node, Runnable done) {
+ if (!getChildren(update, node, done)) return false;
+ update.setChildCount(data.children.length);
+ return true;
+ }
+
+ /**
+ * Retrieve children for a presentation context.
+ * The method is always called on TCF dispatch thread.
+ * @param update - children update request.
+ * @param node - debug model node.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ public boolean getData(IChildrenUpdate update, TCFNode node, Runnable done) {
+ if (!getChildren(update, node, done)) return false;
+ int offset = 0;
+ int r_offset = update.getOffset();
+ int r_length = update.getLength();
+ for (TCFNode n : data.children) {
+ if (offset >= r_offset && offset < r_offset + r_length) {
+ update.setChild(n, offset);
+ }
+ offset++;
+ }
+ return true;
+ }
+
+ /**
+ * Check if the node has children in a presentation context.
+ * The method is always called on TCF dispatch thread.
+ * @param update - "has children" update request.
+ * @param node - debug model node.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ public boolean getData(IHasChildrenUpdate update, TCFNode node, Runnable done) {
+ if (!getChildren(update, node, done)) return false;
+ update.setHasChilren(data.children.length > 0);
+ return true;
+ }
+
+ /**
+ * Retrieve node label for a presentation context.
+ * The method is always called on TCF dispatch thread.
+ * @param update - label update request.
+ * @param node - debug model node.
+ * @param done - client call back interface, during data waiting it is
+ * called every time new portion of data becomes available.
+ * @return false if waiting data retrieval, true if all done.
+ */
+ public boolean getData(ILabelUpdate update, TCFNode node, Runnable done) {
+ if (!getLabel(update, node, done)) return false;
+ String[] ids_update = update.getColumnIds();
+ String[] ids_data = columns;
+ if (ids_update != ids_data && !Arrays.equals(ids_update, ids_data)) {
+ int n = ids_update == null ? 1 : ids_update.length;
+ for (int i = 0; i < n; i++) update.setBackground(rgb_stalled, i);
+ }
+ else {
+ if (data.label != null) {
+ for (int i = 0; i < data.label.length; i++) {
+ if (data.label[i] != null) update.setLabel(data.label[i], i);
+ }
+ }
+ if (data.font_data != null) {
+ for (int i = 0; i < data.font_data.length; i++) {
+ if (data.font_data[i] != null) update.setFontData(data.font_data[i], i);
+ }
+ }
+ if (data.image_desc != null) {
+ for (int i = 0; i < data.image_desc.length; i++) {
+ if (data.image_desc[i] != null) update.setImageDescriptor(data.image_desc[i], i);
+ }
+ }
+ if (data.stalled) {
+ int n = ids_update == null ? 1 : ids_update.length;
+ for (int i = 0; i < n; i++) update.setForeground(rgb_stalled, i);
+ }
+ else {
+ if (data.fg_color != null) {
+ for (int i = 0; i < data.fg_color.length; i++) {
+ if (data.fg_color[i] != null) update.setForeground(data.fg_color[i], i);
+ }
+ }
+ }
+ if (!ignore_bg_color && data.bg_color != null) {
+ for (int i = 0; i < data.bg_color.length; i++) {
+ if (data.bg_color[i] != null) update.setBackground(data.bg_color[i], i);
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean getChildren(IViewerUpdate update, TCFNode node, Runnable done) {
+ data = cache.get(node);
+ if (data == null) cache.put(node, data = new PresentationData());
+ assert data.update != update;
+ if (data.children_done) return true;
+ if (data.update != null) {
+ data.waiting_list.add(done);
+ return false;
+ }
+ data.update = update;
+ data.done = done;
+ if (data.children == null) {
+ if (!node.getData((IChildrenCountUpdate)data, data)) return false;
+ assert data.children != null;
+ }
+ if (!node.getData((IChildrenUpdate)data, data)) return false;
+ data.children_done = true;
+ data.update = null;
+ data.done = null;
+ return true;
+ }
+
+ private boolean getLabel(IViewerUpdate update, TCFNode node, Runnable done) {
+ data = cache.get(node);
+ if (data == null) cache.put(node, data = new PresentationData());
+ assert data.update != update;
+ if (data.label_done && data.stalled) return true;
+ if (data.update != null) {
+ data.waiting_list.add(done);
+ return false;
+ }
+ data.update = update;
+ data.done = done;
+ if (!node.getData((ILabelUpdate)data, data)) return false;
+ data.label_done = true;
+ data.update = null;
+ data.done = null;
+ return true;
+ }
+}

Back to the top