Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 298883 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java (-7 / +7 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2008, 2009  QNX Software Systems and others.
2
 * Copyright (c) 2010 QNX Software Systems and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 9-14 Link Here
9
 * QNX Software Systems   - Initial API and implementation
9
 * QNX Software Systems   - Initial API and implementation
10
 * Windriver and Ericsson - Updated for DSF
10
 * Windriver and Ericsson - Updated for DSF
11
 * IBM Corporation 
11
 * IBM Corporation 
12
 * Ericsson               - Added support for Mac OS
12
 *******************************************************************************/
13
 *******************************************************************************/
13
package org.eclipse.cdt.dsf.gdb.launching; 
14
package org.eclipse.cdt.dsf.gdb.launching; 
14
15
Lines 382-387 Link Here
382
	}
383
	}
383
	
384
	
384
	private boolean isNonStopSupported(String version) {
385
	private boolean isNonStopSupported(String version) {
386
		if (version.startsWith(LaunchUtils.MACOS_GDB_PREFIX)) {
387
			// Mac OS's GDB does not support Non-Stop
388
			return false;
389
		}
390
		
385
		if (NON_STOP_FIRST_VERSION.compareTo(version) <= 0) {
391
		if (NON_STOP_FIRST_VERSION.compareTo(version) <= 0) {
386
			return true;
392
			return true;
387
		}
393
		}
Lines 395-406 Link Here
395
			return new GdbDebugServicesFactoryNS(version);
401
			return new GdbDebugServicesFactoryNS(version);
396
		}
402
		}
397
403
398
		if (version.startsWith("6.6") ||  //$NON-NLS-1$
399
			version.startsWith("6.7") ||  //$NON-NLS-1$
400
			version.startsWith("6.8")) {  //$NON-NLS-1$
401
			return new GdbDebugServicesFactory(version);
402
		}
403
404
		return new GdbDebugServicesFactory(version);
404
		return new GdbDebugServicesFactory(version);
405
	}
405
	}
406
}
406
}
(-)src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java (-2 / +29 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2008 Ericsson and others.
2
 * Copyright (c) 2010 Ericsson and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 * Ericsson   - Initial API and implementation
9
 * Ericsson   - Initial API and implementation
10
 * Ericsson   - Added support for Mac OS
10
 *******************************************************************************/
11
 *******************************************************************************/
11
package org.eclipse.cdt.dsf.gdb.launching;
12
package org.eclipse.cdt.dsf.gdb.launching;
12
13
Lines 43-48 Link Here
43
44
44
public class LaunchUtils {
45
public class LaunchUtils {
45
46
47
	/**
48
	 * A prefix that we use to indicate that a GDB version is for MAC OS
49
	 * @since 2.1
50
	 */
51
	public static final String MACOS_GDB_PREFIX = "APPLE"; //$NON-NLS-1$
52
	
46
   	/**
53
   	/**
47
	 * Verify the following things about the project:
54
	 * Verify the following things about the project:
48
	 * - is a valid project name given
55
	 * - is a valid project name given
Lines 212-218 Link Here
212
     */
219
     */
213
	public static String getGDBVersionFromText(String versionOutput) {
220
	public static String getGDBVersionFromText(String versionOutput) {
214
        String version = "";//$NON-NLS-1$
221
        String version = "";//$NON-NLS-1$
215
        
222
223
        // First look for the case of Apple's GDB, since the version must be handled differently
224
        // The format is:
225
        // GNU gdb 6.3.50-20050815 (Apple version gdb-696) (Sat Oct 20 18:20:28 GMT 2007)
226
        // GNU gdb 6.3.50-20050815 (Apple version gdb-966) (Tue Mar 10 02:43:13 UTC 2009)
227
        // GNU gdb 6.3.50-20050815 (Apple version gdb-1346) (Fri Sep 18 20:40:51 UTC 2009)
228
        // Also, it seems the version that changes is the "Apple version" so that is the
229
        // one we will extract.  The normal GDB version is fixed and won't help us.
230
        if (versionOutput.toLowerCase().indexOf("apple") != -1) {  //$NON-NLS-1$
231
        	// Add a prefix to indicate we are dealing with an Apple GDB
232
        	version = MACOS_GDB_PREFIX;
233
    		Pattern pattern = Pattern.compile(" \\(Apple version gdb-(\\d*)\\)",  Pattern.MULTILINE); //$NON-NLS-1$
234
235
    		Matcher matcher = pattern.matcher(versionOutput);
236
    		if (matcher.find()) {
237
    			version += matcher.group(1);
238
    		}
239
    		
240
    		return version;
241
        }
242
        		
216
		// These are the GDB version patterns I have seen up to now
243
		// These are the GDB version patterns I have seen up to now
217
		// The pattern works for all of them extracting the version of 6.8.50.20080730
244
		// The pattern works for all of them extracting the version of 6.8.50.20080730
218
		// GNU gdb 6.8.50.20080730
245
		// GNU gdb 6.8.50.20080730
(-)src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java (-5 / +72 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2008 Ericsson and others.
2
 * Copyright (c) 2010 Ericsson and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 7-13 Link Here
7
 * 
7
 * 
8
 * Contributors:
8
 * Contributors:
9
 *     Ericsson - initial API and implementation
9
 *     Ericsson - initial API and implementation
10
 *     Nokia - create and use backend service. 
10
 *     Nokia    - create and use backend service.
11
 *     Ericsson - Added support for Mac OS
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.cdt.dsf.gdb.service;
13
package org.eclipse.cdt.dsf.gdb.service;
13
14
Lines 24-37 Link Here
24
import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
25
import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
25
import org.eclipse.cdt.dsf.debug.service.IStack;
26
import org.eclipse.cdt.dsf.debug.service.IStack;
26
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
27
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
28
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
27
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl;
29
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl;
28
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_0;
30
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_0;
31
import org.eclipse.cdt.dsf.gdb.service.macos.MacOSGDBExpressions;
29
import org.eclipse.cdt.dsf.mi.service.CSourceLookup;
32
import org.eclipse.cdt.dsf.mi.service.CSourceLookup;
30
import org.eclipse.cdt.dsf.mi.service.MIExpressions;
31
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
33
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
32
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints;
34
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints;
33
import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager;
35
import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager;
34
import org.eclipse.cdt.dsf.mi.service.MIDisassembly;
36
import org.eclipse.cdt.dsf.mi.service.MIDisassembly;
37
import org.eclipse.cdt.dsf.mi.service.MIExpressions;
35
import org.eclipse.cdt.dsf.mi.service.MIMemory;
38
import org.eclipse.cdt.dsf.mi.service.MIMemory;
36
import org.eclipse.cdt.dsf.mi.service.MIModules;
39
import org.eclipse.cdt.dsf.mi.service.MIModules;
37
import org.eclipse.cdt.dsf.mi.service.MIRegisters;
40
import org.eclipse.cdt.dsf.mi.service.MIRegisters;
Lines 44-53 Link Here
44
	// This should eventually be "7.0" once GDB 7.0 is released
47
	// This should eventually be "7.0" once GDB 7.0 is released
45
	private static final String GDB_7_0_VERSION = "6.8.50.20090218"; //$NON-NLS-1$
48
	private static final String GDB_7_0_VERSION = "6.8.50.20090218"; //$NON-NLS-1$
46
	
49
	
50
	// Contains the GDB version string such as
51
	// 7.0
52
	// 6.8.50.20090218
53
	// For MacOS, it contains the Apple version number, such as
54
	// 969
55
	// 1465
47
	private final String fVersion;
56
	private final String fVersion;
48
	
57
	
58
	// Indicate if we are dealing with a Mac OS GDB
59
	private boolean fIsMacOSGdb = false;
60
	
49
	public GdbDebugServicesFactory(String version) {
61
	public GdbDebugServicesFactory(String version) {
50
		fVersion = version;
62
		if (version.startsWith(LaunchUtils.MACOS_GDB_PREFIX)) {
63
			fIsMacOSGdb = true;
64
			fVersion = version.substring(LaunchUtils.MACOS_GDB_PREFIX.length());
65
		} else {
66
			fVersion = version;
67
		}
51
	}
68
	}
52
	
69
	
53
	public String getVersion() { return fVersion; }
70
	public String getVersion() { return fVersion; }
Lines 73-88 Link Here
73
			}
90
			}
74
		}
91
		}
75
92
76
77
        return super.createService(clazz, session);
93
        return super.createService(clazz, session);
78
	}
94
	}
79
95
80
	protected MIBreakpointsManager createBreakpointManagerService(DsfSession session) {
96
	protected MIBreakpointsManager createBreakpointManagerService(DsfSession session) {
97
		if (fIsMacOSGdb) {
98
			return new MIBreakpointsManager(session, CDebugCorePlugin.PLUGIN_ID);
99
		}
81
		return new MIBreakpointsManager(session, CDebugCorePlugin.PLUGIN_ID);
100
		return new MIBreakpointsManager(session, CDebugCorePlugin.PLUGIN_ID);
82
	}
101
	}
83
102
84
	@Override
103
	@Override
85
	protected IBreakpoints createBreakpointService(DsfSession session) {
104
	protected IBreakpoints createBreakpointService(DsfSession session) {
105
		if (fIsMacOSGdb) {
106
			return new MIBreakpoints(session);
107
		}
108
		
86
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
109
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
87
			return new GDBBreakpoints_7_0(session);
110
			return new GDBBreakpoints_7_0(session);
88
		}
111
		}
Lines 90-95 Link Here
90
	}
113
	}
91
	
114
	
92
	protected ICommandControl createCommandControl(DsfSession session, ILaunchConfiguration config) {
115
	protected ICommandControl createCommandControl(DsfSession session, ILaunchConfiguration config) {
116
		if (fIsMacOSGdb) {
117
			return new GDBControl(session, config);
118
		}
119
		
93
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
120
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
94
			return new GDBControl_7_0(session, config);
121
			return new GDBControl_7_0(session, config);
95
		}
122
		}
Lines 97-117 Link Here
97
	}
124
	}
98
125
99
	protected IMIBackend createBackendGDBService(DsfSession session, ILaunchConfiguration lc) {
126
	protected IMIBackend createBackendGDBService(DsfSession session, ILaunchConfiguration lc) {
127
		if (fIsMacOSGdb) {
128
			return new GDBBackend(session, lc);
129
		}
130
		
100
		return new GDBBackend(session, lc);
131
		return new GDBBackend(session, lc);
101
	}
132
	}
102
133
103
	@Override
134
	@Override
104
	protected IDisassembly createDisassemblyService(DsfSession session) {
135
	protected IDisassembly createDisassemblyService(DsfSession session) {
136
		if (fIsMacOSGdb) {
137
			return new MIDisassembly(session);
138
		}
139
		
105
		return new MIDisassembly(session);
140
		return new MIDisassembly(session);
106
	}
141
	}
107
	
142
	
108
	@Override
143
	@Override
109
	protected IExpressions createExpressionService(DsfSession session) {
144
	protected IExpressions createExpressionService(DsfSession session) {
145
		if (fIsMacOSGdb) {
146
			return new MacOSGDBExpressions(session);
147
		}
148
		
110
		return new MIExpressions(session);
149
		return new MIExpressions(session);
111
	}
150
	}
112
151
113
	@Override
152
	@Override
114
	protected IMemory createMemoryService(DsfSession session) {
153
	protected IMemory createMemoryService(DsfSession session) {
154
		if (fIsMacOSGdb) {
155
			return new MIMemory(session);
156
		}
157
		
115
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
158
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
116
			return new GDBMemory_7_0(session);
159
			return new GDBMemory_7_0(session);
117
		}
160
		}
Lines 121-131 Link Here
121
164
122
	@Override
165
	@Override
123
	protected IModules createModulesService(DsfSession session) {
166
	protected IModules createModulesService(DsfSession session) {
167
		if (fIsMacOSGdb) {
168
			return new MIModules(session);
169
		}
170
		
124
		return new MIModules(session);
171
		return new MIModules(session);
125
	}
172
	}
126
		
173
		
127
	@Override
174
	@Override
128
	protected IProcesses createProcessesService(DsfSession session) {
175
	protected IProcesses createProcessesService(DsfSession session) {
176
		if (fIsMacOSGdb) {
177
			return new GDBProcesses(session);
178
		}
179
		
129
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
180
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
130
			return new GDBProcesses_7_0(session);
181
			return new GDBProcesses_7_0(session);
131
		}
182
		}
Lines 134-144 Link Here
134
185
135
	@Override
186
	@Override
136
	protected IRegisters createRegistersService(DsfSession session) {
187
	protected IRegisters createRegistersService(DsfSession session) {
188
		if (fIsMacOSGdb) {
189
			return new MIRegisters(session);
190
		}
191
		
137
		return new MIRegisters(session);
192
		return new MIRegisters(session);
138
	}
193
	}
139
194
140
	@Override
195
	@Override
141
	protected IRunControl createRunControlService(DsfSession session) {
196
	protected IRunControl createRunControlService(DsfSession session) {
197
		if (fIsMacOSGdb) {
198
			return new GDBRunControl(session);
199
		}
200
		
142
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
201
		if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
143
			return new GDBRunControl_7_0(session);
202
			return new GDBRunControl_7_0(session);
144
		}
203
		}
Lines 147-157 Link Here
147
206
148
	@Override
207
	@Override
149
	protected ISourceLookup createSourceLookupService(DsfSession session) {
208
	protected ISourceLookup createSourceLookupService(DsfSession session) {
209
		if (fIsMacOSGdb) {
210
			return new CSourceLookup(session);
211
		}
212
		
150
		return new CSourceLookup(session);
213
		return new CSourceLookup(session);
151
	}
214
	}
152
	
215
	
153
	@Override
216
	@Override
154
	protected IStack createStackService(DsfSession session) {
217
	protected IStack createStackService(DsfSession session) {
218
		if (fIsMacOSGdb) {
219
			return new MIStack(session);
220
		}
221
		
155
		return new MIStack(session);
222
		return new MIStack(session);
156
	}
223
	}
