1
/*******************************************************************************
2
* Copyright (c) 2004, 2007 QNX Software Systems and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* QNX Software Systems - Initial API and implementation
10
*******************************************************************************/
11
package org.eclipse.cdt.debug.internal.core.model;
13
import com.ibm.icu.text.MessageFormat;
15
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
16
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
17
import org.eclipse.cdt.debug.core.ICDebugConstants;
18
import org.eclipse.cdt.debug.core.cdi.event.ICDIChangedEvent;
19
import org.eclipse.cdt.debug.core.cdi.event.ICDIEvent;
20
import org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener;
21
import org.eclipse.cdt.debug.core.cdi.event.ICDIMemoryChangedEvent;
22
import org.eclipse.cdt.debug.core.cdi.event.ICDIResumedEvent;
23
import org.eclipse.cdt.debug.core.cdi.model.ICDIObject;
24
import org.eclipse.cdt.debug.core.cdi.model.ICDITarget;
25
import org.eclipse.cdt.debug.core.cdi.model.ICDITargetConfiguration;
26
import org.eclipse.cdt.debug.core.cdi.model.ICDITargetConfiguration2;
27
import org.eclipse.cdt.debug.core.cdi.model.ICDITargetConfiguration3;
28
import org.eclipse.cdt.debug.core.cdi.model.ICDIVariable;
29
import org.eclipse.cdt.debug.core.cdi.model.ICDIVariableDescriptor;
30
import org.eclipse.cdt.debug.core.model.CVariableFormat;
31
import org.eclipse.cdt.debug.core.model.ICDebugElementStatus;
32
import org.eclipse.cdt.debug.core.model.ICType;
33
import org.eclipse.cdt.debug.core.model.ICValue;
34
import org.eclipse.cdt.debug.internal.core.CSettingsManager;
35
import org.eclipse.core.runtime.CoreException;
36
import org.eclipse.debug.core.DebugEvent;
37
import org.eclipse.debug.core.DebugException;
38
import org.eclipse.debug.core.DebugPlugin;
39
import org.eclipse.debug.core.model.IValue;
42
* A thin wrapper over the CVariable for injection into the CDI event manager's
43
* listener collection. We used to directly inject the CVariable, but that's
44
* problematic since CVariable overrides the equals() method to base the
45
* decision on the internal variable object. So if two CVariables were added to
46
* the listener list for the same underlying value, trying to later remove one
47
* of the listeners had a 50/50 chance of removing the wrong one.
49
* How can you end up with two CVariables for the same internal variable on the
50
* listener list? Easy.
51
* 1. View a register in the Registers view.
52
* 2. Create a custom register group that contains the same register.
53
* 3. Expand the custom register group
54
* 4. Remove the custom register group
55
* Step 4 removed the wrong CVariable from the listener list.
58
class VariableEventListener implements ICDIEventListener {
59
private CVariable fVar;
60
public VariableEventListener(CVariable var) {
64
* @see org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener#handleDebugEvents(org.eclipse.cdt.debug.core.cdi.event.ICDIEvent[])
66
public void handleDebugEvents(ICDIEvent[] events) {
67
fVar.handleDebugEvents(events);
72
* Represents a variable in the CDI model.
74
public abstract class CVariable extends AbstractCVariable implements ICDIEventListener {
76
interface IInternalVariable {
77
IInternalVariable createShadow( int start, int length ) throws DebugException;
78
IInternalVariable createShadow( String type ) throws DebugException;
79
CType getType() throws DebugException;
80
String getQualifiedName() throws DebugException;
81
ICValue getValue() throws DebugException;
82
void setValue( String expression ) throws DebugException;
84
void setChanged( boolean changed );
85
void dispose( boolean destroy );
86
boolean isSameDescriptor( ICDIVariableDescriptor desc );
87
boolean isSameVariable( ICDIVariable cdiVar );
89
boolean isEditable() throws DebugException;
92
void invalidateValue();
95
// Note: the CDI object association can change; e.g., if a "Cast to Type"
96
// or "Display as Array" is done on the element.
97
ICDIObject getCdiObject();
101
* Whether this variable is currently enabled.
103
private boolean fIsEnabled = true;
106
* The original internal variable.
108
private IInternalVariable fOriginal;
111
* The shadow internal variable used for casting.
113
private IInternalVariable fShadow;
116
* The name of this variable.
118
private String fName;
121
* The current format of this variable.
123
private CVariableFormat fFormat = CVariableFormat.getFormat( CDebugCorePlugin.getDefault().getPluginPreferences().getInt( ICDebugConstants.PREF_DEFAULT_VARIABLE_FORMAT ) );
126
* Whether this variable has been disposed.
128
private boolean fIsDisposed = false;
131
* Thin wrapper for instertion into the CDI event manager's listener list
133
private VariableEventListener fEventListenerWrapper;
136
* Constructor for CVariable.
138
protected CVariable( CDebugElement parent, ICDIVariableDescriptor cdiVariableObject ) {
140
fEventListenerWrapper = new VariableEventListener(this);
141
if ( cdiVariableObject != null ) {
142
setName( cdiVariableObject.getName() );
143
createOriginal( cdiVariableObject );
145
fIsEnabled = ( parent instanceof AbstractCValue ) ? ((AbstractCValue)parent).getParentVariable().isEnabled() : !isBookkeepingEnabled();
146
getCDISession().getEventManager().addEventListener( fEventListenerWrapper );
147
if ( cdiVariableObject != null ) {
153
* Constructor for CVariable.
155
protected CVariable( CDebugElement parent, ICDIVariableDescriptor cdiVariableObject, String errorMessage ) {
157
fEventListenerWrapper = new VariableEventListener(this);
158
if ( cdiVariableObject != null ) {
159
setName( cdiVariableObject.getName() );
160
createOriginal( cdiVariableObject );
162
fIsEnabled = !isBookkeepingEnabled();
163
setStatus( ICDebugElementStatus.ERROR, MessageFormat.format( CoreModelMessages.getString( "CVariable.1" ), (Object[])new String[]{ errorMessage } ) ); //$NON-NLS-1$
164
getCDISession().getEventManager().addEventListener( fEventListenerWrapper );
165
if ( cdiVariableObject != null ) {
173
* @see org.eclipse.cdt.debug.core.model.ICVariable#getType()
175
public ICType getType() throws DebugException {
178
IInternalVariable iv = getCurrentInternalVariable();
179
return ( iv != null ) ? iv.getType() : null;
185
* @see org.eclipse.cdt.debug.core.model.ICVariable#isEnabled()
187
public boolean isEnabled() {
194
* @see org.eclipse.cdt.debug.core.model.ICVariable#setEnabled(boolean)
196
public void setEnabled( boolean enabled ) throws DebugException {
197
// Debugger engines that use active variable objects will benefit
198
// performance-wise if we dispose the internal variable when it's
199
// disabled by the user (it will automatically get lazily recreated if
200
// it's ever needed again). Engines using passive variables probably
201
// won't, so we can defer the dispose until we have no use for the
202
// variable altogether.
203
boolean disposeVariable = true;
204
ICDITargetConfiguration configuration = getParent().getCDITarget().getConfiguration();
205
if (configuration instanceof ICDITargetConfiguration2) {
206
disposeVariable = !((ICDITargetConfiguration2)configuration).supportsPassiveVariableUpdate();
208
if (disposeVariable) {
209
IInternalVariable iv = getOriginal();
216
fIsEnabled = enabled;
217
fireChangeEvent( DebugEvent.STATE );
223
* @see org.eclipse.cdt.debug.core.model.ICVariable#canEnableDisable()
225
public boolean canEnableDisable() {
226
return !( getParent() instanceof IValue );
232
* @see org.eclipse.cdt.debug.core.model.ICVariable#isArgument()
234
public boolean isArgument() {
235
IInternalVariable iv = getOriginal();
236
return ( iv != null ) ? iv.isArgument() : false;
242
* @see org.eclipse.debug.core.model.IVariable#getValue()
244
public IValue getValue() throws DebugException {
245
if ( !isDisposed() && isEnabled() ) {
246
IInternalVariable iv = getCurrentInternalVariable();
249
return iv.getValue();
251
catch( DebugException e ) {
252
setStatus( ICDebugElementStatus.ERROR, e.getMessage() );
256
return CValueFactory.NULL_VALUE;
262
* @see org.eclipse.debug.core.model.IVariable#getName()
264
public String getName() throws DebugException {
271
* @see org.eclipse.debug.core.model.IVariable#getReferenceTypeName()
273
public String getReferenceTypeName() throws DebugException {
274
ICType type = getType();
275
return ( type != null ) ? type.getName() : ""; //$NON-NLS-1$
281
* @see org.eclipse.debug.core.model.IVariable#hasValueChanged()
283
public boolean hasValueChanged() throws DebugException {
286
IInternalVariable iv = getCurrentInternalVariable();
287
return ( iv != null ) ? iv.isChanged() : false;
293
* @see org.eclipse.cdt.debug.core.model.IFormatSupport#supportsFormatting()
295
public boolean supportsFormatting() {
302
* @see org.eclipse.cdt.debug.core.model.IFormatSupport#getFormat()
304
public CVariableFormat getFormat() {
311
* @see org.eclipse.cdt.debug.core.model.IFormatSupport#changeFormat(org.eclipse.cdt.debug.core.model.CVariableFormat)
313
public void changeFormat( CVariableFormat format ) throws DebugException {
315
storeFormat( format );
321
* Allow this operation only for the pointer types (???).
323
* @see org.eclipse.cdt.debug.core.model.ICastToArray#canCastToArray()
325
public boolean canCastToArray() {
329
return ( getOriginal() != null && isEnabled() && type != null && type.isPointer() );
331
catch( DebugException e ) {
339
* @see org.eclipse.cdt.debug.core.model.ICastToArray#castToArray(int, int)
341
public void castToArray( int startIndex, int length ) throws DebugException {
342
IInternalVariable current = getCurrentInternalVariable();
343
if ( current != null ) {
344
IInternalVariable newVar = current.createShadow( startIndex, length );
345
if ( getShadow() != null )
346
getShadow().dispose( true );
348
// If casting of variable to a type or array causes an error, the status
349
// of the variable is set to "error" and it can't be reset by subsequent castings.
351
storeCastToArray( startIndex, length );
358
* @see org.eclipse.debug.core.model.IValueModification#setValue(java.lang.String)
360
public void setValue( String expression ) throws DebugException {
361
IInternalVariable iv = getCurrentInternalVariable();
363
String newExpression = processExpression( expression );
364
iv.setValue( newExpression );
371
* @see org.eclipse.debug.core.model.IValueModification#setValue(org.eclipse.debug.core.model.IValue)
373
public void setValue( IValue value ) throws DebugException {
374
notSupported( CoreModelMessages.getString( "CVariable.3" ) ); //$NON-NLS-1$
380
* @see org.eclipse.debug.core.model.IValueModification#supportsValueModification()
382
public boolean supportsValueModification() {
384
return fIsEnabled ? getCurrentInternalVariable().isEditable() : false;
386
catch( DebugException e ) {
394
* @see org.eclipse.debug.core.model.IValueModification#verifyValue(java.lang.String)
396
public boolean verifyValue( String expression ) throws DebugException {
403
* @see org.eclipse.debug.core.model.IValueModification#verifyValue(org.eclipse.debug.core.model.IValue)
405
public boolean verifyValue( IValue value ) throws DebugException {
406
return value.getDebugTarget().equals( getDebugTarget() );
412
* @see org.eclipse.cdt.debug.core.model.ICastToType#canCast()
414
public boolean canCast() {
415
return ( getOriginal() != null && isEnabled() );
421
* @see org.eclipse.cdt.debug.core.model.ICastToType#getCurrentType()
423
public String getCurrentType() {
424
String typeName = ""; //$NON-NLS-1$
426
typeName = getReferenceTypeName();
428
catch( DebugException e ) {
436
* @see org.eclipse.cdt.debug.core.model.ICastToType#cast(java.lang.String)
438
public void cast( String type ) throws DebugException {
439
IInternalVariable current = getCurrentInternalVariable();
440
if ( current != null ) {
441
IInternalVariable newVar = current.createShadow( type );
442
if ( getShadow() != null )
443
getShadow().dispose( true );
445
// If casting of variable to a type or array causes an error, the status
446
// of the variable is set to "error" and it can't be reset by subsequent castings.
455
* @see org.eclipse.cdt.debug.core.model.ICastToType#restoreOriginal()
457
public void restoreOriginal() throws DebugException {
458
IInternalVariable oldVar = getShadow();
460
if ( oldVar != null )
461
oldVar.dispose( true );
462
IInternalVariable iv = getOriginal();
464
iv.invalidateValue();
465
// If casting of variable to a type or array causes an error, the status
466
// of the variable is set to "error" and it can't be reset by subsequent castings.
475
* @see org.eclipse.cdt.debug.core.model.ICastToType#isCasted()
477
public boolean isCasted() {
478
return ( getShadow() != null );
484
* @see org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener#handleDebugEvents(org.eclipse.cdt.debug.core.cdi.event.ICDIEvent[])
486
public void handleDebugEvents( ICDIEvent[] events ) {
487
IInternalVariable iv = getCurrentInternalVariable();
490
for( int i = 0; i < events.length; i++ ) {
491
ICDIEvent event = events[i];
492
ICDIObject source = event.getSource();
493
if ( source == null )
495
ICDITarget target = source.getTarget();
496
if ( target.equals( getCDITarget() ) ) {
497
if ( event instanceof ICDIMemoryChangedEvent &&
498
target.getConfiguration() instanceof ICDITargetConfiguration3 &&
499
((ICDITargetConfiguration3)target.getConfiguration()).needsVariablesUpdated(event)) {
502
else if ( event instanceof ICDIChangedEvent ) {
503
if ( source instanceof ICDIVariable && iv.isSameVariable( (ICDIVariable)source ) ) {
504
handleChangedEvent( (ICDIChangedEvent)event );
507
else if ( event instanceof ICDIResumedEvent ) {
508
handleResumedEvent( (ICDIResumedEvent)event );
514
private void handleResumedEvent( ICDIResumedEvent event ) {
515
boolean changed = false;
519
IInternalVariable iv = getCurrentInternalVariable();
521
iv.invalidateValue();
524
fireChangeEvent( DebugEvent.STATE );
527
private void handleChangedEvent( ICDIChangedEvent event ) {
528
IInternalVariable iv = getCurrentInternalVariable();
530
iv.setChanged( true );
531
fireChangeEvent( DebugEvent.STATE );
535
private IInternalVariable getCurrentInternalVariable() {
536
if ( getShadow() != null )
538
return getOriginal();
541
private IInternalVariable getOriginal() {
545
protected void setOriginal( IInternalVariable original ) {
546
fOriginal = original;
549
private IInternalVariable getShadow() {
553
private void setShadow( IInternalVariable shadow ) {
557
protected boolean isBookkeepingEnabled() {
558
boolean result = false;
560
result = getLaunch().getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ENABLE_VARIABLE_BOOKKEEPING, false );
562
catch( CoreException e ) {
567
abstract protected void createOriginal( ICDIVariableDescriptor vo );
569
protected boolean hasErrors() {
574
* @see org.eclipse.cdt.debug.internal.core.model.AbstractCVariable#setChanged(boolean)
577
protected void setChanged( boolean changed ) {
578
IInternalVariable iv = getCurrentInternalVariable();
580
iv.setChanged( changed );
585
* @see org.eclipse.cdt.debug.internal.core.model.AbstractCVariable#resetValue()
588
protected void resetValue() {
589
IInternalVariable iv = getCurrentInternalVariable();
593
fireChangeEvent( DebugEvent.STATE );
597
private String processExpression( String oldExpression ) {
598
return oldExpression;
602
* @see org.eclipse.cdt.debug.internal.core.model.AbstractCVariable#dispose()
605
public void dispose() {
606
// Hack: do not destroy local variables
607
internalDispose( false );
611
public int sizeof() {
612
IInternalVariable iv = getCurrentInternalVariable();
613
return ( iv != null ) ? iv.sizeof() : -1;
617
* Compares the original internal variables.
618
* @see java.lang.Object#equals(java.lang.Object)
621
public boolean equals( Object obj ) {
622
if ( obj instanceof CVariable ) {
623
// A disposed copy can be stored in the viewer.
624
// false should be returned to force the viewer to
625
// replace it by a new variable. See bug #115385
626
if ( isDisposed() != ((CVariable)obj).isDisposed() )
628
IInternalVariable iv = getOriginal();
629
return ( iv != null ) ? iv.equals( ((CVariable)obj).getOriginal() ) : false;
634
protected boolean sameVariable( ICDIVariableDescriptor vo ) {
635
IInternalVariable iv = getOriginal();
636
return ( iv != null && iv.isSameDescriptor( vo ) );
639
protected void setFormat( CVariableFormat format ) {
644
* @see org.eclipse.cdt.debug.core.model.ICVariable#getExpressionString()
646
public String getExpressionString() throws DebugException {
647
IInternalVariable iv = getCurrentInternalVariable();
648
return ( iv != null ) ? iv.getQualifiedName() : null;
652
* @see org.eclipse.cdt.debug.internal.core.model.AbstractCVariable#preserve()
655
protected void preserve() {
657
IInternalVariable iv = getCurrentInternalVariable();
662
protected void internalDispose( boolean destroy ) {
663
getCDISession().getEventManager().removeEventListener( fEventListenerWrapper );
664
IInternalVariable iv = getOriginal();
666
iv.dispose( destroy );
669
iv.dispose( destroy );
672
protected boolean isDisposed() {
676
protected void setDisposed( boolean isDisposed ) {
677
fIsDisposed = isDisposed;
680
protected void invalidateValue() {
682
IInternalVariable iv = getCurrentInternalVariable();
684
iv.invalidateValue();
687
protected void setName( String name ) {
691
public ICDIObject getCdiObject() {
692
IInternalVariable iv = getCurrentInternalVariable();
694
return iv.getCdiObject();
699
protected CSettingsManager getFormatManager() {
700
return ((CDebugTarget) getDebugTarget()).getFormatManager();
704
* used to concatenate multiple names to a single identifier
706
private final static String NAME_PART_SEPARATOR = "-"; //$NON-NLS-1$
709
* suffix used to identify format informations
711
private final static String FORMAT_SUFFIX = NAME_PART_SEPARATOR + "(format)"; //$NON-NLS-1$
714
* suffix used to identify cast settings
716
private final static String CAST_SUFFIX = NAME_PART_SEPARATOR + "(cast)"; //$NON-NLS-1$
719
* suffix used to identify cast to array settings
721
private final static String CAST_TO_ARRAY_SUFFIX = NAME_PART_SEPARATOR + "(cast_to_array)"; //$NON-NLS-1$
723
/** retrieve the identification for this variable.
724
* @return a string identifying this variable, to be used to store settings
725
* @throws DebugException
727
String getVariableID() throws DebugException {
728
return getName(); // TODO: better identification if multiple variables have the same name
731
/** helper to generate a string id used to persist the settings.
732
* @param next_obj next object to encode into the id
733
* @param buf contains the id of the part encoded so far.
734
* @throws DebugException
736
static private void buildPesistID( CDebugElement next_obj, StringBuffer buf ) throws DebugException {
737
if ( next_obj instanceof CVariable ) {
738
CVariable cVariableParent = (CVariable) next_obj;
739
buf.append( NAME_PART_SEPARATOR );
740
buf.append( cVariableParent.getVariableID() );
741
buildPesistID( cVariableParent.getParent(), buf );
742
} else if ( next_obj instanceof CStackFrame ) {
743
buf.append(NAME_PART_SEPARATOR);
744
// TODO: better identification if multiple functions have the same name (say for static functions)
745
buf.append( ((CStackFrame)next_obj ).getFunction() );
746
} else if ( next_obj instanceof CDebugTarget ) {
747
// global, we use a root NAME_PART_SEPARATOR as indicator of that
748
buf.append( NAME_PART_SEPARATOR );
749
} else if ( next_obj instanceof AbstractCValue ) {
750
// index or indirection.
751
AbstractCValue av = (AbstractCValue) next_obj;
752
buildPesistID( av.getParentVariable(), buf );
756
/** returns an string used to identify this variable
758
* @throws DebugException
760
private final String getPersistID() throws DebugException {
761
StringBuffer id = new StringBuffer();
762
id.append( getVariableID() );
763
buildPesistID( getParent(), id );
764
return id.toString();
767
/** stores the given format
768
* @param format the format to be used for this variable
770
protected void storeFormat( CVariableFormat format ) {
772
String formatString = Integer.toString( format.getFormatNumber() );
774
getFormatManager().putValue( getPersistID() + FORMAT_SUFFIX, formatString );
775
} catch ( DebugException e ) {
776
// if we do not get the name, we use the default format, no reason for the creation to fail too.
777
DebugPlugin.log( e );
781
/** stores the cast information.
782
* @param type the type to be displayed instead
784
protected void storeCast( String type ) {
786
String id = getPersistID() + CAST_SUFFIX;
787
getFormatManager().putValue( id, type );
788
} catch ( DebugException e ) {
789
DebugPlugin.log( e );
793
/** drops the cast information.
795
protected void forgetCast() {
797
String id = getPersistID() + CAST_SUFFIX;
798
getFormatManager().removeValue( id );
799
} catch ( DebugException e ) {
800
DebugPlugin.log( e );
804
/** stores the cast array information.
805
* @param startIndex the first item to be displayed in the cast array operation
806
* @param length the number of elements to display
808
protected void storeCastToArray(int startIndex, int length) {
810
// we persist the information in a (startIndex):(Length) format.
811
String content = Integer.toString( startIndex ) + ":" + Integer.toString( length ); //$NON-NLS-1$
812
getFormatManager().putValue( getPersistID() + CAST_TO_ARRAY_SUFFIX, content );
813
} catch ( DebugException e ) {
814
DebugPlugin.log( e );
818
/** drops previously stored cast array information.
820
protected void forgetCastToArray() {
822
String id = getPersistID() + CAST_TO_ARRAY_SUFFIX;
823
getFormatManager().removeValue( id );
824
} catch ( DebugException e ) {
825
DebugPlugin.log( e );
830
* restore the format stored previously for this variable.
831
* Only sets explicitly retrieved formats in order to maintain defaults.
833
protected void setInitialFormat() {
835
String persistID= getPersistID();
836
String stringFormat = getFormatManager().getValue( persistID + FORMAT_SUFFIX );
837
if ( stringFormat != null ) {
839
CVariableFormat format = CVariableFormat.getFormat( Integer.parseInt( stringFormat ) );
841
} catch ( NumberFormatException e ) {
842
DebugPlugin.log( e );
847
String castString = getFormatManager().getValue( persistID + CAST_SUFFIX );
848
if ( castString != null ) {
852
if ( canCastToArray() ) {
853
String castToArrayString = getFormatManager().getValue( persistID + CAST_TO_ARRAY_SUFFIX );
854
if (castToArrayString != null) {
855
int index = castToArrayString.indexOf( ':' );
858
int beg = Integer.parseInt( castToArrayString.substring( 0, index ) );
859
int num = Integer.parseInt( castToArrayString.substring( index + 1 ) );
860
castToArray( beg, num );
861
} catch ( NumberFormatException e ) {
862
DebugPlugin.log( e );
865
DebugPlugin.logMessage( "did not find expected : for cast to array", null ); //$NON-NLS-1$
869
} catch ( DebugException e ) {
870
DebugPlugin.log( e );
871
// we drop (and log) the exception here.
872
// even if the initial setup fails, we still want the complete creation to be successful