| /******************************************************************************* |
| * Copyright (c) 2006, 2016 Wind River Systems and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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) |
| * Marc Khouzam (Ericsson) - Added support for expression aliases for return values of functions (bug 341731) |
| * Abeer Bagul (Tensilica) - Extra partition created for arrays of length 20000 or greater (Bug 443687) |
| *******************************************************************************/ |
| package org.eclipse.cdt.dsf.mi.service; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| 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.ImmediateRequestMonitor; |
| 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.IExpressions3; |
| import org.eclipse.cdt.dsf.debug.service.IFormattedValues; |
| import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; |
| import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IMemorySpaces; |
| import org.eclipse.cdt.dsf.debug.service.IMemorySpaces.DecodeResult; |
| import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; |
| 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.events.IMIDMEvent; |
| import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent; |
| import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; |
| 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.mi.service.command.output.MIFrame; |
| import org.eclipse.cdt.dsf.service.AbstractDsfService; |
| import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; |
| import org.eclipse.cdt.dsf.service.DsfServicesTracker; |
| import org.eclipse.cdt.dsf.service.DsfSession; |
| import org.eclipse.cdt.utils.Addr32; |
| import org.eclipse.cdt.utils.Addr64; |
| import org.eclipse.core.runtime.CoreException; |
| 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 { |
| |
| private static final int PARTITION_LENGTH = 100; |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * @return if this expression is part of the memory space or not. |
| * If it not part of the memory space, it won't have an address. |
| * @since 4.3 |
| */ |
| public boolean inMemory() { |
| // Registers and convenience variables which both start with $ |
| // are not part of memory. We care about the top-most parent |
| // as it is the only one that can be a register or convenience var. |
| if (getParent() == null) { |
| if (getRelExpr().startsWith("$")) { //$NON-NLS-1$ |
| return false; |
| } |
| return true; |
| } |
| |
| return getParent().inMemory(); |
| } |
| } |
| |
| /** |
| * This class represents an expression. |
| * @noextend This class is not intended to be subclassed by clients. |
| * @since 4.3 |
| */ |
| public 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.3 |
| */ |
| public 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 |
| */ |
| @Override |
| 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$ |
| } |
| |
| @Override |
| public String getExpression() { |
| return expression; |
| } |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| protected static class IndexedPartitionDMC extends MIExpressionDMC implements IIndexedPartitionDMContext { |
| |
| final private MIExpressionDMC fParentExpression; |
| private final int fIndex; |
| private final int fLength; |
| |
| /** |
| * @deprecated This method does not keep track of casted expressions. |
| * It has been replaced by the constructor that takes an MIExpressionDMC |
| * as a parameter. |
| */ |
| @Deprecated |
| public IndexedPartitionDMC(String sessionId, ExpressionInfo parentInfo, IFrameDMContext frameCtx, int index, |
| int length) { |
| this(new MIExpressionDMC(sessionId, parentInfo, frameCtx), frameCtx, index, length); |
| } |
| |
| /** |
| * @since 4.2 |
| */ |
| public IndexedPartitionDMC(MIExpressionDMC parentExpr, int index, int length) { |
| this(parentExpr, getParentDmc(parentExpr), index, length); |
| } |
| |
| /** |
| * @param parentExpr The expression of the array. This can be a casted expression. |
| * This is not the parent that will be used in the context hierarchy, as we chose |
| * not to stack up partitions. |
| * @param parentDmc The frame or thread context that will be used as a parent in the context hierarchy. |
| */ |
| private IndexedPartitionDMC(MIExpressionDMC parentExpr, IDMContext parentDmc, int index, int length) { |
| super(parentExpr.getSessionId(), createExpressionInfo(parentExpr.getExpressionInfo(), index, length), |
| parentDmc); |
| fIndex = index; |
| fLength = length; |
| fParentExpression = parentExpr; |
| } |
| |
| /** |
| * Find the frame context that will be the parent of this partition in the context hierarchy. |
| * Not to be confused with the original parent array that contains the partition. That parent |
| * can be obtained using getParentExpressionContext() |
| */ |
| private static IDMContext getParentDmc(MIExpressionDMC parentExpr) { |
| IFrameDMContext frameDmc = DMContexts.getAncestorOfType(parentExpr, IFrameDMContext.class); |
| if (frameDmc != null) { |
| return frameDmc; |
| } |
| |
| IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(parentExpr, 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. |
| DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), |
| parentExpr.getSessionId()); |
| MIStack stackService = tracker.getService(MIStack.class); |
| tracker.dispose(); |
| |
| if (stackService != null) { |
| return stackService.createFrameDMContext(execCtx, 0); |
| } |
| } |
| |
| return parentExpr; |
| } |
| |
| public ExpressionInfo getParentInfo() { |
| return fParentExpression.getExpressionInfo(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.debug.service.IExpressions4.IIndexedPartitionDMContext#getParentExpression() |
| */ |
| @Override |
| public String getParentExpression() { |
| return getParentExpressionContext().getExpression(); |
| } |
| |
| /** |
| * Get the context of the parent array. This can be used to know if the |
| * parent array is a casted expression. |
| * @since 4.2 |
| */ |
| public MIExpressionDMC getParentExpressionContext() { |
| return fParentExpression; |
| } |
| |
| @Override |
| public int getIndex() { |
| return fIndex; |
| } |
| |
| @Override |
| public int getLength() { |
| return fLength; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| return super.baseEquals(other) |
| && ((IndexedPartitionDMC) other).getParentExpressionContext().equals(getParentExpressionContext()) |
| && ((IndexedPartitionDMC) other).getIndex() == getIndex() |
| && ((IndexedPartitionDMC) other).getLength() == getLength(); |
| } |
| |
| @Override |
| public int hashCode() { |
| return super.baseHashCode() + 17 * getIndex() + 31 * getLength(); |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("%s.expr[%s][%d-%d]", baseToString(), getParentExpression(), getIndex(), //$NON-NLS-1$ |
| getIndex() + getLength() - 1); |
| } |
| |
| private static ExpressionInfo createExpressionInfo(ExpressionInfo parentInfo, int index, int length) { |
| String expression = String.format("*((%s)+%d)@%d", //$NON-NLS-1$ |
| parentInfo.getFullExpr(), Integer.valueOf(index), Integer.valueOf(length)); |
| return new ExpressionInfo(expression, 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; |
| String fMemSpace = ""; //$NON-NLS-1$ |
| |
| 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); |
| } |
| } |
| |
| /** |
| * @since 5.0 |
| */ |
| public ExpressionDMAddress(String addrStr, int size, String memSpace) { |
| this(addrStr, size); |
| fMemSpace = memSpace; |
| } |
| |
| @Override |
| public IAddress getAddress() { |
| return fAddr; |
| } |
| |
| @Override |
| public int getSize() { |
| return fSize; |
| } |
| |
| /** |
| * @since 5.0 |
| */ |
| @Override |
| public String getMemorySpaceID() { |
| return fMemSpace; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (other instanceof ExpressionDMAddress) { |
| ExpressionDMAddress otherAddr = (ExpressionDMAddress) other; |
| boolean sameAddr = fAddr == null ? otherAddr.getAddress() == null |
| : fAddr.equals(otherAddr.getAddress()); |
| boolean sameMemSpace = fMemSpace == null ? otherAddr.getMemorySpaceID() == null |
| : fMemSpace.equals(otherAddr.getMemorySpaceID()); |
| return (fSize == otherAddr.getSize()) && sameAddr && sameMemSpace; |
| } |
| 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 { |
| |
| @Override |
| public IAddress getAddress() { |
| return IExpressions.IExpressionDMLocation.INVALID_ADDRESS; |
| } |
| |
| @Override |
| public int getSize() { |
| return 0; |
| } |
| |
| @Override |
| 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; |
| } |
| |
| @Override |
| public BasicType getBasicType() { |
| return fBasicType; |
| } |
| |
| @Override |
| public String getEncoding() { |
| return null; |
| } |
| |
| @Override |
| public Map<String, Integer> getEnumerations() { |
| return new HashMap<>(); |
| } |
| |
| @Override |
| public String getName() { |
| return relativeExpression; |
| } |
| |
| @Override |
| public IRegisterDMContext getRegister() { |
| return null; |
| } |
| |
| // See class VariableVMNode for an example of usage of this method |
| public String getStringValue() { |
| return null; |
| } |
| |
| @Override |
| public String getTypeId() { |
| return null; |
| } |
| |
| @Override |
| 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 |
| */ |
| @Override |
| 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); |
| } |
| } |
| |
| /** |
| * Keeps track of aliases for return values of methods. |
| */ |
| private class ReturnValueAliasing { |
| /** |
| * Map of expression to alias. The expression is the name of the convenience variable |
| * storing the return value, e.g., $1 -> "foo() returned" |
| * This map allows to quickly find the alias to be used for return value variables. |
| */ |
| private Map<String, String> fExpressionAliasesMap = new HashMap<>(); |
| /** |
| * Map of thread to aliases expression list. This map allows to know which aliases are related |
| * to a thread of execution. This is important to allow us to delete aliases when a |
| * thread exits. Note that we need a list because we keep all previous aliases until |
| * the thread exits. |
| */ |
| private Map<IMIExecutionDMContext, List<String>> fThreadToAliasedExpressionsMap = new HashMap<>(); |
| /** |
| * Map of thread to the name of the method the thread last stopped in. |
| * This allows us to create the alias based on the method the thread was in |
| * before it returned out of the method. |
| */ |
| private Map<IMIExecutionDMContext, String> fThreadToTopMethodName = new HashMap<>(); |
| |
| /** |
| * Create an alias for expr with respect to threadDmc. |
| * The alias is created based on where threadDmc was previously stopped. |
| */ |
| public void createAlias(IMIExecutionDMContext threadDmc, String expr) { |
| String alias = expr; |
| String methodName = fThreadToTopMethodName.get(threadDmc); |
| if (methodName != null) { |
| alias = String.format(Messages.MIExpressions_ReturnValueAlias, methodName + "()"); //$NON-NLS-1$ |
| } |
| |
| fExpressionAliasesMap.put(expr, alias); |
| |
| List<String> aliasedExprList = fThreadToAliasedExpressionsMap.get(threadDmc); |
| if (aliasedExprList == null) { |
| aliasedExprList = new ArrayList<>(); |
| fThreadToAliasedExpressionsMap.put(threadDmc, aliasedExprList); |
| } |
| aliasedExprList.add(expr); |
| } |
| |
| /** |
| * Clear all information related to a particular thread of execution. |
| */ |
| public void clearThread(IMIExecutionDMContext threadDmc) { |
| fThreadToTopMethodName.remove(threadDmc); |
| clearAliases(threadDmc); |
| } |
| |
| /** |
| * Clear all aliased expressions related to a particular thread of execution. |
| * It is good to keep the aliases around as long as the thread is alive; |
| * even if we won't show the return value automatically, the user |
| * could add the expression in the expression view, and the alias |
| * would then be used. |
| */ |
| public void clearAliases(IMIExecutionDMContext threadDmc) { |
| List<String> aliasedExprList = fThreadToAliasedExpressionsMap.remove(threadDmc); |
| if (aliasedExprList != null) { |
| for (String expr : aliasedExprList) { |
| fExpressionAliasesMap.remove(expr); |
| } |
| } |
| } |
| |
| /** |
| * Update the method name of the last location where threadDmc was stopped. |
| */ |
| public void updateStoppedLocation(IMIExecutionDMContext threadDmc, String methodName) { |
| fThreadToTopMethodName.put(threadDmc, methodName); |
| } |
| |
| /** |
| * @return The alias for 'expr' if there is one. null if there |
| * is no alias for that expression. |
| */ |
| public String getAlias(String expr) { |
| String alias = fExpressionAliasesMap.get(expr); |
| if (alias == null) { |
| // Check if the expression contains the string that must be aliased. |
| // E.g., $1[0], *$2 |
| // If it does, just replace that string within the expression to |
| // create the full alias |
| for (Entry<String, String> entry : fExpressionAliasesMap.entrySet()) { |
| int index = expr.indexOf(entry.getKey()); |
| if (index != -1) { |
| // Found the string! Now replace it with our alias. |
| // We put it between () to make things clearer to the user. |
| // Note that there can only be one string contained |
| // in the expression, so once we found it, we are done. |
| alias = expr.substring(0, index) + "(" + entry.getValue() + ")" + //$NON-NLS-1$ //$NON-NLS-2$ |
| expr.substring(index + entry.getKey().length()); |
| break; |
| } |
| } |
| } |
| return alias; |
| } |
| } |
| |
| /** Structure to keep track of aliases for method return values. */ |
| private ReturnValueAliasing fReturnValueAliases = new ReturnValueAliasing(); |
| |
| /** |
| * @since 4.3 |
| */ |
| protected CommandCache fExpressionCache; |
| |
| private CommandFactory fCommandFactory; |
| private MIVariableManager varManager; |
| |
| /** |
| * Indicates that we are currently visualizing trace data. |
| * In this case, some errors should not be reported. |
| */ |
| private boolean fTraceVisualization; |
| private IMemorySpaces fMemorySpaceService; |
| |
| 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 ImmediateRequestMonitor(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, but only if we don't already have an |
| // IExpression service present. This allows another expression |
| // service to be used, while delegating calls to this service. |
| if (getServicesTracker().getService(IExpressions.class) == null) { |
| register(new String[] { IExpressions.class.getName(), IExpressions2.class.getName(), |
| IExpressions3.class.getName(), IMIExpressions.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(); |
| |
| fMemorySpaceService = getServicesTracker().getService(IMemorySpaces.class); |
| |
| 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 |
| */ |
| @Override |
| 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. |
| */ |
| |
| @Override |
| 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. |
| * |
| */ |
| |
| @Override |
| 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 |
| */ |
| @Override |
| 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 = getBasicType(getData()); |
| |
| String relativeExpr = getData().getExpr(); |
| String alias = fReturnValueAliases.getAlias(relativeExpr); |
| if (alias != null) { |
| relativeExpr = alias; |
| } |
| rm.setData(new ExpressionDMData(relativeExpr, 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.", //$NON-NLS-1$ |
| null)); |
| rm.done(); |
| } |
| } |
| |
| /** |
| * @since 4.7 |
| */ |
| protected IExpressionDMData.BasicType getBasicType(ExprMetaGetVarInfo varInfo) { |
| IExpressionDMData.BasicType basicType = null; |
| |
| GDBType gdbType = varInfo.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 (varInfo.hasChildren() || varInfo.getCollectionHint()) { |
| basicType = IExpressionDMData.BasicType.composite; |
| } else { |
| basicType = IExpressionDMData.BasicType.basic; |
| } |
| break; |
| } |
| } |
| return basicType; |
| } |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public void getExpressionAddressData(final IExpressionDMContext dmc, |
| final DataRequestMonitor<IExpressionDMAddress> rm) { |
| |
| if (dmc instanceof MIExpressionDMC) { |
| MIExpressionDMC miDMC = (MIExpressionDMC) dmc; |
| if (miDMC.getExpressionInfo().hasDynamicAncestor() || !miDMC.getExpressionInfo().inMemory()) { |
| // For children of dynamic varobjs, there is no full expression that gdb |
| // could evaluate in order to provide address and size. |
| // Also, if an expression is not in memory, such as a register |
| // or a GDB convenience variable, there is no address to return |
| 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(); |
| |
| DecodeResult memSpaceParsed = null; |
| if (fMemorySpaceService != null) { |
| try { |
| memSpaceParsed = fMemorySpaceService.decodeAddress(tmpAddrStr); |
| } catch (CoreException e1) { |
| // No memory space id found |
| } |
| } |
| |
| String tMemSpace = ""; //$NON-NLS-1$ |
| if (memSpaceParsed != null) { |
| tmpAddrStr = memSpaceParsed.getExpression(); |
| tMemSpace = memSpaceParsed.getMemorySpaceId(); |
| } |
| |
| final String memSpaceId = tMemSpace; |
| |
| // 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, memSpaceId)); |
| } catch (NumberFormatException e) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, |
| INVALID_HANDLE, "Unexpected size format from backend: " //$NON-NLS-1$ |
| + getData().getValue(), |
| null)); |
| } |
| 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 |
| */ |
| @Override |
| 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) |
| */ |
| @Override |
| 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 |
| */ |
| @Override |
| public void getSubExpressions(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMContext[]> rm) { |
| getSubExpressions(dmc, -1, -1, rm); |
| } |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public void getSubExpressions(final IExpressionDMContext exprCtx, final int startIndex, final int length, |
| final DataRequestMonitor<IExpressionDMContext[]> rm) { |
| |
| if (exprCtx instanceof IndexedPartitionDMC) { |
| getIndexedPartitionChildren((IndexedPartitionDMC) exprCtx, startIndex, length, rm); |
| } else if (exprCtx instanceof MIExpressionDMC) { |
| getRealSubExpressionCount(exprCtx, IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED, |
| new DataRequestMonitor<Integer>(getExecutor(), rm) { |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.concurrent.RequestMonitor#handleSuccess() |
| */ |
| @Override |
| protected void handleSuccess() { |
| final int realNumChildren = getData().intValue(); |
| if (realNumChildren == 0) { |
| rm.setData(new IExpressionDMContext[0]); |
| rm.done(); |
| return; |
| } |
| |
| if (realNumChildren <= getArrayPartitionLength()) { |
| getRealSubExpressions(exprCtx, startIndex, length, rm); |
| } else { |
| getExpressionData(exprCtx, |
| new DataRequestMonitor<IExpressionDMData>(ImmediateExecutor.getInstance(), rm) { |
| |
| @Override |
| protected void handleSuccess() { |
| if (IExpressionDMData.BasicType.array |
| .equals(getData().getBasicType())) { |
| rm.setData(getTopLevelIndexedPartitions((MIExpressionDMC) exprCtx, |
| realNumChildren, startIndex, length)); |
| rm.done(); |
| } else { |
| getRealSubExpressions(exprCtx, startIndex, length, rm); |
| } |
| } |
| }); |
| } |
| } |
| }); |
| } 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.", //$NON-NLS-1$ |
| null)); |
| rm.done(); |
| } |
| } |
| |
| /** |
| * @since 4.0 |
| */ |
| @Override |
| 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.", //$NON-NLS-1$ |
| null)); |
| rm.done(); |
| } |
| } |
| |
| /** |
| * @since 4.0 |
| */ |
| @Override |
| public void getSubExpressionCount(final IExpressionDMContext dmc, final int numChildLimit, |
| final DataRequestMonitor<Integer> rm) { |
| |
| if (dmc instanceof MIExpressionDMC) { |
| if (dmc instanceof IndexedPartitionDMC) { |
| int length = ((IndexedPartitionDMC) dmc).getLength(); |
| rm.setData(computeNumberOfChildren(length)); |
| rm.done(); |
| } else { |
| getRealSubExpressionCount(dmc, numChildLimit, new DataRequestMonitor<Integer>(getExecutor(), rm) { |
| |
| @Override |
| protected void handleSuccess() { |
| final int realNum = getData().intValue(); |
| if (realNum <= getArrayPartitionLength()) { |
| rm.setData(Integer.valueOf(realNum)); |
| rm.done(); |
| } else { |
| getExpressionData(dmc, |
| new DataRequestMonitor<IExpressionDMData>(ImmediateExecutor.getInstance(), rm) { |
| |
| @Override |
| protected void handleSuccess() { |
| if (IExpressionDMData.BasicType.array.equals(getData().getBasicType())) { |
| rm.setData(computeNumberOfChildren(realNum)); |
| } else { |
| rm.setData(Integer.valueOf(realNum)); |
| } |
| 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.", //$NON-NLS-1$ |
| null)); |
| 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 |
| */ |
| @Override |
| 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. |
| */ |
| |
| @Override |
| 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.", //$NON-NLS-1$ |
| null)); |
| 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 |
| */ |
| @Override |
| 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.", //$NON-NLS-1$ |
| null)); |
| rm.done(); |
| } |
| } |
| |
| @DsfServiceEventHandler |
| public void eventDispatched(IResumedDMEvent e) { |
| fExpressionCache.setContextAvailable(e.getDMContext(), false); |
| if (e.getReason() != StateChangeReason.STEP) { |
| fExpressionCache.reset(); |
| } |
| } |
| |
| @DsfServiceEventHandler |
| public void eventDispatched(ISuspendedDMEvent e) { |
| fExpressionCache.setContextAvailable(e.getDMContext(), true); |
| fExpressionCache.reset(); |
| |
| handleReturnValueAliasing(e); |
| } |
| |
| private void handleReturnValueAliasing(ISuspendedDMEvent e) { |
| // Process MIStoppedEvent from within the ISuspendedDMEvent |
| // to avoid any race conditions where the actual MIStoppedEvent |
| // can arrive faster that a preceding IResumedDMEvent |
| if (e instanceof IMIDMEvent) { |
| Object miEvent = ((IMIDMEvent) e).getMIEvent(); |
| if (miEvent instanceof MIStoppedEvent) { |
| IMIExecutionDMContext stoppedEventThread = null; |
| if (e instanceof IContainerSuspendedDMEvent) { |
| // All-stop mode |
| IExecutionDMContext[] triggerContexts = ((IContainerSuspendedDMEvent) e).getTriggeringContexts(); |
| if (triggerContexts.length != 0 && triggerContexts[0] instanceof IMIExecutionDMContext) { |
| stoppedEventThread = (IMIExecutionDMContext) triggerContexts[0]; |
| } |
| } else { |
| // Non-stop mode |
| IDMContext dmc = e.getDMContext(); |
| if (dmc instanceof IMIExecutionDMContext) { |
| stoppedEventThread = (IMIExecutionDMContext) dmc; |
| } |
| } |
| |
| if (stoppedEventThread != null) { |
| if (miEvent instanceof MIFunctionFinishedEvent) { |
| // When getting an MIFunctionFinishedEvent we must set |
| // a proper alias for the convenience variable |
| String resultVar = ((MIFunctionFinishedEvent) miEvent).getGDBResultVar(); |
| if (resultVar != null && !resultVar.isEmpty()) { |
| fReturnValueAliases.createAlias(stoppedEventThread, resultVar); |
| } |
| } |
| |
| // Keep track of the latest method the thread is stopped in. |
| // Must do this after creating any alias, or else we will overwrite |
| // the previous function name, which we need for the alias |
| MIFrame frame = ((MIStoppedEvent) miEvent).getFrame(); |
| if (frame != null) { |
| fReturnValueAliases.updateStoppedLocation(stoppedEventThread, frame.getFunction()); |
| } |
| } |
| } |
| } |
| } |
| |
| @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; |
| } |
| } |
| |
| /** |
| * @nooverride This method is not intended to be re-implemented or extended by clients. |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| @DsfServiceEventHandler |
| public void eventDispatched(IExitedDMEvent e) { |
| IDMContext ctx = e.getDMContext(); |
| if (ctx instanceof IMIExecutionDMContext) { |
| // When a thread exits, clear the alias structure for that |
| // thread to avoid leaks |
| fReturnValueAliases.clearThread((IMIExecutionDMContext) ctx); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @since 1.1 |
| */ |
| @Override |
| 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; |
| |
| public CastedExpressionDMC(MIExpressionDMC exprDMC, String castExpression, CastInfo castInfo) { |
| super(getSession().getId(), castExpression, exprDMC.getRelativeExpression(), exprDMC); |
| fCastInfo = castInfo; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext#getCastInfo() |
| */ |
| @Override |
| public CastInfo getCastInfo() { |
| return fCastInfo; |
| } |
| |
| /** |
| * @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); |
| } |
| } |
| |
| /* (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 */ |
| @Override |
| 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.isEmpty()) { |
| StringBuilder buffer = new StringBuilder(); |
| 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) { |
| StringBuilder buffer = new StringBuilder(); |
| buffer.append("*("); //$NON-NLS-1$ |
| buffer.append('(').append(castExpression).append(')'); |
| buffer.append('+').append(castingIndex).append(')'); |
| buffer.append('@').append(castingLength); |
| castExpression = buffer.toString(); |
| } |
| |
| // Surround the entire casted expression with parenthesis in case we are |
| // dealing with an array. Arrays must be parenthesized before they are |
| // subscripted. Note that we can be casting to an array or displaying |
| // as an array, so we must do this all the time. |
| castExpression = String.format("(%s)", castExpression); //$NON-NLS-1$ |
| |
| 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 */ |
| @Override |
| 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(); |
| } |
| }); |
| } |
| |
| private IndexedPartitionDMC[] getTopLevelIndexedPartitions(MIExpressionDMC exprCtx, int realNumChildren, |
| int startIndex, int length) { |
| |
| int numChildren = computeNumberOfChildren(realNumChildren); |
| if (startIndex >= numChildren) |
| return new IndexedPartitionDMC[0]; |
| int startIndex1 = (startIndex < 0) ? 0 : startIndex; |
| int length1 = (length < 0) ? numChildren - startIndex1 : Math.min(length, numChildren - startIndex1); |
| |
| IndexedPartitionDMC[] children = new IndexedPartitionDMC[numChildren]; |
| int index = 0; |
| // If the parent array is a casted expression it could have a different |
| // start index. We want the partition to start at the right index, not always 0 |
| // if (exprCtx instanceof ICastedExpressionDMContext) { |
| // index = ((ICastedExpressionDMContext)exprCtx).getCastInfo().getArrayStartIndex(); |
| // } |
| for (int i = 0; i < children.length; ++i) { |
| int partLength = computePartitionLength(realNumChildren, i); |
| children[i] = createIndexedPartition(exprCtx, index, partLength); |
| index += partLength; |
| } |
| return Arrays.copyOfRange(children, startIndex1, startIndex1 + length1); |
| } |
| |
| private void getIndexedPartitionChildren(final IndexedPartitionDMC partDmc, final int startIndex, final int length, |
| final DataRequestMonitor<IExpressionDMContext[]> rm) { |
| |
| final int startIndex1 = (startIndex < 0) ? 0 : startIndex; |
| final int length1 = (length < 0) ? Integer.MAX_VALUE : length; |
| |
| final int partStartIndex = partDmc.getIndex(); |
| final int partLength = partDmc.getLength(); |
| if (partLength > getArrayPartitionLength()) { |
| // create subpartitions |
| int numChildren = computeNumberOfChildren(partLength); |
| |
| if (startIndex1 >= numChildren) { |
| 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 numPart = Math.min(numChildren, length1); |
| IndexedPartitionDMC[] children = new IndexedPartitionDMC[numPart]; |
| int index = partStartIndex; |
| for (int i = 0; i < startIndex1; ++i) |
| index += computePartitionLength(partLength, i); |
| for (int i = 0; i < children.length; ++i) { |
| int childPartLength = computePartitionLength(partLength, i + startIndex1); |
| children[i] = createIndexedPartition(partDmc.getParentExpressionContext(), index, childPartLength); |
| index += childPartLength; |
| } |
| rm.setData(children); |
| rm.done(); |
| } else { |
| // this is the last partition level, create "real" children |
| if (startIndex1 > partLength) { |
| rm.setData(new IExpressionDMContext[0]); |
| rm.done(); |
| } else { |
| getRealSubExpressions(partDmc.getParentExpressionContext(), partStartIndex + startIndex1, |
| Math.min(length1, partLength - startIndex1), rm); |
| } |
| } |
| } |
| |
| void getRealSubExpressions(final IExpressionDMContext exprCtx, int startIndex, int length, |
| final DataRequestMonitor<IExpressionDMContext[]> rm) { |
| |
| ExprMetaGetChildren getChildren = (startIndex < 0 || length < 0) ? new ExprMetaGetChildren(exprCtx) |
| : new ExprMetaGetChildren(exprCtx, startIndex + length); |
| final int startIndex1 = (startIndex < 0) ? 0 : startIndex; |
| final int length1 = (length < 0) ? Integer.MAX_VALUE : length; |
| fExpressionCache.execute(getChildren, new DataRequestMonitor<ExprMetaGetChildrenInfo>(getExecutor(), rm) { |
| @Override |
| protected void handleSuccess() { |
| ExpressionInfo[] childrenExpr = getData().getChildrenExpressions(); |
| |
| if (startIndex1 >= 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 - startIndex1; |
| numChildren = Math.min(length1, numChildren); |
| IExpressionDMContext[] childrenArray = new IExpressionDMContext[numChildren]; |
| for (int i = 0; i < numChildren; i++) { |
| childrenArray[i] = createExpression(exprCtx.getParents()[0], childrenExpr[startIndex1 + i]); |
| } |
| rm.setData(childrenArray); |
| rm.done(); |
| } |
| }); |
| } |
| |
| /** |
| * Returns the number of "real" children if it is less or equal to the partition size, |
| * otherwise returns the number of partitions. |
| */ |
| private int computeNumberOfChildren(int realNumberOfChildren) { |
| int childNum = realNumberOfChildren; |
| int partLength = getArrayPartitionLength(); |
| int maxPartitionLength = 1; |
| while (childNum > partLength) { |
| childNum /= partLength; |
| maxPartitionLength *= partLength; |
| } |
| if (childNum * maxPartitionLength < realNumberOfChildren) |
| ++childNum; |
| return childNum; |
| } |
| |
| private int computePartitionLength(int realNumberOfChildren, int index) { |
| int childNum = realNumberOfChildren; |
| int depth = 0; |
| int partLength = getArrayPartitionLength(); |
| int length = partLength; |
| while (childNum > partLength) { |
| childNum /= partLength; |
| if (depth > 0) |
| length *= partLength; |
| ++depth; |
| } |
| int diff = realNumberOfChildren - length * index; |
| return (diff > length) ? length : diff; |
| } |
| |
| private IndexedPartitionDMC createIndexedPartition(MIExpressionDMC parentExpr, int index, int length) { |
| return new IndexedPartitionDMC(parentExpr, index, length); |
| } |
| |
| private void getRealSubExpressionCount(IExpressionDMContext dmc, 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.", //$NON-NLS-1$ |
| null)); |
| rm.done(); |
| } |
| } |
| |
| private int getArrayPartitionLength() { |
| // Replace this in case we or the platform decide to add a user preference. |
| // See org.eclipse.debug.internal.ui.model.elements.VariableContentProvider. |
| return PARTITION_LENGTH; |
| } |
| } |