157
}
224
}
(-)src/org/eclipse/cdt/dsf/gdb/service/macos/MacOSGDBExpressions.java (+895 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2010 Wind River 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
7
 * 
8
 * Contributors:
9
 *     Wind River Systems - initial API and implementation
10
 *     Ericsson 		  - Modified for handling of multiple execution contexts
11
 *     Ericsson           - Created the MacOS version
12
 *******************************************************************************/
13
package org.eclipse.cdt.dsf.gdb.service.macos;
14
15
import java.util.HashMap;
16
import java.util.Hashtable;
17
import java.util.Map;
18
19
import org.eclipse.cdt.core.IAddress;
20
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
21
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
22
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
23
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
24
import org.eclipse.cdt.dsf.datamodel.DMContexts;
25
import org.eclipse.cdt.dsf.datamodel.IDMContext;
26
import org.eclipse.cdt.dsf.debug.service.ICachingService;
27
import org.eclipse.cdt.dsf.debug.service.IExpressions;
28
import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
29
import org.eclipse.cdt.dsf.debug.service.IRunControl;
30
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent;
31
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
32
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
33
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
34
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
35
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
36
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
37
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
38
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
39
import org.eclipse.cdt.dsf.mi.service.MIStack;
40
import org.eclipse.cdt.dsf.mi.service.MIExpressions.ExpressionInfo;
41
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetAttributes;
42
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount;
43
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildren;
44
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetValue;
45
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetVar;
46
import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataEvaluateExpression;
47
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo;
48
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo;
49
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo;
50
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo;
51
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo;
52
import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo;
53
import org.eclipse.cdt.dsf.service.AbstractDsfService;
54
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
55
import org.eclipse.cdt.dsf.service.DsfSession;
56
import org.eclipse.cdt.utils.Addr32;
57
import org.eclipse.cdt.utils.Addr64;
58
import org.eclipse.core.runtime.IStatus;
59
import org.eclipse.core.runtime.Status;
60
import org.osgi.framework.BundleContext;
61
62
/**
63
 * This class implements a debugger expression evaluator as a DSF service. The
64
 * primary interface that clients of this class should use is IExpressions.
65
 * 
66
 * This class used to be name ExpressionService in the 1.1 release.
67
 * 
68
 * @since 2.1
69
 */
70
public class MacOSGDBExpressions extends AbstractDsfService implements IExpressions, ICachingService {
71
72
    /**
73
     * This class represents an expression.
74
     */
75
    protected static class MIExpressionDMC extends AbstractDMContext implements IExpressionDMContext {
76
        /**
77
         * This field holds an expression to be evaluated.
78
         */
79
    	private ExpressionInfo exprInfo;
80
81
        /**
82
         * ExpressionDMC Constructor for expression to be evaluated in context of 
83
         * a stack frame.
84
         * 
85
         * @param sessionId
86
         *            The session ID in which this context is created.
87
         * @param expression
88
         *            The expression to be described by this ExpressionDMC
89
         * @param relExpr
90
         *            The relative expression if this expression was created as a child
91
         * @param frameCtx
92
         *            The parent stack frame context for this ExpressionDMC. 
93
         */
94
        public MIExpressionDMC(String sessionId, String expression, String relExpr, IFrameDMContext frameCtx) {
95
            this(sessionId, expression, relExpr, (IDMContext)frameCtx);
96
        }
97
98
       /**
99
         * ExpressionDMC Constructor for expression to be evaluated in context of 
100
         * an thread.
101
         * 
102
         * @param sessionId
103
         *            The session ID in which this context is created.
104
         * @param expression
105
         *            The expression to be described by this ExpressionDMC
106
         * @param relExpr
107
         *            The relative expression if this expression was created as a child
108
         * @param execCtx
109
         *            The parent thread context for this ExpressionDMC. 
110
         */
111
        public MIExpressionDMC(String sessionId, String expression, String relExpr, IMIExecutionDMContext execCtx) {
112
            this(sessionId, expression, relExpr, (IDMContext)execCtx);
113
        }
114
115
        /**
116
         * ExpressionDMC Constructor for expression to be evaluated in context of 
117
         * a memory space.
118
         * 
119
         * @param sessionId
120
         *            The session ID in which this context is created.
121
         * @param expression
122
         *            The expression to be described by this ExpressionDMC
123
         * @param relExpr
124
         *            The relative expression if this expression was created as a child
125
         * @param memoryCtx
126
         *            The parent memory space context for this ExpressionDMC. 
127
         */
128
        public MIExpressionDMC(String sessionId, String expression, String relExpr, IMemoryDMContext memoryCtx) {
129
            this(sessionId, expression, relExpr, (IDMContext)memoryCtx);
130
        }
131
132
        private MIExpressionDMC(String sessionId, String expr, String relExpr, IDMContext parent) {
133
            super(sessionId, new IDMContext[] { parent });
134
            exprInfo = new ExpressionInfo(expr, relExpr);
135
        }
136
137
        /**
138
         * @return True if the two objects are equal, false otherwise.
139
         */
140
        @Override
141
        public boolean equals(Object other) {
142
            return super.baseEquals(other) && exprInfo.equals(((MIExpressionDMC)other).exprInfo);
143
        }
144
145
        /**
146
         * 
147
         * @return The hash code of this ExpressionDMC object.
148
         */
149
        @Override
150
        public int hashCode() {
151
            return super.baseHashCode() + exprInfo.hashCode();
152
        }
153
154
        /**
155
         * 
156
         * @return A string representation of this ExpressionDMC (including the
157
         *         expression to which it is bound).
158
         */
159
        @Override
160
        public String toString() {
161
            return baseToString() + ".expr" + exprInfo.toString(); //$NON-NLS-1$ 
162
        }
163
164
        /**
165
         * @return The full expression string represented by this ExpressionDMC
166
         */
167
        public String getExpression() {
168
            return exprInfo.getFullExpr();
169
        }
170
        
171
        /**
172
         * @return The relative expression string represented by this ExpressionDMC
173
         */
174
        public String getRelativeExpression() {
175
            return exprInfo.getRelExpr();
176
        }
177
    }
178
    
179
    private static class InvalidContextExpressionDMC extends AbstractDMContext 
180
        implements IExpressionDMContext
181
    {
182
        private final String expression;
183
184
        public InvalidContextExpressionDMC(String sessionId, String expr, IDMContext parent) {
185
            super(sessionId, new IDMContext[] { parent });
186
            expression = expr;
187
        }
188
189
        @Override
190
        public boolean equals(Object other) {
191
            return super.baseEquals(other) && 
192
                expression == null ? ((InvalidContextExpressionDMC) other).getExpression() == null : expression.equals(((InvalidContextExpressionDMC) other).getExpression());
193
        }
194
195
        @Override
196
        public int hashCode() {
197
            return expression == null ? super.baseHashCode() : super.baseHashCode() ^ expression.hashCode();
198
        }
199
200
        @Override
201
        public String toString() {
202
            return baseToString() + ".invalid_expr[" + expression + "]"; //$NON-NLS-1$ //$NON-NLS-2$
203
        }
204
205
        public String getExpression() {
206
            return expression;
207
        }
208
    }
209
    
210
    
211
	/**
212
	 * Contains the address of an expression as well as the size of its type.
213
	 */
214
    private static class ExpressionDMAddress implements IExpressionDMAddress {
215
    	IAddress fAddr;
216
    	int fSize;
217
    	
218
    	public ExpressionDMAddress(IAddress addr, int size) {
219
    		fAddr = addr;
220
    		fSize = size;
221
    	}
222
223
    	public ExpressionDMAddress(String addrStr, int size) {
224
    		fSize = size;
225
    		// We must count the "0x" and that
226
    		// is why we compare with 10 characters
227
    		// instead of 8
228
    		if (addrStr.length() <= 10) {
229
    			fAddr = new Addr32(addrStr);
230
    		} else {
231
    			fAddr = new Addr64(addrStr);
232
    		}
233
    	}
234
235
    	public IAddress getAddress() { return fAddr; }
236
    	public int getSize() { return fSize; }
237
		
238
		@Override
239
		public boolean equals(Object other) {
240
			if (other instanceof ExpressionDMAddress) {
241
				ExpressionDMAddress otherAddr = (ExpressionDMAddress) other;
242
				return (fSize == otherAddr.getSize()) && 
243
    				(fAddr == null ? otherAddr.getAddress() == null : fAddr.equals(otherAddr.getAddress()));
244
			}
245
			return false;
246
		}
247
248
		@Override
249
		public int hashCode() {
250
			return (fAddr == null ? 0 :fAddr.hashCode()) + fSize;
251
		}
252
253
		@Override
254
		public String toString() {
255
			return (fAddr == null ? "null" : "(0x" + fAddr.toString()) + ", " + fSize + ")"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
256
		}
257
    }
258
259
	/**
260
	 * This class represents the static data referenced by an instance of ExpressionDMC,
261
	 * such as its type and number of children; it does not contain the value or format
262
	 * of the expression.
263
	 */
264
    private static class ExpressionDMData implements IExpressionDMData {
265
		// This is the relative expression, such as the name of a field within a structure,
266
		// in contrast to the fully-qualified expression contained in the ExpressionDMC,
267
		// which refers to the full name, including parent structure.
268
		private final String relativeExpression;
269
		private final String exprType;
270
		private final int numChildren;
271
		private final boolean editable;
272
273
		/**
274
		 * ExpressionDMData constructor.
275
		 */
276
		public ExpressionDMData(String expr, String type, int num, boolean edit) {
277
			relativeExpression = expr;
278
			exprType = type;
279
			numChildren = num;
280
			editable = edit;
281
		}
282
283
		public BasicType getBasicType() {
284
			return null;
285
		}
286
		
287
		public String getEncoding() {
288
			return null;
289
		}
290
291
		public Map<String, Integer> getEnumerations() {
292
			return new HashMap<String, Integer>();
293
		}
294
295
		public String getName() {
296
			return relativeExpression;
297
		}
298
299
		public IRegisterDMContext getRegister() {
300
			return null;
301
		}
302
303
		// See class VariableVMNode for an example of usage of this method
304
		public String getStringValue() {
305
			return null;
306
		}
307
308
		public String getTypeId() {
309
			return null;
310
		}
311
312
		public String getTypeName() {
313
			return exprType;
314
		}
315
316
		public int getNumChildren() {
317
			return numChildren;	
318
		}
319
		
320
		public boolean isEditable() {
321
			return editable;
322
		}
323
		
324
		@Override
325
		public boolean equals(Object other) {
326
			if (other instanceof ExpressionDMData) {
327
				ExpressionDMData otherData = (ExpressionDMData) other;
328
				return (getNumChildren() == otherData.getNumChildren()) && 
329
    				(getTypeName() == null ? otherData.getTypeName() == null : getTypeName().equals(otherData.getTypeName())) &&
330
    				(getName() == null ? otherData.getName() == null : getName().equals(otherData.getName()));
331
			}
332
			return false;
333
		}
334
335
		@Override
336
		public int hashCode() {
337
			return relativeExpression == null ? 0 : relativeExpression.hashCode() + 
338
					exprType == null ? 0 : exprType.hashCode() + numChildren;
339
		}
340
341
		@Override
342
		public String toString() {
343
			return "relExpr=" + relativeExpression + ", type=" + exprType + ", numchildren=" + numChildren; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
344
		}
345
	}
346
347
	/**
348
	 * Event generated every time an expression is changed by the ExpressionService.
349
	 * 
350
	 * A client wishing to receive such events has to register as a service
351
	 * event listener and implement the corresponding eventDispatched method.
352
	 * 
353
	 * E.g.:
354
	 *
355
	 *    getSession().addServiceEventListener(listenerObject, null);
356
	 *     
357
	 *    @DsfServiceEventHandler
358
	 *    public void eventDispatched(ExpressionChangedEvent e) {
359
	 *       IExpressionDMContext context = e.getDMContext();
360
	 *       // do something...
361
	 *    }
362
	 */
363
    private static class ExpressionChangedEvent extends AbstractDMEvent<IExpressionDMContext>
364
                        implements IExpressionChangedDMEvent {
365
    	
366
        public ExpressionChangedEvent(IExpressionDMContext context) {
367
        	super(context);
368
        }
369
    }
370
371
	private CommandCache fExpressionCache;
372
	private MacOSGDBVariableManager varManager;
373
374
	
375
	public MacOSGDBExpressions(DsfSession session) {
376
		super(session);
377
	}
378
379
	/**
380
	 * This method initializes this service.
381
	 * 
382
	 * @param requestMonitor
383
	 *            The request monitor indicating the operation is finished
384
	 */
385
	@Override
386
	public void initialize(final RequestMonitor requestMonitor) {
387
		super.initialize(new RequestMonitor(getExecutor(), requestMonitor) {
388
			@Override
389
			protected void handleSuccess() {
390
				doInitialize(requestMonitor);
391
			}
392
		});
393
	}
394
	
395
	/**
396
	 * This method initializes this service after our superclass's initialize()
397
	 * method succeeds.
398
	 * 
399
	 * @param requestMonitor
400
	 *            The call-back object to notify when this service's
401
	 *            initialization is done.
402
	 */
403
	private void doInitialize(RequestMonitor requestMonitor) {
404
405
		// Register to receive service events for this session.
406
        getSession().addServiceEventListener(this, null);
407
        
408
		// Register this service.
409
		register(new String[] { IExpressions.class.getName(),
410
				MacOSGDBExpressions.class.getName() },
411
				new Hashtable<String, String>());
412
		
413
		// Create the expressionService-specific CommandControl which is our
414
        // variable object manager.
415
        // It will deal with the meta-commands, before sending real MI commands
416
        // to the back-end, through the MICommandControl service
417
		// It must be created after the ExpressionService is registered
418
		// since it will need to find it.
419
        varManager = new MacOSGDBVariableManager(getSession(), getServicesTracker());
420
421
        // Create the meta command cache which will use the variable manager
422
        // to actually send MI commands to the back-end
423
        fExpressionCache = new CommandCache(getSession(), varManager);
424
        ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class);
425
        fExpressionCache.setContextAvailable(commandControl.getContext(), true);
426
        
427
		requestMonitor.done();
428
	}
429
430
	/**
431
	 * This method shuts down this service. It unregisters the service, stops
432
	 * receiving service events, and calls the superclass shutdown() method to
433
	 * finish the shutdown process.
434
	 * 
435
	 * @return void
436
	 */
437
	@Override
438
	public void shutdown(RequestMonitor requestMonitor) {
439
		unregister();
440
		varManager.dispose();
441
		getSession().removeServiceEventListener(this);
442
		super.shutdown(requestMonitor);
443
	}
444
	
445
	/**
446
	 * @return The bundle context of the plug-in to which this service belongs.
447
	 */
448
	@Override
449
	protected BundleContext getBundleContext() {
450
		return GdbPlugin.getBundleContext();
451
	}
452
	
453
	/**
454
	 * Create an expression context with the same full and relative expression
455
	 */
456
	public IExpressionDMContext createExpression(IDMContext ctx, String expression) {
457
		return createExpression(ctx, expression, expression);
458
	}
459
460
	/**
461
	 * Create an expression context.
462
	 */
463
	public IExpressionDMContext createExpression(IDMContext ctx, String expression, String relExpr) {
464
	    IFrameDMContext frameDmc = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class);
465
	    if (frameDmc != null) {
466
	        return new MIExpressionDMC(getSession().getId(), expression, relExpr, frameDmc);
467
	    } 
468
	    
469
	    IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class);
470
	    if (execCtx != null) {
471
	    	// If we have a thread context but not a frame context, we give the user
472
	    	// the expression as per the top-most frame of the specified thread.
473
	    	// To do this, we create our own frame context.
474
	    	MIStack stackService = getServicesTracker().getService(MIStack.class);
475
	    	if (stackService != null) {
476
	    		frameDmc = stackService.createFrameDMContext(execCtx, 0);
477
	            return new MIExpressionDMC(getSession().getId(), expression, relExpr, frameDmc);
478
	    	}
479
480
            return new InvalidContextExpressionDMC(getSession().getId(), expression, execCtx);
481
        } 
482
	    
483
        IMemoryDMContext memoryCtx = DMContexts.getAncestorOfType(ctx, IMemoryDMContext.class);
484
        if (memoryCtx != null) {
485
            return new MIExpressionDMC(getSession().getId(), expression, relExpr, memoryCtx);
486
        } 
487
        
488
        // Don't care about the relative expression at this point
489
        return new InvalidContextExpressionDMC(getSession().getId(), expression, ctx);
490
	}
491
492
	/**
493
	 * @see IFormattedValues.getFormattedValueContext(IFormattedDataDMContext, String)
494
	 * 
495
	 * @param dmc
496
	 *            The context describing the data for which we want to create
497
	 *            a Formatted context.
498
	 * @param formatId
499
	 *            The format that will be used to create the Formatted context
500
	 *            
501
	 * @return A FormattedValueDMContext that can be used to obtain the value
502
	 *         of an expression in a specific format. 
503
	 */
