/*******************************************************************************
* Copyright (c) 2006, 2010 Wind River Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
* Ericsson - Modified for handling of multiple execution contexts
* Axel Mueller - Bug 306555 - Add support for cast to type / view as array (IExpressions2)
* Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121)
*******************************************************************************/
package org.eclipse.cdt.dsf.mi.service;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IExpressions;
import org.eclipse.cdt.dsf.debug.service.IExpressions2;
import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent;
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
import org.eclipse.cdt.dsf.gdb.GDBTypeParser.GDBType;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordSelectedChangedDMEvent;
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetAttributes;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildren;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetValue;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetVar;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.Addr32;
import org.eclipse.cdt.utils.Addr64;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;
/**
* This class implements a debugger expression evaluator as a DSF service. The
* primary interface that clients of this class should use is IExpressions.
*
* This class used to be name ExpressionService in the 1.1 release.
*
* @since 2.0
*/
public class MIExpressions extends AbstractDsfService implements IMIExpressions, ICachingService {
/**
* A format that gives more details about an expression and supports pretty-printing
* provided by the backend.
*
* @since 3.0
*/
public static final String DETAILS_FORMAT = "Details"; //$NON-NLS-1$
/* The order given here is the order that will be used by DSF in the Details Pane */
private static final String[] FORMATS_SUPPORTED = new String[] {
DETAILS_FORMAT,
IFormattedValues.NATURAL_FORMAT,
IFormattedValues.DECIMAL_FORMAT,
IFormattedValues.HEX_FORMAT,
IFormattedValues.BINARY_FORMAT,
IFormattedValues.OCTAL_FORMAT };
/**
* This class represents the two expressions that characterize an Expression Context.
*/
public static class ExpressionInfo {
private final String fullExpression;
private final String relativeExpression;
private boolean isDynamic = false;
private ExpressionInfo parent;
private int indexInParent = -1;
private int childCountLimit = IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED;
public ExpressionInfo(String full, String relative) {
fullExpression = full;
relativeExpression = relative;
}
/**
* @since 4.0
*/
public ExpressionInfo(String full, String relative, boolean isDynamic,
ExpressionInfo parent, int indexInParent) {
fullExpression = full;
relativeExpression = relative;
this.isDynamic = isDynamic;
this.parent = parent;
this.indexInParent = indexInParent;
}
public String getFullExpr() { return fullExpression; }
public String getRelExpr() { return relativeExpression; }
@Override
public boolean equals(Object other) {
if (other instanceof ExpressionInfo) {
if (fullExpression == null ? ((ExpressionInfo) other).fullExpression == null :
fullExpression.equals(((ExpressionInfo) other).fullExpression)) {
if (relativeExpression == null ? ((ExpressionInfo) other).relativeExpression == null :
relativeExpression.equals(((ExpressionInfo) other).relativeExpression)) {
// The other members don't play any role for equality.
return true;
}
}
}
return false;
}
@Override
public int hashCode() {
return (fullExpression == null ? 0 : fullExpression.hashCode()) ^
(relativeExpression == null ? 0 : relativeExpression.hashCode());
// The other members don't play any role for equality.
}
@Override
public String toString() {
return "[" + fullExpression +", " + relativeExpression + ", isDynamic=" + isDynamic + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$
}
/**
* @return The parent expression info, if existing.
* @since 4.0
*/
public ExpressionInfo getParent() {
return parent;
}
/**
* @return The index in the child array of the parent. Only valid if
* {@link #getParent()} returns not null.
* @since 4.0
*/
public int getIndexInParentExpression() {
return indexInParent;
}
/**
* @return Whether the corresponding variable object is dynamic,
* i.e. it's value and children are provided by a pretty printer.
* @since 4.0
*/
public boolean isDynamic() {
return isDynamic;
}
/**
* @return Whether the expression info has any ancestor that is dynamic.
* @since 4.0
*/
public boolean hasDynamicAncestor() {
for (ExpressionInfo parent = getParent(); parent != null; parent = parent.getParent()) {
if (parent.isDynamic()) {
return true;
}
}
return false;
}
/**
* @param isDynamic
* Whether the value and children of this expression is
* currently provided by a pretty printer or not.
* @since 4.0
*/
public void setDynamic(boolean isDynamic) {
this.isDynamic = isDynamic;
}
/**
* @param parent The new parent expression info.
* @since 4.0
*/
public void setParent(ExpressionInfo parent) {
this.parent = parent;
}
/**
* @param index The index in the children array of the parent.
* @since 4.0
*/
public void setIndexInParent(int index) {
this.indexInParent = index;
}
/**
* @return The current limit on the number of children to be fetched.
* @since 4.0
*/
public int getChildCountLimit() {
return childCountLimit;
}
/**
* @param newLimit
* The new limit on the number of children to be fetched.
* @since 4.0
*/
public void setChildCountLimit(int newLimit) {
this.childCountLimit = newLimit;
}
}
/**
* This class represents an expression.
*/
protected static class MIExpressionDMC extends AbstractDMContext implements IExpressionDMContext {
/**
* This field holds an expression to be evaluated.
*/
private ExpressionInfo exprInfo;
/**
* ExpressionDMC Constructor for expression to be evaluated in context of
* a stack frame.
*
* @param sessionId
* The session ID in which this context is created.
* @param expression
* The expression to be described by this ExpressionDMC
* @param relExpr
* The relative expression if this expression was created as a child
* @param frameCtx
* The parent stack frame context for this ExpressionDMC.
*/
public MIExpressionDMC(String sessionId, String expression, String relExpr, IFrameDMContext frameCtx) {
this(sessionId, expression, relExpr, (IDMContext)frameCtx);
}
/**
* ExpressionDMC Constructor for expression to be evaluated in context of
* an thread.
*
* @param sessionId
* The session ID in which this context is created.
* @param expression
* The expression to be described by this ExpressionDMC
* @param relExpr
* The relative expression if this expression was created as a child
* @param execCtx
* The parent thread context for this ExpressionDMC.
*/
public MIExpressionDMC(String sessionId, String expression, String relExpr, IMIExecutionDMContext execCtx) {
this(sessionId, expression, relExpr, (IDMContext)execCtx);
}
/**
* ExpressionDMC Constructor for expression to be evaluated in context of
* a memory space.
*
* @param sessionId
* The session ID in which this context is created.
* @param expression
* The expression to be described by this ExpressionDMC
* @param relExpr
* The relative expression if this expression was created as a child
* @param memoryCtx
* The parent memory space context for this ExpressionDMC.
*/
public MIExpressionDMC(String sessionId, String expression, String relExpr, IMemoryDMContext memoryCtx) {
this(sessionId, expression, relExpr, (IDMContext)memoryCtx);
}
private MIExpressionDMC(String sessionId, String expr, String relExpr, IDMContext parent) {
this(sessionId, new ExpressionInfo(expr, relExpr), parent);
}
/**
* ExpressionDMC Constructor for expression to be evaluated in context
* of a stack frame.
*
* @param sessionId
* The session ID in which this context is created.
* @param info
* The expression info that this expression is to use.
* @param frameCtx
* The parent stack frame context for this ExpressionDMC.
*
* @since 4.0
*/
public MIExpressionDMC(String sessionId, ExpressionInfo info, IFrameDMContext frameCtx) {
this(sessionId, info, (IDMContext)frameCtx);
}
/**
* @since 4.0
*/
private MIExpressionDMC(String sessionId, ExpressionInfo info, IDMContext parent) {
super(sessionId, new IDMContext[] { parent });
exprInfo = info;
}
/**
* @return True if the two objects are equal, false otherwise.
*/
@Override
public boolean equals(Object other) {
return super.baseEquals(other) && exprInfo.equals(((MIExpressionDMC)other).exprInfo);
}
/**
*
* @return The hash code of this ExpressionDMC object.
*/
@Override
public int hashCode() {
return super.baseHashCode() + exprInfo.hashCode();
}
/**
*
* @return A string representation of this ExpressionDMC (including the
* expression to which it is bound).
*/
@Override
public String toString() {
return baseToString() + ".expr" + exprInfo.toString(); //$NON-NLS-1$
}
/**
* @return The full expression string represented by this ExpressionDMC
*/
public String getExpression() {
return exprInfo.getFullExpr();
}
/**
* @return The relative expression string represented by this ExpressionDMC
*/
public String getRelativeExpression() {
return exprInfo.getRelExpr();
}
/**
* @return Get the expression info for this context.
* @since 4.0
*/
public ExpressionInfo getExpressionInfo() {
return exprInfo;
}
/**
* @param info
*
* @since 4.0
*/
public void setExpressionInfo(ExpressionInfo info) {
assert (this.exprInfo.getFullExpr().equals(info.getFullExpr()));
assert (this.exprInfo.getRelExpr().equals(info.getRelExpr()));
this.exprInfo = info;
}
}
protected static class InvalidContextExpressionDMC extends AbstractDMContext
implements IExpressionDMContext
{
private final String expression;
public InvalidContextExpressionDMC(String sessionId, String expr, IDMContext parent) {
super(sessionId, new IDMContext[] { parent });
expression = expr;
}
@Override
public boolean equals(Object other) {
return super.baseEquals(other) &&
expression == null ? ((InvalidContextExpressionDMC) other).getExpression() == null : expression.equals(((InvalidContextExpressionDMC) other).getExpression());
}
@Override
public int hashCode() {
return expression == null ? super.baseHashCode() : super.baseHashCode() ^ expression.hashCode();
}
@Override
public String toString() {
return baseToString() + ".invalid_expr[" + expression + "]"; //$NON-NLS-1$ //$NON-NLS-2$
}
public String getExpression() {
return expression;
}
}
/**
* Contains the address of an expression as well as the size of its type.
*/
protected static class ExpressionDMAddress implements IExpressionDMAddress {
IAddress fAddr;
int fSize;
public ExpressionDMAddress(IAddress addr, int size) {
fAddr = addr;
fSize = size;
}
public ExpressionDMAddress(String addrStr, int size) {
fSize = size;
// We must count the "0x" and that
// is why we compare with 10 characters
// instead of 8
if (addrStr.length() <= 10) {
fAddr = new Addr32(addrStr);
} else {
fAddr = new Addr64(addrStr);
}
}
public IAddress getAddress() { return fAddr; }
public int getSize() { return fSize; }
@Override
public boolean equals(Object other) {
if (other instanceof ExpressionDMAddress) {
ExpressionDMAddress otherAddr = (ExpressionDMAddress) other;
return (fSize == otherAddr.getSize()) &&
(fAddr == null ? otherAddr.getAddress() == null : fAddr.equals(otherAddr.getAddress()));
}
return false;
}
@Override
public int hashCode() {
return (fAddr == null ? 0 :fAddr.hashCode()) + fSize;
}
@Override
public String toString() {
return (fAddr == null ? "null" : "(" + fAddr.toHexAddressString()) + ", " + fSize + ")"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
}
}
/**
* If an expressions doesn't have an address, or it cannot be determined,
* use this class.
* @since 4.0
*/
protected class InvalidDMAddress implements IExpressionDMLocation {
public IAddress getAddress() {
return IExpressions.IExpressionDMLocation.INVALID_ADDRESS;
}
public int getSize() {
return 0;
}
public String getLocation() {
return ""; //$NON-NLS-1$
}
}
/**
* This class represents the static data referenced by an instance of ExpressionDMC,
* such as its type and number of children; it does not contain the value or format
* of the expression.
*/
protected static class ExpressionDMData implements IExpressionDMDataExtension {
// This is the relative expression, such as the name of a field within a structure,
// in contrast to the fully-qualified expression contained in the ExpressionDMC,
// which refers to the full name, including parent structure.
private final String relativeExpression;
private final String exprType;
/**
* A hint at the number of children.
* In the case of C++ complex structures, this number will not be the
* actual number of children. This is because GDB considers
* 'private/protected/public' as an actual level of children, but
* we do not. This number is meant to be used to know if the expression
* has children at all.
*/
private final int numChildrenHint;
private final boolean editable;
private final BasicType fBasicType;
/**
* ExpressionDMData constructor.
*/
public ExpressionDMData(String expr, String type, int num, boolean edit) {
this (expr, type, num, edit, null);
}
/**
* ExpressionDMData constructor.
* @since 3.0
*/
public ExpressionDMData(String expr, String type, int num, boolean edit, BasicType basicType) {
relativeExpression = expr;
exprType = type;
numChildrenHint = num;
editable = edit;
fBasicType = basicType;
}
public BasicType getBasicType() {
return fBasicType;
}
public String getEncoding() {
return null;
}
public Map<String, Integer> getEnumerations() {
return new HashMap<String, Integer>();
}
public String getName() {
return relativeExpression;
}
public IRegisterDMContext getRegister() {
return null;
}
// See class VariableVMNode for an example of usage of this method
public String getStringValue() {
return null;
}
public String getTypeId() {
return null;
}
public String getTypeName() {
return exprType;
}
/**
* This method only returns a 'hint' to the number of children.
* In the case of C++ complex structures, this number will not be the
* actual number of children. This is because GDB considers
* 'private/protected/public' as an actual level of children, but
* we do not.
*
* This method can be used reliably to know if the expression
* does have children or not. However, for this particular use,
* the new {@link IExpressionDMDataExtension#hasChildren()} method should be used instead.
*
* To get the correct number of children of an expression, a call
* to {@link IExpressions#getSubExpressionCount} should be used.
*
* @deprecated
*/
@Deprecated
public int getNumChildren() {
return numChildrenHint;
}
public boolean isEditable() {
return editable;
}
/**
* @since 4.0
*/
public boolean hasChildren() {
return numChildrenHint > 0;
}
@Override
public boolean equals(Object other) {
if (other instanceof ExpressionDMData) {
ExpressionDMData otherData = (ExpressionDMData) other;
return (numChildrenHint == otherData.numChildrenHint) &&
(getTypeName() == null ? otherData.getTypeName() == null : getTypeName().equals(otherData.getTypeName())) &&
(getName() == null ? otherData.getName() == null : getName().equals(otherData.getName()));
}
return false;
}
@Override
public int hashCode() {
return relativeExpression == null ? 0 : relativeExpression.hashCode() +
exprType == null ? 0 : exprType.hashCode() + numChildrenHint;
}
@Override
public String toString() {
return "relExpr=" + relativeExpression + ", type=" + exprType + ", numchildren=" + numChildrenHint; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
}
}
/**
* Event generated every time an expression is changed by the ExpressionService.
*
* A client wishing to receive such events has to register as a service
* event listener and implement the corresponding eventDispatched method.
*
* E.g.:
*
* getSession().addServiceEventListener(listenerObject, null);
*
* @DsfServiceEventHandler
* public void eventDispatched(ExpressionChangedEvent e) {
* IExpressionDMContext context = e.getDMContext();
* // do something...
* }
*/
protected static class ExpressionChangedEvent extends AbstractDMEvent<IExpressionDMContext>
implements IExpressionChangedDMEvent {
public ExpressionChangedEvent(IExpressionDMContext context) {
super(context);
}
}
private CommandCache fExpressionCache;
private MIVariableManager varManager;
private CommandFactory fCommandFactory;
/**
* Indicates that we are currently visualizing trace data.
* In this case, some errors should not be reported.
*/
private boolean fTraceVisualization;
public MIExpressions(DsfSession session) {
super(session);
}
/**
* This method initializes this service.
*
* @param requestMonitor
* The request monitor indicating the operation is finished
*/
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize(new RequestMonitor(ImmediateExecutor.getInstance(), requestMonitor) {
@Override
protected void handleSuccess() {
doInitialize(requestMonitor);
}
});
}
/**
* This method initializes this service after our superclass's initialize()
* method succeeds.
*
* @param requestMonitor
* The call-back object to notify when this service's
* initialization is done.
*/
private void doInitialize(RequestMonitor requestMonitor) {
// Register to receive service events for this session.
getSession().addServiceEventListener(this, null);
// Register this service.
register(new String[] { IExpressions.class.getName(),
IExpressions2.class.getName(),
MIExpressions.class.getName() },
new Hashtable<String, String>());
// Create the expressionService-specific CommandControl which is our
// variable object manager.
// It will deal with the meta-commands, before sending real MI commands
// to the back-end, through the MICommandControl service
// It must be created after the ExpressionService is registered
// since it will need to find it.
varManager = createMIVariableManager();
// Create the meta command cache which will use the variable manager
// to actually send MI commands to the back-end
fExpressionCache = new CommandCache(getSession(), varManager);
ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class);
fExpressionCache.setContextAvailable(commandControl.getContext(), true);
fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
requestMonitor.done();
}
/**
* Creates the MI variable manager to be used by this expression service.
* Overriding classes may override to provide a custom services tracker.
*
* @since 3.0
*/
protected MIVariableManager createMIVariableManager() {
return new MIVariableManager(getSession(), getServicesTracker());
}
/**
* This method shuts down this service. It unregisters the service, stops
* receiving service events, and calls the superclass shutdown() method to
* finish the shutdown process.
*/
@Override
public void shutdown(RequestMonitor requestMonitor) {
unregister();
varManager.dispose();
getSession().removeServiceEventListener(this);
super.shutdown(requestMonitor);
}
/**
* @return The bundle context of the plug-in to which this service belongs.
*/
@Override
protected BundleContext getBundleContext() {
return GdbPlugin.getBundleContext();
}
/**
* Create an expression context with the same full and relative expression
*/
public IExpressionDMContext createExpression(IDMContext ctx, String expression) {
return createExpression(ctx, expression, expression);
}
/**
* Create an expression context.
*/
public IExpressionDMContext createExpression(IDMContext ctx, String expression, String relExpr) {
return createExpression(ctx, new ExpressionInfo(expression, relExpr));
}
/**
* Create an expression context from a given expression info.
* @since 4.0
*/
private IExpressionDMContext createExpression(IDMContext ctx, ExpressionInfo info) {
String expression = info.getFullExpr();
IFrameDMContext frameDmc = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class);
if (frameDmc != null) {
return new MIExpressionDMC(getSession().getId(), info, frameDmc);
}
IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class);
if (execCtx != null) {
// If we have a thread context but not a frame context, we give the user
// the expression as per the top-most frame of the specified thread.
// To do this, we create our own frame context.
MIStack stackService = getServicesTracker().getService(MIStack.class);
if (stackService != null) {
frameDmc = stackService.createFrameDMContext(execCtx, 0);
return new MIExpressionDMC(getSession().getId(), info, frameDmc);
}
return new InvalidContextExpressionDMC(getSession().getId(), expression, execCtx);
}
IMemoryDMContext memoryCtx = DMContexts.getAncestorOfType(ctx, IMemoryDMContext.class);
if (memoryCtx != null) {
return new MIExpressionDMC(getSession().getId(), info, memoryCtx);
}
// Don't care about the relative expression at this point
return new InvalidContextExpressionDMC(getSession().getId(), expression, ctx);
}
/**
* @see IFormattedValues.getFormattedValueContext(IFormattedDataDMContext, String)
*
* @param dmc
* The context describing the data for which we want to create
* a Formatted context.
* @param formatId
* The format that will be used to create the Formatted context
*
* @return A FormattedValueDMContext that can be used to obtain the value
* of an expression in a specific format.
*/
public FormattedValueDMContext getFormattedValueContext(
IFormattedDataDMContext dmc, String formatId) {
return new FormattedValueDMContext(this, dmc, formatId);
}
/**
* @see IFormattedValues.getAvailableFormats(IFormattedDataDMContext, DataRequestMonitor)
*
* @param dmc
* The context describing the data for which we want to know
* which formats are available.
* @param rm
* The data request monitor for this asynchronous operation.
*
*/
public void getAvailableFormats(IFormattedDataDMContext dmc,
final DataRequestMonitor<String[]> rm) {
rm.setData(FORMATS_SUPPORTED);
rm.done();
}
/**
* Obtains the static data of an expression represented
* by an ExpressionDMC object (<tt>dmc</tt>).
*
* @param dmc
* The ExpressionDMC for the expression to be evaluated.
* @param rm
* The data request monitor that will contain the requested data
*/
public void getExpressionData(
final IExpressionDMContext dmc,
final DataRequestMonitor<IExpressionDMData> rm)
{
if (dmc instanceof MIExpressionDMC) {
fExpressionCache.execute(
new ExprMetaGetVar(dmc),
new DataRequestMonitor<ExprMetaGetVarInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
IExpressionDMData.BasicType basicType = null;
GDBType gdbType = getData().getGDBType();
if (gdbType != null) {
switch (gdbType.getType()) {
case GDBType.ARRAY:
basicType = IExpressionDMData.BasicType.array;
break;
case GDBType.FUNCTION:
basicType = IExpressionDMData.BasicType.function;
break;
case GDBType.POINTER:
case GDBType.REFERENCE:
basicType = IExpressionDMData.BasicType.pointer;
break;
case GDBType.GENERIC:
default:
// The interesting question is not hasChildren,
// but canHaveChildren. E.g. an empty
// collection still is a composite.
if (getData().hasChildren() || getData().getCollectionHint()) {
basicType = IExpressionDMData.BasicType.composite;
} else {
basicType = IExpressionDMData.BasicType.basic;
}
break;
}
}
rm.setData(new ExpressionDMData(
getData().getExpr(),getData().getType(), getData().getNumChildren(),
getData().getEditable(), basicType));
rm.done();
}
});
} else if (dmc instanceof InvalidContextExpressionDMC) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
rm.done();
}
}
/**
* Obtains the address of an expression and the size of its type.
*
* @param dmc
* The ExpressionDMC for the expression.
* @param rm
* The data request monitor that will contain the requested data
*/
public void getExpressionAddressData(
IExpressionDMContext dmc,
final DataRequestMonitor<IExpressionDMAddress> rm) {
if (dmc instanceof MIExpressionDMC) {
MIExpressionDMC miDMC = (MIExpressionDMC) dmc;
if (miDMC.getExpressionInfo().hasDynamicAncestor()) {
// For children of dynamic varobjs, there is no full expression that gdb
// could evaluate in order to provide address and size.
rm.setData(new InvalidDMAddress());
rm.done();
return;
}
}
// First create an address expression and a size expression
// to be used in back-end calls
final IExpressionDMContext addressDmc =
createExpression( dmc, "&(" + dmc.getExpression() + ")" );//$NON-NLS-1$//$NON-NLS-2$
final IExpressionDMContext sizeDmc =
createExpression( dmc, "sizeof(" + dmc.getExpression() + ")" ); //$NON-NLS-1$//$NON-NLS-2$
if (addressDmc instanceof InvalidContextExpressionDMC || sizeDmc instanceof InvalidContextExpressionDMC) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
fExpressionCache.execute(
fCommandFactory.createMIDataEvaluateExpression(addressDmc),
new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
String tmpAddrStr = getData().getValue();
// Deal with addresses of contents of a char* which is in
// the form of "0x12345678 \"This is a string\""
int split = tmpAddrStr.indexOf(' ');
if (split != -1) tmpAddrStr = tmpAddrStr.substring(0, split);
final String addrStr = tmpAddrStr;
fExpressionCache.execute(
fCommandFactory.createMIDataEvaluateExpression(sizeDmc),
new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
try {
int size = Integer.parseInt(getData().getValue());
rm.setData(new ExpressionDMAddress(addrStr, size));
} catch (NumberFormatException e) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
"Unexpected size format from backend: " + getData().getValue(), null)); //$NON-NLS-1$
}
rm.done();
}
});
}
});
}
}
/**
* Obtains the value of an expression in a specific format.
*
* @param dmc
* The context for the format of the value requested and
* for the expression to be evaluated. The expression context
* should be a parent of the FormattedValueDMContext.
* @param rm
* The data request monitor that will contain the requested data
*/
public void getFormattedExpressionValue(
final FormattedValueDMContext dmc,
final DataRequestMonitor<FormattedValueDMData> rm)
{
// We need to make sure the FormattedValueDMContext also holds an ExpressionContext,
// or else this method cannot do its work.
// Note that we look for MIExpressionDMC and not IExpressionDMC, because
// looking for IExpressionDMC could yield InvalidContextExpressionDMC which is still
// not what we need.
MIExpressionDMC exprDmc = DMContexts.getAncestorOfType(dmc, MIExpressionDMC.class);
if (exprDmc == null ) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
if (DETAILS_FORMAT.equals(dmc.getFormatID())) {
if (exprDmc.getExpressionInfo().hasDynamicAncestor()) {
// -data-evaluate-expression does not work for children of
// dynamic varobjs, since there is no full expression
// that gdb could evaluate.
rm.setData(new FormattedValueDMData(Messages.MIExpressions_NotAvailableBecauseChildOfDynamicVarobj));
rm.done();
} else {
// This format is obtained through a different GDB command.
// It yields more details than the variableObject output.
// Starting with GDB 7.0, this format automatically supports pretty-printing, as long as
// GDB has been configured to support it.
fExpressionCache.execute(
fCommandFactory.createMIDataEvaluateExpression(exprDmc),
new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
rm.setData(new FormattedValueDMData(getData().getValue()));
rm.done();
}
@Override
protected void handleError() {
if (fTraceVisualization) {
rm.setData(new FormattedValueDMData("")); //$NON-NLS-1$
rm.done();
} else {
super.handleError();
}
}
});
}
} else {
fExpressionCache.execute(
new ExprMetaGetValue(dmc),
new DataRequestMonitor<ExprMetaGetValueInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
rm.setData(new FormattedValueDMData(getData().getValue()));
rm.done();
}
});
}
}
}
/* Not implemented
*
* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IExpressions#getBaseExpressions(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
*/
public void getBaseExpressions(IExpressionDMContext exprContext,
DataRequestMonitor<IExpressionDMContext[]> rm) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$
rm.done();
}
/**
* Retrieves the children expressions of the specified expression
*
* @param dmc
* The context for the expression for which the children
* should be retrieved.
* @param rm
* The data request monitor that will contain the requested data
*/
public void getSubExpressions(final IExpressionDMContext dmc,
final DataRequestMonitor<IExpressionDMContext[]> rm)
{
if (dmc instanceof MIExpressionDMC) {
fExpressionCache.execute(
new ExprMetaGetChildren(dmc),
new DataRequestMonitor<ExprMetaGetChildrenInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
ExpressionInfo[] childrenExpr = getData().getChildrenExpressions();
IExpressionDMContext[] childArray = new IExpressionDMContext[childrenExpr.length];
for (int i=0; i<childArray.length; i++) {
childArray[i] = createExpression(
dmc.getParents()[0], childrenExpr[i]);
}
rm.setData(childArray);
rm.done();
}
});
} else if (dmc instanceof InvalidContextExpressionDMC) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
rm.done();
}
}
/**
* Retrieves a range of children expressions of the specified expression
*
* @param exprCtx
* The context for the expression for which the children
* should be retrieved.
* @param startIndex
* The starting index within the list of all children of the parent
* expression. Must be a positive integer.
* @param length
* The length or number of elements of the range requested.
* Must be a positive integer.
* @param rm
* The data request monitor that will contain the requested data
*/
public void getSubExpressions(final IExpressionDMContext exprCtx, final int startIndex,
final int length, final DataRequestMonitor<IExpressionDMContext[]> rm) {
if (startIndex < 0 || length < 0) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$
rm.done();
return;
}
if (exprCtx instanceof MIExpressionDMC) {
fExpressionCache.execute(
new ExprMetaGetChildren(exprCtx, startIndex + length),
new DataRequestMonitor<ExprMetaGetChildrenInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
ExpressionInfo[] childrenExpr = getData().getChildrenExpressions();
if (startIndex >= childrenExpr.length) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, "Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$
rm.done();
return;
}
int numChildren = childrenExpr.length - startIndex;
numChildren = Math.min(length, numChildren);
IExpressionDMContext[] childrenArray = new IExpressionDMContext[numChildren];
for (int i=0; i < numChildren; i++) {
childrenArray[i] = createExpression(
exprCtx.getParents()[0], childrenExpr[startIndex + i]);
}
rm.setData(childrenArray);
rm.done();
}
});
} else if (exprCtx instanceof InvalidContextExpressionDMC) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
rm.done();
}
}
/**
* @since 4.0
*/
public void safeToAskForAllSubExpressions(IExpressionDMContext dmc,
final DataRequestMonitor<Boolean> rm) {
if (dmc instanceof MIExpressionDMC) {
fExpressionCache.execute(
new ExprMetaGetVar(dmc),
new DataRequestMonitor<ExprMetaGetVarInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
boolean safe = getData().isSafeToAskForAllChildren();
rm.setData(safe);
rm.done();
}
});
} else if (dmc instanceof InvalidContextExpressionDMC) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
rm.done();
}
}
/**
* @since 4.0
*/
public void getSubExpressionCount(IExpressionDMContext dmc,
final int numChildLimit, final DataRequestMonitor<Integer> rm) {
if (dmc instanceof MIExpressionDMC) {
fExpressionCache.execute(
new ExprMetaGetChildCount(dmc, numChildLimit),
new DataRequestMonitor<ExprMetaGetChildCountInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
rm.setData(getData().getChildNum());
rm.done();
}
});
} else if (dmc instanceof InvalidContextExpressionDMC) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
rm.done();
}
}
/**
* Retrieves the count of children expressions of the specified expression
*
* @param dmc
* The context for the expression for which the children count
* should be retrieved.
* @param rm
* The data request monitor that will contain the requested data
*/
public void getSubExpressionCount(IExpressionDMContext dmc,
final DataRequestMonitor<Integer> rm)
{
getSubExpressionCount(dmc, IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED, rm);
}
/**
* This method indicates if an expression can be written to.
*
* @param dmc The data model context representing an expression.
*
* @param rm Data Request monitor containing True if this expression's value can be edited. False otherwise.
*/
public void canWriteExpression(IExpressionDMContext dmc, final DataRequestMonitor<Boolean> rm) {
if (dmc instanceof MIExpressionDMC) {
fExpressionCache.execute(
new ExprMetaGetAttributes(dmc),
new DataRequestMonitor<ExprMetaGetAttributesInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
rm.setData(getData().getEditable());
rm.done();
}
});
} else if (dmc instanceof InvalidContextExpressionDMC) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
rm.done();
}
}
/**
* Changes the value of the specified expression based on the new value and format.
*
* @param dmc
* The context for the expression for which the value
* should be changed.
* @param expressionValue
* The new value for the specified expression
* @param formatId
* The format in which the value is specified
* @param rm
* The request monitor that will indicate the completion of the operation
*/
public void writeExpression(final IExpressionDMContext dmc, String expressionValue,
String formatId, final RequestMonitor rm) {
if (dmc instanceof MIExpressionDMC) {
// This command must not be cached, since it changes the state of the back-end.
// We must send it directly to the variable manager
varManager.writeValue(
dmc,
expressionValue,
formatId,
new RequestMonitor(getExecutor(), rm) {
@Override
protected void handleSuccess() {
// A value has changed, we should remove any references to that
// value in our cache. Since we don't have such granularity,
// we must clear the entire cache.
// We cannot use the context to do a more-specific reset, because
// the same global variable can be set with different contexts
fExpressionCache.reset();
// Issue event that the expression has changed
getSession().dispatchEvent(new ExpressionChangedEvent(dmc), getProperties());
rm.done();
}
});
} else if (dmc instanceof InvalidContextExpressionDMC) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
rm.done();
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$
rm.done();
}
}
@DsfServiceEventHandler
public void eventDispatched(IRunControl.IResumedDMEvent e) {
fExpressionCache.setContextAvailable(e.getDMContext(), false);
if (e.getReason() != StateChangeReason.STEP) {
fExpressionCache.reset();
}
}
@DsfServiceEventHandler
public void eventDispatched(IRunControl.ISuspendedDMEvent e) {
fExpressionCache.setContextAvailable(e.getDMContext(), true);
fExpressionCache.reset();
}
@DsfServiceEventHandler
public void eventDispatched(IMemoryChangedEvent e) {
fExpressionCache.reset();
// MIVariableManager separately traps this event
}
/** @since 3.0 */
@DsfServiceEventHandler
public void eventDispatched(ITraceRecordSelectedChangedDMEvent e) {
if (e.isVisualizationModeEnabled()) {
fTraceVisualization = true;
} else {
fTraceVisualization = false;
}
}
/**
* {@inheritDoc}
* @since 1.1
*/
public void flushCache(IDMContext context) {
fExpressionCache.reset(context);
// We must also mark all variable objects as out-of-date
// to refresh them as well
varManager.markAllOutOfDate();
}
/**
* A casted or array-displayed expression.
* @since 3.0
*/
protected class CastedExpressionDMC extends MIExpressionDMC implements ICastedExpressionDMContext {
private final CastInfo fCastInfo;
/** if non-null, interpret result as this type rather than the raw expression's type */
private String fCastExpression;
public CastedExpressionDMC(MIExpressionDMC exprDMC, String castExpression, CastInfo castInfo) {
super(getSession().getId(), exprDMC.getExpression(), exprDMC.getRelativeExpression(), exprDMC);
fCastInfo = castInfo;
fCastExpression = castExpression;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext#getCastInfo()
*/
public CastInfo getCastInfo() {
return fCastInfo;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.mi.service.MIExpressions.java#getExpression()
*/
@Override
public String getExpression() {
return fCastExpression;
}
/**
* @return True if the two objects are equal, false otherwise.
*/
@Override
public boolean equals(Object other) {
return super.equals(other)
&& fCastInfo.equals(((CastedExpressionDMC) other).fCastInfo);
}
@Override
public String toString() {
return baseToString() + ".expr" + "[" + //$NON-NLS-1$ //$NON-NLS-2$
getExpression() +", " + getRelativeExpression() + "]"; //$NON-NLS-1$//$NON-NLS-2$
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IExpressions2#createCastedExpression(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext)
*/
/** @since 3.0 */
public ICastedExpressionDMContext createCastedExpression(IExpressionDMContext exprDMC, CastInfo castInfo) {
if (exprDMC instanceof MIExpressionDMC && castInfo != null) {
String castType = castInfo.getTypeString();
String castExpression = exprDMC.getExpression();
int castingLength = castInfo.getArrayCount();
int castingIndex = castInfo.getArrayStartIndex();
// cast to type
if (castType != null && castType.length() > 0) {
StringBuffer buffer = new StringBuffer();
buffer.append('(').append(castType).append(')');
buffer.append('(').append(castExpression).append(')');
castExpression = buffer.toString();
}
// cast to array (can be in addition to cast to type)
if (castingLength > 0) {
StringBuffer buffer = new StringBuffer();
buffer.append("*("); //$NON-NLS-1$
buffer.append('(').append(castExpression).append(')');
buffer.append('+').append(castingIndex).append(')');
buffer.append('@').append(castingLength);
castExpression = buffer.toString();
}
return new CastedExpressionDMC((MIExpressionDMC) exprDMC, castExpression, castInfo);
} else {
assert false;
return null;
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IExpressions3#getExpressionDataExtension(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
*/
/** @since 4.0 */
public void getExpressionDataExtension(IExpressionDMContext dmc, final DataRequestMonitor<IExpressionDMDataExtension> rm) {
getExpressionData(dmc, new DataRequestMonitor<IExpressionDMData>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
rm.setData((IExpressionDMDataExtension)getData());
super.handleSuccess();
}
});
}
}