From 1ffc84fb58e13147b733e557660af4ee0bdf494b Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 5 Nov 2010 01:28:52 +0000 Subject: [PATCH] Bug 302121: Fully support gdb pretty printers --- .../plugin.properties | 9 + dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml | 14 +- .../ui/actions/FetchMoreChildrenAction.java | 113 ++ .../preferences/GdbDebugPreferencePage.java | 74 + .../preferences/GdbPreferenceInitializer.java | 3 + .../preferences/MessagesForPreferences.java | 9 + .../MessagesForPreferences.properties | 6 + .../ui/viewmodel/FetchMoreChildrenEvent.java | 34 + .../ui/viewmodel/GdbExpressionVMProvider.java | 105 ++ .../ui/viewmodel/GdbVariableVMNode.java | 426 ++++- .../ui/viewmodel/GdbVariableVMProvider.java | 108 +- .../internal/ui/viewmodel/Message.properties | 2 + .../gdb/internal/ui/viewmodel/Messages.java | 2 + .../dsf/gdb/IGdbDebugPreferenceConstants.java | 17 +- .../gdb/launching/FinalLaunchSequence.java | 27 + .../dsf/gdb/service/command/GDBControl.java | 15 + .../gdb/service/command/GDBControl_7_0.java | 24 + .../dsf/gdb/service/command/IGDBControl.java | 22 + .../cdt/dsf/mi/service/IMIExpressions.java | 101 ++ .../cdt/dsf/mi/service/MIExpressions.java | 368 ++++- .../cdt/dsf/mi/service/MIVariableManager.java | 1377 ++++++++++++++--- .../eclipse/cdt/dsf/mi/service/Messages.java | 2 + .../cdt/dsf/mi/service/Messages.properties | 3 + .../mi/service/command/CommandFactory.java | 24 + .../command/commands/CLIMaintenance.java | 25 + .../commands/ExprMetaGetChildCount.java | 44 +- .../command/commands/ExprMetaGetChildren.java | 44 +- .../commands/MIEnablePrettyPrinting.java | 31 + .../commands/MIVarInfoNumChildren.java | 16 +- .../commands/MIVarInfoPathExpression.java | 7 +- .../command/commands/MIVarListChildren.java | 18 +- .../command/commands/MIVarSetUpdateRange.java | 40 + .../command/output/ExprMetaGetVarInfo.java | 79 +- .../service/command/output/MIDisplayHint.java | 135 ++ .../dsf/mi/service/command/output/MIVar.java | 95 +- .../service/command/output/MIVarChange.java | 106 +- .../command/output/MIVarCreateInfo.java | 55 +- .../output/MIVarInfoNumChildrenInfo.java | 6 +- .../command/output/MIVarListChildrenInfo.java | 24 +- .../command/output/MIVarUpdateInfo.java | 41 +- 40 files changed, 3373 insertions(+), 278 deletions(-) create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/FetchMoreChildrenAction.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/FetchMoreChildrenEvent.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIExpressions.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIMaintenance.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnablePrettyPrinting.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarSetUpdateRange.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDisplayHint.java diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties index 642aa5b1d3f..4a343bfaf70 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties @@ -8,6 +8,7 @@ # Contributors: # Ericsson - initial API and implementation # IBM Corporation +# Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) ############################################################################### pluginName=GDB DSF Debugger Integration UI providerName=Eclipse CDT @@ -41,3 +42,11 @@ command.nextTraceRecord.name=Next Trace Record command.prevTraceRecord.name=Previous Trace Record command.nextTraceRecord.description=Select Next Trace Record command.prevTraceRecord.description=Select Previous Trace Record + +category.description = C/C++ debugging with the DSF GDB debugger +category.name = CDT DSF-GDB - GDB Debugging +activity.description = C/C++ debugging with the DSF GDB debugger +activity.name = CDT DSF-GDB - GDB Debugging + +# Pretty Printing +action.fetchMoreChildren.label=Fetch More Children diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml index b161b9d1d48..a71b10471e1 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml @@ -459,5 +459,17 @@ - + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/FetchMoreChildrenAction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/FetchMoreChildrenAction.java new file mode 100644 index 00000000000..84842f340c6 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/FetchMoreChildrenAction.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2010 Verigy 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: + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.actions; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.AbstractVMProviderActionDelegate; +import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.FetchMoreChildrenEvent; +import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbExpressionVMProvider; +import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbVariableVMNode; +import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbVariableVMNode.IncompleteChildrenVMC; +import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbVariableVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.debug.ui.contexts.DebugContextEvent; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbenchPart; + +/** + * @since 3.0 + */ +public class FetchMoreChildrenAction extends AbstractVMProviderActionDelegate + implements IObjectActionDelegate { + + private ISelection selection; + + public void run(IAction action) { + IncompleteChildrenVMC incompleteChildrenVmc = getIncompleteChildrenVMC(); + + if (incompleteChildrenVmc != null) { + if (selection instanceof ITreeSelection) { + ITreeSelection treeSelection = (ITreeSelection) selection; + TreePath path = treeSelection.getPaths()[0]; + + IVMNode node = incompleteChildrenVmc.getVMNode(); + + IExpressionDMContext exprCtx = incompleteChildrenVmc.getParentDMContext(); + ((GdbVariableVMNode) node).incrementChildCountLimit(exprCtx); + final FetchMoreChildrenEvent fetchMoreChildrenEvent = new FetchMoreChildrenEvent(exprCtx, path); + final AbstractVMProvider vmProvider = (AbstractVMProvider) getVMProvider(); + vmProvider.getExecutor().execute(new DsfRunnable() { + public void run() { + vmProvider.handleEvent(fetchMoreChildrenEvent); + } + }); + } + } + } + + @Override + public void init(IViewPart view) { + super.init(view); + updateEnablement(); + } + + @Override + public void debugContextChanged(DebugContextEvent event) { + super.debugContextChanged(event); + updateEnablement(); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + this.selection = selection; + updateEnablement(); + } + + private void updateEnablement() { + boolean enabled = false; + if ((getVMProvider() instanceof GdbExpressionVMProvider) + || (getVMProvider() instanceof GdbVariableVMProvider)) { + enabled = (getIncompleteChildrenVMC() != null); + } + getAction().setEnabled(enabled); + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + if (targetPart instanceof IViewPart) { + init((IViewPart) targetPart); + } + } + + private IncompleteChildrenVMC getIncompleteChildrenVMC() { + if (selection instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection) selection; + + if (ss.size() == 1) { + // Only single selection is supported. + Object selectedObject = ss.getFirstElement(); + if (selectedObject instanceof IncompleteChildrenVMC) { + return (IncompleteChildrenVMC) selectedObject; + } + } + } + + return null; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/GdbDebugPreferencePage.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/GdbDebugPreferencePage.java index 792640f2593..97090690e8c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/GdbDebugPreferencePage.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/GdbDebugPreferencePage.java @@ -7,6 +7,7 @@ * * Contributors: * Ericsson - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.preferences; @@ -15,9 +16,13 @@ import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.IntegerFieldEditor; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Group; import org.eclipse.ui.IWorkbench; @@ -29,6 +34,26 @@ import org.eclipse.ui.PlatformUI; */ public class GdbDebugPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + /** + * A vehicle in order to be able to register a selection listener with + * a {@link BooleanFieldEditor}. + */ + private class ListenableBooleanFieldEditor extends BooleanFieldEditor { + + public ListenableBooleanFieldEditor( + String name, + String labelText, + int style, + Composite parent) { + super(name, labelText, style, parent); + } + + @Override + public Button getChangeControl(Composite parent) { + return super.getChangeControl(parent); + } + } + public GdbDebugPreferencePage() { super(FLAT); IPreferenceStore store= GdbUIPlugin.getDefault().getPreferenceStore(); @@ -100,6 +125,55 @@ public class GdbDebugPreferencePage extends FieldEditorPreferencePage implements // need to set layout again group.setLayout(groupLayout); + group = new Group(parent, SWT.NONE); + group.setText(MessagesForPreferences.GdbDebugPreferencePage_prettyPrinting_label); + groupLayout = new GridLayout(3, false); + group.setLayout(groupLayout); + group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + final ListenableBooleanFieldEditor enablePrettyPrintingField = new ListenableBooleanFieldEditor( + IGdbDebugPreferenceConstants.PREF_ENABLE_PRETTY_PRINTING, + MessagesForPreferences.GdbDebugPreferencePage_enablePrettyPrinting_label1 + "\n" //$NON-NLS-1$ + + MessagesForPreferences.GdbDebugPreferencePage_enablePrettyPrinting_label2, + SWT.NONE, group); + + enablePrettyPrintingField.fillIntoGrid(group, 3); + addField(enablePrettyPrintingField); + + final Composite indentHelper = new Composite(group, SWT.NONE); + GridLayout helperLayout = new GridLayout(3, false); + indentHelper.setLayout(helperLayout); + GridData helperData = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1); + helperData.horizontalIndent = 20; + indentHelper.setLayoutData(helperData); + + final IntegerFieldEditor childCountLimitField = new IntegerFieldEditor( + IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS, + MessagesForPreferences.GdbDebugPreferencePage_initialChildCountLimitForCollections_label, + indentHelper); + + childCountLimitField.setValidRange(1, 10000); + childCountLimitField.fillIntoGrid(indentHelper, 3); + + IPreferenceStore store = GdbUIPlugin.getDefault().getPreferenceStore(); + boolean prettyPrintingEnabled = store + .getBoolean(IGdbDebugPreferenceConstants.PREF_ENABLE_PRETTY_PRINTING); + childCountLimitField.setEnabled(prettyPrintingEnabled, indentHelper); + + addField(childCountLimitField); + + enablePrettyPrintingField.getChangeControl(group).addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + boolean enabled = enablePrettyPrintingField.getBooleanValue(); + childCountLimitField.setEnabled(enabled, indentHelper); + } + }); + + // need to set layouts again + indentHelper.setLayout(helperLayout); + group.setLayout(groupLayout); } @Override diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/GdbPreferenceInitializer.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/GdbPreferenceInitializer.java index ed11c079108..2efb43368b9 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/GdbPreferenceInitializer.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/GdbPreferenceInitializer.java @@ -7,6 +7,7 @@ * * Contributors: * Ericsson - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.preferences; @@ -25,5 +26,7 @@ public class GdbPreferenceInitializer extends AbstractPreferenceInitializer { store.setDefault(IGdbDebugPreferenceConstants.PREF_TRACES_ENABLE, true); store.setDefault(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, true); store.setDefault(IGdbDebugPreferenceConstants.PREF_USE_INSPECTOR_HOVER, true); + store.setDefault(IGdbDebugPreferenceConstants.PREF_ENABLE_PRETTY_PRINTING, true); + store.setDefault(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS, 100); } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/MessagesForPreferences.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/MessagesForPreferences.java index 74af3e2e94d..1687ac00a6a 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/MessagesForPreferences.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/MessagesForPreferences.java @@ -7,6 +7,7 @@ * * Contributors: * Ericsson - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.preferences; @@ -23,6 +24,14 @@ class MessagesForPreferences extends NLS { public static String GdbDebugPreferencePage_autoTerminateGdb_label; public static String GdbDebugPreferencePage_hover_label; public static String GdbDebugPreferencePage_useInspectorHover_label; + /** @since 3.0 */ + public static String GdbDebugPreferencePage_prettyPrinting_label; + /** @since 3.0 */ + public static String GdbDebugPreferencePage_enablePrettyPrinting_label1; + /** @since 3.0 */ + public static String GdbDebugPreferencePage_enablePrettyPrinting_label2; + /** @since 3.0 */ + public static String GdbDebugPreferencePage_initialChildCountLimitForCollections_label; static { // initialize resource bundle diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/MessagesForPreferences.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/MessagesForPreferences.properties index 5718c184ad5..b5995a08afa 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/MessagesForPreferences.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/preferences/MessagesForPreferences.properties @@ -7,6 +7,7 @@ # # Contributors: # Ericsson - initial API and implementation +# Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) ############################################################################### GdbDebugPreferencePage_description=General settings for GDB Debugging @@ -19,3 +20,8 @@ GdbDebugPreferencePage_autoTerminateGdb_label=Terminate GDB when last process ex GdbDebugPreferencePage_hover_label=Debug Text Hover GdbDebugPreferencePage_useInspectorHover_label=Use enhanced debug hover + +GdbDebugPreferencePage_prettyPrinting_label=Pretty Printing +GdbDebugPreferencePage_enablePrettyPrinting_label1=Enable pretty printers in variable/expression tree +GdbDebugPreferencePage_enablePrettyPrinting_label2=(requires python-enabled GDB) +GdbDebugPreferencePage_initialChildCountLimitForCollections_label=For collections, initially limit child count to diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/FetchMoreChildrenEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/FetchMoreChildrenEvent.java new file mode 100644 index 00000000000..ac7617eeb21 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/FetchMoreChildrenEvent.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2010 Verigy 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: + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel; + +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.jface.viewers.TreePath; + +/** + * Event to fetch additional children for and expression context. + * + * @since 3.0 + */ +public class FetchMoreChildrenEvent extends AbstractDMEvent { + + private TreePath path; + + public FetchMoreChildrenEvent(IExpressionDMContext exprCtx, TreePath path) { + super(exprCtx); + this.path = path; + } + + public TreePath getPath() { + return path; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbExpressionVMProvider.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbExpressionVMProvider.java index dbd2caf21ee..424438918f6 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbExpressionVMProvider.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbExpressionVMProvider.java @@ -8,10 +8,14 @@ * Contributors: * Freescale Semiconductor - initial API and implementation * Axel Mueller - Bug 306555 - Add support for cast to type / view as array (IExpressions2) + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.DsfCastToTypeSupport; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.DisabledExpressionVMNode; import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.ExpressionManagerVMNode; @@ -24,12 +28,23 @@ import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.RegisterVMNode; import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.SyncRegisterDataAccess; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.SyncVariableDataAccess; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMNode; +import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbVariableVMNode.IncompleteChildrenVMC; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.RootDMVMNode; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; /** * A specialization of ExpressionVMProvider that uses a GDB-specific variable VM @@ -38,12 +53,35 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationCont @SuppressWarnings("restriction") public class GdbExpressionVMProvider extends ExpressionVMProvider { + private IPropertyChangeListener fPreferencesListener; + /** * Constructor (passthru) */ public GdbExpressionVMProvider(AbstractVMAdapter adapter, IPresentationContext context, DsfSession session) { super(adapter, context, session); + final IPreferenceStore store = GdbUIPlugin.getDefault().getPreferenceStore(); + + Integer childCountLimit = store.getInt(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS); + if (childCountLimit != 0) { + getPresentationContext().setProperty(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS, + childCountLimit); + } + + fPreferencesListener = new IPropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent event) { + handlePropertyChanged(store, event); + }}; + store.addPropertyChangeListener(fPreferencesListener); + } + + @Override + public void dispose() { + super.dispose(); + + final IPreferenceStore store = GdbUIPlugin.getDefault().getPreferenceStore(); + store.removePropertyChangeListener(fPreferencesListener); } /** @@ -126,4 +164,71 @@ public class GdbExpressionVMProvider extends ExpressionVMProvider { */ setRootNode(rootNode); } + + @Override + public void handleEvent(Object event, final RequestMonitor rm) { + if (event instanceof DoubleClickEvent && !isDisposed()) { + + final ISelection selection= ((DoubleClickEvent) event).getSelection(); + if (selection instanceof IStructuredSelection) { + + Object element= ((IStructuredSelection) selection).getFirstElement(); + if (element instanceof IncompleteChildrenVMC) { + + IncompleteChildrenVMC incompleteChildrenVmc = ((IncompleteChildrenVMC) element); + IVMNode node = incompleteChildrenVmc.getVMNode(); + if (node instanceof GdbVariableVMNode && node.getVMProvider() == this) { + + if (selection instanceof ITreeSelection) { + ITreeSelection treeSelection = (ITreeSelection) selection; + TreePath path = treeSelection.getPaths()[0]; + + IExpressionDMContext exprCtx = incompleteChildrenVmc.getParentDMContext(); + ((GdbVariableVMNode) node).incrementChildCountLimit(exprCtx); + + // replace double click event with the fetch more children event. + final FetchMoreChildrenEvent fetchMoreChildrenEvent = new FetchMoreChildrenEvent( + exprCtx, path); + getExecutor().execute(new DsfRunnable() { + public void run() { + handleEvent(fetchMoreChildrenEvent, rm); + } + }); + + return; + } + } + } + } + } + + super.handleEvent(event, rm); + } + + /** + * @param store + * @param event + * + * @since 3.0 + */ + protected void handlePropertyChanged(final IPreferenceStore store, final PropertyChangeEvent event) { + String property = event.getProperty(); + if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) { + Integer childCountLimit = store.getInt(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS); + + if (childCountLimit != 0) { + getPresentationContext().setProperty(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS, + childCountLimit); + } else { + getPresentationContext().setProperty(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS, + null); + } + + getExecutor().execute(new DsfRunnable() { + public void run() { + handleEvent(event); + } + }); + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMNode.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMNode.java index 45c9fa73075..934a304ac01 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMNode.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMNode.java @@ -7,23 +7,48 @@ * * Contributors: * Freescale Semiconductor - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.eclipse.cdt.debug.internal.core.ICWatchpointTarget; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.AbstractExpressionVMNode; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.SyncVariableDataAccess; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMNode; +import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIExpressions; import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.IExpression; +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.IModelDelta; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.TreePath; /** * Specialization of DSF's VariableVMNode. See @@ -31,6 +56,42 @@ import org.eclipse.core.runtime.Status; */ public class GdbVariableVMNode extends VariableVMNode { + // Notes on gdb's pretty printer support (bug 302121): + // If + // - an expression has children + // - and those children are provided by a pretty printer + // - and the expression is not yet initialized + // the expression might have a large number of children. Asking gdb to + // provide all children, or even just the number of all children, will + // lead to extremely slow response times. + // Furthermore, there are C/C++ data structures (e.g. linked lists) that + // can lead to endless loops if not correctly initialized and a pretty + // printer tries to obtain the number of children. In this case, gdb + // will never return. + // + // In order to address this problem, IMIExpressions deriving from + // IExpressions has been introduced. + // It lets the client specify a maximum number of children to be considered, + // both when asking the number of sub-expression, or the sub-expressions + // itself. + // + // The algorithm how it is used is as following: + // - We don't show all children in the UI, but only up to a certain limit. + // A special context type IncompleteChildrenVMC is used to show that + // there are more children than those currently visible. + // The user can fetch more children on demand. + // - updateHasElementsInSessionThread asks only for up to one child. + // - updateElementCountInSessionThread checks whether the expression + // requires a limit on the child count limit. If yes, it asks + // the expression service for up to limit + 1 children. The + 1 + // represent the child for the <...more children...> node. I.e., + // if the returned number of children is limit + 1, then there is + // an <...more_children...> node. Otherwise, there is not. + // - updateElementsInSessionThread sooner or later delegates to + // fillUpdateWithVMCs. fillUpdateWithVMCs checks whether there are + // limit + 1 children, and if so, will create an IncompleteChildrenVMC + // for the last child, discarding the original expression context. + /** * Specialization of VariableVMNode.VariableExpressionVMC that participates * in the "Add Watchpoint" object contribution action. @@ -98,8 +159,7 @@ public class GdbVariableVMNode extends VariableVMNode { @Override public void handleCompleted() { if (isSuccess()) { - assert getData().getSize() > 0; - request.setCanCreate(true); + request.setCanCreate(getData().getSize() > 0); } request.setStatus(getStatus()); request.done(); @@ -122,6 +182,41 @@ public class GdbVariableVMNode extends VariableVMNode { } }; + /** + * The special context representing more children to be available. + * + * @since 3.0 + */ + public class IncompleteChildrenVMC extends AbstractVMContext { + + private IExpressionDMContext parentDmc; + + public IncompleteChildrenVMC(IExpressionDMContext exprDmc, int childCountLimit) { + super(GdbVariableVMNode.this); + this.parentDmc = exprDmc; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof IncompleteChildrenVMC && + ((IncompleteChildrenVMC)obj).parentDmc.equals(parentDmc); + } + + @Override + public int hashCode() { + return parentDmc.hashCode(); + } + + public IExpressionDMContext getParentDMContext() { + return parentDmc; + } + } + + /** + * Maps expressions to their current limit on the maximum number of children. + */ + private Map childCountLimits = new HashMap(); + /** * Utility method to create an IStatus object for an internal error */ @@ -149,4 +244,331 @@ public class GdbVariableVMNode extends VariableVMNode { protected IDMVMContext createVMContext(IDMContext dmc) { return new GdbVariableExpressionVMC(dmc); } + + @Override + protected void updateHasElementsInSessionThread(final IHasChildrenUpdate update) { + if (update.getElement() instanceof IncompleteChildrenVMC) { + update.setHasChilren(false); + update.done(); + return; + } + + super.updateHasElementsInSessionThread(update); + } + + @Override + protected void updateElementCountInSessionThread(final IChildrenCountUpdate update) { + // Get the data model context object for the current node in the hierarchy. + + final IExpressionDMContext expressionDMC = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExpressionDMContext.class); + + if ( expressionDMC != null ) { + final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); + + if (expressionService == null) { + handleFailedUpdate(update); + return; + } + + if (expressionService instanceof IMIExpressions) { + final IMIExpressions miExpressions = (IMIExpressions) expressionService; + + miExpressions.safeToAskForAllSubExpressions(expressionDMC, + new ViewerDataRequestMonitor(getSession().getExecutor(), update) { + + @Override + protected void handleCompleted() { + if (! isSuccess()) { + handleFailedUpdate(update); + return; + } + + boolean limitRequired = ! getData().booleanValue(); + if (limitRequired) { + + final int childCountLimit = getOrInitChildCountLimit(expressionDMC); + + miExpressions.getSubExpressionCount( + expressionDMC, childCountLimit + 1, + new ViewerDataRequestMonitor(getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + + int childCount = getData(); + if (childCountLimit < childCount) { + childCount = childCountLimit + 1; + } + + update.setChildCount(childCount); + update.done(); + } + }); + } else { + GdbVariableVMNode.super.updateElementCountInSessionThread(update); + } + } + }); + + return; + } + } + + super.updateElementCountInSessionThread(update); + } + + @Override + protected void fillUpdateWithVMCs(IChildrenUpdate update, + IDMContext[] dmcs, int firstIndex) { + super.fillUpdateWithVMCs(update, dmcs, firstIndex); + + IExpressionDMContext expressionDMC = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExpressionDMContext.class); + + if (expressionDMC != null) { + int childCountLimit = getChildCountLimit(expressionDMC); + int childCount = firstIndex + update.getLength(); + if (childCountLimit < childCount) { + update.setChild(new IncompleteChildrenVMC(expressionDMC, childCountLimit), childCountLimit); + } + } + } + + @Override + public void update(IPropertiesUpdate[] updates) { + List realExpressions = new ArrayList(); + + for (IPropertiesUpdate update : updates) { + if (update.getElement() instanceof IncompleteChildrenVMC) { + if (update.getProperties().contains( + AbstractExpressionVMNode.PROP_ELEMENT_EXPRESSION)) { + update.setProperty( + AbstractExpressionVMNode.PROP_ELEMENT_EXPRESSION, + Messages.More_Children); + + } + + if (update.getProperties().contains(PROP_NAME)) { + update.setProperty(PROP_NAME, Messages.More_Children); + } + update.done(); + } else { + realExpressions.add(update); + } + } + + super.update(realExpressions.toArray(new IPropertiesUpdate[realExpressions.size()])); + } + + private int getInitialChildCountLimit() { + Object initialLimitProperty = getVMProvider().getPresentationContext().getProperty( + IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS); + + return (initialLimitProperty instanceof Integer) ? (Integer) initialLimitProperty + : 100; + } + + /** + * The given expression context requires a child count limit. If a limit + * is already available from preceding calls, obtain this limit. Otherwise + * calculate the initial value, store it, and return it. + * + * @param expressionDMC + * @return The child count limit to apply for the given expression. + * + * @since 3.0 + */ + protected int getOrInitChildCountLimit(IExpressionDMContext expressionDMC) { + if (childCountLimits.containsKey(expressionDMC)) { + return childCountLimits.get(expressionDMC); + } + + int initialLimit = getInitialChildCountLimit(); + childCountLimits.put(expressionDMC, initialLimit); + + return initialLimit; + } + + /** + * @param expressionDMC + * @return The currently stored child count limit for the given expression, + * or {@link Integer#MAX_VALUE} if no child count limit is currently + * stored. + * + * @since 3.0 + */ + protected int getChildCountLimit(IExpressionDMContext expressionDMC) { + if (childCountLimits.containsKey(expressionDMC)) { + return childCountLimits.get(expressionDMC); + } + return Integer.MAX_VALUE; + } + + private void resetChildCountLimits(IExecutionDMContext execCtx) { + int initialLimit = getInitialChildCountLimit(); + for (IExpressionDMContext limitCtx : childCountLimits.keySet()) { + if (DMContexts.isAncestorOf(limitCtx, execCtx)) { + childCountLimits.put(limitCtx, initialLimit); + } + } + } + + private void resetAllChildCountLimits() { + int initialLimit = getInitialChildCountLimit(); + for (IExpressionDMContext limitCtx : childCountLimits.keySet()) { + childCountLimits.put(limitCtx, initialLimit); + } + } + + /** + * Increment the child count limit by the default increment. + * This implementation doubles the current limit. + * + * @since 3.0 + */ + public void incrementChildCountLimit(IExpressionDMContext expressionDMC) { + assert(childCountLimits.containsKey(expressionDMC)); + + int childCountLimit = getChildCountLimit(expressionDMC); + if (childCountLimit < Integer.MAX_VALUE / 2) { + childCountLimits.put(expressionDMC, childCountLimit * 2); + } + } + + @Override + public int getDeltaFlags(Object e) { + int flags = super.getDeltaFlags(e); + + if (e instanceof FetchMoreChildrenEvent) { + flags |= IModelDelta.CONTENT; + } else if (e instanceof ISuspendedDMEvent) { + // The child count limit must be reset. + flags |= IModelDelta.CONTENT; + } else if (e instanceof PropertyChangeEvent) { + String property = ((PropertyChangeEvent)e).getProperty(); + if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) + { + flags |= IModelDelta.CONTENT; + } + } + + return flags; + } + + + @Override + public int getDeltaFlagsForExpression(IExpression expression, Object event) { + int flags = super.getDeltaFlagsForExpression(expression, event); + + if (event instanceof FetchMoreChildrenEvent) { + flags |= IModelDelta.CONTENT; + } else if (event instanceof PropertyChangeEvent) { + String property = ((PropertyChangeEvent) event).getProperty(); + if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS + .equals(property)) { + flags |= IModelDelta.CONTENT; + } + } + + return flags; + } + + @Override + public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, + RequestMonitor rm) { + + if (e instanceof FetchMoreChildrenEvent) { + buildDeltaForFetchMoreChildrenEvent((FetchMoreChildrenEvent) e, parentDelta, rm); + return; + } else if (e instanceof ISuspendedDMEvent) { + resetChildCountLimits(((ISuspendedDMEvent) e).getDMContext()); + } else if (e instanceof PropertyChangeEvent) { + String property = ((PropertyChangeEvent)e).getProperty(); + if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) + { + resetAllChildCountLimits(); + buildDeltaForChildCountLimitPreferenceChangedEvent(parentDelta, rm); + return; + } + } + + super.buildDelta(e, parentDelta, nodeOffset, rm); + } + + @Override + public void buildDeltaForExpressionElement(Object element, int elementIdx, + Object event, VMDelta parentDelta, RequestMonitor rm) { + + if (event instanceof FetchMoreChildrenEvent) { + FetchMoreChildrenEvent fetchMoreEvent = (FetchMoreChildrenEvent) event; + GdbVariableExpressionVMC topLevelExpressionVMC = (GdbVariableExpressionVMC) element; + if (topLevelExpressionVMC.equals(fetchMoreEvent.getPath().getFirstSegment())) { + buildDeltaForFetchMoreChildrenEvent(fetchMoreEvent, parentDelta, rm); + return; + } + } else if (event instanceof ISuspendedDMEvent) { + resetChildCountLimits(((ISuspendedDMEvent) event).getDMContext()); + } else if (event instanceof IContainerSuspendedDMEvent) { + resetChildCountLimits(((IContainerSuspendedDMEvent) event).getDMContext()); + } else if (event instanceof PropertyChangeEvent) { + String property = ((PropertyChangeEvent)event).getProperty(); + if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) + { + resetAllChildCountLimits(); + buildDeltaForChildCountLimitPreferenceChangedEvent(parentDelta, rm); + return; + } + } + + super.buildDeltaForExpressionElement(element, elementIdx, event, parentDelta, + rm); + } + + private void buildDeltaForFetchMoreChildrenEvent( + FetchMoreChildrenEvent fetchMoreChidrenEvent, + VMDelta parentDelta, final RequestMonitor rm) { + + TreePath path = fetchMoreChidrenEvent.getPath(); + + // Add all the parents of the expression. Those didn't change, however. + for (int i = 0; i < path.getSegmentCount() - 2; ++i) { + parentDelta = parentDelta.addNode(path.getSegment(i), IModelDelta.NO_CHANGE); + } + + // Add the node for the expression. This one changed, of course. + final VMDelta expressionDelta = + parentDelta.addNode(path.getSegment(path.getSegmentCount() - 2), IModelDelta.CONTENT); + + // Make sure the element formerly know as <...more_children...> is selected + // afterwards. + + final int offset = getChildCountLimit(fetchMoreChidrenEvent.getDMContext()) / 2; + // The one trailing element is to see whether there are more children. + final int maxLength = offset + 1; + getVMProvider().updateNode( + this, + new VMChildrenUpdate( + expressionDelta, getVMProvider().getPresentationContext(), offset, maxLength, + new DataRequestMonitor>(getExecutor(), rm) { + @Override + public void handleCompleted() { + + // FIXME if the new child has children they do not appear because of this code. +// final List data= getData(); +// if (data != null && data.size() != 0) { +// expressionDelta.addNode(data.get(0), offset, IModelDelta.SELECT); +// } + rm.done(); + } + }) + ); + } + + private void buildDeltaForChildCountLimitPreferenceChangedEvent( + final VMDelta parentDelta, final RequestMonitor rm) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + rm.done(); + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMProvider.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMProvider.java index 7df51ff683f..7832b053c30 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMProvider.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMProvider.java @@ -8,19 +8,34 @@ * Contributors: * Freescale Semiconductor - initial API and implementation * Axel Mueller - Bug 306555 - Add support for cast to type / view as array (IExpressions2) + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.DsfCastToTypeSupport; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.SyncVariableDataAccess; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMNode; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMProvider; +import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbVariableVMNode.IncompleteChildrenVMC; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.RootDMVMNode; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; /** * A specialization of VariableVMProvider that uses a GDB-specific variable VM @@ -29,15 +44,39 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationCont @SuppressWarnings("restriction") public class GdbVariableVMProvider extends VariableVMProvider { + private IPropertyChangeListener fPreferencesListener; + /** * Constructor (passthru) */ public GdbVariableVMProvider(AbstractVMAdapter adapter, IPresentationContext context, DsfSession session) { super(adapter, context, session); + + final IPreferenceStore store = GdbUIPlugin.getDefault().getPreferenceStore(); + + Integer childCountLimit = store.getInt(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS); + if (childCountLimit != 0) { + getPresentationContext().setProperty(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS, + childCountLimit); + } + + fPreferencesListener = new IPropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent event) { + handlePropertyChanged(store, event); + }}; + store.addPropertyChangeListener(fPreferencesListener); } - /* (non-Javadoc) + @Override + public void dispose() { + super.dispose(); + + final IPreferenceStore store = GdbUIPlugin.getDefault().getPreferenceStore(); + store.removePropertyChangeListener(fPreferencesListener); + } + + /* (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMProvider#configureLayout(org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.SyncVariableDataAccess) */ @Override @@ -62,4 +101,71 @@ public class GdbVariableVMProvider extends VariableVMProvider { // provider will recursively drill-down the variable hierarchy. addChildNodes(subExpressioNode, new IVMNode[] { subExpressioNode }); } + + @Override + public void handleEvent(Object event, final RequestMonitor rm) { + if (event instanceof DoubleClickEvent && !isDisposed()) { + + final ISelection selection= ((DoubleClickEvent) event).getSelection(); + if (selection instanceof IStructuredSelection) { + + Object element= ((IStructuredSelection) selection).getFirstElement(); + if (element instanceof IncompleteChildrenVMC) { + + IncompleteChildrenVMC incompleteChildrenVmc = ((IncompleteChildrenVMC) element); + IVMNode node = incompleteChildrenVmc.getVMNode(); + if (node instanceof GdbVariableVMNode && node.getVMProvider() == this) { + + if (selection instanceof ITreeSelection) { + + ITreeSelection treeSelection = (ITreeSelection) selection; + TreePath path = treeSelection.getPaths()[0]; + IExpressionDMContext exprCtx = incompleteChildrenVmc.getParentDMContext(); + ((GdbVariableVMNode) node).incrementChildCountLimit(exprCtx); + + // replace double click event with the fetch more children event. + final FetchMoreChildrenEvent fetchMoreChildrenEvent = new FetchMoreChildrenEvent( + exprCtx, path); + getExecutor().execute(new DsfRunnable() { + public void run() { + handleEvent(fetchMoreChildrenEvent, rm); + } + }); + + return; + } + } + } + } + } + + super.handleEvent(event, rm); + } + + /** + * @param store + * @param event + * + * @since 3.0 + */ + protected void handlePropertyChanged(final IPreferenceStore store, final PropertyChangeEvent event) { + String property = event.getProperty(); + if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) { + Integer childCountLimit = store.getInt(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS); + + if (childCountLimit != 0) { + getPresentationContext().setProperty(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS, + childCountLimit); + } else { + getPresentationContext().setProperty(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS, + null); + } + + getExecutor().execute(new DsfRunnable() { + public void run() { + handleEvent(event); + } + }); + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/Message.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/Message.properties index 3100ae9ddc5..41f5389dc23 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/Message.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/Message.properties @@ -7,5 +7,7 @@ # # Contributors: # IBM Corporation - initial API and implementation +# Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) ############################################################################### Internal_Error=Internal Error +More_Children=<...more children...> diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/Messages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/Messages.java index 709fa163f3b..a86e0cbd659 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/Messages.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/Messages.java @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel; @@ -23,4 +24,5 @@ public class Messages extends NLS { private Messages() {} public static String Internal_Error; + public static String More_Children; } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGdbDebugPreferenceConstants.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGdbDebugPreferenceConstants.java index d768c7ab2e1..8a62c2731f4 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGdbDebugPreferenceConstants.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGdbDebugPreferenceConstants.java @@ -7,6 +7,7 @@ * * Contributors: * Ericsson - initial implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb; @@ -36,7 +37,21 @@ public interface IGdbDebugPreferenceConstants { */ public static final String PREF_USE_INSPECTOR_HOVER = "useInspectorHover"; //$NON-NLS-1$ - /** + /** + * Boolean preference whether to enable pretty printers for MI variable + * objects. Default is true. + * @since 4.0 + */ + public static final String PREF_ENABLE_PRETTY_PRINTING = "enablePrettyPrinting"; //$NON-NLS-1$ + + /** + * The maximum limit of children to be initially fetched by GDB for + * collections. Default is 100. + * @since 4.0 + */ + public static final String PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS = "initialChildCountLimitForCollections"; //$NON-NLS-1$ + + /** * Help prefixes. */ public static final String PREFIX = GdbPlugin.PLUGIN_ID + "."; //$NON-NLS-1$ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java index aee4ba769d1..f96fb0e68b4 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java @@ -9,6 +9,7 @@ * Ericsson - initial API and implementation * Nokia - create and use backend service. * IBM Corporation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.launching; @@ -27,6 +28,7 @@ import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.actions.IConnect; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; @@ -44,6 +46,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; @@ -76,6 +79,7 @@ public class FinalLaunchSequence extends ReflectionSequence { "stepInitializeFinalLaunchSequence", //$NON-NLS-1$ "stepSetEnvironmentDirectory", //$NON-NLS-1$ "stepSetBreakpointPending", //$NON-NLS-1$ + "stepEnablePrettyPrinting", //$NON-NLS-1$ "stepSourceGDBInitFile", //$NON-NLS-1$ "stepSetEnvironmentVariables", //$NON-NLS-1$ "stepSetExecutable", //$NON-NLS-1$ @@ -181,6 +185,29 @@ public class FinalLaunchSequence extends ReflectionSequence { } } + /** + * Turn on pretty printers for MI variable objects, if enabled in preferences. + * Also, turn off error messages from python, all the time. + * @since 4.0 + */ + @Execute + public void stepEnablePrettyPrinting(final RequestMonitor requestMonitor) { + if (Platform.getPreferencesService().getBoolean("org.eclipse.cdt.dsf.gdb.ui", //$NON-NLS-1$ + IGdbDebugPreferenceConstants.PREF_ENABLE_PRETTY_PRINTING, + false, null)) { + + fCommandControl.enablePrettyPrintingForMIVariableObjects( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + fCommandControl.setPrintPythonErrors(false, requestMonitor); + } + }); + } else { + fCommandControl.setPrintPythonErrors(false, requestMonitor); + } + } + /** * Source the gdbinit file specified in the launch. * @since 4.0 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java index 442b9294bb2..061b34cc4ec 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java @@ -10,6 +10,7 @@ * Ericsson - Modified for additional features in DSF Reference implementation * Nokia - create and use backend service. * Vladimir Prus (CodeSourcery) - Support for -data-read-memory-bytes (bug 322658) + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service.command; @@ -609,4 +610,18 @@ public class GDBControl extends AbstractMIControl implements IGDBControl { public List getFeatures() { return fFeatures; } + + /** + * @since 4.0 + */ + public void enablePrettyPrintingForMIVariableObjects(RequestMonitor rm) { + rm.done(); + } + + /** + * @since 4.0 + */ + public void setPrintPythonErrors(boolean enabled, RequestMonitor rm) { + rm.done(); + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java index 935b3c47ff4..cdfdc6958f4 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java @@ -10,6 +10,7 @@ * Ericsson - Modified for additional features in DSF Reference implementation * Ericsson - New version for 7_0 * Vladimir Prus (CodeSourcery) - Support for -data-read-memory-bytes (bug 322658) + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service.command; @@ -783,4 +784,27 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl { requestMonitor.done(); } } + + /** + * @since 4.0 + */ + public void enablePrettyPrintingForMIVariableObjects( + final RequestMonitor rm) { + + queueCommand( + getCommandFactory().createMIEnablePrettyPrinting(fControlDmc), + new DataRequestMonitor(getExecutor(), rm)); + } + + /** + * @since 4.0 + */ + public void setPrintPythonErrors(boolean enabled, RequestMonitor rm) { + + String subCommand = "set python print-stack " + (enabled ? "on" : "off"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + + queueCommand( + getCommandFactory().createCLIMaintenance(fControlDmc, subCommand), + new DataRequestMonitor(getExecutor(), rm)); + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java index 02ecee9aa84..bccf575377d 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java @@ -8,6 +8,7 @@ * Contributors: * Ericsson - initial API and implementation * Vladimir Prus (CodeSourcery) - Support for -data-read-memory-bytes (bug 322658) + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service.command; @@ -66,4 +67,25 @@ public interface IGDBControl extends IMICommandControl { * @since 4.0 */ List getFeatures(); + + /** + * Enable the pretty printers also for MI variable objects. This basically + * sends -enable-pretty-printing. + * + * @param rm + * + * @since 4.0 + */ + void enablePrettyPrintingForMIVariableObjects(RequestMonitor rm); + + /** + * Turns the printing of python errors on or off. + * + * @param enabled + * If true, printing errors is turned on. + * @param rm + * + * @since 4.0 + */ + void setPrintPythonErrors(boolean enabled, RequestMonitor rm); } \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIExpressions.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIExpressions.java new file mode 100644 index 00000000000..7baf0a1df77 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIExpressions.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2010 Verigy 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: + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IExpressions3; + +/** + * An extension of {@link IExpressions} which became necessary because the MI + * implementation of {@link IExpressions} has problems if asked for all + * sub-expressions. Problems may arise if uninitialized data objects are + * inspected. In the worst case, pretty printers may run into endless loops + * (e.g. linked list that become cycle), and gdb never returns. But also in the + * normal case of uninitialized collections, you easily end up with millions of + * useless elements, damaging the responsiveness of the workbench. + * + * In order to avoid those problems, this extension lets the client specify a + * maximum number of children that it is interested in. + * + * If you have an instance implementing {@link IExpressions}, you should always + * check whether it implements this extension, and if so, use the methods of the + * extension. + * + * @since 4.0 + */ +public interface IMIExpressions extends IExpressions3 { + + /** + * A special constant that can be used in methods that expect a child count + * limit. If this constant is passed, the implementation will use the most + * recent child count limit for the expression. If such a limit was never + * specified before, at least one child will be fetched in order to tell + * whether an expression has children or not. + */ + public static final int CHILD_COUNT_LIMIT_UNSPECIFIED = -1; + + /** + * This method indicates whether the given expression can safely be asked + * for all its sub-expressions. + * + * If this method returns false, this has the following impact: + *
    + *
  • you should not call + * {@link IExpressions#getSubExpressionCount(IExpressionDMContext, DataRequestMonitor)}, + * but + * {@link IMIExpressions#getSubExpressionCount(IExpressionDMContext, int, DataRequestMonitor)} + * instead.
  • + * + *
  • you should not call + * {@link IExpressions#getSubExpressions(IExpressionDMContext, DataRequestMonitor)}, + * but + * {@link IExpressions#getSubExpressions(IExpressionDMContext, int, int, DataRequestMonitor)} + *
  • + *
+ * + * @param exprCtx + * The data model context representing an expression. + * + * @param rm + * Data Request monitor containing true if this expression can + * safely fetch all its sub-expressions. false otherwise. + */ + public void safeToAskForAllSubExpressions(IExpressionDMContext exprCtx, + DataRequestMonitor rm); + + /** + * This method is the same as + * {@link IExpressions#getSubExpressionCount(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, DataRequestMonitor)} + * , with the slight but important difference that this method allows to + * provide an upper limit of children we are interested in. + * As long as {@link #safeToAskForAllSubExpressions(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, DataRequestMonitor)} + * returns true, the original method can be called, and this method is not of further interest. + * However, if {@link #safeToAskForAllSubExpressions(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, DataRequestMonitor)} + * returns false, the original method must not be called, and this method must instead be used. + * Otherwise, the gdb response time may be very slow, or it even may hang. + * + * @param exprCtx + * The data model context representing an expression. + * + * @param maxNumberOfChildren + * The implementation needs not check whether there are more than + * this number of children. However, if the implementation has + * already knowledge of more children than this, or can obtain + * them equally efficient, it might also return a higher count. + * + * @param rm + * Request completion monitor containing the number of + * sub-expressions of the specified expression + */ + void getSubExpressionCount(IExpressionDMContext exprCtx, + int maxNumberOfChildren, DataRequestMonitor rm); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIExpressions.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIExpressions.java index 6f55564fad2..036fd2f30d9 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIExpressions.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIExpressions.java @@ -7,8 +7,9 @@ * * Contributors: * Wind River Systems - initial API and implementation - * Ericsson - Modified for handling of multiple execution contexts + * Ericsson - Modified for handling of multiple execution contexts * Axel Mueller - Bug 306555 - Add support for cast to type / view as array (IExpressions2) + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; @@ -26,7 +27,6 @@ import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.ICachingService; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IExpressions2; -import org.eclipse.cdt.dsf.debug.service.IExpressions3; import org.eclipse.cdt.dsf.debug.service.IFormattedValues; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; @@ -68,7 +68,7 @@ import org.osgi.framework.BundleContext; * * @since 2.0 */ -public class MIExpressions extends AbstractDsfService implements IExpressions3, ICachingService { +public class MIExpressions extends AbstractDsfService implements IMIExpressions, ICachingService { /** * A format that gives more details about an expression and supports pretty-printing @@ -93,12 +93,28 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, public static class ExpressionInfo { private final String fullExpression; private final String relativeExpression; - + private boolean isDynamic = false; + private ExpressionInfo parent; + private int indexInParent = -1; + private int childCountLimit = IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED; + public ExpressionInfo(String full, String relative) { fullExpression = full; relativeExpression = relative; } - + + /** + * @since 4.0 + */ + public ExpressionInfo(String full, String relative, boolean isDynamic, + ExpressionInfo parent, int indexInParent) { + fullExpression = full; + relativeExpression = relative; + this.isDynamic = isDynamic; + this.parent = parent; + this.indexInParent = indexInParent; + } + public String getFullExpr() { return fullExpression; } public String getRelExpr() { return relativeExpression; } @@ -109,6 +125,7 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, fullExpression.equals(((ExpressionInfo) other).fullExpression)) { if (relativeExpression == null ? ((ExpressionInfo) other).relativeExpression == null : relativeExpression.equals(((ExpressionInfo) other).relativeExpression)) { + // The other members don't play any role for equality. return true; } } @@ -120,13 +137,98 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, public int hashCode() { return (fullExpression == null ? 0 : fullExpression.hashCode()) ^ (relativeExpression == null ? 0 : relativeExpression.hashCode()); + // The other members don't play any role for equality. } @Override public String toString() { - return "[" + fullExpression +", " + relativeExpression + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + return "[" + fullExpression +", " + relativeExpression + ", isDynamic=" + isDynamic + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ } + + /** + * @return The parent expression info, if existing. + * @since 4.0 + */ + public ExpressionInfo getParent() { + return parent; + } + + /** + * @return The index in the child array of the parent. Only valid if + * {@link #getParent()} returns not null. + * @since 4.0 + */ + public int getIndexInParentExpression() { + return indexInParent; + } + + /** + * @return Whether the corresponding variable object is dynamic, + * i.e. it's value and children are provided by a pretty printer. + * @since 4.0 + */ + public boolean isDynamic() { + return isDynamic; + } + + /** + * @return Whether the expression info has any ancestor that is dynamic. + * @since 4.0 + */ + public boolean hasDynamicAncestor() { + for (ExpressionInfo parent = getParent(); parent != null; parent = parent.getParent()) { + if (parent.isDynamic()) { + return true; + } + } + + return false; + } + + /** + * @param isDynamic + * Whether the value and children of this expression is + * currently provided by a pretty printer or not. + * @since 4.0 + */ + public void setDynamic(boolean isDynamic) { + this.isDynamic = isDynamic; + } + + /** + * @param parent The new parent expression info. + * @since 4.0 + */ + public void setParent(ExpressionInfo parent) { + this.parent = parent; + } + + /** + * @param index The index in the children array of the parent. + * @since 4.0 + */ + public void setIndexInParent(int index) { + this.indexInParent = index; + } + + /** + * @return The current limit on the number of children to be fetched. + * @since 4.0 + */ + public int getChildCountLimit() { + return childCountLimit; + } + + /** + * @param newLimit + * The new limit on the number of children to be fetched. + * @since 4.0 + */ + public void setChildCountLimit(int newLimit) { + this.childCountLimit = newLimit; + } } + /** * This class represents an expression. */ @@ -188,8 +290,32 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, } private MIExpressionDMC(String sessionId, String expr, String relExpr, IDMContext parent) { + this(sessionId, new ExpressionInfo(expr, relExpr), parent); + } + + /** + * ExpressionDMC Constructor for expression to be evaluated in context + * of a stack frame. + * + * @param sessionId + * The session ID in which this context is created. + * @param info + * The expression info that this expression is to use. + * @param frameCtx + * The parent stack frame context for this ExpressionDMC. + * + * @since 4.0 + */ + public MIExpressionDMC(String sessionId, ExpressionInfo info, IFrameDMContext frameCtx) { + this(sessionId, info, (IDMContext)frameCtx); + } + + /** + * @since 4.0 + */ + private MIExpressionDMC(String sessionId, ExpressionInfo info, IDMContext parent) { super(sessionId, new IDMContext[] { parent }); - exprInfo = new ExpressionInfo(expr, relExpr); + exprInfo = info; } /** @@ -232,6 +358,26 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, public String getRelativeExpression() { return exprInfo.getRelExpr(); } + + /** + * @return Get the expression info for this context. + * @since 4.0 + */ + public ExpressionInfo getExpressionInfo() { + return exprInfo; + } + + /** + * @param info + * + * @since 4.0 + */ + public void setExpressionInfo(ExpressionInfo info) { + assert (this.exprInfo.getFullExpr().equals(info.getFullExpr())); + assert (this.exprInfo.getRelExpr().equals(info.getRelExpr())); + + this.exprInfo = info; + } } protected static class InvalidContextExpressionDMC extends AbstractDMContext @@ -313,7 +459,27 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, return (fAddr == null ? "null" : "(" + fAddr.toHexAddressString()) + ", " + fSize + ")"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ } } + + /** + * If an expressions doesn't have an address, or it cannot be determined, + * use this class. + * @since 4.0 + */ + protected class InvalidDMAddress implements IExpressionDMLocation { + public IAddress getAddress() { + return IExpressions.IExpressionDMLocation.INVALID_ADDRESS; + } + + public int getSize() { + return 0; + } + + public String getLocation() { + return ""; //$NON-NLS-1$ + } + } + /** * This class represents the static data referenced by an instance of ExpressionDMC, * such as its type and number of children; it does not contain the value or format @@ -552,8 +718,6 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, * This method shuts down this service. It unregisters the service, stops * receiving service events, and calls the superclass shutdown() method to * finish the shutdown process. - * - * @return void */ @Override public void shutdown(RequestMonitor requestMonitor) { @@ -582,9 +746,18 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, * Create an expression context. */ public IExpressionDMContext createExpression(IDMContext ctx, String expression, String relExpr) { + return createExpression(ctx, new ExpressionInfo(expression, relExpr)); + } + + /** + * Create an expression context from a given expression info. + * @since 4.0 + */ + private IExpressionDMContext createExpression(IDMContext ctx, ExpressionInfo info) { + String expression = info.getFullExpr(); IFrameDMContext frameDmc = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class); if (frameDmc != null) { - return new MIExpressionDMC(getSession().getId(), expression, relExpr, frameDmc); + return new MIExpressionDMC(getSession().getId(), info, frameDmc); } IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); @@ -595,7 +768,7 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, MIStack stackService = getServicesTracker().getService(MIStack.class); if (stackService != null) { frameDmc = stackService.createFrameDMContext(execCtx, 0); - return new MIExpressionDMC(getSession().getId(), expression, relExpr, frameDmc); + return new MIExpressionDMC(getSession().getId(), info, frameDmc); } return new InvalidContextExpressionDMC(getSession().getId(), expression, execCtx); @@ -603,7 +776,7 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, IMemoryDMContext memoryCtx = DMContexts.getAncestorOfType(ctx, IMemoryDMContext.class); if (memoryCtx != null) { - return new MIExpressionDMC(getSession().getId(), expression, relExpr, memoryCtx); + return new MIExpressionDMC(getSession().getId(), info, memoryCtx); } // Don't care about the relative expression at this point @@ -662,7 +835,8 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, fExpressionCache.execute( new ExprMetaGetVar(dmc), new DataRequestMonitor(getExecutor(), rm) { - @Override + + @Override protected void handleSuccess() { IExpressionDMData.BasicType basicType = null; @@ -682,7 +856,10 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, break; case GDBType.GENERIC: default: - if (getData().getNumChildren() > 0) { + // The interesting question is not hasChildren, + // but canHaveChildren. E.g. an empty + // collection still is a composite. + if (getData().hasChildren() || getData().getCollectionHint()) { basicType = IExpressionDMData.BasicType.composite; } else { basicType = IExpressionDMData.BasicType.basic; @@ -718,6 +895,17 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, IExpressionDMContext dmc, final DataRequestMonitor rm) { + if (dmc instanceof MIExpressionDMC) { + MIExpressionDMC miDMC = (MIExpressionDMC) dmc; + if (miDMC.getExpressionInfo().hasDynamicAncestor()) { + // For children of dynamic varobjs, there is no full expression that gdb + // could evaluate in order to provide address and size. + rm.setData(new InvalidDMAddress()); + rm.done(); + return; + } + } + // First create an address expression and a size expression // to be used in back-end calls final IExpressionDMContext addressDmc = @@ -778,37 +966,45 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, { // We need to make sure the FormattedValueDMContext also holds an ExpressionContext, // or else this method cannot do its work. - // Note that we look for MIExpressionDMC and not IExpressionDMC, because getting + // Note that we look for MIExpressionDMC and not IExpressionDMC, because // looking for IExpressionDMC could yield InvalidContextExpressionDMC which is still - // not what we need to have. + // not what we need. MIExpressionDMC exprDmc = DMContexts.getAncestorOfType(dmc, MIExpressionDMC.class); if (exprDmc == null ) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { if (DETAILS_FORMAT.equals(dmc.getFormatID())) { - // This format is obtained through a different GDB command. - // It yields more details than the variableObject output. - // Starting with GDB 7.0, this format automatically supports pretty-printing, as long as - // GDB has been configured to support it. - fExpressionCache.execute( - fCommandFactory.createMIDataEvaluateExpression(exprDmc), - new DataRequestMonitor(getExecutor(), rm) { - @Override - protected void handleSuccess() { - rm.setData(new FormattedValueDMData(getData().getValue())); - rm.done(); - } - @Override - protected void handleError() { - if (fTraceVisualization) { - rm.setData(new FormattedValueDMData("")); //$NON-NLS-1$ - rm.done(); - } else { - super.handleError(); - } - } - }); + if (exprDmc.getExpressionInfo().hasDynamicAncestor()) { + // -data-evaluate-expression does not work for children of + // dynamic varobjs, since there is no full expression + // that gdb could evaluate. + rm.setData(new FormattedValueDMData(Messages.MIExpressions_NotAvailableBecauseChildOfDynamicVarobj)); + rm.done(); + } else { + // This format is obtained through a different GDB command. + // It yields more details than the variableObject output. + // Starting with GDB 7.0, this format automatically supports pretty-printing, as long as + // GDB has been configured to support it. + fExpressionCache.execute( + fCommandFactory.createMIDataEvaluateExpression(exprDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(new FormattedValueDMData(getData().getValue())); + rm.done(); + } + @Override + protected void handleError() { + if (fTraceVisualization) { + rm.setData(new FormattedValueDMData("")); //$NON-NLS-1$ + rm.done(); + } else { + super.handleError(); + } + } + }); + } } else { fExpressionCache.execute( new ExprMetaGetValue(dmc), @@ -838,7 +1034,7 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, /** * Retrieves the children expressions of the specified expression * - * @param exprCtx + * @param dmc * The context for the expression for which the children * should be retrieved. * @param rm @@ -857,9 +1053,7 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, IExpressionDMContext[] childArray = new IExpressionDMContext[childrenExpr.length]; for (int i=0; i rm) { if (startIndex < 0 || length < 0) { @@ -900,28 +1094,27 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, } if (exprCtx instanceof MIExpressionDMC) { - getSubExpressions( - exprCtx, - new DataRequestMonitor(getExecutor(), rm) { + fExpressionCache.execute( + new ExprMetaGetChildren(exprCtx, startIndex + length), + new DataRequestMonitor(getExecutor(), rm) { @Override protected void handleSuccess() { - IExpressionDMContext[] subExpressions = getData(); + ExpressionInfo[] childrenExpr = getData().getChildrenExpressions(); - if (startIndex >= subExpressions.length) { + if (startIndex >= childrenExpr.length) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, "Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$ rm.done(); return; } - - int realLength = length; - if (startIndex + length > subExpressions.length) { - realLength = subExpressions.length - startIndex; - } - - IExpressionDMContext[] subRange = new IExpressionDMContext[realLength]; - System.arraycopy(subExpressions, startIndex, subRange, 0, realLength); - rm.setData(subRange); + int numChildren = childrenExpr.length - startIndex; + numChildren = Math.min(length, numChildren); + IExpressionDMContext[] childrenArray = new IExpressionDMContext[numChildren]; + for (int i=0; i < numChildren; i++) { + childrenArray[i] = createExpression( + exprCtx.getParents()[0], childrenExpr[startIndex + i]); + } + rm.setData(childrenArray); rm.done(); } }); @@ -935,20 +1128,40 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, } /** - * Retrieves the count of children expressions of the specified expression - * - * @param exprCtx - * The context for the expression for which the children count - * should be retrieved. - * @param rm - * The data request monitor that will contain the requested data + * @since 4.0 + */ + public void safeToAskForAllSubExpressions(IExpressionDMContext dmc, + final DataRequestMonitor rm) { + if (dmc instanceof MIExpressionDMC) { + fExpressionCache.execute( + new ExprMetaGetVar(dmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + boolean safe = getData().isSafeToAskForAllChildren(); + + rm.setData(safe); + rm.done(); + } + }); + } else if (dmc instanceof InvalidContextExpressionDMC) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ + rm.done(); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + /** + * @since 4.0 */ public void getSubExpressionCount(IExpressionDMContext dmc, - final DataRequestMonitor rm) - { + final int numChildLimit, final DataRequestMonitor rm) { + if (dmc instanceof MIExpressionDMC) { fExpressionCache.execute( - new ExprMetaGetChildCount(dmc), + new ExprMetaGetChildCount(dmc, numChildLimit), new DataRequestMonitor(getExecutor(), rm) { @Override protected void handleSuccess() { @@ -964,13 +1177,28 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, rm.done(); } } + + /** + * Retrieves the count of children expressions of the specified expression + * + * @param dmc + * The context for the expression for which the children count + * should be retrieved. + * @param rm + * The data request monitor that will contain the requested data + */ + public void getSubExpressionCount(IExpressionDMContext dmc, + final DataRequestMonitor rm) + { + getSubExpressionCount(dmc, IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED, rm); + } /** * This method indicates if an expression can be written to. * - * @param dmc: The data model context representing an expression. + * @param dmc The data model context representing an expression. * - * @param rm: Data Request monitor containing True if this expression's value can be edited. False otherwise. + * @param rm Data Request monitor containing True if this expression's value can be edited. False otherwise. */ public void canWriteExpression(IExpressionDMContext dmc, final DataRequestMonitor rm) { @@ -997,7 +1225,7 @@ public class MIExpressions extends AbstractDsfService implements IExpressions3, /** * Changes the value of the specified expression based on the new value and format. * - * @param expressionContext + * @param dmc * The context for the expression for which the value * should be changed. * @param expressionValue diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIVariableManager.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIVariableManager.java index 68cad5a05c7..c35f1fcf4b9 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIVariableManager.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIVariableManager.java @@ -11,6 +11,7 @@ * Ericsson - Major updates for GDB/MI implementation * Ericsson - Major re-factoring to deal with children * Axel Mueller - Bug 306555 - Add support for cast to type / view as array (IExpressions2) + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; @@ -63,6 +64,9 @@ import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDisplayHint; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDisplayHint.GdbDisplayHint; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIVar; import org.eclipse.cdt.dsf.mi.service.command.output.MIVarAssignInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIVarChange; @@ -183,10 +187,127 @@ import org.eclipse.core.runtime.Status; * Note that versions of GDB after 6.7 will allows to issue -var-evaluate-expression * with a specified format, therefore allowing us to never use -var-set-format, and * consequently, to easily keep the display format of all variable objects to natural. + * + * Notes on Dynamic Variable Objects (varobj) + * ------------------------------------------ + * - with version 7.0, gdb support so-called pretty printers. + * + * - pretty printers are registered for certain types + * + * - if there is a pretty printer registered for the type of a variable, + * the pretty printer provides the value and the children of that variable + * + * - a varobj whose value and/or children are provided by a pretty printer, + * are referred to as dynamic variable objects + * + * - dynamic varobjs change the game: it's not wise to ask it about all its + * children, not even the number of children it has. The reason is that + * in order to find out about the number of children the pretty printer + * must fetch all the children from the inferiors memory. If the variable + * is not yet initialized, the set of children are random, and thus might + * be huge. Even worse, there are data structures where fetching all + * children may result in an endless loop. The Eclipse debugger then hangs. + * + * - In order to avoid this, we will always fetch up to a certain maximum + * number of children. Furthermore, it is possible to find out whether there + * are more children available. In the UI, all the currently fetched + * children are available. In addition, if there are more children + * available, a special node will be appended to indicate that there is + * more the user could fetch. + * + * - Dynamic varobjs can change their value, as leaf varobjs can do. + * Especially, children can be added or removed during an update. + * + * - There is no expression for children of dynamic varobjs (at least not + * yet, http://sourceware.org/bugzilla/show_bug.cgi?id=10252 would fix that). + * The reason -var-info-path-expression returns garbage for children of + * dynamic varobjs. + * + * - Because of this, the variable of an expression that is a child of + * a dynamic varobj cannot be created again using -var-create, once + * the LRU cache has deleted it. Instead, we track the parent and index + * within this parent for each non-root variable, and later use + * -var-list-children parent indexInParent (indexInParent + 1) + * in order to create the MI variable anew. + * + * - The fetching of children for dynamic varobjs becomes a bit more complicated. + * For the traditional varobjs, once children where requested, all children + * were fetched. For dynamic varobjs, we can no longer fetch all children. + * Instead, the client will provide a maximum number of children that + * is to be fetched. Every time the child count or children are requested, + * we must check whether there are additional children to be fetched, + * because the limit might have extended. + * Fetching additional children can only be done by one request monitor at a time. + * The serialization of the request monitors is done in getChildren by + * ensuring that fetchChildren is called for one request monitor at a time. + * fetchChildren in turn checks whether enough children are fetched, and + * if not, fetches the additional children. */ public class MIVariableManager implements ICommandControl { + /** + * Stores the information about children of a variable object. + * + * @since 4.0 + */ + protected static class ChildrenInfo { + private final ExpressionInfo[] children; + private final boolean hasMore; + + public ChildrenInfo(ExpressionInfo[] children, boolean hasMore) { + this.children = children; + this.hasMore = hasMore; + } + /** + * @return The currently fetched children. Ask {@link #hasMore()} in + * order to find out whether there are more to fetch. + */ + public ExpressionInfo[] getChildren() { + return children; + } + + /** + * @return true, if there are more than just those returned by + * {@link #getChildren()}. + */ + public boolean hasMore() { + return hasMore; + } + } + + /** + * Stores the information about the children count of a variable object. + * + * @since 4.0 + */ + protected static class ChildrenCountInfo { + private final int childrenCount; + private final boolean hasMore; + + public ChildrenCountInfo(int childrenCount, boolean hasMore) { + this.childrenCount = childrenCount; + this.hasMore = hasMore; + } + + /** + * The number of children that we currently know of. Ask + * {@link #hasMore()} in order to find out whether there is at least one + * more. + */ + public int getChildrenCount() { + return childrenCount; + } + + /** + * @return true if there are more children than actually + * returned by {@link #getChildrenCount()}. + */ + public boolean hasMore() { + return hasMore; + } + } + /** * Utility class to track the progress and information of MI variable objects */ @@ -195,6 +316,12 @@ public class MIVariableManager implements ICommandControl { // Don't use an enumeration to allow subclasses to extend this protected static final int STATE_READY = 0; protected static final int STATE_UPDATING = 1; + /** @since 4.0 */ + protected static final int STATE_NOT_CREATED = 10; + /** @since 4.0 */ + protected static final int STATE_CREATING = 11; + /** @since 4.0 */ + protected static final int STATE_CREATION_FAILED = 12; protected int currentState; @@ -211,7 +338,9 @@ public class MIVariableManager implements ICommandControl { private String format = IFormattedValues.NATURAL_FORMAT; // The full expression that can be used to characterize this object - private String fullExp = null; + // plus some other information that shall live longer than the + // MIVariableObject. + private ExpressionInfo exprInfo; private String type = null; private GDBType gdbType; // A hint at the number of children. This value is obtained @@ -228,11 +357,16 @@ public class MIVariableManager implements ICommandControl { // A queue of request monitors that requested an update protected LinkedList> updatesPending; + + /** @since 4.0 */ + protected LinkedList> fetchChildrenPending; - // The relative expressions of the children of this variable, if any. + // The children of this variable, if any. // Null means we didn't fetch them yet, while an empty array means no children private ExpressionInfo[] children = null; - + private boolean hasMore = false; + private MIDisplayHint displayHint = MIDisplayHint.NONE; + // The parent of this variable object within GDB. Null if this object has no parent private MIVariableObject parent = null; @@ -241,12 +375,21 @@ public class MIVariableManager implements ICommandControl { private MIRootVariableObject rootToUpdate = null; protected boolean outOfScope = false; - + + private boolean fetchingChildren = false; + public MIVariableObject(VariableObjectId id, MIVariableObject parentObj) { - currentState = STATE_READY; + this(id, parentObj, false); + } + + /** @since 4.0 */ + public MIVariableObject(VariableObjectId id, MIVariableObject parentObj, + boolean needsCreation) { + currentState = needsCreation ? STATE_NOT_CREATED : STATE_READY; operationsPending = new LinkedList(); updatesPending = new LinkedList>(); + fetchChildrenPending = new LinkedList>(); internalId = id; setParent(parentObj); @@ -262,8 +405,48 @@ public class MIVariableManager implements ICommandControl { public MIVariableObject getParent() { return parent; } public MIRootVariableObject getRootToUpdate() { return rootToUpdate; } - public String getExpression() { return fullExp; } + public String getExpression() { return exprInfo.getFullExpr(); } public String getType() { return type; } + + /** + * @since 4.0 + */ + public ExpressionInfo getExpressionInfo() { return exprInfo; } + + /** + * @return true if value and children of this varobj are + * currently provided by a pretty printer. + * + * @since 4.0 + */ + public boolean isDynamic() { return exprInfo.isDynamic(); } + + /** + * @return For dynamic varobjs ({@link #isDynamic() returns true}) this + * method returns whether there are children in addition to the + * currently fetched, i.e. whether there are more children than + * {@link #getNumChildrenHint()} returns. + * + * @since 4.0 + */ + public boolean hasMore() { return hasMore; } + + /** + * @since 4.0 + */ + public MIDisplayHint getDisplayHint() { return displayHint; }; + + /** + * @since 4.0 + */ + protected void setDisplayHint(MIDisplayHint displayHint) { + this.displayHint = displayHint; + }; + + /** + * @since 4.0 + */ + public boolean hasChildren() { return (getNumChildrenHint() != 0 || hasMore()); } /** @since 3.0 */ public GDBType getGDBType() { return gdbType; } @@ -275,7 +458,8 @@ public class MIVariableManager implements ICommandControl { * Use isNumChildrenHintTrustworthy() to know if the * hint can be trusted. * - * Note that a hint of 0 children can always be trusted. + * Note that a hint of 0 children can always be trusted, except for + * {@link #hasMore} == true. * * @since 3.0 */ public int getNumChildrenHint() { return numChildrenHint; } @@ -296,7 +480,7 @@ public class MIVariableManager implements ICommandControl { // -var-info-expression. Do we have to use -var-info-expression for each // variable object, or can we do it one time only for the whole program? // Right now, we always assume we could be using C++ - return (getNumChildrenHint() == 0 || isArray()); + return ((getNumChildrenHint() == 0 && ! hasMore()) || isArray()); } public String getValue(String format) { return valueMap.get(format); } @@ -310,16 +494,74 @@ public class MIVariableManager implements ICommandControl { // A complex variable is one with children. However, it must not be a pointer since a pointer // does have children, but is still a 'simple' variable, as it can be modifed. // Note that the numChildrenHint can be trusted when asking if the number of children is 0 or not - public boolean isComplex() { return (getGDBType() == null) ? false : getGDBType().getType() != GDBType.POINTER && getNumChildrenHint() > 0; } + public boolean isComplex() { + return (getGDBType() == null) ? false + : getGDBType().getType() != GDBType.POINTER + && (getNumChildrenHint() > 0 + || hasMore() || getDisplayHint().isCollectionHint()); + } + + /** + * @return Whether this varobj can safely be asked for all its children. + * + * @since 4.0 + */ + public boolean isSafeToAskForAllChildren() { + GdbDisplayHint displayHint = getDisplayHint().getGdbDisplayHint(); + + // Here we balance usability against a slight risk of instability: + // + // Usability: if you have a class/struct-like pretty printer + // all children are fetched, regardless of any limit. This + // should be safe from gdb side. + // + // Risk: If somebody provides a pretty printer for a collection, + // but forgets to implement the display_hint method, viewing + // the collection while it is uninitiliazed may cause gdb + // to never return. + // + // => The risk seams reasonable, so we require a limit only + // for collections. + boolean isDynamicButSafe = (displayHint == GdbDisplayHint.GDB_DISPLAY_HINT_STRING) + || (displayHint == GdbDisplayHint.GDB_DISPLAY_HINT_NONE); + + return !isDynamic() || isDynamicButSafe; + } public void setGdbName(String n) { gdbName = n; } public void setCurrentFormat(String f) { format = f; } - + + /** + * @param fullExpression + * @param t + * @param num + * + * @deprecated Use + * {@link #setExpressionData(ExpressionInfo, String, int, boolean)} + * instead. + */ + @Deprecated public void setExpressionData(String fullExpression, String t, int num) { - fullExp = fullExpression; + new ExpressionInfo(fullExpression, fullExpression); + } + + /** + * @param info + * @param t + * @param num + * If the correspinding MI variable is dynamic, the number of + * children currently fetched by gdb. + * @param hasMore + * Whether their are more children to fetch. + * + * @since 4.0 + */ + public void setExpressionData(ExpressionInfo info, String t, int num, boolean hasMore) { + exprInfo = info; type = t; gdbType = fGDBTypeParser.parse(t); numChildrenHint = num; + this.hasMore = hasMore; } public void setValue(String format, String val) { valueMap.put(format, val); } @@ -337,12 +579,106 @@ public class MIVariableManager implements ICommandControl { valueMap.put(IFormattedValues.DECIMAL_FORMAT, null); } - public void setChildren(ExpressionInfo[] c) { children = c; } + /** + * @param c + * The new children, or null in order to force fetching + * children anew. + */ + public void setChildren(ExpressionInfo[] c) { + children = c; + if (children != null) { + numChildrenHint = children.length; + } + + if (children != null) { + for (ExpressionInfo child : children) { + assert (child != null); + } + } + } + + /** + * @param newChildren + * + * @since 4.0 + */ + public void addChildren(ExpressionInfo[] newChildren) { + if (children == null) { + children = new ExpressionInfo[newChildren.length]; + System.arraycopy(newChildren, 0, children, 0, newChildren.length); + } else { + ExpressionInfo[] oldChildren = children; + + children = new ExpressionInfo[children.length + newChildren.length]; + + System.arraycopy(oldChildren, 0, children, 0, oldChildren.length); + System.arraycopy(newChildren, 0, children, oldChildren.length, newChildren.length); + } + + numChildrenHint = children.length; + + for (ExpressionInfo child : children) { + assert (child != null); + } + } + + /** + * @param newChildren + * + * @since 4.0 + */ + protected void addChildren(ExpressionInfo[][] newChildren) { + int requiredSize = 0; + + for (ExpressionInfo[] subArray : newChildren) { + requiredSize += subArray.length; + } + + ExpressionInfo[] plainChildren = new ExpressionInfo[requiredSize]; + + int i = 0; + for (ExpressionInfo[] subArray : newChildren) { + System.arraycopy(subArray, 0, plainChildren, i, subArray.length); + i += subArray.length; + } + + addChildren(plainChildren); + } + + /** + * @param newNumChildren + * + * @since 4.0 + */ + public void shrinkChildrenTo(int newNumChildren) { + if (children != null) { + ExpressionInfo[] oldChildren = children; + for (int i = oldChildren.length - 1; i >= newNumChildren; --i) { + String childFullExpression = children[i].getFullExpr(); + + VariableObjectId childId = new VariableObjectId(); + childId.generateId(childFullExpression, getInternalId()); + lruVariableList.remove(childId); + } + + + children = new ExpressionInfo[newNumChildren]; + System.arraycopy(oldChildren, 0, children, 0, newNumChildren); + } + + numChildrenHint = newNumChildren; + } + public void setParent(MIVariableObject p) { parent = p; - rootToUpdate = (p == null ? (MIRootVariableObject)this : p.getRootToUpdate()); + if (p == null) { + rootToUpdate = (this instanceof MIRootVariableObject) ? (MIRootVariableObject) this + : null; + } else { + rootToUpdate = p.getRootToUpdate(); + } } - + public void executeWhenNotUpdating(RequestMonitor rm) { getRootToUpdate().executeWhenNotUpdating(rm); } @@ -361,6 +697,35 @@ public class MIVariableManager implements ICommandControl { public boolean isOutOfScope() { return outOfScope; } + /** + * @param success + * + * @since 4.0 + */ + protected void creationCompleted(boolean success) { + // A creation completed we must be up-to-date, so we + // can tell any pending monitors that updates are done + if (success) { + currentState = STATE_READY; + while (updatesPending.size() > 0) { + DataRequestMonitor rm = updatesPending.poll(); + // Nothing to be re-created + rm.setData(false); + rm.done(); + } + } else { + currentState = STATE_CREATION_FAILED; + + // Creation failed, inform anyone waiting. + while (updatesPending.size() > 0) { + RequestMonitor rm = updatesPending.poll(); + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "Unable to create variable object", null)); //$NON-NLS-1$ + rm.done(); + } + } + } + /** * This method updates the variable object. * Updating a variable object is done by updating its root. @@ -373,7 +738,7 @@ public class MIVariableManager implements ICommandControl { if (isOutOfScope()) { rm.setData(false); rm.done(); - } else if (currentState == STATE_UPDATING) { + } else if (currentState != STATE_READY) { // If we were already updating, we just queue the request monitor // until the on-going update finishes. updatesPending.add(rm); @@ -413,6 +778,169 @@ public class MIVariableManager implements ICommandControl { } } + /** + * Process an update on this variable object. + * + * @param update What has changed. + * + * @since 4.0 + */ + protected void processChange(final MIVarChange update, final RequestMonitor rm) { + + MIVar[] newChildren = update.getNewChildren(); + + // children == null means fetchChildren will happen later, so + // don't try to create a sparsely filled children array here. + final boolean addNewChildren = (children != null); + + final ExpressionInfo[] addedChildren = (addNewChildren && (newChildren != null)) ? new ExpressionInfo[newChildren.length] + : null; + + final CountingRequestMonitor countingRm = new CountingRequestMonitor(fSession.getExecutor(), rm) { + + @Override + protected void handleCompleted() { + + if (! isSuccess()) { + rm.setStatus(getStatus()); + } else { + if (update.numChildrenChanged()) { + if (children != null) { + // Remove those children that don't exist any longer. + if (children.length > update.getNewNumChildren()) { + shrinkChildrenTo(update.getNewNumChildren()); + } + } else { + // Just update the child count. + numChildrenHint = update.getNewNumChildren(); + } + + // Add the new children. + if ((addedChildren != null) && (addedChildren.length != 0)) { + addChildren(addedChildren); + } + } + + assert((children == null) || (children.length == numChildrenHint)); + + hasMore = update.hasMore(); + + // If there was no -var-list-children yet for a varobj, + // the new children will not be reported by -var-update. + // Set the children to null such that the next time children + // are requested, they will be fetched. + if (hasMore() && (numChildrenHint == 0)) { + setChildren(null); + } + + resetValues(update.getValue()); + } + rm.done(); + } + }; + + // Process all the child MIVariableObjects. + int pendingVariableCreationCount = 0; + if (newChildren != null && newChildren.length != 0) { + int i = update.getNewNumChildren() - newChildren.length; + int arrayPosition = 0; + + for (final MIVar newChild : newChildren) { + + // As long as http://sourceware.org/bugzilla/show_bug.cgi?id=10252 + // is not fixed, it doesn't make sense to use + // -var-info-path-expression. New children can only + // be added during the update, if we are a child of a + // dynamic varobj, and in this case -var-info-path-expression + // won't work. + final String childFullExpression = buildChildExpression( + getExpression(), newChild.getExp()); + + // Now try to see if we already have this variable + // object in our Map + // Since our map names use the expression, and not the + // GDB given + // name, we must determine the correct map name from the + // varName + final VariableObjectId childId = new VariableObjectId(); + childId.generateId(childFullExpression, getInternalId()); + MIVariableObject childVar = lruVariableList.get(childId); + + if (childVar != null) { + if ((childVar.currentState == STATE_CREATING) + || (childVar.currentState == STATE_NOT_CREATED)) { + + // We must wait until the child MIVariableObject is fully created. + // This might succeed, or fail. If it succeeds, we can reuse it as + // a child, otherwise we create a new MIVariableObject for the + // varobj just provided by gdb. + + ++pendingVariableCreationCount; + + final int insertPosition = arrayPosition; + final MIVariableObject monitoredVar = childVar; + final int indexInParent = i; + + // varobj is not fully created so add RequestMonitor to pending queue + childVar.updatesPending.add(new DataRequestMonitor(fSession.getExecutor(), countingRm) { + + @Override + protected void handleCompleted() { + if (isSuccess()) { + if (addNewChildren) { + addedChildren[insertPosition] = monitoredVar.exprInfo; + } + } else { + // Create a fresh MIVariableObject for this child, using + // the new varobj provided by gdb. + MIVariableObject newVar = createChild(childId, childFullExpression, + indexInParent, newChild); + if (addNewChildren) { + addedChildren[insertPosition] = newVar.exprInfo; + } + } + + countingRm.done(); + } + + }); + + } else if (childVar.currentState == STATE_CREATION_FAILED) { + // There has been an attempt the create a MIRootVariableObject for a full + // expression representing a child of a dynamic varobj. Such an attempt + // always fails. But here we can now create it (see below). + childVar = null; + } + // Note that we must check the root to know if it is out-of-scope. + // We cannot check the child as it has not be updated and its + // outOfScope variable is not updated either. + else if (childVar.getRootToUpdate().isOutOfScope()) { + childVar.deleteInGdb(); + childVar = null; + } + } + + // Note: we don't need to check for fake children (public, protected, private) here. + // We enter this code only if the children are provided by a pretty printer and + // they don't return such children. + + if (childVar == null) { + // Create a fresh MIVariableObject for this child, using + // the new varobj provided by -var-update. + childVar = createChild(childId, childFullExpression, i, newChild); + if (addNewChildren) { + addedChildren[arrayPosition] = childVar.exprInfo; + } + } + + ++i; + ++arrayPosition; + } + } + + countingRm.setDoneCount(pendingVariableCreationCount); + } + /** * Variable objects need not be deleted unless they are root. * This method is specialized in the MIRootVariableObject class. @@ -476,7 +1004,7 @@ public class MIVariableManager implements ICommandControl { // If the variable is a complex structure, there is no need to ask the back-end for a value, // we can give it the {...} ourselves // Unless we are dealing with an array, in which case, we want to get the address of it - if (isComplex()) { + if (isComplex() && ! isDynamic()) { if (isArray()) { // Figure out the address IExpressionDMContext exprCxt = DMContexts.getAncestorOfType(dmc, IExpressionDMContext.class); @@ -613,29 +1141,108 @@ public class MIVariableManager implements ICommandControl { } } - /** - * This method returns the list of children of the variable object passed as a parameter. + /** + * This method returns the list of children of the variable object + * passed as a parameter. * + * @param exprDmc + * + * @param clientNumChildrenLimit + * If the current limit for the given expression is smaller, + * this limit will be applied. * @param rm - * The data request monitor that will hold the children returned + * The data request monitor that will hold the children + * returned */ - private void getChildren(final MIExpressionDMC exprDmc, final DataRequestMonitor rm) { + private void getChildren(final MIExpressionDMC exprDmc, + final int clientNumChildrenLimit, final DataRequestMonitor rm) { + + if (fetchingChildren) { + // Only one request monitor can fetch children at a time. + fetchChildrenPending.add(new DataRequestMonitor(fSession.getExecutor(), rm) { + + @Override + protected void handleSuccess() { + ChildrenInfo info = getData(); + int numChildren = info.getChildren().length; + if (! info.hasMore() || numChildren >= clientNumChildrenLimit) { + // No need to fetch further children. + rm.setData(getData()); + rm.done(); + } else { + // Need to retry. + getChildren(exprDmc, clientNumChildrenLimit, rm); + } + } + + }); + } else { + + fetchingChildren = true; + + fetchChildren(exprDmc, clientNumChildrenLimit, new DataRequestMonitor(fSession.getExecutor(), rm) { + + @Override + protected void handleCompleted() { + fetchingChildren = false; + + if (isSuccess()) { + rm.setData(getData()); + rm.done(); + + while (fetchChildrenPending.size() > 0) { + DataRequestMonitor pendingRm = fetchChildrenPending.poll(); + pendingRm.setData(getData()); + pendingRm.done(); + } + } else { + rm.setStatus(getStatus()); + rm.done(); + + while (fetchChildrenPending.size() > 0) { + DataRequestMonitor pendingRm = fetchChildrenPending.poll(); + pendingRm.setStatus(getStatus()); + pendingRm.done(); + } + } + } + }); + } + } + + /** + * Fetch the out-standing children. + * + * @param exprDmc + * + * @param clientNumChildrenLimit + * If the current limit for the given expression is smaller, + * this limit will be applied. + * @param rm + * The data request monitor that will hold the children + * returned + */ + private void fetchChildren(final MIExpressionDMC exprDmc, + int clientNumChildrenLimit, final DataRequestMonitor rm) { + + final int newNumChildrenLimit = clientNumChildrenLimit != IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED ? + clientNumChildrenLimit : 1; + + boolean addChildren = requiresAdditionalChildren(newNumChildrenLimit); // If we already know the children, no need to go to the back-end ExpressionInfo[] childrenArray = getChildren(); - if (childrenArray != null) { - rm.setData(childrenArray); + if (childrenArray != null && ! addChildren) { + rm.setData(new ChildrenInfo(childrenArray, hasMore)); rm.done(); return; } // If the variable does not have children, we can return an empty list right away - // The numChildrenHint value is trustworthy when wanting to know if there are children - // at all. - if (getNumChildrenHint() == 0) { + if (! hasChildren()) { // First store the empty list, for the next time setChildren(new ExpressionInfo[0]); - rm.setData(getChildren()); + rm.setData(new ChildrenInfo(getChildren(), hasMore)); rm.done(); return; } @@ -643,9 +1250,8 @@ public class MIVariableManager implements ICommandControl { // For arrays (which could be very large), we create the children ourselves. This is // to avoid creating an enormous amount of children variable objects that the view may // never need. Using -var-list-children will create a variable object for every child - // immediately, that is why won't don't want to use it for arrays. + // immediately, that is why we don't want to use it for arrays. if (isArray()) { - // We can trust the numChildrenHint value for arrays. ExpressionInfo[] childrenOfArray = new ExpressionInfo[getNumChildrenHint()]; String exprName = exprDmc.getExpression(); @@ -661,12 +1267,13 @@ public class MIVariableManager implements ICommandControl { String fullExpr = exprName + "[" + i + "]";//$NON-NLS-1$//$NON-NLS-2$ String relExpr = exprDmc.getRelativeExpression() + "[" + (castingIndex + i) + "]";//$NON-NLS-1$//$NON-NLS-2$ - childrenOfArray[i] = new ExpressionInfo(fullExpr, relExpr); + childrenOfArray[i] = new ExpressionInfo(fullExpr, relExpr, false, exprInfo, i); } // First store these children, for the next time setChildren(childrenOfArray); - rm.setData(getChildren()); + hasMore = false; + rm.setData(new ChildrenInfo(getChildren(), hasMore)); rm.done(); return; } @@ -675,26 +1282,58 @@ public class MIVariableManager implements ICommandControl { // at any time, as long as the object is created, which we know it is, since we can only // be called here with a fully created object. // Also no need to lock the object, since getting the children won't affect other operations + + final int from = (addChildren && (children != null)) ? getNumChildrenHint() : 0; + final int to = Math.max(newNumChildrenLimit, exprInfo.getChildCountLimit()); + + ICommand varListChildren = isSafeToAskForAllChildren() ? + fCommandFactory.createMIVarListChildren(getRootToUpdate().getControlDMContext(), getGdbName()) + :fCommandFactory.createMIVarListChildren(getRootToUpdate().getControlDMContext(), getGdbName(), from, to); + fCommandControl.queueCommand( - fCommandFactory.createMIVarListChildren(getRootToUpdate().getControlDMContext(), getGdbName()), + varListChildren, new DataRequestMonitor(fSession.getExecutor(), rm) { @Override protected void handleSuccess() { MIVar[] children = getData().getMIVars(); - final List realChildren = new ArrayList(); + final boolean localHasMore = getData().hasMore(); + + // The elements of this array normally are an ExpressionInfo, unless it corresponds to + // a fake child (public, protected, private). In this case it is an ExpressionInfo[] + // representing the children of the fake child. + // This in done in order to preserve the order (the index-in-parent information), + // when replacing a fake child by its real children. + final ExpressionInfo[][] realChildren = new ExpressionInfo[children.length][]; final CountingRequestMonitor countingRm = new CountingRequestMonitor(fSession.getExecutor(), rm) { @Override protected void handleSuccess() { // Store the children in our variable object cache - setChildren(realChildren.toArray(new ExpressionInfo[realChildren.size()])); - rm.setData(getChildren()); - rm.done(); + addChildren(realChildren); + hasMore = localHasMore; + rm.setData(new ChildrenInfo(getChildren(), hasMore)); + + int updateLimit = updateLimit(to); + + if (! isSafeToAskForAllChildren()) { + // Make sure the gdb will not hang, if later + // the varobj is updated, but the underlying + // data is still uninitialized. + fCommandControl.queueCommand( + fCommandFactory.createMIVarSetUpdateRange(getRootToUpdate().getControlDMContext(), + getGdbName(), 0, updateLimit), new DataRequestMonitor(fSession.getExecutor(), rm)); + } else { + rm.done(); + } } }; int numSubRequests = 0; - for (final MIVar child : children) { + for (int i = 0; i < children.length; ++i) { + final MIVar child = children[i]; + final int indexInParent = from + i; + final int arrayPosition = i; + // These children get created automatically as variable objects in GDB, so we should // add them to the LRU. // Note that if this variable object already exists, we can be in three scenarios: @@ -717,68 +1356,84 @@ public class MIVariableManager implements ICommandControl { new DataRequestMonitor(fSession.getExecutor(), countingRm) { @Override protected void handleSuccess() { - String childFullExpression = getData(); - // For children that do not map to a real expression (such as f.public) // GDB returns an empty string. In this case, we can use another unique // name, such as the variable name - boolean fakeChild = false; - if (childFullExpression.length() == 0) { - fakeChild = true; - childFullExpression = child.getVarName(); - } - + final boolean fakeChild = (getData().length() == 0); + final String childFullExpression = fakeChild ? child.getVarName() : getData(); + // Now try to see if we already have this variable object in our Map // Since our map names use the expression, and not the GDB given // name, we must determine the correct map name from the varName - VariableObjectId childId = new VariableObjectId(); + final VariableObjectId childId = new VariableObjectId(); childId.generateId(childFullExpression, getInternalId()); MIVariableObject childVar = lruVariableList.get(childId); - // Note that we must check the root to know if it is out-of-scope. - // We cannot check the child as it has not be updated and its - // outOfScope variable is not updated either. - if (childVar != null && childVar.getRootToUpdate().isOutOfScope()) { - childVar.deleteInGdb(); - childVar = null; - } + if (childVar != null) { + + if ((childVar.currentState == STATE_CREATING) + || (childVar.currentState == STATE_NOT_CREATED)) { + + // We must wait until the child MIVariableObject is fully created. + // This might succeed, or fail. If it succeeds, we can reuse it as + // a child, otherwise we create a new MIVariableObject for the + // varobj just provided by gdb. + + final MIVariableObject monitoredVar = childVar; + + // childVar is not fully created so add a RequestMonitor to the queue + childVar.updatesPending.add(new DataRequestMonitor(fSession.getExecutor(), countingRm) { + + @Override + protected void handleCompleted() { + MIVariableObject var = monitoredVar; + + if (! isSuccess()) { + + // Create a fresh MIVariableObject for this child, using + // the new varobj provided by gdb. + var = createChild(childId, childFullExpression, indexInParent, child); + } + + if (fakeChild) { + + addRealChildrenOfFake(var, exprDmc, realChildren, + arrayPosition, countingRm); + } else { + // This is a real child + realChildren[arrayPosition] = new ExpressionInfo[] { var.exprInfo }; + countingRm.done(); + } + } + }); + + } else if (childVar.currentState == STATE_CREATION_FAILED) { + // There has been an attempt the create a MIRootVariableObject for a full + // expression representing a child of a dynamic varobj. Such an attempt always + // fails. But here we can now create it (see below). + childVar = null; + } + // Note that we must check the root to know if it is out-of-scope. + // We cannot check the child as it has not be updated and its + // outOfScope variable is not updated either. + else if (childVar.getRootToUpdate().isOutOfScope()) { + childVar.deleteInGdb(); + childVar = null; + } + } if (childVar == null) { - childVar = createVariableObject(childId, MIVariableObject.this); - childVar.setGdbName(child.getVarName()); - childVar.setExpressionData( - childFullExpression, - child.getType(), - child.getNumChild()); - - // This will replace any existing entry - lruVariableList.put(childId, childVar); - - // Is this new child a modifiable descendant of the root? - if (childVar.isModifiable()) { - getRootToUpdate().addModifiableDescendant(child.getVarName(), childVar); - } - } - - if (fakeChild) { - // This is just a qualifier level of C++, and we must - // get the children of this child to get the real children - childVar.getChildren( - exprDmc, - new DataRequestMonitor(fSession.getExecutor(), countingRm) { - @Override - protected void handleSuccess() { - ExpressionInfo[] vars = getData(); - for (ExpressionInfo realChild : vars) { - realChildren.add(realChild); - } - countingRm.done(); - } - }); - } else { - // This is a real child - realChildren.add(new ExpressionInfo(childFullExpression, child.getExp())); - countingRm.done(); + childVar = createChild(childId, childFullExpression, indexInParent, child); + + if (fakeChild) { + + addRealChildrenOfFake(childVar, exprDmc, realChildren, + arrayPosition, countingRm); + } else { + // This is a real child + realChildren[arrayPosition] = new ExpressionInfo[] { childVar.exprInfo }; + countingRm.done(); + } } } }; @@ -789,6 +1444,12 @@ public class MIVariableManager implements ICommandControl { // to call -var-info-path-expression for real, but just pretend we did. childPathRm.setData(""); //$NON-NLS-1$ childPathRm.done(); + } else if (isDynamic() || exprInfo.hasDynamicAncestor()) { + // Equivalent to (which can't be implemented): child.hasDynamicAncestor + // The new child has a dynamic ancestor. Such children don't support + // var-info-path-expression. Build the expression ourselves. + childPathRm.setData(buildChildExpression(exprDmc.getExpression(), child.getExp())); + childPathRm.done(); } else { // To build the child id, we need the fully qualified expression which we // can get from -var-info-path-expression starting from GDB 6.7 @@ -816,6 +1477,111 @@ public class MIVariableManager implements ICommandControl { }); } + /** + * Create a child variable of this MIVariableObject and initialize + * it from the given MIVar data. + * + * @param childId + * @param childFullExpression + * @param indexInParent + * @param childData + * + * @return The new child. + * + * @since 4.0 + */ + protected MIVariableObject createChild(final VariableObjectId childId, + final String childFullExpression, final int indexInParent, + final MIVar childData) { + + MIVariableObject var = createVariableObject(childId, this); + ExpressionInfo childInfo = new ExpressionInfo(childFullExpression, + childData.getExp(), childData.isDynamic(), exprInfo, + indexInParent); + + var.initFrom(childData, childInfo); + return var; + } + + /** + * @param clientLimit + * @return True, if the client specified limit requires to check for + * further children. + * + * @since 4.0 + */ + protected boolean requiresAdditionalChildren(int clientLimit) { + return !isSafeToAskForAllChildren() + && (exprInfo.getChildCountLimit() < calculateNewLimit(clientLimit)); + } + + /** + * @param newLimit + * The new limit on the number of children being asked for or + * being updated. + * + * @return The new limit. + * @since 4.0 + */ + protected int updateLimit(int newLimit) { + exprInfo.setChildCountLimit(calculateNewLimit(newLimit)); + return exprInfo.getChildCountLimit(); + } + + private int calculateNewLimit(int clientLimit) { + int limit = exprInfo.getChildCountLimit(); + + if (!isSafeToAskForAllChildren() && clientLimit != IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED) { + if (limit == IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED) { + return clientLimit; + } else if (limit < clientLimit) { + return clientLimit; + } + } + + return limit; + } + + /** + * Obtain the children of the given fake child (public, protected, or + * private) and insert them into a list of children at a given position. + * + * @param fakeChild + * @param frameCtxProvider + * @param realChildren + * @param position + * @param rm The request monitor on which to call done upon completion. + */ + private void addRealChildrenOfFake(MIVariableObject fakeChild, + IExpressionDMContext frameCtxProvider, + final ExpressionInfo[][] realChildren, final int position, + final CountingRequestMonitor rm) { + + MIExpressionDMC fakeExprCtx = createExpressionCtx(frameCtxProvider, + fakeChild.exprInfo); + + // This is just a qualifier level of C++, and we must get the + // children of this child to get the real children + fakeChild.getChildren(fakeExprCtx, + IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED, + new DataRequestMonitor( + fSession.getExecutor(), rm) { + + @Override + protected void handleCompleted() { + if (isSuccess()) { + realChildren[position] = getData() + .getChildren(); + + // Fake nodes can never be dynamic varobjs, and because + // of this, hasMore is always false. + assert (!getData().hasMore()); + } + rm.done(); + } + }); + } + /** * This method builds a child expression based on its parent's expression. * It is a fallback solution for when GDB doesn't support the var-info-path-expression. @@ -839,28 +1605,40 @@ public class MIVariableManager implements ICommandControl { // and don't call this method for them } - /** - * This method returns the count of children of the variable object passed as a parameter. + /** + * This method returns the count of children of the variable object + * passed as a parameter. * + * @param exprDmc + * + * @param numChildrenLimit + * No need to check for more than this number of children. + * However, it is legal to return a higher count if + * we already new from earlier call that there are more + * children. + * * @param rm - * The data request monitor that will hold the count of children returned + * The data request monitor that will hold the count of + * children returned */ - private void getChildrenCount(MIExpressionDMC exprDmc, final DataRequestMonitor rm) { + private void getChildrenCount(MIExpressionDMC exprDmc, final int numChildrenLimit, + final DataRequestMonitor rm) { if (isNumChildrenHintTrustworthy()){ - rm.setData(getNumChildrenHint()); + rm.setData(new ChildrenCountInfo(getNumChildrenHint(), hasMore())); rm.done(); return; } - getChildren( - exprDmc, - new DataRequestMonitor(fSession.getExecutor(), rm) { - @Override - protected void handleSuccess() { - rm.setData(getData().length); - rm.done(); - } - }); + getChildren(exprDmc, numChildrenLimit, + new DataRequestMonitor(fSession.getExecutor(), rm) { + + @Override + protected void handleSuccess() { + rm.setData(new ChildrenCountInfo(getData() + .getChildren().length, getData().hasMore())); + rm.done(); + } + }); } @@ -966,10 +1744,91 @@ public class MIVariableManager implements ICommandControl { return str.equals("private") || str.equals("public") || str.equals("protected"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } + /** + * @return If true, this variable object can be reported as changed in + * a -var-update MI command. + */ public boolean isModifiable() { - if (!isComplex()) return true; + if (!isComplex() || isDynamic()) return true; return false; } + + /** + * @param exprCtx + * @param rm + * + * @since 4.0 + */ + public void create(final IExpressionDMContext exprCtx, + final RequestMonitor rm) { + + if (currentState == STATE_NOT_CREATED) { + + currentState = STATE_CREATING; + + final MIExpressionDMC miExprCtx = (MIExpressionDMC) exprCtx; + final int indexInParent = miExprCtx.getExpressionInfo().getIndexInParentExpression(); + + fCommandControl.queueCommand(fCommandFactory.createMIVarListChildren(getParent().getRootToUpdate().getControlDMContext(), + getParent().getGdbName(), indexInParent, indexInParent + 1), + new DataRequestMonitor(fSession.getExecutor(), rm) { + + @Override + protected void handleSuccess() { + if (getData().getMIVars().length == 1) { + MIVar miVar = getData().getMIVars()[0]; + + ExpressionInfo localExprInfo = miExprCtx.getExpressionInfo(); + + localExprInfo.setDynamic(miVar.isDynamic()); + + initFrom(miVar, localExprInfo); + + if (exprInfo.isDynamic() + && (exprInfo.getChildCountLimit() != IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED)) { + // Restore the original update range. + fCommandControl.queueCommand(fCommandFactory.createMIVarSetUpdateRange( + getRootToUpdate().getControlDMContext(), + getGdbName(), 0, exprInfo.getChildCountLimit()), + new DataRequestMonitor(fSession.getExecutor(), rm)); + } else { + rm.done(); + } + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Unexpected return on -var-list-children", null)); //$NON-NLS-1$ + rm.done(); + } + } + }); + } else { + assert false; + } + } + + private void initFrom(MIVar miVar, ExpressionInfo newExprInfo) { + // Possible Optimization in GDB: In -var-list-children, the has_more + // field is missing for the children. As a workaround, we assume that + // if numChild is 0 and has_more is omitted, we have more children. + boolean newHasMore = miVar.hasMore() + || (miVar.isDynamic() && (miVar.getNumChild() == 0)); + + setGdbName(miVar.getVarName()); + setDisplayHint(miVar.getDisplayHint()); + setExpressionData( + newExprInfo, + miVar.getType(), + miVar.getNumChild(), + newHasMore); + + // This will replace any existing entry + lruVariableList.put(getInternalId(), this); + + // Is this new child a modifiable descendant of the root? + if (isModifiable()) { + getRootToUpdate().addModifiableDescendant(miVar.getVarName(), this); + } + } } /** @@ -980,16 +1839,22 @@ public class MIVariableManager implements ICommandControl { protected MIVariableObject createVariableObject(VariableObjectId id, MIVariableObject parentObj) { return new MIVariableObject(id, parentObj); } - + + /** + * Method to allow to override the MIVariableObject creation + * + * @since 4.0 + */ + protected MIVariableObject createVariableObject(VariableObjectId id, + MIVariableObject parentObj, boolean needsCreation) { + return new MIVariableObject(id, parentObj, needsCreation); + } + /** * @since 3.0 */ public class MIRootVariableObject extends MIVariableObject { - // Only root variables go through the GDB creation process - protected static final int STATE_NOT_CREATED = 10; - protected static final int STATE_CREATING = 11; - // The control context within which this variable object was created // It only needs to be stored in the Root VarObj since any children // will have the same control context @@ -997,8 +1862,11 @@ public class MIVariableManager implements ICommandControl { private boolean fOutOfDate = false; - // Modifiable descendants are any variable object that is a descendant or itself for - // which the value can change. + /** + * A modifiable descendant is any variable object that is a descendant and + * for which the value (leaf variable objects and dynamic variable objects) + * or number of children (dynamic variable objects) can change. + */ private Map modifiableDescendants; public MIRootVariableObject(VariableObjectId id) { @@ -1020,14 +1888,29 @@ public class MIVariableManager implements ICommandControl { modifiableDescendants.put(gdbName, descendant); } - public void processChanges(MIVarChange[] updates) { + /** + * @since 4.0 + */ + public void processChanges(MIVarChange[] updates, RequestMonitor rm) { + CountingRequestMonitor countingRm = new CountingRequestMonitor(fSession.getExecutor(), rm); + countingRm.setDoneCount(updates.length); + for (MIVarChange update : updates) { MIVariableObject descendant = modifiableDescendants.get(update.getVarName()); + // Descendant should never be null, but just to be safe - if (descendant != null) descendant.resetValues(update.getValue()); + if (descendant != null) { + descendant.processChange(update, countingRm); + } else { + // This can for instance happen if a child MIVariableObject is deleted + // from the cache. The corresponding varobj in gdb does still exist, + // and if it is changed, gdb reports it as changed in -var-update. + countingRm.done(); + } } } + @Override public void create(final IExpressionDMContext exprCtx, final RequestMonitor rm) { @@ -1043,10 +1926,22 @@ public class MIVariableManager implements ICommandControl { protected void handleCompleted() { if (isSuccess()) { setGdbName(getData().getName()); + setDisplayHint(getData().getDisplayHint()); + + MIExpressionDMC miExprCtx = (MIExpressionDMC) exprCtx; + ExpressionInfo localExprInfo = miExprCtx + .getExpressionInfo(); + + localExprInfo.setDynamic(getData() + .isDynamic()); + localExprInfo.setParent(null); + localExprInfo.setIndexInParent(-1); + setExpressionData( - exprCtx.getExpression(), - getData().getType(), - getData().getNumChildren()); + localExprInfo, + getData().getType(), + getData().getNumChildren(), + getData().hasMore()); // Store the value returned at create (available in GDB 6.7) // Don't store if it is an array, since we want to show @@ -1059,11 +1954,22 @@ public class MIVariableManager implements ICommandControl { if (isModifiable()) { addModifiableDescendant(getData().getName(), MIRootVariableObject.this); } + + if (localExprInfo.isDynamic() + && (localExprInfo.getChildCountLimit() != IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED)) { + + // Restore the original update range. + fCommandControl.queueCommand(fCommandFactory.createMIVarSetUpdateRange( + getRootToUpdate().getControlDMContext(),getGdbName(), + 0,localExprInfo.getChildCountLimit()), + new DataRequestMonitor(fSession.getExecutor(), rm)); + } else { + rm.done(); + } } else { rm.setStatus(getStatus()); - } - - rm.done(); + rm.done(); + } } }); } else { @@ -1071,30 +1977,6 @@ public class MIVariableManager implements ICommandControl { } } - private void creationCompleted(boolean success) { - // A creation completed we must be up-to-date, so we - // can tell any pending monitors that updates are done - if (success) { - currentState = STATE_READY; - while (updatesPending.size() > 0) { - DataRequestMonitor rm = updatesPending.poll(); - // Nothing to be re-created - rm.setData(false); - rm.done(); - } - } else { - currentState = STATE_NOT_CREATED; - - // Creation failed, inform anyone waiting. - while (updatesPending.size() > 0) { - RequestMonitor rm = updatesPending.poll(); - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, - "Unable to create variable object", null)); //$NON-NLS-1$ - rm.done(); - } - } - } - @Override public void update(final DataRequestMonitor rm) { @@ -1136,7 +2018,32 @@ public class MIVariableManager implements ICommandControl { if (isSuccess()) { setOutOfDate(false); + + CountingRequestMonitor countingRm = new CountingRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleCompleted() { + + if (!isSuccess()) { + rm.setStatus(getStatus()); + } + rm.done(); + + while (updatesPending.size() > 0) { + DataRequestMonitor pendingRm = updatesPending.poll(); + if (isSuccess()) { + pendingRm.setData(false); + } else { + rm.setStatus(getStatus()); + } + + pendingRm.done(); + } + } + }; + + int rmCount = 0; + MIVarChange[] changes = getData().getMIVarChanges(); if (changes.length > 0 && changes[0].isInScope() == false) { // Object is out-of-scope @@ -1152,10 +2059,10 @@ public class MIVariableManager implements ICommandControl { lruVariableList.remove(getInternalId()); rm.setData(true); - rm.done(); } else { // The root object is now up-to-date, we must parse the changes, if any. - processChanges(changes); + ++rmCount; + processChanges(changes, new RequestMonitor(fSession.getExecutor(), countingRm)); // We only mark this root as updated in our list if it is in-scope. // For out-of-scope object, we don't ever need to re-update them so @@ -1163,14 +2070,10 @@ public class MIVariableManager implements ICommandControl { rootVariableUpdated(MIRootVariableObject.this); rm.setData(false); - rm.done(); } - while (updatesPending.size() > 0) { - DataRequestMonitor pendingRm = updatesPending.poll(); - pendingRm.setData(false); - pendingRm.done(); - } + countingRm.setDoneCount(rmCount); + } else { // We were not able to update for some reason rm.setData(false); @@ -1195,7 +2098,7 @@ public class MIVariableManager implements ICommandControl { * to be deleted automatically when their root is deleted. */ @Override - public void deleteInGdb() { + public void deleteInGdb() { if (getGdbName() != null) { fCommandControl.queueCommand( fCommandFactory.createMIVarDelete(getRootToUpdate().getControlDMContext(), getGdbName()), @@ -1210,6 +2113,8 @@ public class MIVariableManager implements ICommandControl { } else { // Variable was never created or was already deleted, no need to do anything. } + + super.deleteInGdb(); } } @@ -1385,7 +2290,9 @@ public class MIVariableManager implements ICommandControl { @Override public MIVariableObject remove(Object key) { MIVariableObject varObj = super.remove(key); - varObj.deleteInGdb(); + if (varObj != null) { + varObj.deleteInGdb(); + } return varObj; } } @@ -1542,6 +2449,16 @@ public class MIVariableManager implements ICommandControl { } } else { // The variable object is up-to-date and valid + + MIExpressionDMC miExprCtx = (MIExpressionDMC) exprCtx; + ExpressionInfo ctxExprInfo = miExprCtx.getExpressionInfo(); + ExpressionInfo varExprInfo = varObj.getExpressionInfo(); + if (ctxExprInfo != varExprInfo) { + // exprCtrx could just be created via IExpressions.createExpression, + // and thus the parent-child relationship is not yet set. + miExprCtx.setExpressionInfo(varExprInfo); + } + rm.setData(varObj); rm.done(); } @@ -1559,6 +2476,72 @@ public class MIVariableManager implements ICommandControl { final IExpressionDMContext exprCtx, final DataRequestMonitor rm) { + // If we have a dynamic variable object as ancestor, we cannot use + // -var-create, so we must create ourselves creating the root, and + // then use -var-list-children for each further ancestor. + final MIExpressionDMC miExprCtx =(MIExpressionDMC) exprCtx; + final ExpressionInfo parentInfo = miExprCtx.getExpressionInfo().getParent(); + + if ((parentInfo != null) && miExprCtx.getExpressionInfo().hasDynamicAncestor()) { + + // Need to set parent when it is known. + final MIVariableObject newVarObj = createVariableObject(id, null, true); + + // We must put this object in our map right away, in case it is + // requested again, before it completes its creation. + // Note that this will replace any old entry with the same id. + lruVariableList.put(id, newVarObj); + + MIExpressionDMC parentExprCtx = createExpressionCtx(exprCtx, + parentInfo); + + getVariable(parentExprCtx, + new DataRequestMonitor( + fSession.getExecutor(), rm) { + + @Override + protected void handleCompleted() { + + if (isSuccess()) { + final MIVariableObject parentObj = getData(); + newVarObj.setParent(parentObj); + + newVarObj.create(miExprCtx, new RequestMonitor(fSession.getExecutor(), rm) { + + @Override + protected void handleCompleted() { + if (isSuccess()) { + rm.setData(newVarObj); + newVarObj.creationCompleted(true); + } else { + // Object was not created, remove it from our list + lruVariableList.remove(id); + // We avoid this race condition by sending the notifications _after_ removing + // the object from the LRU, to avoid any new requests being queue. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=231655 + newVarObj.creationCompleted(false); + rm.setStatus(getStatus()); + } + rm.done(); + } + }); + } else { + // Object was not created, remove it from our list + lruVariableList.remove(id); + // We avoid this race condition by sending the notifications _after_ removing + // the object from the LRU, to avoid any new requests being queue. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=231655 + newVarObj.creationCompleted(false); + + rm.setStatus(getStatus()); + rm.done(); + } + } + }); + + return; + } + // Variable objects that are created directly like this, are considered ROOT variable objects // in comparison to variable objects that are children of other variable objects. final MIRootVariableObject newVarObj = createRootVariableObject(id); @@ -1602,10 +2585,22 @@ public class MIVariableManager implements ICommandControl { }); } + private MIExpressionDMC createExpressionCtx( + final IExpressionDMContext frameCtxProvider, + final ExpressionInfo exprInfo) { + + IFrameDMContext frameCtx = DMContexts.getAncestorOfType(frameCtxProvider, IFrameDMContext.class); + + MIExpressionDMC exprCtx = new MIExpressionDMC( + frameCtxProvider.getSessionId(), exprInfo, frameCtx); + + return exprCtx; + } + /** * This method requests the back-end to change the value of an expression. * - * @param expressionContext + * @param ctx * The context of the expression we want to change * @param expressionValue * The new value of the expression @@ -1652,18 +2647,52 @@ public class MIVariableManager implements ICommandControl { new DataRequestMonitor(fSession.getExecutor(), drm) { @Override protected void handleSuccess() { - drm.setData( - new ExprMetaGetVarInfo( - exprCtx.getRelativeExpression(), - // We only provide the hint here. It will be used for hasChildren() - // To obtain the correct number of children, the user should use - // IExpressions#getSubExpressionCount() - getData().getNumChildrenHint(), - getData().getType(), - getData().getGDBType(), - !getData().isComplex())); - drm.done(); - processCommandDone(token, drm.getData()); + final MIVariableObject varObj = getData(); + + if (varObj.isDynamic() && (varObj.getNumChildrenHint() == 0) && varObj.hasMore()) { + // Bug/feature in gdb? MI sometimes reports 0 number of children, and hasMore=1. + // This however, is not a safe indicator that there are children, + // unless there has been a -var-list-children before. + // The following call will + // 1) try to fetch at least one child, because isNumChildrenHintTrustworthy() + // returns false for this combination + // 2) result in the desired -var-list-children, unless it has happened already + // such that the number of children can at least be used to reliably + // tell whether there are children or not. + varObj.getChildrenCount( + exprCtx, 1, + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + drm.setData( + new ExprMetaGetVarInfo( + exprCtx.getRelativeExpression(), + varObj.isSafeToAskForAllChildren(), + getData().getChildrenCount(), + varObj.getType(), + varObj.getGDBType(), + !varObj.isComplex(), + varObj.getDisplayHint().isCollectionHint())); + drm.done(); + processCommandDone(token, drm.getData()); + } + }); + } else { + drm.setData( + new ExprMetaGetVarInfo( + exprCtx.getRelativeExpression(), + varObj.isSafeToAskForAllChildren(), + // We only provide the hint here. It will be used for hasChildren() + // To obtain the correct number of children, the user should use + // IExpressions#getSubExpressionCount() + varObj.getNumChildrenHint(), + varObj.getType(), + varObj.getGDBType(), + !varObj.isComplex(), + varObj.getDisplayHint().isCollectionHint())); + drm.done(); + processCommandDone(token, drm.getData()); + } } }); } else if (command instanceof ExprMetaGetAttributes) { @@ -1724,14 +2753,14 @@ public class MIVariableManager implements ICommandControl { new DataRequestMonitor(fSession.getExecutor(), drm) { @Override protected void handleSuccess() { - getData().getChildren( - exprCtx, - new DataRequestMonitor(fSession.getExecutor(), drm) { + getData().getChildren(exprCtx, ((ExprMetaGetChildren)command).getNumChildLimit(), + new DataRequestMonitor(fSession.getExecutor(), drm) { @Override protected void handleSuccess() { - drm.setData(new ExprMetaGetChildrenInfo(getData())); + drm.setData(new ExprMetaGetChildrenInfo( + getData().getChildren())); drm.done(); - processCommandDone(token, drm.getData()); + processCommandDone(token, drm.getData()); } }); } @@ -1741,18 +2770,20 @@ public class MIVariableManager implements ICommandControl { @SuppressWarnings("unchecked") final DataRequestMonitor drm = (DataRequestMonitor)rm; final MIExpressionDMC exprCtx = (MIExpressionDMC)(command.getContext()); - + getVariable( exprCtx, new DataRequestMonitor(fSession.getExecutor(), drm) { @Override protected void handleSuccess() { + getData().getChildrenCount( - exprCtx, - new DataRequestMonitor(fSession.getExecutor(), drm) { + exprCtx, ((ExprMetaGetChildCount) command).getNumChildLimit(), + new DataRequestMonitor(fSession.getExecutor(), drm) { @Override protected void handleSuccess() { - drm.setData(new ExprMetaGetChildCountInfo(getData())); + drm.setData(new ExprMetaGetChildCountInfo( + getData().getChildrenCount())); drm.done(); processCommandDone(token, drm.getData()); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java index 0daabfffee4..50b394e7f42 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java @@ -7,6 +7,7 @@ * * Contributors: * Ericsson - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; @@ -19,6 +20,7 @@ import org.eclipse.osgi.util.NLS; class Messages extends NLS { public static String Breakpoint_attribute_problem; public static String Breakpoint_installation_failed; + public static String MIExpressions_NotAvailableBecauseChildOfDynamicVarobj; static { // initialize resource bundle diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties index e04b5f77beb..8114e6d0e19 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties @@ -7,7 +7,10 @@ # # Contributors: # Ericsson - initial API and implementation +# Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) ############################################################################### Breakpoint_attribute_problem=Breakpoint attribute problem: {0} Breakpoint_installation_failed=installation failed + +MIExpressions_NotAvailableBecauseChildOfDynamicVarobj=N/A (child of pretty-printed object) diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java index aca7b0f37a8..1681b2fe7dd 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java @@ -11,6 +11,7 @@ * Ericsson - Implementation for DSF-GDB * Anna Dushistova (Mentor Graphics) - [318322] Add set solib-absolute-prefix * Vladimir Prus (CodeSourcery) - Support for -data-read-memory-bytes (bug 322658) + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command; @@ -36,6 +37,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.CLIInfoProgram; import org.eclipse.cdt.dsf.mi.service.command.commands.CLIInfoSharedLibrary; import org.eclipse.cdt.dsf.mi.service.command.commands.CLIInfoThreads; import org.eclipse.cdt.dsf.mi.service.command.commands.CLIJump; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLIMaintenance; import org.eclipse.cdt.dsf.mi.service.command.commands.CLIPasscount; import org.eclipse.cdt.dsf.mi.service.command.commands.CLIRecord; import org.eclipse.cdt.dsf.mi.service.command.commands.CLISource; @@ -60,6 +62,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataListRegisterValues; import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataReadMemory; import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataReadMemoryBytes; import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataWriteMemory; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIEnablePrettyPrinting; import org.eclipse.cdt.dsf.mi.service.command.commands.MIEnvironmentCD; import org.eclipse.cdt.dsf.mi.service.command.commands.MIEnvironmentDirectory; import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; @@ -133,6 +136,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarInfoPathExpression; import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarInfoType; import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarListChildren; import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarSetFormat; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarSetUpdateRange; import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarShowAttributes; import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarShowFormat; import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarUpdate; @@ -229,6 +233,11 @@ public class CommandFactory { return new CLIJump(ctx, location); } + /** @since 4.0 */ + public ICommand createCLIMaintenance(ICommandControlDMContext ctx, String subCommand) { + return new CLIMaintenance(ctx, subCommand); + } + public ICommand createCLIPasscount(IBreakpointsTargetDMContext ctx, int breakpoint, int passcount) { return new CLIPasscount(ctx, breakpoint, passcount); } @@ -375,6 +384,11 @@ public class CommandFactory { return new MIDataWriteMemory(ctx, offset, address, wordFormat, wordSize, value); } + /** @since 4.0 */ + public ICommand createMIEnablePrettyPrinting(ICommandControlDMContext ctx) { + return new MIEnablePrettyPrinting(ctx); + } + public ICommand createMIEnvironmentCD(ICommandControlDMContext ctx, String path) { return new MIEnvironmentCD(ctx, path); } @@ -801,10 +815,20 @@ public class CommandFactory { return new MIVarListChildren(ctx, name); } + /** @since 4.0 */ + public ICommand createMIVarListChildren(ICommandControlDMContext ctx, String name, int from, int to) { + return new MIVarListChildren(ctx, name, from, to); + } + public ICommand createMIVarSetFormat(ICommandControlDMContext ctx, String name, String fmt) { return new MIVarSetFormat(ctx, name, fmt); } + /** @since 4.0 */ + public ICommand createMIVarSetUpdateRange(ICommandControlDMContext ctx,String name, int from, int to) { + return new MIVarSetUpdateRange(ctx, name, from, to); + } + public ICommand createMIVarShowAttributes(ICommandControlDMContext ctx, String name) { return new MIVarShowAttributes(ctx, name); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIMaintenance.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIMaintenance.java new file mode 100644 index 00000000000..6fa3ec1628c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIMaintenance.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2010 Verigy 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: + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * Executes "maintenance" command. + * @since 4.0 + */ +public class CLIMaintenance extends CLICommand { + + public CLIMaintenance(ICommandControlDMContext ctx, String arguments) { + super(ctx, "maintenance " + arguments); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildCount.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildCount.java index 2241dc86c94..e3fc66a8e91 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildCount.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildCount.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2009 Ericsson and others. + * Copyright (c) 2007, 2010 Ericsson 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 @@ -7,15 +7,57 @@ * * Contributors: * Ericsson - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.commands; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIExpressions; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo; public class ExprMetaGetChildCount extends ExprMetaCommand { + private int numChildLimit = IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED; + public ExprMetaGetChildCount(IExpressionDMContext ctx) { super(ctx); } + + /** + * @param ctx + * @param numChildLimit + * + * @since 4.0 + */ + public ExprMetaGetChildCount(IExpressionDMContext ctx, int numChildLimit) { + super(ctx); + this.numChildLimit = numChildLimit; + } + + /** + * @since 4.0 + */ + public int getNumChildLimit() { + return numChildLimit; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + numChildLimit; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + ExprMetaGetChildCount other = (ExprMetaGetChildCount) obj; + if (numChildLimit != other.numChildLimit) + return false; + return true; + } } \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildren.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildren.java index 2c6c24e7cb9..16c1bb0a935 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildren.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildren.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2009 Ericsson and others. + * Copyright (c) 2007, 2010 Ericsson 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 @@ -7,15 +7,57 @@ * * Contributors: * Ericsson - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.commands; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIExpressions; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo; public class ExprMetaGetChildren extends ExprMetaCommand { + private int numChildLimit = IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED; + public ExprMetaGetChildren(IExpressionDMContext ctx) { super(ctx); } + + /** + * @param ctx + * @param numChildLimit + * + * @since 4.0 + */ + public ExprMetaGetChildren(IExpressionDMContext ctx, int numChildLimit) { + super(ctx); + this.numChildLimit = numChildLimit; + } + + /** + * @since 4.0 + */ + public int getNumChildLimit() { + return numChildLimit; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + numChildLimit; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + ExprMetaGetChildren other = (ExprMetaGetChildren) obj; + if (numChildLimit != other.numChildLimit) + return false; + return true; + } } \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnablePrettyPrinting.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnablePrettyPrinting.java new file mode 100644 index 00000000000..00fd6e31249 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnablePrettyPrinting.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010 Verigy 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: + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * -enable-pretty-printing + * + * Enables Python based Pretty printing + * + * @since 4.0 + */ +public class MIEnablePrettyPrinting extends MICommand +{ + /** + * @param dmc + */ + public MIEnablePrettyPrinting(ICommandControlDMContext dmc) { + super(dmc, "-enable-pretty-printing"); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoNumChildren.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoNumChildren.java index 01d77951405..6cba555ec81 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoNumChildren.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoNumChildren.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 QNX Software Systems and others. + * Copyright (c) 2000, 2010 QNX Software Systems 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 @@ -7,6 +7,7 @@ * * Contributors: * QNX Software Systems - Initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.commands; @@ -17,12 +18,15 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIVarInfoNumChildrenInfo; /** * - * -var-info-num-children NAME - * - * Returns the number of children of a variable object NAME: - * - * numchild=N + * -var-info-num-children NAME * + * Returns the number of children of a variable object NAME: + * + * numchild=N + * + * Note that this number is not completely reliable for a dynamic varobjs. It + * will return the current number of children, but more children may be + * available. */ public class MIVarInfoNumChildren extends MICommand { diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoPathExpression.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoPathExpression.java index 62ea8a37140..c5ecbdc2acb 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoPathExpression.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoPathExpression.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009 Ericsson and others. + * Copyright (c) 2009, 2010 Ericsson 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 @@ -7,6 +7,7 @@ * * Contributors: * Ericsson - Initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.commands; @@ -25,7 +26,9 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIVarInfoPathExpressionInfo * * (gdb) -var-info-path-expression C.Base.public.m_size * ^done,path_expr=((Base)c).m_size) - * + * + * Cannot be used for dynamic varobjs, or varobjs that have a dynamic varobj + * as ancestor. */ public class MIVarInfoPathExpression extends MICommand diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarListChildren.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarListChildren.java index fda2ffb856e..d2679e2967f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarListChildren.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarListChildren.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009 QNX Software Systems and others. + * Copyright (c) 2009, 2010 QNX Software Systems 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 @@ -8,6 +8,7 @@ * Contributors: * QNX Software Systems - Initial API and implementation * Ericsson - Modified for handling of frame contexts + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.commands; @@ -35,6 +36,21 @@ public class MIVarListChildren extends MICommand public MIVarListChildren(ICommandControlDMContext ctx, String name) { super(ctx, "-var-list-children", new String[]{name}); //$NON-NLS-1$ } + + /** + * @param ctx + * @param name + * @param from + * The index of the first child to be listed, if there is one + * with this index. + * @param to + * One behind the last child to be listed. + * + * @since 4.0 + */ + public MIVarListChildren(ICommandControlDMContext ctx, String name, int from, int to) { + super(ctx, "-var-list-children", new String[]{name, String.valueOf(from), String.valueOf(to)}); //$NON-NLS-1$ + } @Override public MIVarListChildrenInfo getResult(MIOutput out) { diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarSetUpdateRange.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarSetUpdateRange.java new file mode 100644 index 00000000000..0134af22921 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarSetUpdateRange.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2010 Verigy 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: + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * -var-set-update-range name from to + * + * Set the range of children to be returned by future invocations of + * -var-update. + * + * from and to indicate the range of children to + * report in subsequent -var-update call. If from or to is less than zero, the + * range is reset and all children will be reported. Otherwise, children + * starting at from (zero-based) and up to and excluding to will be reported. + * + * @since 4.0 + */ +public class MIVarSetUpdateRange extends MICommand { + + /** + * @param ctx + * @param name The name of the varobj for which the range is set. + * @param from Index of the first child to be updated with future -var-update. + * @param to One behind the last child to be updated. + */ + public MIVarSetUpdateRange(ICommandControlDMContext ctx, String name, int from, int to) { + super(ctx, "-var-set-update-range", new String[]{name, String.valueOf(from), String.valueOf(to)}); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetVarInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetVarInfo.java index 80054269978..6fc6fa6931f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetVarInfo.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetVarInfo.java @@ -7,21 +7,26 @@ * * Contributors: * Ericsson - initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.output; import org.eclipse.cdt.dsf.debug.service.command.ICommand; import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; import org.eclipse.cdt.dsf.gdb.GDBTypeParser.GDBType; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount; public class ExprMetaGetVarInfo implements ICommandResult { private final String expression; - private final int numChild; + private final int numChildHint; private final String type; private final boolean editable; private final GDBType gdbType; - + /** If true, the variable is a collection, i.e. it may have children. */ + private final boolean isCollectionHint; + private final boolean isSafeToAskForAllChildren; + public ExprMetaGetVarInfo(String e, int n, String t, boolean edit) { this (e, n, t, null, edit); } @@ -30,30 +35,92 @@ public class ExprMetaGetVarInfo implements ICommandResult { * @since 3.0 */ public ExprMetaGetVarInfo(String e, int n, String t, GDBType gt, boolean edit) { + this(e, true, n, t, gt, edit, false); + } + + /** + * @since 4.0 + */ + public ExprMetaGetVarInfo(String e, boolean isSafeToAskForAllChildren, int n, + String t, GDBType gt, boolean edit, boolean isCollectionHint) { expression = e; - numChild = n; + this.isSafeToAskForAllChildren = isSafeToAskForAllChildren; + numChildHint = n; type = t; editable = edit; gdbType = gt; + this.isCollectionHint = isCollectionHint; } public String getExpr() { return expression; } - public int getNumChildren() { return numChild; } + + /** + * This method only returns a 'hint' to the number of children. In the case + * of C++ complex structures, this number will not be the actual number of + * children. This is because GDB considers 'private/protected/public' as an + * actual level of children, but we do not. + * In case of variable backed by a pretty printer, the number represents + * only the number of currently fetched children, not all children that + * might be available. + * + * @return The hint on the number of children. + * + * @deprecated Its not possible to tell the exact number of children, but + * you can use {@link #hasChildren()} in order to find out + * whether the variable has children at all. In order to find + * out about the correct number of children, use {@link ExprMetaGetChildCount}. + */ + @Deprecated + public int getNumChildren() { return numChildHint; } + + /** + * @return Whether the variable has children or not (reliable). + * + * @since 4.0 + */ + public boolean hasChildren() { + return (numChildHint > 0); + } + public String getType() { return type; } + /** * @since 3.0 */ public GDBType getGDBType() { return gdbType; } + public boolean getEditable() { return editable; } - + + /** + * @return If true, the variable is definitely a collection, + * if false, it's most probably not. + * + * @since 4.0 + */ + public boolean getCollectionHint() { + return isCollectionHint; + } + public V getSubsetResult(ICommand command) { return null; } + /** + * @return Whether this variable can be safely ask for all its children, or + * whether clients need to specify a limit on the number of children + * to be fetched, because otherwise the gdb might hang up. + * + * @since 4.0 + */ + public boolean isSafeToAskForAllChildren() { + return isSafeToAskForAllChildren; + } + @Override public String toString() { return getClass().getSimpleName() + " (" + //$NON-NLS-1$ getExpr() + ", " + getNumChildren() + ", " + //$NON-NLS-1$ //$NON-NLS-2$ - getType() + ", " + getEditable() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + getType() + ", " + getEditable() + ", " + //$NON-NLS-1$ //$NON-NLS-2$ + getCollectionHint() + ")"; //$NON-NLS-1$ } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDisplayHint.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDisplayHint.java new file mode 100644 index 00000000000..2749dd81fc6 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDisplayHint.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2010 Verigy 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: + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * Some utilities around the display hint provided by the python pretty printers + * via MI. + * + * @since 4.0 + */ +public class MIDisplayHint { + + public static final MIDisplayHint NONE = new MIDisplayHint(GdbDisplayHint.GDB_DISPLAY_HINT_NONE, ""); //$NON-NLS-1$ + + /** + * The set of display hints that are of particular interest to DSF GDB. + */ + public enum GdbDisplayHint { + + /** + * No hint given. + */ + GDB_DISPLAY_HINT_NONE(null), + + /** + * Display an expression or variable as string. Strings don't have children. + */ + GDB_DISPLAY_HINT_STRING("string"), //$NON-NLS-1$ + + /** + * Display an expression or variable as array. + */ + GDB_DISPLAY_HINT_ARRAY("array"), //$NON-NLS-1$ + + /** + * Display an expression or variable as map. This means each child with an + * odd index is a key, each child with an even index is the corresponding + * value. + */ + GDB_DISPLAY_HINT_MAP("map"), //$NON-NLS-1$ + + /** + * A user defined hint. It has no further meaning to gdb. + */ + GDB_DISPLAY_USER_DEFINED(null); + + private final String miToken; + + private GdbDisplayHint(String miToken) { + this.miToken = miToken; + } + + /** + * @return The string that is used by MI to denote this display hint, if + * any. + */ + public String getMIToken() { + return miToken; + } + } + + private final GdbDisplayHint gdbHint; + + private final String displayHint; + + private MIDisplayHint(GdbDisplayHint gdbHint, String hint) { + this.gdbHint = gdbHint; + this.displayHint = hint; + } + + /** + * Create the hint from the given string. + * + * @param text The string representation to parse in order to initialize from. + */ + public MIDisplayHint(String text) { + gdbHint = parseDisplayHint(text); + displayHint = text.trim(); + } + + /** + * @return The display hint as returned by the pretty printer printer. + */ + public String getDisplayHint() { + return displayHint; + } + + /** + * @return One of the display hints that are of particular interest to DSF GDB. + */ + public GdbDisplayHint getGdbDisplayHint() { + return gdbHint; + } + + /** + * @return If true, the variable is definitely a collection, + * if false, it still might be a collection. + */ + public boolean isCollectionHint() { + switch(getGdbDisplayHint()) { + case GDB_DISPLAY_HINT_ARRAY: + case GDB_DISPLAY_HINT_MAP: + return true; + } + + return false; + } + + /** + * @param text + * The snipped from the MI response. + * @return The decoded display hint predefined by gdb. + */ + private static GdbDisplayHint parseDisplayHint(String text) { + + String hint = text.trim(); + + for (GdbDisplayHint gdbHint : GdbDisplayHint.values()) { + String miToken = gdbHint.getMIToken(); + if (miToken != null && miToken.equals(hint)) { + return gdbHint; + } + } + + return GdbDisplayHint.GDB_DISPLAY_USER_DEFINED; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVar.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVar.java index b38329f0a4f..d9acba86612 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVar.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVar.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 QNX Software Systems and others. + * Copyright (c) 2000, 2010 QNX Software Systems 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 @@ -8,28 +8,73 @@ * Contributors: * QNX Software Systems - Initial API and implementation * Wind River Systems - Modified for new DSF Reference Implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.output; + /** * GDB/MI var-list-children * -var-list-children var2 * ^done,numchild="6",children={child={name="var2.0",exp="0",numchild="0",type="char"},child={name="var2.1",exp="1",numchild="0",type="char"},child={name="var2.2",exp="2",numchild="0",type="char"},child={name="var2.3",exp="3",numchild="0",type="char"},child={name="var2.4",exp="4",numchild="0",type="char"},child={name="var2.5",exp="5",numchild="0",type="char"}} - * + * + * -var-list-children var3 + * ^done,numchild="3",displayhint="array",children=[child={name="var6.[0].[1]",exp="[1]",numchild="0",type="std::basic_string, std::allocator >",thread-id="1"\ +,displayhint="string",dynamic="1"},child={name="var6.[0].[2]",exp="[2]",numchild="0",type="std::basic_string, std::allocator >",thread-id="1",displayhint="string",dy\ +namic="1"},child={name="var6.[0].[3]",exp="[3]",numchild="0",type="std::basic_string, std::allocator >",thread-id="1",displayhint="string",dynamic="1"}],has_more="0"\ */ public class MIVar { String name = ""; //$NON-NLS-1$ String type = ""; //$NON-NLS-1$ String exp = ""; //$NON-NLS-1$ + private boolean isDynamic = false; int numchild; - + private boolean hasMore = false; + private MIDisplayHint displayHint = MIDisplayHint.NONE; public MIVar(String n, int num, String t) { + this(n, false, num, false, t, MIDisplayHint.NONE); + } + + /** + * @param n + * @param isDynamic + * @param num + * If isDynamic is true, the number of children currently fetched + * by gdb. + * @param hasMore + * If isDynamic is true, whether there are more children + * available than just num. + * @param t + * + * @since 4.0 + */ + public MIVar(String n, boolean isDynamic, int num, boolean hasMore, String t) { + this(n, isDynamic, num, hasMore, t, MIDisplayHint.NONE); + } + + /** + * @param n + * @param isDynamic + * @param num + * If isDynamic is true, the number of children currently fetched + * by gdb. + * @param hasMore + * If isDynamic is true, whether there are more children + * available than just num. + * @param t + * @param displayHint + * @since 4.0 + */ + public MIVar(String n, boolean isDynamic, int num, boolean hasMore, String t, MIDisplayHint displayHint) { name = n; + this.isDynamic = isDynamic; numchild = num; + this.hasMore = hasMore; type = t; + this.displayHint = displayHint; } public MIVar(MITuple tuple) { @@ -44,14 +89,52 @@ public class MIVar { return type; } + /** + * @return Whether the value and children of this variable are provided + * by a pretty printer. + * + * @since 4.0 + */ + public boolean isDynamic() { + return isDynamic; + } + + /** + * @return The number of children. If {@link #isDynamic()} returns true, + * the returned value only reflects the number of children currently + * fetched by gdb. Check {@link #hasMore()} in order to find out + * whether the are more children. + */ public int getNumChild() { return numchild; } + /** + * @return For dynamic varobjs ({@link #isDynamic() returns true} this + * method returns whether there are children in addition to the + * currently fetched, i.e. whether there are more children than + * {@link #getNumChild()} returns. + * + * @since 4.0 + */ + public boolean hasMore() { + return hasMore; + } + public String getExp() { return exp; } + /** + * @return Whether the underlying value conceptually represents a string, + * array, or map. + * + * @since 4.0 + */ + public MIDisplayHint getDisplayHint() { + return displayHint; + } + void parse(MITuple tuple) { MIResult[] results = tuple.getMIResults(); for (int i = 0; i < results.length; i++) { @@ -73,6 +156,12 @@ public class MIVar { type = str; } else if (var.equals("exp")) { //$NON-NLS-1$ exp = str; + } else if (var.equals("dynamic") && str.trim().equals("1")) { //$NON-NLS-1$ //$NON-NLS-2$ + isDynamic = true; + } else if (var.equals("has_more") && str.trim().equals("1")) { //$NON-NLS-1$ //$NON-NLS-2$ + hasMore = true; + } else if (var.equals("displayhint")) { //$NON-NLS-1$ + displayHint = new MIDisplayHint(str); } } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarChange.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarChange.java index 933354a5969..dd5c45fc9d8 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarChange.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarChange.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 QNX Software Systems and others. + * Copyright (c) 2000, 2010 QNX Software Systems 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 @@ -7,9 +7,11 @@ * * Contributors: * QNX Software Systems - Initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.output; + /** * GDB/MI var-update. */ @@ -19,7 +21,12 @@ public class MIVarChange { String value; boolean inScope; boolean changed; - + private boolean isDynamic = false; + private int newNumChildren = -1; + private boolean hasMore = false; + private MIVar[] newChildren; + private MIDisplayHint displayHint = MIDisplayHint.NONE; + public MIVarChange(String n) { name = n; } @@ -40,6 +47,64 @@ public class MIVarChange { return changed; } + /** + * @return Whether the associated variable's value and children are provided + * by a pretty printer. + * + * @since 4.0 + */ + public boolean isDynamic() { + return isDynamic; + } + + /** + * @return Whether the number of children changed since the last update. + * + * @since 4.0 + */ + public boolean numChildrenChanged() { + return (newNumChildren != -1); + } + + /** + * Only call if {@link #numChildrenChanged()} returns true. + * + * @return The new number of children the associated varobj now has already fetched. + * + * @since 4.0 + */ + public int getNewNumChildren() { + assert(newNumChildren != -1); + return newNumChildren; + } + + /** + * @return Whether there more children available than {@link #getNewNumChildren()}. + * + * @since 4.0 + */ + public boolean hasMore() { + return hasMore; + } + + /** + * @return The children added within the current update range. + * + * @since 4.0 + */ + public MIVar[] getNewChildren() { + return newChildren; + } + + /** + * @return The new display hint + * + * @since 4.0 + */ + public MIDisplayHint getDisplayHint() { + return displayHint; + } + public void setValue(String v) { value = v; } @@ -51,4 +116,41 @@ public class MIVarChange { public void setChanged(boolean c) { changed = c; } + + /** + * @since 4.0 + */ + public void setDynamic(boolean isDynamic) { + this.isDynamic = isDynamic; + } + + /** + * @since 4.0 + */ + public void setNewNumChildren(int newNumChildren) { + this.newNumChildren = newNumChildren; + } + + /** + * @since 4.0 + */ + public void setHasMore(boolean hasMore) { + this.hasMore = hasMore; + } + + /** + * @since 4.0 + */ + public void setNewChildren(MIVar[] newChildren) { + this.newChildren = newChildren; + } + + /** + * @param hint + * + * @since 4.0 + */ + public void setDisplayHint(MIDisplayHint hint) { + displayHint = hint; + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarCreateInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarCreateInfo.java index 2db675b1ab2..77bd416ffff 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarCreateInfo.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarCreateInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 QNX Software Systems and others. + * Copyright (c) 2000, 2010 QNX Software Systems 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 @@ -8,10 +8,12 @@ * Contributors: * QNX Software Systems - Initial API and implementation * Wind River Systems - Modified for new DSF Reference Implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.output; + /** * GDB/MI var-create. * -var-create "-" * a @@ -28,7 +30,10 @@ public class MIVarCreateInfo extends MIInfo { String type = ""; //$NON-NLS-1$ MIVar child; String value = null; - + private boolean isDynamic = false; + private boolean hasMore = false; + private MIDisplayHint displayHint = MIDisplayHint.NONE; + public MIVarCreateInfo(MIOutput record) { super(record); if (isDone()) { @@ -55,6 +60,12 @@ public class MIVarCreateInfo extends MIInfo { type = str; } else if (var.equals("value")) { //$NON-NLS-1$ value = str; + } else if (var.equals("dynamic") && str.trim().equals("1")) { //$NON-NLS-1$ //$NON-NLS-2$ + isDynamic = true; + } else if (var.equals("has_more") && str.trim().equals("1")) { //$NON-NLS-1$ //$NON-NLS-2$ + hasMore = true; + } else if (var.equals("displayhint")) { //$NON-NLS-1$ + displayHint = new MIDisplayHint(str); } } } @@ -66,11 +77,39 @@ public class MIVarCreateInfo extends MIInfo { return type; } + /** + * @return Whether the created variable's value and children are provided + * by a pretty printer. + * + * @since 4.0 + */ + public boolean isDynamic() { + return isDynamic; + } + + /** + * @return The number of children. If {@link #isDynamic()} returns true, + * the returned value only reflects the number of children currently + * fetched by gdb. Check {@link #hasMore()} in order to find out + * whether the are more children. + */ public int getNumChildren() { return numChild; } + /** + * @return For dynamic varobjs ({@link #isDynamic() returns true} this + * method returns whether there are children in addition to the + * currently fetched, i.e. whether there are more children than + * {@link #getNumChildren()} returns. + * + * @since 4.0 + */ + public boolean hasMore() { + return hasMore; + } + public String getName() { return name; @@ -80,10 +119,20 @@ public class MIVarCreateInfo extends MIInfo { { return value; } + + /** + * @return Whether the underlying value conceptually represents a string, + * array, or map. + * + * @since 4.0 + */ + public MIDisplayHint getDisplayHint() { + return displayHint; + } public MIVar getMIVar() { if (child == null) { - child = new MIVar(name, numChild, type); + child = new MIVar(name, isDynamic, numChild, hasMore, type, displayHint); } return child; } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarInfoNumChildrenInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarInfoNumChildrenInfo.java index 7bb832d1345..f4c2a911953 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarInfoNumChildrenInfo.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarInfoNumChildrenInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 QNX Software Systems and others. + * Copyright (c) 2000, 2010 QNX Software Systems 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 @@ -7,6 +7,7 @@ * * Contributors: * QNX Software Systems - Initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.output; @@ -14,6 +15,9 @@ package org.eclipse.cdt.dsf.mi.service.command.output; /** * GDB/MI var-info-num-children. + * + * For dynamic variable objects, only the number children currently fetched + * by gdb is returned. */ public class MIVarInfoNumChildrenInfo extends MIInfo { diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarListChildrenInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarListChildrenInfo.java index 8fe8ab79486..bc99cdcd113 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarListChildrenInfo.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarListChildrenInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 QNX Software Systems and others. + * Copyright (c) 2000, 2010 QNX Software Systems 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 @@ -7,6 +7,7 @@ * * Contributors: * QNX Software Systems - Initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.output; @@ -23,7 +24,8 @@ public class MIVarListChildrenInfo extends MIInfo { MIVar[] children; int numchild; - + private boolean hasMore = false; + public MIVarListChildrenInfo(MIOutput record) { super(record); List aList = new ArrayList(); @@ -46,7 +48,14 @@ public class MIVarListChildrenInfo extends MIInfo { } } else if (var.equals("children")) { //$NON-NLS-1$ parseChildren(value, aList); - } + } else if (var.equals("has_more")) { //$NON-NLS-1$ + if (value instanceof MIConst) { + String str = ((MIConst) value).getString(); + if (str.trim().equals("1")) { //$NON-NLS-1$ + hasMore = true; + } + } + } } } } @@ -57,6 +66,15 @@ public class MIVarListChildrenInfo extends MIInfo { return children; } + /** + * @return Whether the are more children to fetch. + * + * @since 4.0 + */ + public boolean hasMore() { + return hasMore; + } + /* * Some gdb MacOSX do not return a MITuple so we have * to check for different format. diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarUpdateInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarUpdateInfo.java index 8ae196f640a..78438adcec4 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarUpdateInfo.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarUpdateInfo.java @@ -7,6 +7,7 @@ * * Contributors: * QNX Software Systems - Initial API and implementation + * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.output; @@ -98,7 +99,45 @@ public class MIVarUpdateInfo extends MIInfo { if (change != null) { change.setChanged("true".equals(str)); //$NON-NLS-1$ } - } + } else if (var.equals("new_num_children")) { //$NON-NLS-1$ + if (change != null) { + try { + change.setNewNumChildren(Integer.parseInt(str.trim())); + } catch (NumberFormatException e) { + change.setNewNumChildren(0); + } + } + } else if (var.equals("dynamic")) { //$NON-NLS-1$ + if (change != null) { + change.setDynamic(str.trim().equals("1")); //$NON-NLS-1$ + } + } else if (var.equals("has_more")) { //$NON-NLS-1$ + if (change != null) { + change.setHasMore(str.trim().equals("1")); //$NON-NLS-1$ + } + } else if (var.equals("new_children")) { //$NON-NLS-1$ + if (change != null) { + List newChildren = new ArrayList(); + parseNewChildren(value, newChildren); + change.setNewChildren(newChildren.toArray(new MIVar[newChildren.size()])); + } + } else if (var.equals("displayhint")) { //$NON-NLS-1$ + if (change != null) { + change.setDisplayHint(new MIDisplayHint(str)); + } + } + } + } + } + + private void parseNewChildren(MIValue value, List aList) { + if (value instanceof MIList) { + MIValue[] children = ((MIList)value).getMIValues(); + + for (MIValue child : children) { + if (child instanceof MITuple) { + aList.add(new MIVar((MITuple)child)); + } } } }