504
505
	public FormattedValueDMContext getFormattedValueContext(
506
			IFormattedDataDMContext dmc, String formatId) {
507
		return new FormattedValueDMContext(this, dmc, formatId);
508
	}
509
510
	/**
511
	 * @see IFormattedValues.getAvailableFormats(IFormattedDataDMContext, DataRequestMonitor)
512
	 * 
513
	 * @param dmc
514
	 *            The context describing the data for which we want to know
515
	 *            which formats are available.
516
	 * @param rm
517
	 *            The data request monitor for this asynchronous operation. 
518
	 *  
519
	 */
520
521
	public void getAvailableFormats(IFormattedDataDMContext dmc,
522
			final DataRequestMonitor<String[]> rm) {
523
		rm.setData(new String[] { IFormattedValues.BINARY_FORMAT,
524
				IFormattedValues.NATURAL_FORMAT, IFormattedValues.HEX_FORMAT,
525
				IFormattedValues.OCTAL_FORMAT, IFormattedValues.DECIMAL_FORMAT });
526
		rm.done();
527
	}
528
529
	/**
530
	 * Obtains the static data of an expression represented 
531
	 * by an ExpressionDMC object (<tt>dmc</tt>).
532
	 * 
533
	 * @param dmc
534
	 *            The ExpressionDMC for the expression to be evaluated.
535
	 * @param rm
536
	 *            The data request monitor that will contain the requested data
537
	 */
538
	public void getExpressionData(
539
			final IExpressionDMContext dmc,
540
			final DataRequestMonitor<IExpressionDMData> rm) 
541
	{
542
	    if (dmc instanceof MIExpressionDMC) {
543
    		fExpressionCache.execute(
544
    				new ExprMetaGetVar(dmc), 
545
    				new DataRequestMonitor<ExprMetaGetVarInfo>(getExecutor(), rm) {
546
    					@Override
547
    					protected void handleSuccess() {
548
    						rm.setData(new ExpressionDMData(getData().getExpr(), 
549
    								getData().getType(), getData().getNumChildren(), getData().getEditable()));
550
    						rm.done();
551
    					}
552
    				});
553
	    } else if (dmc instanceof InvalidContextExpressionDMC) {
554
            rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
555
            rm.done();
556
	    } else {
557
            rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
558
            rm.done();
559
	    }
560
	}
561
	
562
	/**
563
	 * Obtains the address of an expression and the size of its type. 
564
	 * 
565
	 * @param dmc
566
	 *            The ExpressionDMC for the expression.
567
	 * @param rm
568
	 *            The data request monitor that will contain the requested data
569
	 */
570
    public void getExpressionAddressData(
571
    		IExpressionDMContext dmc, 
572
    		final DataRequestMonitor<IExpressionDMAddress> rm) {
573
        
574
    	// First create an address expression and a size expression
575
    	// to be used in back-end calls
576
    	final IExpressionDMContext addressDmc = 
577
    	    createExpression( dmc, "&(" + dmc.getExpression() + ")" );//$NON-NLS-1$//$NON-NLS-2$
578
    	final IExpressionDMContext sizeDmc = 
579
    	    createExpression( dmc, "sizeof(" + dmc.getExpression() + ")" ); //$NON-NLS-1$//$NON-NLS-2$
580
581
    	if (addressDmc instanceof InvalidContextExpressionDMC || sizeDmc instanceof InvalidContextExpressionDMC) {
582
            rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
583
            rm.done();
584
    	} else {
585
        	fExpressionCache.execute(
586
    			new MIDataEvaluateExpression<MIDataEvaluateExpressionInfo>(addressDmc), 
587
    			new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) {
588
    				@Override
589
    				protected void handleSuccess() {
590
    					String tmpAddrStr = getData().getValue();
591
    					
592
    					// Deal with adresses of contents of a char* which is in
593
    					// the form of "0x12345678 \"This is a string\""
594
    					int split = tmpAddrStr.indexOf(' '); 
595
    			    	if (split != -1) tmpAddrStr = tmpAddrStr.substring(0, split);
596
    			    	final String addrStr = tmpAddrStr;
597
    			    	
598
    					fExpressionCache.execute(
599
    						new MIDataEvaluateExpression<MIDataEvaluateExpressionInfo>(sizeDmc), 
600
    						new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) {
601
    							@Override
602
    							protected void handleSuccess() {
603
    								try {
604
    									int size = Integer.parseInt(getData().getValue());
605
    									rm.setData(new ExpressionDMAddress(addrStr, size));
606
    								} catch (NumberFormatException e) {
607
    						            rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
608
    						            		"Unexpected size format from backend: " + getData().getValue(), null)); //$NON-NLS-1$
609
    								}
610
    								rm.done();
611
    							}
612
    						});
613
    				}
614
    			});
615
    	}
616
	}
617
618
	/**
619
	 * Obtains the value of an expression in a specific format.
620
	 * 
621
	 * @param dmc
622
	 *            The context for the format of the value requested and 
623
	 *            for the expression to be evaluated.  The expression context
624
	 *            should be a parent of the FormattedValueDMContext.
625
	 * @param rm
626
	 *            The data request monitor that will contain the requested data
627
	 */
628
	public void getFormattedExpressionValue(
629
			final FormattedValueDMContext dmc,
630
			final DataRequestMonitor<FormattedValueDMData> rm) 
631
	{
632
		// We need to make sure the FormattedValueDMContext also holds an ExpressionContext,
633
		// or else this method cannot do its work.
634
		// Note that we look for MIExpressionDMC and not IExpressionDMC, because getting
635
		// looking for IExpressionDMC could yield InvalidContextExpressionDMC which is still
636
		// not what we need to have.
637
        if (DMContexts.getAncestorOfType(dmc, MIExpressionDMC.class) == null ) {
638
        	rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
639
        	rm.done();
640
        } else {
641
        	fExpressionCache.execute(
642
        			new ExprMetaGetValue(dmc),
643
        			new DataRequestMonitor<ExprMetaGetValueInfo>(getExecutor(), rm) {
644
        				@Override
645
        				protected void handleSuccess() {
646
        					rm.setData(new FormattedValueDMData(getData().getValue()));
647
        					rm.done();
648
        				}
649
        			});
650
        }
651
	}
652
653
	/* Not implemented
654
	 * 
655
	 * (non-Javadoc)
656
	 * @see org.eclipse.cdt.dsf.debug.service.IExpressions#getBaseExpressions(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
657
	 */
658
	public void getBaseExpressions(IExpressionDMContext exprContext,
659
			DataRequestMonitor<IExpressionDMContext[]> rm) {
660
		rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
661
				NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$
662
		rm.done();
663
	}
664
665
	/**
666
	 * Retrieves the children expressions of the specified expression
667
	 * 
668
	 * @param exprCtx
669
	 *            The context for the expression for which the children
670
	 *            should be retrieved.
671
	 * @param rm
672
	 *            The data request monitor that will contain the requested data
673
	 */
674
	public void getSubExpressions(final IExpressionDMContext dmc,
675
			final DataRequestMonitor<IExpressionDMContext[]> rm) 
676
	{		
677
		if (dmc instanceof MIExpressionDMC) {
678
			fExpressionCache.execute(
679
					new ExprMetaGetChildren(dmc),				
680
					new DataRequestMonitor<ExprMetaGetChildrenInfo>(getExecutor(), rm) {
681
						@Override
682
						protected void handleSuccess() {
683
							ExpressionInfo[] childrenExpr = getData().getChildrenExpressions();
684
							IExpressionDMContext[] childArray = new IExpressionDMContext[childrenExpr.length];
685
							for (int i=0; i<childArray.length; i++) {
686
								childArray[i] = createExpression(
687
										dmc.getParents()[0], 
688
										childrenExpr[i].getFullExpr(),
689
										childrenExpr[i].getRelExpr());
690
							}
691
692
							rm.setData(childArray);
693
							rm.done();
694
						}
695
					});
696
		} else if (dmc instanceof InvalidContextExpressionDMC) {
697
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
698
			rm.done();
699
		} else {
700
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
701
			rm.done();
702
		}
703
	}
704
705
	/**
706
	 * Retrieves a range of children expressions of the specified expression
707
	 * 
708
	 * @param exprCtx
709
	 *            The context for the expression for which the children
710
	 *            should be retrieved.
711
	 * @param startIndex
712
	 *            The starting index within the list of all children of the parent
713
	 *            expression.  Must be a positive integer.  
714
	 * @param length
715
	 *            The length or number of elements of the range requested.  
716
	 *            Must be a positive integer.
717
	 * @param rm
718
	 *            The data request monitor that will contain the requested data
719
	 */
720
	public void getSubExpressions(IExpressionDMContext exprCtx, final int startIndex,
721
			final int length, final DataRequestMonitor<IExpressionDMContext[]> rm) {
722
723
		if (startIndex < 0 || length < 0) {
724
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$
725
			rm.done();
726
			return;
727
		}
728
		
729
		if (exprCtx instanceof MIExpressionDMC) {
730
			getSubExpressions(
731
					exprCtx,
732
					new DataRequestMonitor<IExpressionDMContext[]>(getExecutor(), rm) {
733
						@Override
734
						protected void handleSuccess() {
735
							IExpressionDMContext[] subExpressions = getData();
736
737
							if (startIndex >= subExpressions.length || startIndex + length > subExpressions.length) {
738
								rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, "Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$
739
								rm.done();
740
								return;
741
							}
742
							
743
							IExpressionDMContext[] subRange = new IExpressionDMContext[length];
744
							for (int i=0; i<subRange.length; i++) {
745
								subRange[i] = subExpressions[i+startIndex]; 
746
							}
747
							rm.setData(subRange);
748
							rm.done();
749
						}
750
					});
751
		} else if (exprCtx instanceof InvalidContextExpressionDMC) {
752
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
753
			rm.done();
754
		} else {
755
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
756
			rm.done();
757
		}		
758
	}
759
	
760
	/**
761
	 * Retrieves the count of children expressions of the specified expression
762
	 * 
763
	 * @param exprCtx
764
	 *            The context for the expression for which the children count
765
	 *            should be retrieved.
766
	 * @param rm
767
	 *            The data request monitor that will contain the requested data
768
	 */
769
	public void getSubExpressionCount(IExpressionDMContext dmc,
770
			final DataRequestMonitor<Integer> rm) 
771
	{
772
		if (dmc instanceof MIExpressionDMC) {
773
			fExpressionCache.execute(
774
					new ExprMetaGetChildCount(dmc),				
775
					new DataRequestMonitor<ExprMetaGetChildCountInfo>(getExecutor(), rm) {
776
						@Override
777
						protected void handleSuccess() {
778
							rm.setData(getData().getChildNum());
779
							rm.done();
780
						}
781
					});
782
		} else if (dmc instanceof InvalidContextExpressionDMC) {
783
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
784
			rm.done();
785
		} else {
786
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
787
			rm.done();
788
		}
789
	}
790
	
791
    /**
792
     * This method indicates if an expression can be written to.
793
     * 
794
     * @param dmc: The data model context representing an expression.
795
     *
796
     * @param rm: Data Request monitor containing True if this expression's value can be edited.  False otherwise.
797
     */
798
799
	public void canWriteExpression(IExpressionDMContext dmc, final DataRequestMonitor<Boolean> rm) {
800
        if (dmc instanceof MIExpressionDMC) {
801
            fExpressionCache.execute(
802
                    new ExprMetaGetAttributes(dmc),
803
                    new DataRequestMonitor<ExprMetaGetAttributesInfo>(getExecutor(), rm) {
804
                        @Override
805
                        protected void handleSuccess() {
806
                            rm.setData(getData().getEditable());
807
                            rm.done();
808
                        }
809
                    });
810
        } else if (dmc instanceof InvalidContextExpressionDMC) {
811
            rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
812
            rm.done();
813
        } else {
814
            rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
815
            rm.done();
816
        }
817
	}
818
819
820
	/**
821
	 * Changes the value of the specified expression based on the new value and format.
822
	 * 
823
	 * @param expressionContext
824
	 *            The context for the expression for which the value 
825
	 *            should be changed.
826
	 * @param expressionValue
827
	 *            The new value for the specified expression
828
	 * @param formatId
829
	 *            The format in which the value is specified
830
	 * @param rm
831
	 *            The request monitor that will indicate the completion of the operation
832
	 */
833
	public void writeExpression(final IExpressionDMContext dmc, String expressionValue, 
834
			String formatId, final RequestMonitor rm) {
835
836
		if (dmc instanceof MIExpressionDMC) {
837
			// This command must not be cached, since it changes the state of the back-end.
838
			// We must send it directly to the variable manager
839
			varManager.writeValue(
840
			        dmc, 
841
					expressionValue, 
842
					formatId, 
843
					new RequestMonitor(getExecutor(), rm) {
844
						@Override
845
						protected void handleSuccess() {
846
							// A value has changed, we should remove any references to that
847
							// value in our cache.  Since we don't have such granularity,
848
							// we must clear the entire cache.
849
							// We cannot use the context to do a more-specific reset, because
850
							// the same global variable can be set with different contexts
851
							fExpressionCache.reset();
852
							
853
							// Issue event that the expression has changed
854
							getSession().dispatchEvent(new ExpressionChangedEvent(dmc), getProperties());
855
							
856
							rm.done();
857
						}
858
					});
859
		} else if (dmc instanceof InvalidContextExpressionDMC) {
860
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
861
			rm.done();
862
		} else {
863
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
864
			rm.done();
865
		}
866
	}
867
868
    @DsfServiceEventHandler 
869
    public void eventDispatched(IRunControl.IResumedDMEvent e) {
870
        fExpressionCache.setContextAvailable(e.getDMContext(), false);
871
        if (e.getReason() != StateChangeReason.STEP) {
872
            fExpressionCache.reset();
873
        }
874
    }
875
    
876
    @DsfServiceEventHandler 
877
    public void eventDispatched(IRunControl.ISuspendedDMEvent e) {
878
        fExpressionCache.setContextAvailable(e.getDMContext(), true);
879
        fExpressionCache.reset();
880
    }
881
882
    @DsfServiceEventHandler 
883
    public void eventDispatched(IMemoryChangedEvent e) {
884
        fExpressionCache.reset();
885
        // VariableManager separately traps this event
886
    }
887
888
    public void flushCache(IDMContext context) {
889
        fExpressionCache.reset(context);
890
        // We must also mark all variable objects as out-of-date
891
        // to refresh them as well
892
        varManager.markAllOutOfDate();
893
    }
894
895
}
(-)src/org/eclipse/cdt/dsf/gdb/service/macos/MacOSGDBVariableManager.java (+1712 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2010 Monta Vista 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
7
 * 
8
 * Contributors:
9
 *     Monta Vista - initial API and implementation
10
 *     Ericsson    - Modified for handling of multiple execution contexts
11
 *     Ericsson    - Major updates for GDB/MI implementation
12
 *     Ericsson    - Major re-factoring to deal with children
13
 *     Ericsson           - Created the MacOS version
14
 *******************************************************************************/
15
package org.eclipse.cdt.dsf.gdb.service.macos;
16
17
import java.util.ArrayList;
18
import java.util.HashMap;
19
import java.util.LinkedHashMap;
20
import java.util.LinkedList;
21
import java.util.List;
22
import java.util.Map;
23
24
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
25
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
26
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
27
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
28
import org.eclipse.cdt.dsf.datamodel.DMContexts;
29
import org.eclipse.cdt.dsf.debug.service.IExpressions;
30
import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
31
import org.eclipse.cdt.dsf.debug.service.IRunControl;
32
import org.eclipse.cdt.dsf.debug.service.IStack;
33
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
34
import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
35
import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData;
36
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent;
37
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
38
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
39
import org.eclipse.cdt.dsf.debug.service.command.ICommand;
40
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
41
import org.eclipse.cdt.dsf.debug.service.command.ICommandListener;
42
import org.eclipse.cdt.dsf.debug.service.command.ICommandResult;
43
import org.eclipse.cdt.dsf.debug.service.command.ICommandToken;
44
import org.eclipse.cdt.dsf.debug.service.command.IEventListener;
45
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
46
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
47
import org.eclipse.cdt.dsf.gdb.service.macos.MacOSGDBExpressions.MIExpressionDMC;
48
import org.eclipse.cdt.dsf.mi.service.MIExpressions.ExpressionInfo;
49
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetAttributes;
50
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount;
51
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildren;
52
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetValue;
53
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetVar;
54
import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataEvaluateExpression;
55
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarAssign;
56
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarCreate;
57
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarDelete;
58
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarEvaluateExpression;
59
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarInfoPathExpression;
60
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarListChildren;
61
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarSetFormat;
62
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarShowAttributes;
63
import org.eclipse.cdt.dsf.mi.service.command.commands.macos.MacOSMIVarUpdate;
64
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo;
65
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo;
66
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo;
67
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo;
68
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo;
69
import org.eclipse.cdt.dsf.mi.service.command.output.MIVar;
70
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarAssignInfo;
71
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarChange;
72
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarCreateInfo;
73
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarDeleteInfo;
74
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarEvaluateExpressionInfo;
75
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarInfoPathExpressionInfo;
76
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarListChildrenInfo;
77
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarSetFormatInfo;
78
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarShowAttributesInfo;
79
import org.eclipse.cdt.dsf.mi.service.command.output.macos.MacOSMIVarUpdateInfo;
80
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
81
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
82
import org.eclipse.cdt.dsf.service.DsfSession;
83
import org.eclipse.core.runtime.IStatus;
84
import org.eclipse.core.runtime.Status;
85
86
/**
87
 * Manages a list of variable objects as created through GDB/MI commands.
88
 * 
89
 * This class is passed expression-meta-commands which have their own cache.
90
 * Therefore, we don't use the standard MICommandCache in this class.
91
 * In fact, we can't even use it, because many variableObject MI commands,
92
 * should not be cached as they alter the state of the back-end.
93
 * 
94
 * Design details:
95
 * ==============
96
 * 
97
 * GDB variable object information
98
 * -------------------------------
99
 * o Variable objects are recursively hierarchical, where children can be created through 
100
 *   the parent.
101
 * o A varObject created with -var-create is a ROOT
102
 * o A varObject created with -var-list-children, is not a root
103
 * o Only varObject with no children or varObjects that are pointers can change values 
104
 *   and therefore 
105
 *   those objects can be used with -var-assign
106
 * o After a program stops, a varObject must be 'updated' before used 
107
 * o Only root varObject can be updated with -var-update, which will trigger all
108
 *   of the root's descendants to be updated.
109
 * o Once updated, a varObject need not be updated until the program resumes again;
110
 *   this is true even after -var-assign is used (because it does an implicit -var-update)
111
 * o -var-update will return the list of all modifiable descendants of the udpated root which
112
 *   have changed
113
 * o -var-update will indicate if a root is out-of-scope (which implies that all
114
 *   its descendants are out-of-scope)
115
 * o if a varObject is out-of-scope, another varObject may be valid for the same
116
 *   expression as the out-of-scope varObject
117
 * o deleting a varObject will delete all its descendants, therefore, it is only
118
 *   necessary to delete roots
119
 * 
120
 * 
121
 * Class details
122
 * -------------
123
 * - We have an MIVariableObject class which represents a variable object in GDB
124
 * 
125
 * - MIVariableObject includes a buffered value for each allowed format.
126
 * 
127
 * - We have an MIRootVariableObject class inheriting from MIVariableObject to describe
128
 *   root varObjects created with -var-create.  Objects created with -var-list-children
129
 *   are MIVariableObjects only.  The root class will keep track of if the root object 
130
 *   needs to be updated, if the root object is out-of-scope, and of a list of all 
131
 *   modifiable descendants of this root.  The list of modifiable descendants is 
132
 *   accessed using the gdb-given name to allow quick updates from the -var-update 
133
 *   result (see below.)
134
 * 
135
 * - we do not use -var-list-children for arrays, but create them manually
136
 *  
137
 * - when the program stops, we should mark all roots as needing to be updated. 
138
 * To achieve this efficiently, we have a dedicated list of roots that are updated.
139
 * When the program stops, we go through this list, remove each element and mark it
140
 * as needing to be updated.
141
 * 
142
 * - when a varObject is accessed, if its root must be updated, the var-update
143
 * command shall be used.  The result of that command will indicate all
144
 * modifiable descendants that have changed.  We also use --all-values with -var-update
145
 * to get the new value (in the current format) for each modified descendant.  Using the list of modifiable
146
 * descendants of the root, we can quickly update the changed ones to invalidate their buffered
147
 * values and store the new current format value.
148
 * 
149
 * - all values of non-modifiable varObjects (except arrays) will be set to {...}
150
 * without going to the back-end
151
 * 
152
 * - requesting the value of an array varObject will trigger the creation of a new
153
 * varObject for the array's address.  Note that we must still use a variable
154
 * object and not the command -data-evaluate-expression, because we still need to get
155
 * the array address in multiple formats.
156
 * 
157
 * - we keep an LRU (Least Recently Used) structure of all variable objects.  This LRU
158
 * will be bounded to a maximum allowed number of variable objects.  Whenever we get an
159
 * object from the LRU cleanup will be done if the maximum size has been reached.
160
 * The LRU will not delete a parent varObject until all its children are deleted; this is
161
 * achieved by touching each of the parents of an object whenever that object is put or get
162
 *
163
 * - It may happen that when accessing a varObject we find its root to be
164
 * out-of-scope.  The expression for which we are trying to access a varObject
165
 * could still be valid, and therefore we should try to create a new varObject for
166
 * that expression.  This can happen for example if two methods use the same name
167
 * for a variable. In the case when we find that a varObject is out-of-scope (when
168
 * its root is out-of-scope) the following should be done:
169
 *  - replace the varObject in the LRU with a newly created one in GDB
170
 *  - if the old object was a root, delete it in GDB.
171
 *  
172
 * - In GDB, -var-update will only report a change if -var-evaluate-expression has
173
 *   changed -- in the current format--.  This means that situations like
174
 *    double z = 1.2;
175
 *    z = 1.4;
176
 *   Will not report a change if the format is anything else than natural.
177
 *   This is because 1.2 and 1.4 are both printed as 1, 0x1, etc
178
 *   Since we cache the values of every format, we must know if the value has
179
 *   change in -any- format, not just the current one.
180
 *   To solve this, we always keep the display format of variable objects (and their
181
 *   children) to the natural format; we believe that if the value changes in any
182
 *   format, it guarantees that it will change in the natural format.
183
 *   The simplest way to do this is that whenever we change the format
184
 *   of a variable object, we immediately set it back to natural with a second
185
 *   var-set-format command.
186
 *   Note that versions of GDB after 6.7 will allows to issue -var-evaluate-expression
187
 *   with a specified format, therefore allowing us to never use -var-set-format, and
188
 *   consequently, to easily keep the display format of all variable objects to natural.
189
 *   
190
 *   @since 2.1
191
 */
192
public class MacOSGDBVariableManager implements ICommandControl {
193
194
	/**
195
	 * Utility class to track the progress and information of MI variable objects
196
	 */
197
	public class MIVariableObject {
198
199
		// Don't use an enumeration to allow subclasses to extend this
200
		protected static final int STATE_READY = 0;
201
		protected static final int STATE_UPDATING = 1;
202
	    
203
		protected int currentState;	
204
205
		// This is the lock used when we must run multiple
206
		// operations at once.  This lock should be independent of the
207
		// UPDATING state, which is why we don't make it part of the state
208
		private boolean locked = false;
209
		
210
	    // This id is the one used to search for this object in our hash-map
211
	    private final VariableObjectId internalId;
212
	    // This is the name of the variable object, as given by GDB (e.g., var1 or var1.public.x)
213
		private String gdbName = null;
214
		// The current format of this variable object, within GDB
215
		private String format = IFormattedValues.NATURAL_FORMAT;
216
217
		// The full expression that can be used to characterize this object
218
		private String fullExp = null;
219
		private String type = null;
220
		private int numChildren = 0;
221
		private Boolean editable = null;
222
223
		// The current values of the expression for each format. (null if not known yet)
224
		private Map<String, String> valueMap = null;
225
		
226
		// A queue of request monitors waiting for this object to be ready
227
		private LinkedList<RequestMonitor> operationsPending;
228
		
229
		// A queue of request monitors that requested an update
230
		protected LinkedList<DataRequestMonitor<Boolean>> updatesPending;
231
232
		// The relative expressions of the children of this variable, if any.  
233
		// Null means we didn't fetch them yet, while an empty array means no children
234
        private ExpressionInfo[] children = null; 
235
        
236
        // The parent of this variable object within GDB.  Null if this object has no parent
237
        private MIVariableObject parent = null;
238
        
239
        // The root parent that must be used to issue -var-update.
240
        // If this object is a root, then the rootToUpdate is itself
241
        private MIRootVariableObject rootToUpdate = null;
242
                
243
		protected boolean outOfScope = false;
244
245
		public MIVariableObject(VariableObjectId id, MIVariableObject parentObj) {
246
			currentState = STATE_READY;
247
			
248
			operationsPending = new LinkedList<RequestMonitor>();
249
			updatesPending = new LinkedList<DataRequestMonitor<Boolean>>();
250
			
251
			internalId = id;
252
			setParent(parentObj);
253
			
254
			// No values are available yet
255
			valueMap = new HashMap<String, String>();
256
			resetValues();
257
		}
258
		
259
		public VariableObjectId getInternalId() { return internalId; }
260
		public String getGdbName() { return gdbName; }
261
		public String getCurrentFormat() {	return format; }
262
		public MIVariableObject getParent() { return parent; }
263
		public MIRootVariableObject getRootToUpdate() { return rootToUpdate; }
264
		
265
		public String getExpression() { return fullExp; }
266
		public String getType() { return type; }
267
		public int getNumChildren() { return numChildren; }
268
		public String getValue(String format) { return valueMap.get(format); }
269
		
270
        public ExpressionInfo[] getChildren() { return children; }
271
272
        
273
        //FIX replace these methods with CDT's GDBTypeParser (see bug 200897 comment #5)
274
        // int(*)[5] is a pointer to an array (so it is a pointer but not an array) (e.g., &b where int b[5])
275
        // int *[5] is an array of pointers (so it is an array but not a pointer) (e.g., int *b[5])
276
		public boolean isArray() { return (getType() == null) ? false : getType().endsWith("]") && !getType().contains("(*)") ; }//$NON-NLS-1$//$NON-NLS-2$
277
		public boolean isPointer() { return (getType() == null) ? false : getType().contains("*")&& !isArray(); }//$NON-NLS-1$
278
		public boolean isMethod() { return (getType() == null) ? false : getType().contains("()"); }//$NON-NLS-1$
279
		// A complex variable is one with children.  However, it must not be a pointer since a pointer has one child
280
		// according to GDB, but is still a 'simple' variable
281
		public boolean isComplex() { return (getType() == null) ? false : getNumChildren() > 0 && !isPointer(); }
282
		
283
		public void setGdbName(String n) { gdbName = n; }
284
		public void setCurrentFormat(String f) { format = f; }
285
		
286
		public void setExpressionData(String fullExpression, String t, int num) {
287
			fullExp = fullExpression;
288
			type = t;
289
			numChildren = num;
290
		}
291
292
		public void setValue(String format, String val) { valueMap.put(format, val); }
293
		
294
		public void resetValues(String valueInCurrentFormat) {
295
			resetValues();
296
			setValue(getCurrentFormat(), valueInCurrentFormat); 
297
		}
298
		
299
		public void resetValues() {
300
			valueMap.put(IFormattedValues.NATURAL_FORMAT, null);
301
			valueMap.put(IFormattedValues.BINARY_FORMAT, null);
302
			valueMap.put(IFormattedValues.HEX_FORMAT, null);
303
			valueMap.put(IFormattedValues.OCTAL_FORMAT, null);
304
			valueMap.put(IFormattedValues.DECIMAL_FORMAT, null);
305
		}
306
		
307
        public void setChildren(ExpressionInfo[] c) { children = c; }
308
        public void setParent(MIVariableObject p) { 
309
        	parent = p; 
310
        	rootToUpdate = (p == null ? (MIRootVariableObject)this : p.getRootToUpdate());
311
        }
312
	
313
        public void executeWhenNotUpdating(RequestMonitor rm) {
314
			getRootToUpdate().executeWhenNotUpdating(rm);
315
		}
316
317
		private void lock() {
318
			locked = true;
319
		}
320
		
321
		private void unlock() {
322
			locked = false;
323
324
			while (operationsPending.size() > 0) {
325
				operationsPending.poll().done();
326
			}
327
		}
328
329
		public boolean isOutOfScope() { return outOfScope; }
330
331
		/**
332
		 * This method updates the variable object.
333
		 * Updating a variable object is done by updating its root.
334
		 */
335
		public void update(final DataRequestMonitor<Boolean> rm) {
336
			
337
			// We check to see if we are already out-of-scope
338
			// We must do this to avoid the risk of re-creating this object
339
			// twice, due to race conditions
340
			if (isOutOfScope()) {
341
				rm.setData(false);
342
				rm.done();
343
			} else if (currentState == STATE_UPDATING) {
344
				// If we were already updating, we just queue the request monitor
345
				// until the on-going update finishes.
346
				updatesPending.add(rm);
347
			} else {			
348
				currentState = STATE_UPDATING;
349
				getRootToUpdate().update(new DataRequestMonitor<Boolean>(fSession.getExecutor(), rm) {
350
					@Override
351
					protected void handleCompleted() {
352
						currentState = STATE_READY;
353
						
354
						if (isSuccess()) {
355
							outOfScope = getRootToUpdate().isOutOfScope();
356
							// This request monitor is the one that should re-create
357
							// the variable object if the old one was out-of-scope							
358
							rm.setData(outOfScope);
359
							rm.done();
360
							
361
							// All the other request monitors must be notified but must
362
							// not re-create the object, even if it is out-of-scope
363
							while (updatesPending.size() > 0) {
364
								DataRequestMonitor<Boolean> pendingRm = updatesPending.poll();
365
								pendingRm.setData(false);
366
								pendingRm.done();
367
							}
368
						} else {
369
							rm.setStatus(getStatus());
370
							rm.done();
371
372
							while (updatesPending.size() > 0) {
373
								DataRequestMonitor<Boolean> pendingRm = updatesPending.poll();
374
								pendingRm.setStatus(getStatus());
375
								pendingRm.done();
376
							}							
377
						}
378
					}
379
				});
380
			}
381
		}
382
		
383
		/**
384
		 * Variable objects need not be deleted unless they are root.
385
		 * This method is specialized in the MIRootVariableObject class.
386
		 */
387
		public void deleteInGdb() {}
388
		
389
		/** 
390
		 * This method returns the value of the variable object attributes by
391
		 * using -var-show-attributes.
392
		 * Currently, the only attribute available is 'editable'.
393
		 *  
394
		 * @param rm
395
		 *            The data request monitor that will hold the value returned
396
		 */
397
        private void getAttributes(final DataRequestMonitor<Boolean> rm) {
398
            if (editable != null) { 
399
                rm.setData(editable);
400
                rm.done();
401
            } else if (isComplex()) {
402
                editable = false;
403
                rm.setData(editable);
404
                rm.done();
405
            } else {
406
                fCommandControl.queueCommand(
407
                        new MIVarShowAttributes(getRootToUpdate().getControlDMContext(), getGdbName()), 
408
                        new DataRequestMonitor<MIVarShowAttributesInfo>(fSession.getExecutor(), rm) {
409
                            @Override
410
                            protected void handleSuccess() {
411
                            	editable = getData().isEditable();
412
413
                            	rm.setData(editable);
414
                            	rm.done();
415
                            }
416
                        });
417
            }
418
        }
419
420
		/** 
421
		 * This method returns the value of the variable object.
422
		 * This operation translates to multiple MI commands which affect the state of the
423
		 * variable object in the back-end; therefore, we must make sure the object is not
424
		 * locked doing another operation, and we must lock the object once it is our turn
425
		 * to use it.
426
		 * 
427
		 * @param dmc  
428
		 *            The context containing the format to be used for the evaluation
429
		 * @param rm
430
		 *            The data request monitor that will hold the value returned
431
		 */
432
		private void getValue(final FormattedValueDMContext dmc,
433
				              final DataRequestMonitor<FormattedValueDMData> rm) {
434
435
			// We might already know the value
436
			String value = getValue(dmc.getFormatID());
437
			if (value != null) {
438
				rm.setData(new FormattedValueDMData(value));
439
				rm.done();
440
				return;
441
			}
442
443
			// If the variable is a complex structure, there is no need to ask the back-end for a value,
444
			// we can give it the {...} ourselves
445
			// Unless we are dealing with an array, in which case, we want to get the address of it
446
			if (isComplex()) {
447
				if (isArray()) {
448
					// Figure out the address
449
					IExpressionDMContext exprCxt = DMContexts.getAncestorOfType(dmc, IExpressionDMContext.class);
450
					IExpressionDMContext addrCxt = fExpressionService.createExpression(exprCxt, "&(" + exprCxt.getExpression() + ")");  //$NON-NLS-1$//$NON-NLS-2$
451
452
					final FormattedValueDMContext formatCxt = new FormattedValueDMContext(
453
							fSession.getId(),
454
							addrCxt,
455
							dmc.getFormatID()
456
					);
457
458
					getVariable(
459
							addrCxt,
460
							new DataRequestMonitor<MIVariableObject>(fSession.getExecutor(), rm) {
461
								@Override
462
								protected void handleSuccess() {
463
									getData().getValue(formatCxt, rm);
464
465
								}
466
							});
467
				} else {
468
					// Other complex structure
469
					String complexValue = "{...}";     //$NON-NLS-1$
470
					setValue(dmc.getFormatID(), complexValue);
471
					rm.setData(new FormattedValueDMData(complexValue));
472
					rm.done();
473
				}
474
				
475
				return;
476
			}
477
478
			if (locked) {
479
				operationsPending.add(new RequestMonitor(fSession.getExecutor(), rm) {
480
					@Override
481
					protected void handleSuccess() {
482
						getValue(dmc, rm);
483
					}
484
				});
485
			} else {
486
				lock();
487
488
				// If the format is already the one set for this variable object,
489
				// we don't need to set it again
490
				if (dmc.getFormatID().equals(getCurrentFormat())) {
491
					evaluate(rm);								
492
				} else {
493
					// We must first set the new format and then evaluate the variable
494
					fCommandControl.queueCommand(
495
							new MIVarSetFormat(getRootToUpdate().getControlDMContext(), getGdbName(), dmc.getFormatID()), 
496
							new DataRequestMonitor<MIVarSetFormatInfo>(fSession.getExecutor(), rm) {
497
								@Override
498
								protected void handleCompleted() {
499
									if (isSuccess()) {
500
										setCurrentFormat(dmc.getFormatID());
501
										
502
										// If set-format returned the value, no need to evaluate
503
										// This is only valid after GDB 6.7
504
										if (getData().getValue() != null) {
505
    										setValue(dmc.getFormatID(), getData().getValue());
506
											rm.setData(new FormattedValueDMData(getData().getValue()));
507
											rm.done();
508
											
509
											// Unlock is done within this method
510
											resetFormatToNatural();
511
										} else {
512
										    evaluate(rm);
513
										}
514
									} else {
515
										rm.setStatus(getStatus());
516
										rm.done();
517
518
										unlock();
519
									}
520
								}
521
							});
522
				}
523
			}
524
		}
525
526
		/** 
527
		 * This method evaluates a variable object 
528
		 */
529
		private void evaluate(final DataRequestMonitor<FormattedValueDMData> rm) {
530
			fCommandControl.queueCommand(
531
					new MIVarEvaluateExpression(getRootToUpdate().getControlDMContext(), getGdbName()), 
532
					new DataRequestMonitor<MIVarEvaluateExpressionInfo>(fSession.getExecutor(), rm) {
533
						@Override
534
						protected void handleCompleted() {
535
							if (isSuccess()) {
536
								setValue(getCurrentFormat(), getData().getValue());
537
								rm.setData(new FormattedValueDMData(getData().getValue()));
538
								rm.done();
539
							} else {
540
								rm.setStatus(getStatus());
541
								rm.done();
542
							}
543
							
544
							// Unlock is done within this method
545
							resetFormatToNatural();
546
						}
547
					});
548
		}
549
550
		// In GDB, var-update will only report a change if -var-evaluate-expression has
551
		// changed -- in the current format--.  This means that situations like
552
		// double z = 1.2;
553
		// z = 1.4;
554
		// Will not report a change if the format is anything else than natural.
555
		// This is because 1.2 and 1.4 are both printed as 1, 0x1, etc
556
		// Since we cache the values of every format, we must know if -any- format has
557
		// changed, not just the current one.
558
		// To solve this, we always do an update in the natural format; I am not aware
559
		// of any case where the natural format would stay the same, but another format
560
		// would change.  However, since a var-update update all children as well,
561
		// we must make sure these children are also in the natural format
562
		// The simplest way to do this is that whenever we change the format
563
		// of a variable object, we immediately set it back to natural with a second
564
		// var-set-format command.
565
		private void resetFormatToNatural() {
566
			if (!getCurrentFormat().equals(IFormattedValues.NATURAL_FORMAT)) {
567
				fCommandControl.queueCommand(
568
						new MIVarSetFormat(getRootToUpdate().getControlDMContext(), getGdbName(), IFormattedValues.NATURAL_FORMAT), 
569
						new DataRequestMonitor<MIVarSetFormatInfo>(fSession.getExecutor(), null) {
570
							@Override
571
							protected void handleCompleted() {
572
								if (isSuccess()) {
573
									setCurrentFormat(IFormattedValues.NATURAL_FORMAT);
574
								}
575
								unlock();
576
							}
577
						});
578
			} else {
579
				unlock();
580
			}			
581
		}
582
583
		/** 
584
		 * This method returns the list of children of the variable object passed as a parameter.
585
		 * 
586
		 * @param rm
587
		 *            The data request monitor that will hold the children returned
588
		 */
589
		private void getChildren(final MIExpressionDMC exprDmc, final DataRequestMonitor<ExpressionInfo[]> rm) {
590
			
591
	        // If we already know the children, no need to go to the back-end
592
			ExpressionInfo[] childrenArray = getChildren();
593
	        if (childrenArray != null) {
594
	            rm.setData(childrenArray);
595
	            rm.done();
596
	            return;
597
	        }
598
	        
599
			// If the variable does not have children, we can return an empty list right away
600
			if (getNumChildren() == 0) {
601
	        	// First store the empty list, for the next time
602
				setChildren(new ExpressionInfo[0]);
603
				rm.setData(getChildren());
604
				rm.done();
605
				return;
606
			}
607
	        
608
	        // For arrays (which could be very large), we create the children ourselves.  This is
609
	        // to avoid creating an enormous amount of children variable objects that the view may
610
	        // never need.  Using -var-list-children will create a variable object for every child
611
	        // immediately, that is why won't don't want to use it for arrays.
612
	        if (isArray()) {
613
	        	ExpressionInfo[] childrenOfArray = new ExpressionInfo[getNumChildren()];
614
	        	for (int i= 0; i < childrenOfArray.length; i++) {
615
	        		String indexStr = "[" + i + "]";//$NON-NLS-1$//$NON-NLS-2$
616
	        		String fullExpr = exprDmc.getExpression() + indexStr;
617
	        		String relExpr = exprDmc.getRelativeExpression() + indexStr;
618
619
	        		childrenOfArray[i] = new ExpressionInfo(fullExpr, relExpr);
620
	        	}
621
622
	        	// First store these children, for the next time
623
				setChildren(childrenOfArray);
624
				rm.setData(getChildren());
625
	        	rm.done();
626
	        	return;
627
	        }
628
	        
629
	        // No need to wait for the object to be ready since listing children can be performed
630
	        // at any time, as long as the object is created, which we know it is, since we can only
631
	        // be called here with a fully created object.
632
	        // Also no need to lock the object, since getting the children won't affect other operations
633
	        fCommandControl.queueCommand(
634
	        		new MIVarListChildren(getRootToUpdate().getControlDMContext(), getGdbName()),
635
	        		new DataRequestMonitor<MIVarListChildrenInfo>(fSession.getExecutor(), rm) {
636
	        			@Override
637
	        			protected void handleSuccess() {
638
	        				MIVar[] children = getData().getMIVars();
639
	        				final List<ExpressionInfo> realChildren = new ArrayList<ExpressionInfo>();
640
641
	        				final CountingRequestMonitor countingRm = new CountingRequestMonitor(fSession.getExecutor(), rm) {
642
	        					@Override
643
	        					protected void handleSuccess() {
644
	        						// Store the children in our variable object cache
645
	        						setChildren(realChildren.toArray(new ExpressionInfo[realChildren.size()]));
646
	        						rm.setData(getChildren());
647
	        						rm.done();
648
	        					}
649
	        				};
650
651
	        				int numSubRequests = 0;
652
	        				for (final MIVar child : children) {
653
	        					// These children get created automatically as variable objects in GDB, so we should
654
	        					// add them to the LRU.
655
	        					// Note that if this variable object already exists, we can be in three scenarios:
656
	        					// 1- the existing object is the same variable object in GDB.  In this case,
657
	        					//    the existing and new one are identical so we can keep either one.
658
	        					// 2- the existing object is out-of-scope and should be replaced by the new one.
659
	        					//    This can happen if a root was found to be out-of-scope, but this child
660
	        					//    had not been accessed and therefore had not been removed.
661
	        					// 3- the existing object is an in-scope root object representing the same expression.
662
	        					//    In this case, either object can be kept and the other one can be deleted.
663
	        					//    The existing root could currently be in-use by another operation and may
664
	        					//    not be deleted; but since we can only have one entry in the LRU, we are
665
	        					//    forced to keep the existing root.  Note that we need not worry about
666
	        					//    the newly created child since it will automatically be deleted when
667
	        					//    its root is deleted.
668
669
	        					numSubRequests++;
670
	        					
671
	        					final DataRequestMonitor<String> childPathRm =
672
	        						new DataRequestMonitor<String>(fSession.getExecutor(), countingRm) {
673
	        						@Override
674
	        						protected void handleSuccess() {
675
	        							String childFullExpression = getData();
676
677
	        							// For children that do not map to a real expression (such as f.public)
678
	        							// GDB returns an empty string.  In this case, we can use another unique
679
	        							// name, such as the variable name
680
	        							boolean fakeChild = false;
681
	        							if (childFullExpression.length() == 0) {
682
	        								fakeChild = true;
683
	        								childFullExpression = child.getVarName();
684
	        							}
685
686
	        							// Now try to see if we already have this variable object in our Map
687
	        							// Since our map names use the expression, and not the GDB given
688
	        							// name, we must determine the correct map name from the varName
689
	        							VariableObjectId childId = new VariableObjectId();
690
	        							childId.generateId(childFullExpression, getInternalId());
691
	        							MIVariableObject childVar = lruVariableList.get(childId);
692
693
	        							// Note that we must check the root to know if it is out-of-scope.
694
	        							// We cannot check the child as it has not be updated and its
695
	        							// outOfScope variable is not updated either.
696
	        							if (childVar != null && childVar.getRootToUpdate().isOutOfScope()) {
697
	        								childVar.deleteInGdb();
698
	        								childVar = null;
699
	        							}
700
701
	        							if (childVar == null) {
702
	        								childVar = new MIVariableObject(childId, MIVariableObject.this);
703
	        								childVar.setGdbName(child.getVarName());
704
	        								childVar.setExpressionData(
705
	        										childFullExpression,
706
	        										child.getType(), 
707
	        										child.getNumChild());
708
709
	        								// This will replace any existing entry
710
	        								lruVariableList.put(childId, childVar);
711
712
	        								// Is this new child a modifiable descendant of the root?
713
	        								if (childVar.isModifiable()) {
714
	        									getRootToUpdate().addModifiableDescendant(child.getVarName(), childVar);
715
	        								}
716
	        							}
717
718
	        							if (fakeChild) {
719
	        								// This is just a qualifier level of C++, and we must
720
	        								// get the children of this child to get the real children
721
	        								childVar.getChildren(
722
	        										exprDmc,
723
	        										new DataRequestMonitor<ExpressionInfo[]>(fSession.getExecutor(), countingRm) {
724
	        											@Override
725
	        											protected void handleSuccess() {
726
	        												ExpressionInfo[] vars = getData();
727
	        												for (ExpressionInfo realChild : vars) {
728
	        													realChildren.add(realChild);
729
	        												}
730
	        												countingRm.done();
731
	        											}
732
	        										});
733
	        							} else {
734
	        								// This is a real child
735
	        								realChildren.add(new ExpressionInfo(childFullExpression, child.getExp()));
736
	        								countingRm.done();
737
	        							}
738
	        						}
739
	        					};
740
741
742
	        					if (isAccessQualifier(child.getExp())) {	        						
743
	        						// This is just a qualifier level of C++, so we don't need
744
	        						// to call -var-info-path-expression for real, but just pretend we did.
745
	        						childPathRm.setData("");  //$NON-NLS-1$
746
	        						childPathRm.done();
747
	        					} else {
748
	        						// To build the child id, we need the fully qualified expression which we
749
	        						// can get from -var-info-path-expression starting from GDB 6.7 
750
	        						fCommandControl.queueCommand(
751
	        								new MIVarInfoPathExpression(getRootToUpdate().getControlDMContext(), child.getVarName()),
752
	    	        						new DataRequestMonitor<MIVarInfoPathExpressionInfo>(fSession.getExecutor(), childPathRm) {
753
	        	        						@Override
754
	        	        						protected void handleCompleted() {
755
	        	        							if (isSuccess()) {
756
	        	        								childPathRm.setData(getData().getFullExpression());
757
	        	        							} else {
758
	        	        								// If we don't have var-info-path-expression
759
	        	        								// build the expression ourselves
760
	        	        								// Note that this does not work well yet
761
	        	        	    						childPathRm.setData(buildChildExpression(exprDmc.getExpression(), child.getExp()));
762
	        	        							}
763
        	        								childPathRm.done();
764
	        	        						}
765
	        								});
766
	        					}
767
	        				}
768
769
	        				countingRm.setDoneCount(numSubRequests);
770
	        			}
771
	        		});
772
		}
773
		
774
		/**
775
		 * This method builds a child expression based on its parent's expression.
776
		 * It is a fallback solution for when GDB doesn't support the var-info-path-expression.
777
		 * 
778
		 * Currently, this does not support inherited class such as
779
		 * class foo : bar {
780
		 * ...
781
		 * }
782
		 * because we'll create foo.bar instead of (bar)foo.
783
		 */
784
		private String buildChildExpression(String parentExp, String childExp) {
785
			// For pointers, the child expression is already contained in the parent,
786
			// so we must simply prefix with *
787
		    //  See Bug219179 for more information.
788
			if (isPointer()) {
789
				return "*("+parentExp+")"; //$NON-NLS-1$//$NON-NLS-2$
790
			}
791
792
		    return parentExp + "." + childExp; //$NON-NLS-1$
793
		    // No need for a special case for arrays since we deal with arrays differently
794
		    // and don't call this method for them
795
		}
796
797
		/** 
798
		 * This method returns the count of children of the variable object passed as a parameter.
799
		 * 
800
		 * @param rm
801
		 *            The data request monitor that will hold the count of children returned
802
		 */
803
		private void getChildrenCount(final DataRequestMonitor<Integer> rm) {
804
			// No need to lock the object or wait for it to be ready since this operation does not
805
			// affect other operations
806
			rm.setData(getNumChildren());
807
			rm.done();
808
		}
809
		
810
811
		
812
		/** 
813
		 * This method request the back-end to change the value of the variable object.
814
		 * 
815
		 * @param value
816
		 *            The new value.
817
		 *  @param formatId
818
		 *            The format the new value is specified in.
819
		 * @param rm
820
		 *            The request monitor to indicate the operation is finished
821
		 */
822
		private void writeValue(String value, String formatId, final RequestMonitor rm) {
823
824
			// If the variable is a complex structure (including an array), then we cannot write to it
825
			if (isComplex()) {
826
				rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, 
827
						"Cannot change the value of a complex expression", null)); //$NON-NLS-1$
828
				rm.done();
829
				return;
830
	        }
831
			
832
			// First deal with the format.  For GDB, the way to specify a format is to prefix the value with
833
			// 0x for hex, 0 for octal etc  So we need to make sure that 'value' has this prefix.
834
			// Note that there is no way to specify a binary format for GDB up to and including
835
			// GDB 6.7.1, so we convert 'value' into a decimal format.  
836
			// If the formatId is NATURAL, we do nothing for now because it is more complicated.
837
			// For example for a bool, a value of "true" is correct and should be left as is,
838
			// but for a pointer a value of 16 should be sent to GDB as 0x16.  To figure this out,
839
			// we need to know the type of the variable, which we don't have yet.
840
			
841
			if (formatId.equals(IFormattedValues.HEX_FORMAT)) {
842
				if (!value.startsWith("0x")) value = "0x" + value;  //$NON-NLS-1$  //$NON-NLS-2$
843
			}
844
			else if (formatId.equals(IFormattedValues.OCTAL_FORMAT)) {
845
				if (!value.startsWith("0")) value = "0" + value;  //$NON-NLS-1$  //$NON-NLS-2$
846
			}
847
			else if (formatId.equals(IFormattedValues.BINARY_FORMAT)) {
848
				// convert from binary to decimal
849
				if (value.startsWith("0b")) value = value.substring(2, value.length());  //$NON-NLS-1$
850
				try {
851
	    			value = Integer.toString(Integer.parseInt(value, 2));
852
				} catch (NumberFormatException e) {
853
					rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, 
854
							"Invalid binary number: " + value, e)); //$NON-NLS-1$
855
					rm.done();
856
					return;
857
				}
858
				
859
				formatId = IFormattedValues.DECIMAL_FORMAT;
860
			}
861
			else if (formatId.equals(IFormattedValues.DECIMAL_FORMAT)) {
862
				// nothing to do
863
			}
864
			else if (formatId.equals(IFormattedValues.NATURAL_FORMAT)) {
865
				// we do nothing for now and let the user have put in the proper value
866
			}
867
			else {
868
				rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, 
869
						"Unknown format: " + formatId, null)); //$NON-NLS-1$
870
				rm.done();
871
				return;
872
			}
873
			
874
			// If the value has not changed, no need to set it.
875
			// Return a warning status so that handleSuccess is not called and we don't send
876
			// an ExpressionChanged event
877
			if (value.equals(getValue(formatId))) {
878
				rm.setStatus(new Status(IStatus.WARNING, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, 
879
						"Setting to the same value of: " + value, null)); //$NON-NLS-1$
880
				rm.done();
881
				return;				
882
			}
883
884
			// No need to be in ready state or to lock the object
885
			fCommandControl.queueCommand(
886
					new MIVarAssign(getRootToUpdate().getControlDMContext(), getGdbName(), value),
887
					new DataRequestMonitor<MIVarAssignInfo>(fSession.getExecutor(), rm) {
888
						@Override
889
						protected void handleSuccess() {
890
							// We must also mark all variable objects
891
							// as out-of-date. This is because some variable objects may be affected
892
							// by this one having changed.
893
							// e.g., 
894
							//    int i;  
895
							//    int* pi = &i;
896
							// Here, if 'i' is changed by the user, then 'pi' will also change
897
							// Since there is no way to know this unless we keep track of all addresses,
898
							// we must mark everything as out-of-date.  See bug 213061
899
							markAllOutOfDate();
900
							
901
							// Useless since we just marked everything as out-of-date
902
							// resetValues(getData().getValue());
903
							
904
							rm.done();
905
						}
906
					});
907
		}
908
909
		private boolean isAccessQualifier(String str) {
910
			return str.equals("private") || str.equals("public") || str.equals("protected");  //$NON-NLS-1$  //$NON-NLS-2$  //$NON-NLS-3$ 
911
	    }
912
913
		public boolean isModifiable() {
914
			if (!isComplex()) return true;
915
			return false;
916
		}
917
	}
918
	
919
	private class MIRootVariableObject extends MIVariableObject {
920
921
		// Only root variables go through the GDB creation process
922
		protected static final int STATE_NOT_CREATED = 10;
923
		protected static final int STATE_CREATING = 11;
924
		
925
		// The control context within which this variable object was created
926
		// It only needs to be stored in the Root VarObj since any children
927
		// will have the same control context
928
	    private ICommandControlDMContext fControlContext = null;
929
	    
930
		private boolean outOfDate = false;
931
		
932
	    // Modifiable descendants are any variable object that is a descendant or itself for
933
	    // which the value can change.
934
		private Map<String, MIVariableObject> modifiableDescendants;
935
936
		public MIRootVariableObject(VariableObjectId id) {
937
			super(id, null);
938
			currentState = STATE_NOT_CREATED;
939
			modifiableDescendants = new HashMap<String, MIVariableObject>();
940
		}
941
942
		public ICommandControlDMContext getControlDMContext() { return fControlContext; }
943
944
		public boolean isUpdating() { return currentState == STATE_UPDATING; }
945
946
		public void markAsOutOfDate() { outOfDate = true; }
947
		
948
		// Remember that we must add ourself as a modifiable descendant if our value can change
949
		public void addModifiableDescendant(String gdbName, MIVariableObject descendant) {
950
			modifiableDescendants.put(gdbName, descendant);
951
		}
952
		
953
		public void processChanges(MIVarChange[] updates) {
954
			for (MIVarChange update : updates) {
955
				MIVariableObject descendant = modifiableDescendants.get(update.getVarName());
956
				// Descendant should never be null, but just to be safe
957
				if (descendant != null) descendant.resetValues(update.getValue());
958
			}
959
		}
960
		
961
		public void create(final IExpressionDMContext exprCtx,
962
                           final RequestMonitor rm) {
963
964
			if (currentState == STATE_NOT_CREATED) {
965
				
966
				currentState = STATE_CREATING;
967
				fControlContext = DMContexts.getAncestorOfType(exprCtx, ICommandControlDMContext.class);
968
969
				fCommandControl.queueCommand(
970
						new MIVarCreate(exprCtx, exprCtx.getExpression()), 
971
						new DataRequestMonitor<MIVarCreateInfo>(fSession.getExecutor(), rm) {
972
							@Override
973
							protected void handleCompleted() {
974
								if (isSuccess()) {
975
									setGdbName(getData().getName());
976
									setExpressionData(
977
											exprCtx.getExpression(), 
978
											getData().getType(), 
979
											getData().getNumChildren());
980
									
981
									// Store the value returned at create (available in GDB 6.7)
982
									// Don't store if it is an array, since we want to show
983
									// the address of an array as its value
984
									if (getData().getValue() != null && !isArray()) { 
985
										setValue(getCurrentFormat(), getData().getValue());
986
									}
987
									
988
									// If we are modifiable, we should be in our modifiable list
989
									if (isModifiable()) {
990
										addModifiableDescendant(getData().getName(), MIRootVariableObject.this);
991
									}
992
								} else {
993
									rm.setStatus(getStatus());
994
								}
995
								
996
								rm.done();
997
							}
998
						});
999
			} else {
1000
				assert false;
1001
			}
1002
		}
1003
		
1004
		private void creationCompleted(boolean success) {
1005
			// A creation completed we must be up-to-date, so we
1006
			// can tell any pending monitors that updates are done
1007
			if (success) {
1008
				currentState = STATE_READY;
1009
				while (updatesPending.size() > 0) {
1010
					DataRequestMonitor<Boolean> rm = updatesPending.poll();
1011
					// Nothing to be re-created
1012
					rm.setData(false);
1013
					rm.done();
1014
				}
1015
			} else {
1016
				currentState = STATE_NOT_CREATED;
1017
1018
				// Creation failed, inform anyone waiting.
1019
				while (updatesPending.size() > 0) {
1020
					RequestMonitor rm = updatesPending.poll();
1021
		            rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, 
1022
		            		"Unable to create variable object", null)); //$NON-NLS-1$
1023
					rm.done();
1024
				}
1025
			}
1026
		}
1027
1028
		@Override
1029
		public void update(final DataRequestMonitor<Boolean> rm) {
1030
1031
			if (isOutOfScope()) {
1032
		    	rm.setData(false);
1033
				rm.done();
1034
			} else if (currentState != STATE_READY) {
1035
				// Object is not fully created or is being updated
1036
				// so add RequestMonitor to pending queue
1037
				updatesPending.add(rm);
1038
			} else if (outOfDate == false) {
1039
				rm.setData(false);
1040
				rm.done();
1041
			} else {
1042
				// Object needs to be updated in the back-end
1043
				currentState = STATE_UPDATING;
1044
1045
				// In GDB, var-update will only report a change if -var-evaluate-expression has
1046
				// changed -- in the current format--.  This means that situations like
1047
				// double z = 1.2;
1048
				// z = 1.4;
1049
				// Will not report a change if the format is anything else than natural.
1050
				// This is because 1.2 and 1.4 are both printed as 1, 0x1, etc
1051
				// Since we cache the values of every format, we must know if -any- format has
1052
				// changed, not just the current one.
1053
				// To solve this, we always do an update in the natural format; I am not aware
1054
				// of any case where the natural format would stay the same, but another format
1055
				// would change.  However, since a var-update update all children as well,
1056
			    // we must make sure these children are also in the natural format
1057
				// The simplest way to do this is that whenever we change the format
1058
				// of a variable object, we immediately set it back to natural with a second
1059
				// var-set-format command.  This is done in the getValue() method
1060
				fCommandControl.queueCommand(
1061
						new MacOSMIVarUpdate(getRootToUpdate().getControlDMContext(), getGdbName()),
1062
						new DataRequestMonitor<MacOSMIVarUpdateInfo>(fSession.getExecutor(), rm) {
1063
							@Override
1064
							protected void handleCompleted() {
1065
								currentState = STATE_READY;
1066
								
1067
								if (isSuccess()) {
1068
									outOfDate = false;
1069
1070
									MIVarChange[] changes = getData().getMIVarChanges();
1071
									if (changes.length > 0 && changes[0].isInScope() == false) {
1072
										// Object is out-of-scope
1073
										outOfScope = true;
1074
										
1075
										// We can delete this root in GDB right away.  This is safe, even
1076
									 	// if the root has children, because they are also out-of-scope.
1077
										// We -must- also remove this entry from our LRU.  If we don't
1078
										// we can end-up with a race condition that create this object
1079
										// twice, or have an infinite loop while never re-creating the object.
1080
										// The can happen if we update a child first then we request 
1081
										// the root later,
1082
										lruVariableList.remove(getInternalId());
1083
1084
										rm.setData(true);
1085
										rm.done();
1086
									} else {
1087
										// The root object is now up-to-date, we must parse the changes, if any.
1088
										processChanges(changes);
1089
1090
										// We only mark this root as updated in our list if it is in-scope.
1091
										// For out-of-scope object, we don't ever need to re-update them so
1092
										// we don't need to add them to this list.
1093
										updatedRootList.add(MIRootVariableObject.this);
1094
1095
										rm.setData(false);
1096
										rm.done();
1097
									}
1098
1099
									while (updatesPending.size() > 0) {
1100
										DataRequestMonitor<Boolean> pendingRm = updatesPending.poll();
1101
										pendingRm.setData(false);
1102
										pendingRm.done();
1103
									}
1104
								} else {
1105
									// We were not able to update for some reason
1106
									rm.setData(false);
1107
									rm.done();
1108
1109
									while (updatesPending.size() > 0) {
1110
										DataRequestMonitor<Boolean> pendingRm = updatesPending.poll();
1111
										pendingRm.setStatus(getStatus());
1112
										pendingRm.done();
1113
									}
1114
								}
1115
							}
1116
						});
1117
		    }
1118
		}
1119
1120
		/**
1121
		 * This method request the back-end to delete a variable object.
1122
		 * We check if the GDB name has been filled to confirm that this object
1123
		 * was actually successfully created on the back-end.
1124
		 * Only root variable objects are deleted, while children are left in GDB 
1125
		 * to be deleted automatically when their root is deleted.
1126
		 */
1127
		@Override
1128
		public void deleteInGdb() {
1129
		    if (getGdbName() != null) {
1130
	    		fCommandControl.queueCommand(
1131
	    				new MIVarDelete(getRootToUpdate().getControlDMContext(), getGdbName()),
1132
	    				new DataRequestMonitor<MIVarDeleteInfo>(fSession.getExecutor(), null));
1133
		        // Nothing to do in the requestMonitor, since the object was already
1134
		        // removed from our list before calling this method.
1135
1136
		    	// Set the GDB name to null to make sure we don't attempt to delete
1137
		    	// this variable a second time.  This can happen if the LRU triggers
1138
		    	// an automatic delete.
1139
		    	setGdbName(null);
1140
		    } else {
1141
		        // Variable was never created or was already deleted, no need to do anything.
1142
		    }
1143
		}		
1144
		
1145
1146
	}
1147
	
1148
	/**
1149
	 * This class represents an unique identifier for a variable object.
1150
	 * 
1151
	 * The following must be considered to obtain a unique name:
1152
	 *     - the expression itself
1153
	 *     - the execution context 
1154
	 *     - relative depth of frame based on the frame context and the total depth of the stack
1155
	 *     
1156
	 * Note that if no frameContext is specified (only Execution, or even only Container), which can
1157
	 * characterize a global variable for example, we will only use the available information.
1158
	 */
1159
	private class VariableObjectId {
1160
		// We don't use the expression context because it is not safe to compare them
1161
		// See bug 187718.  So we store the expression itself, and it's parent execution context.
1162
		String fExpression = null;
1163
		IExecutionDMContext fExecContext = null;
1164
		// We need the depth of the frame.  The frame level is not sufficient because 
1165
        // the same frame will have a different level based on the current depth of the stack 
1166
		Integer fFrameId = null;
1167
		
1168
		@Override
1169
		public boolean equals(Object other) {
1170
			if (other instanceof VariableObjectId) {
1171
				VariableObjectId otherId = (VariableObjectId) other;
1172
				return (fExpression == null ? otherId.fExpression == null : fExpression.equals(otherId.fExpression)) &&
1173
    				(fExecContext == null ? otherId.fExecContext == null : fExecContext.equals(otherId.fExecContext)) &&
1174
                    (fFrameId == null ? otherId.fFrameId == null : fFrameId.equals(otherId.fFrameId));
1175
			}
1176
			return false;
1177
		}
1178
1179
		@Override
1180
		public int hashCode() {
1181
			return (fExpression == null ? 0 : fExpression.hashCode()) + 
1182
		           (fExecContext == null ? 0 : fExecContext.hashCode()) +
1183
			       (fFrameId  == null ? 0 : fFrameId.hashCode());
1184
		}
1185
1186
		public void generateId(IExpressionDMContext exprCtx, final RequestMonitor rm) {
1187
			fExpression = exprCtx.getExpression();
1188
1189
			fExecContext = DMContexts.getAncestorOfType(exprCtx, IExecutionDMContext.class);
1190
			if (fExecContext == null) {
1191
				rm.done();
1192
				return;
1193
			}
1194
			
1195
			final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class);
1196
			if (frameCtx == null) {
1197
				rm.done();
1198
				return;
1199
			}
1200
1201
			// We need the current stack depth to be able to make a unique and reproducible name
1202
			// for this expression.  This is pretty efficient since the stackDepth will be retrieved
1203
			// from the StackService command cache after the first time.
1204
			fStackService.getStackDepth(
1205
					fExecContext, 0,
1206
					new DataRequestMonitor<Integer>(fSession.getExecutor(), rm) {
1207
						@Override
1208
						protected void handleSuccess() {
1209
							fFrameId = new Integer(getData() - frameCtx.getLevel());
1210
							rm.done();
1211
						}
1212
					});
1213
		}
1214
		
1215
		public void generateId(String childFullExp, VariableObjectId parentId) {
1216
			// The execution context and the frame depth are the same as the parent
1217
			fExecContext = parentId.fExecContext;
1218
			fFrameId = parentId.fFrameId;
1219
			// The expression here must be the one that is part of IExpressionContext for this child
1220
			// This will allow us to find a variable object directly
1221
			fExpression = childFullExp;
1222
		}
1223
	}
1224
	
1225
	/**
1226
	 * This is the real work horse of managing our objects. Not only must every
1227
	 * value be unique to get inserted, this also creates an LRU (least recently
1228
	 * used). When we hit our size limitation, the LRUsed will be removed to
1229
	 * make space. Removing means that a GDB request to delete the object is
1230
	 * generated.  We must also take into consideration the fact that GDB will
1231
	 * automatically delete children of a variable object, when deleting the parent
1232
	 * variable object.  Our solution to that is to tweak the LRU to make sure that 
1233
	 * children are always older than their parents, to guarantee the children will 
1234
	 * always be delete before their parents.
1235
	 * 
1236
	 */
1237
	private class LRUVariableCache extends LinkedHashMap<VariableObjectId, MIVariableObject> {
1238
		public static final long serialVersionUID = 0;
1239
1240
		// Maximum allowed concurrent variables
1241
		private static final int MAX_VARIABLE_LIST = 1000;
1242
		
1243
		public LRUVariableCache() {
1244
			super(0,     // Initial load capacity
1245
				  0.75f, // Load factor as defined in JAVA 1.5
1246
				  true); // Order is dictated by access, not insertion
1247
		}
1248
1249
		// We never remove doing put operations.  Instead, we rely on our get() operations
1250
		// to trigger the remove.  See bug 200897
1251
		@Override
1252
		public boolean removeEldestEntry(Map.Entry<VariableObjectId, MIVariableObject> eldest) {
1253
		    return false;
1254
		}
1255
1256
		@Override
1257
		public MIVariableObject get(Object key) {
1258
			MIVariableObject varObj = super.get(key);
1259
		    touchAncestors(varObj);
1260
		    
1261
		    // If we're over our max size, attempt to remove eldest entry.
1262
		    if (size() > MAX_VARIABLE_LIST) {
1263
		    	Map.Entry<VariableObjectId, MIVariableObject> eldest = entrySet().iterator().next();
1264
		    	// First make sure we are not deleting ourselves!
1265
		    	if (!eldest.getValue().equals(varObj)) {
1266
		    		if (eldest.getValue().currentState == MIVariableObject.STATE_READY) {
1267
		    			remove(eldest.getKey());
1268
		    		}
1269
		    	}
1270
		    }
1271
		    return varObj;
1272
		}
1273
		
1274
		private void touchAncestors(MIVariableObject varObj) {
1275
			while (varObj != null) {
1276
				varObj = varObj.getParent();
1277
				// If there is a parent, touch it
1278
				if (varObj != null) super.get(varObj.getInternalId());
1279
			}
1280
		}
1281
1282
        @Override
1283
		public MIVariableObject put(VariableObjectId key, MIVariableObject varObj) {
1284
        	MIVariableObject retVal = super.put(key, varObj);
1285
1286
            // Touch all parents of this element so as
1287
            // to guarantee they are not deleted before their children.
1288
            touchAncestors(varObj);
1289
1290
            return retVal;
1291
        }
1292
1293
		@Override
1294
		public MIVariableObject remove(Object key) {
1295
			MIVariableObject varObj = super.remove(key);
1296
			varObj.deleteInGdb();
1297
			return varObj; 
1298
		}
1299
	}
1300
1301
	private final DsfSession fSession;
1302
	
1303
	/** Provides access to the GDB/MI back-end */
1304
	private final ICommandControl fCommandControl;
1305
	// The stack service needs to be used to get information such
1306
	// as the stack depth to differentiate between expressions that have the
1307
	// same name but refer to a different context
1308
	private final IStack fStackService;
1309
	private IExpressions fExpressionService;
1310
1311
	// Typically, there will only be one listener, since only the ExpressionService will use this class
1312
    private final List<ICommandListener> fCommandProcessors = new ArrayList<ICommandListener>();
1313
    
1314
	/** Our least recently used cache */
1315
	private final LRUVariableCache lruVariableList;
1316
	
1317
	/** The list of root variable objects that have been updated */
1318
	private final LinkedList<MIRootVariableObject> updatedRootList = new LinkedList<MIRootVariableObject>();
1319
1320
	/**
1321
	 * MIVariableManager constructor
1322
	 * 
1323
	 * @param session
1324
	 *            The session we are working with
1325
	 * @param tracker
1326
	 *            The service tracker that can be used to find other services
1327
	 */
1328
	public MacOSGDBVariableManager(DsfSession session, DsfServicesTracker tracker) {
1329
	    fSession = session;
1330
		lruVariableList = new LRUVariableCache();
1331
		fCommandControl = tracker.getService(ICommandControl.class);
1332
		fStackService  = tracker.getService(IStack.class);
1333
		fExpressionService = tracker.getService(IExpressions.class);
1334
		
1335
		// Register to receive service events for this session.
1336
        fSession.addServiceEventListener(this, null);
1337
	}
1338
1339
	public void dispose() {
1340
    	fSession.removeServiceEventListener(this);
1341
	}
1342
1343
	/** 
1344
	 * This method returns a variable object based on the specified
1345
	 * ExpressionDMC, creating it in GDB if it was not created already.
1346
	 * The method guarantees that the variable is finished creating and that
1347
	 * is it not out-of-scope.
1348
	 * 
1349
	 * @param exprCtx
1350
	 *            The expression context to which the variable object is applied to.
1351
	 * 
1352
	 * @param rm
1353
	 *            The data request monitor that will contain the requested variable object
1354
	 */
1355
	private void getVariable(final IExpressionDMContext exprCtx,
1356
                             final DataRequestMonitor<MIVariableObject> rm) {
1357
		// Generate an id for this expression so that we can determine if we already
1358
		// have a variable object tracking it.  If we don't we'll need to create one.
1359
		final VariableObjectId id = new VariableObjectId();
1360
		id.generateId(
1361
				exprCtx,
1362
				new RequestMonitor(fSession.getExecutor(), rm) {
1363
					@Override
1364
					protected void handleSuccess() {
1365
					    getVariable(id, exprCtx, rm);
1366
					}
1367
				});
1368
	}
1369
1370
	private void getVariable(final VariableObjectId id,
1371
			                 final IExpressionDMContext exprCtx,
1372
			                 final DataRequestMonitor<MIVariableObject> rm) {
1373
1374
		final MIVariableObject varObj = lruVariableList.get(id);
1375
1376
		if (varObj == null) {
1377
			// We do not have this varObject, so we create it
1378
			createVariable(id, exprCtx, rm);
1379
		} else {
1380
			// We have found a varObject, but it may not be updated yet.
1381
			// Updating the object will also tell us if it is out-of-scope
1382
			// and if we should re-create it.
1383
			varObj.update(new DataRequestMonitor<Boolean>(fSession.getExecutor(), rm) {
1384
				@Override
1385
				protected void handleSuccess() {
1386
					
1387
					boolean shouldCreateNew = getData().booleanValue();
1388
					
1389
					if (varObj.isOutOfScope()) {
1390
					    // The variable object is out-of-scope and we
1391
						// should not use it.
1392
						if (shouldCreateNew) {
1393
							/**
1394
							 * It may happen that when accessing a varObject we find it to be
1395
							 * out-of-scope.  The expression for which we are trying to access a varObject
1396
							 * could still be valid, and therefore we should try to create a new varObject for
1397
							 * that expression.  This can happen for example if two methods use the same name
1398
							 * for a variable. In the case when we find that a varObject is out-of-scope (when
1399
							 * its root is out-of-scope) the following should be done:
1400
						     *
1401
						     * - create a new varObject for the expression (as a root varObject) and insert it
1402
						     * in the LRU.  Make sure that when creating children of this new varObject, they 
1403
						     * will replace any old children with the same name in the LRU (this is ok since the 
1404
						     * children being replaced are also out-of-scope).
1405
							 */
1406
							
1407
						    createVariable(id, exprCtx, rm);
1408
						} else {
1409
							// Just request the variable object again
1410
							// We must use this call to handle the fact that
1411
							// the new object might be in the middle of being
1412
							// created.
1413
							getVariable(id, exprCtx, rm);
1414
						}
1415
					} else {
1416
						// The variable object is up-to-date and valid
1417
						rm.setData(varObj);
1418
						rm.done();
1419
					}
1420
				}
1421
			});
1422
		}
1423
	}
1424
1425
	
1426
	
1427
	/**
1428
	 * This method creates a variable object in GDB.
1429
	 */
1430
	private void createVariable(final VariableObjectId id, 
1431
			                    final IExpressionDMContext exprCtx,
1432
                                final DataRequestMonitor<MIVariableObject> rm) {
1433
1434
		// Variable objects that are created directly like this, are considered ROOT variable objects
1435
		// in comparison to variable objects that are children of other variable objects.
1436
		final MIRootVariableObject newVarObj = new MIRootVariableObject(id);
1437
		
1438
		// We must put this object in our map right away, in case it is 
1439
		// requested again, before it completes its creation.
1440
		// Note that this will replace any old entry with the same id.
1441
		lruVariableList.put(id, newVarObj);
1442
		
1443
		newVarObj.create(exprCtx, new RequestMonitor(fSession.getExecutor(), rm) {
1444
			@Override
1445
			protected void handleCompleted() {
1446
				if (isSuccess()) {
1447
					// Also store the object as a varObj that is up-to-date
1448
					updatedRootList.add(newVarObj);
1449
					// VarObj can now be used by others
1450
					newVarObj.creationCompleted(true);
1451
1452
					rm.setData(newVarObj);
1453
					rm.done();
1454
				} else {
1455
					// Object was not created, remove it from our list
1456
					lruVariableList.remove(id);
1457
					// Tell any waiting monitors that creation failed.
1458
					// It is important to do this call after we have removed the id
1459
					// from our LRU; this is to avoid the following:
1460
					//   The same varObj is requested before it was removed from the LRU
1461
					//   but after we called creationCompleted().  
1462
					//   In this case, the request for this varObj would be queued, but  
1463
					//   since creationCompleted() already sent the notifications
1464
					//   the newly queue request will never get serviced.
1465
					// We avoid this race condition by sending the notifications _after_ removing 
1466
					// the object from the LRU, to avoid any new requests being queue.
1467
					// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=231655
1468
					newVarObj.creationCompleted(false);
1469
1470
					rm.setStatus(getStatus());
1471
					rm.done();
1472
				}
1473
			}
1474
		});
1475
	}
1476
1477
	/** 
1478
	 * This method requests the back-end to change the value of an expression.
1479
	 * 
1480
	 * @param expressionContext  
1481
	 *            The context of the expression we want to change
1482
	 * @param expressionValue
1483
	 *            The new value of the expression
1484
	 * @param formatId
1485
	 *            The format in which the expressionValue is specified in
1486
	 * @param rm
1487
	 *            The request monitor to indicate the operation is finished
1488
	 */
1489
	// This method can be called directly from the ExpressionService, since it cannot be cached
1490
	public void writeValue(final IExpressionDMContext ctx, final String expressionValue, 
1491
			final String formatId, final RequestMonitor rm) {
1492
		
1493
		getVariable(
1494
				ctx, 
1495
				new DataRequestMonitor<MIVariableObject>(fSession.getExecutor(), rm) {
1496
					@Override
1497
					protected void handleSuccess() {
1498
						getData().writeValue(expressionValue, formatId, rm);
1499
					}
1500
				});
1501
	}
1502
1503
    public <V extends ICommandResult> ICommandToken queueCommand(final ICommand<V> command, DataRequestMonitor<V> rm) {
1504
    	
1505
        final ICommandToken token = new ICommandToken() {
1506
            public ICommand<? extends ICommandResult> getCommand() {
1507
                return command;
1508
            }
1509
        };
1510
        
1511
    	// The MIVariableManager does not buffer commands itself, but sends them directly to the real
1512
    	// MICommandControl service.  Therefore, we must immediately tell our calling cache that the command
1513
    	// has been sent, since we can never cancel it.  Note that this removes any option of coalescing, 
1514
    	// but coalescing was not applicable to variableObjects anyway.
1515
    	processCommandSent(token);
1516
    	
1517
    	if (command instanceof ExprMetaGetVar) {
1518
            @SuppressWarnings("unchecked")            
1519
    		final DataRequestMonitor<ExprMetaGetVarInfo> drm = (DataRequestMonitor<ExprMetaGetVarInfo>)rm;
1520
            final MIExpressionDMC exprCtx = (MIExpressionDMC)(command.getContext());
1521
            
1522
            getVariable(
1523
            		exprCtx, 
1524
            		new DataRequestMonitor<MIVariableObject>(fSession.getExecutor(), drm) {
1525
            			@Override
1526
            			protected void handleSuccess() {
1527
            				drm.setData(
1528
            						new ExprMetaGetVarInfo(
1529
            								exprCtx.getRelativeExpression(),
1530
            								getData().getNumChildren(), 
1531
            								getData().getType(),
1532
            								!getData().isComplex()));
1533
            				drm.done();
1534
            				processCommandDone(token, drm.getData());
1535
            			}
1536
            		});
1537
        } else if (command instanceof ExprMetaGetAttributes) {
1538
            @SuppressWarnings("unchecked")        
1539
            final DataRequestMonitor<ExprMetaGetAttributesInfo> drm = (DataRequestMonitor<ExprMetaGetAttributesInfo>)rm;
1540
            final IExpressionDMContext exprCtx = (IExpressionDMContext)(command.getContext());
1541
 
1542
            getVariable(
1543
                    exprCtx, 
1544
                    new DataRequestMonitor<MIVariableObject>(fSession.getExecutor(), drm) {
1545
                        @Override
1546
                        protected void handleSuccess() {
1547
                            getData().getAttributes(
1548
                                    new DataRequestMonitor<Boolean>(fSession.getExecutor(), drm) {
1549
                                        @Override
1550
                                        protected void handleSuccess() {
1551
                                            drm.setData(new ExprMetaGetAttributesInfo(getData()));
1552
                                            drm.done();
1553
                                            processCommandDone(token, drm.getData());
1554
                                        }
1555
                                    }); 
1556
                        }
1557
                    });
1558
            
1559
1560
    	} else if (command instanceof ExprMetaGetValue) {
1561
            @SuppressWarnings("unchecked")            
1562
    		final DataRequestMonitor<ExprMetaGetValueInfo> drm = (DataRequestMonitor<ExprMetaGetValueInfo>)rm;
1563
            final FormattedValueDMContext valueCtx = (FormattedValueDMContext)(command.getContext());
1564
            final IExpressionDMContext exprCtx = DMContexts.getAncestorOfType(valueCtx, IExpressionDMContext.class);
1565
            
1566
    		getVariable(
1567
    				exprCtx, 
1568
    			    new DataRequestMonitor<MIVariableObject>(fSession.getExecutor(), drm) {
1569
    	                @Override
1570
    	                protected void handleSuccess() {
1571
    	                	getData().getValue(
1572
    	                			valueCtx, 
1573
    	                			new DataRequestMonitor<FormattedValueDMData>(fSession.getExecutor(), drm) {
1574
    	                    			@Override
1575
    	                    			protected void handleSuccess() {
1576
    	                    				drm.setData(
1577
    	                    						new ExprMetaGetValueInfo(getData().getFormattedValue()));
1578
    	                    				drm.done();
1579
    	                                    processCommandDone(token, drm.getData());
1580
    	                    			}
1581
    	                    		});
1582
    	                }
1583
    			    });
1584
    	
1585
    	} else if (command instanceof ExprMetaGetChildren) {
1586
            @SuppressWarnings("unchecked")            
1587
    		final DataRequestMonitor<ExprMetaGetChildrenInfo> drm = (DataRequestMonitor<ExprMetaGetChildrenInfo>)rm;
1588
            final MIExpressionDMC exprCtx = (MIExpressionDMC)(command.getContext());
1589
            
1590
    		getVariable(
1591
    				exprCtx, 
1592
    				new DataRequestMonitor<MIVariableObject>(fSession.getExecutor(), drm) {
1593
    					@Override
1594
    					protected void handleSuccess() {
1595
    						getData().getChildren(
1596
    								exprCtx,
1597
    								new DataRequestMonitor<ExpressionInfo[]>(fSession.getExecutor(), drm) {
1598
    									@Override
1599
    									protected void handleSuccess() {
1600
    										drm.setData(new ExprMetaGetChildrenInfo(getData()));
1601
    										drm.done();
1602
    			                            processCommandDone(token, drm.getData());
1603
    									}
1604
    								});
1605
    					}
1606
    				});
1607
    	
1608
    	} else if (command instanceof ExprMetaGetChildCount) {
1609
            @SuppressWarnings("unchecked")            
1610
    		final DataRequestMonitor<ExprMetaGetChildCountInfo> drm = (DataRequestMonitor<ExprMetaGetChildCountInfo>)rm;
1611
            final IExpressionDMContext exprCtx = (IExpressionDMContext)(command.getContext());
1612
 
1613
    		getVariable(
1614
    				exprCtx, 
1615
    				new DataRequestMonitor<MIVariableObject>(fSession.getExecutor(), drm) {
1616
    					@Override
1617
    					protected void handleSuccess() {
1618
    						getData().getChildrenCount(
1619
    								new DataRequestMonitor<Integer>(fSession.getExecutor(), drm) {
1620
    									@Override
1621
    									protected void handleSuccess() {
1622
    										drm.setData(new ExprMetaGetChildCountInfo(getData()));
1623
    										drm.done();
1624
    			                            processCommandDone(token, drm.getData());
1625
    									}
1626
    								}); 
1627
    					}
1628
    				});
1629
    	
1630
    	} else if (command instanceof MIDataEvaluateExpression) {
1631
    		// This does not use the variable objects but sends the command directly to the back-end
1632
			fCommandControl.queueCommand(command, rm);
1633
    	} else {
1634
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, 
1635
					"Unexpected Expression Meta command", null)); //$NON-NLS-1$
1636
			rm.done();
1637
    	}
1638
    	return token;
1639
    }
1640
    
1641
    /*
1642
     *   This is the command which allows the user to retract a previously issued command. The
1643
     *   state of the command  is that it is in the waiting queue  and has not yet been handed 
1644
     *   to the back-end yet.
1645
     *   
1646
     * (non-Javadoc)
1647
     * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#removeCommand(org.eclipse.cdt.dsf.mi.service.command.commands.ICommand)
1648
     */
1649
    public void removeCommand(ICommandToken token) {
1650
    	// It is impossible to remove a command from the MIVariableManager.
1651
    	// This should never be called, if we did things right.
1652
    	assert false;
1653
    }
1654
   
1655
    /*
1656
     *  This command allows the user to try and cancel commands  which have been handed off to the 
1657
     *  back-end. Some back-ends support this with extended GDB/MI commands. If the support is there
1658
     *  then we will attempt it.  Because of the bidirectional nature of the GDB/MI command stream
1659
     *  there is no guarantee that this will work. The response to the command could be on its way
1660
     *  back when the cancel command is being issued.
1661
     *  
1662
     * (non-Javadoc)
1663
     * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#cancelCommand(org.eclipse.cdt.dsf.mi.service.command.commands.ICommand)
1664
     */
1665
    public void addCommandListener(ICommandListener processor) { fCommandProcessors.add(processor); }
1666
    public void removeCommandListener(ICommandListener processor) { fCommandProcessors.remove(processor); }    
1667
    public void addEventListener(IEventListener processor) {}
1668
    public void removeEventListener(IEventListener processor) {}
1669
1670
    
1671
    private void processCommandSent(ICommandToken token) {
1672
        for (ICommandListener processor : fCommandProcessors) {
1673
            processor.commandSent(token);
1674
        }
1675
    }
1676
1677
    private void processCommandDone(ICommandToken token, ICommandResult result) {
1678
        for (ICommandListener processor : fCommandProcessors) {
1679
            processor.commandDone(token, result);
1680
        }
1681
    }
1682
    
1683
    public void markAllOutOfDate() {
1684
    	MIRootVariableObject root;
1685
    	while ((root = updatedRootList.poll()) != null) {
1686
    		root.markAsOutOfDate();
1687
    	}       
1688
    }
1689
1690
    @DsfServiceEventHandler 
1691
    public void eventDispatched(IRunControl.IResumedDMEvent e) {
1692
    	// Program has resumed, all variable objects need to be updated.
1693
    	// Since only roots can actually be updated in GDB, we only need
1694
    	// to deal with those.  Also, to optimize this operation, we have
1695
    	// a list of all roots that have been updated, so we only have to
1696
    	// set those to needing to be updated.
1697
    	markAllOutOfDate();
1698
    }
1699
    
1700
    @DsfServiceEventHandler 
1701
    public void eventDispatched(IRunControl.ISuspendedDMEvent e) {
1702
    }
1703
    
1704
    @DsfServiceEventHandler 
1705
    public void eventDispatched(IMemoryChangedEvent e) {
1706
    	// Some memory has changed.  We currently do not know the address
1707
    	// of each of our variable objects, so there is no way to know
1708
    	// which one is affected.  Mark them all as out of date.
1709
    	// The views will fully refresh on a MemoryChangedEvent
1710
    	markAllOutOfDate();
1711
    }
1712
}
(-)src/org/eclipse/cdt/dsf/mi/service/command/commands/macos/MacOSMIVarUpdate.java (+49 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2010 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
7
 *
8
 * Contributors:
9
 *     QNX Software Systems - Initial API and implementation
10
 *     Wind River Systems   - Modified for new DSF Reference Implementation
11
 *     Ericsson				- Modified for handling of frame contexts
12
 *******************************************************************************/
13
14
package org.eclipse.cdt.dsf.mi.service.command.commands.macos;
15
16
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
17
import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand;
18
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
19
import org.eclipse.cdt.dsf.mi.service.command.output.macos.MacOSMIVarUpdateInfo;
20
21
/**
22
 * 
23
 *     -var-update [print-values] {NAME | "*"}
24
 *
25
 *  Update the value of the variable object NAME by evaluating its
26
 *  expression after fetching all the new values from memory or registers.
27
 *  A `*' causes all existing variable objects to be updated.
28
 *  If print-values has a value for of 0 or --no-values, print only the names of the variables; 
29
 *  if print-values is 1 or --all-values, also print their values; 
30
 *  if it is 2 or --simple-values print the name and value for simple data types and just 
31
 *  the name for arrays, structures and unions. 
32
 *  
33
 *  It seems that for MacOS, we must use the full string for print-values, such as
34
 *  --all-values.
35
 * 
36
 * @since 2.1
37
 */
38
public class MacOSMIVarUpdate extends MICommand<MacOSMIVarUpdateInfo> {
39
40
	public MacOSMIVarUpdate(ICommandControlDMContext dmc, String name) {
41
		// Must use --all-values instead of 1 for Mac OS
42
		super(dmc, "-var-update", new String[] { "--all-values", name }); //$NON-NLS-1$//$NON-NLS-2$
43
	}
44
	
45
    @Override
46
    public MacOSMIVarUpdateInfo getResult(MIOutput out) {
47
        return new MacOSMIVarUpdateInfo(out);
48
    }
49
}
(-)src/org/eclipse/cdt/dsf/mi/service/command/output/macos/MacOSMIVarUpdateInfo.java (+123 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2010 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
7
 *
8
 * Contributors:
9
 *     QNX Software Systems - Initial API and implementation
10
 *     Ericsson             - Created version for Mac OS
11
 *******************************************************************************/
12
package org.eclipse.cdt.dsf.mi.service.command.output.macos;
13
14
import java.util.ArrayList;
15
import java.util.List;
16
17
import org.eclipse.cdt.dsf.mi.service.command.output.MIConst;
18
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
19
import org.eclipse.cdt.dsf.mi.service.command.output.MIList;
20
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
21
import org.eclipse.cdt.dsf.mi.service.command.output.MIResult;
22
import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord;
23
import org.eclipse.cdt.dsf.mi.service.command.output.MITuple;
24
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
25
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarChange;
26
27
/**
28
 * GDB/MI var-update for Mac OS.
29
 * -var-update *
30
 * ^done,changelist=[varobj={name="var1",in_scope="true",type_changed="false"}],time={.....}
31
 * 
32
 * @since 2.1
33
 */
34
public class MacOSMIVarUpdateInfo extends MIInfo {
35
36
	MIVarChange[] changeList;
37
38
	public MacOSMIVarUpdateInfo(MIOutput record) {
39
		super(record);
40
        List<MIVarChange> aList = new ArrayList<MIVarChange>();
41
        if (isDone()) {
42
            MIOutput out = getMIOutput();
43
            MIResultRecord rr = out.getMIResultRecord();
44
            if (rr != null) {
45
                MIResult[] results =  rr.getMIResults();
46
                for (int i = 0; i < results.length; i++) {
47
                    String var = results[i].getVariable();
48
                    if (var.equals("changelist")) { //$NON-NLS-1$
49
                        MIValue value = results[i].getMIValue();
50
                        if (value instanceof MITuple) {
51
                            parseChangeList((MITuple)value, aList);
52
                        } else if (value instanceof MIList) {
53
                            parseChangeList((MIList)value, aList);
54
                        }
55
                    }
56
                }
57
            }
58
        }
59
        changeList = aList.toArray(new MIVarChange[aList.size()]);
60
	}
61
62
	public MIVarChange[] getMIVarChanges() {
63
		return changeList;
64
	}
65
66
	/**
67
	 * For MI2 the format is now a MIList.
68
	 * @param tuple
69
	 * @param aList
70
	 */
71
	void parseChangeList(MIList miList, List<MIVarChange> aList) {
72
		// The MIList in Apple gdb contains MIResults instead of MIValues. It looks like:
73
		// ^done,changelist=[varobj={name="var1",in_scope="true",type_changed="false"}],time={.....}
74
		// Bug 250037
75
		MIResult[] results = miList.getMIResults();
76
		for (int i = 0; i < results.length; i++) {
77
			String var = results[i].getVariable();
78
			if (var.equals("varobj")) { //$NON-NLS-1$
79
				MIValue value = results[i].getMIValue();
80
				if (value instanceof MITuple) {
81
					parseChangeList((MITuple)value, aList);
82
				} else if (value instanceof MIList) {
83
					parseChangeList((MIList)value, aList);
84
				}
85
			}
86
		}
87
	} 
88
	
89
	void parseChangeList(MITuple tuple, List<MIVarChange> aList) {
90
		MIResult[] results = tuple.getMIResults();
91
		MIVarChange change = null;
92
		for (int i = 0; i < results.length; i++) {
93
			String var = results[i].getVariable();
94
			MIValue value = results[i].getMIValue();
95
			if (value instanceof MITuple) {
96
				parseChangeList((MITuple)value, aList);
97
			}
98
			else
99
			{
100
				String str = ""; //$NON-NLS-1$
101
				if (value instanceof MIConst) {
102
					str = ((MIConst)value).getString();
103
				}
104
				if (var.equals("name")) { //$NON-NLS-1$
105
					change = new MIVarChange(str);
106
					aList.add(change);
107
				} else if (var.equals("value")) { //$NON-NLS-1$
108
					if (change != null) {
109
						change.setValue(str);
110
					}
111
				} else if (var.equals("in_scope")) { //$NON-NLS-1$
112
					if (change != null) {
113
						change.setInScope("true".equals(str)); //$NON-NLS-1$
114
					}
115
				} else if (var.equals("type_changed")) { //$NON-NLS-1$
116
					if (change != null) {
117
						change.setChanged("true".equals(str)); //$NON-NLS-1$
118
					}
119
				}				
120
			}
121
		}
122
	}
123
}

Return to bug 298883