From cd7ffb2dbfc4ab4949fd9a57410762bbdc4f4b09 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Tue, 18 Dec 2012 13:58:08 -0500 Subject: [PATCH] Bug 394408: Support enhanced-expressions for local variables. This includes special handling for array indices. Furthermore, we now require the '=' prefix for all glob-pattern expressions, including registers. Change-Id: If929d539b80d282df383c44d1487611fda928613 Reviewed-on: https://git.eclipse.org/r/8881 Reviewed-by: Marc Khouzam IP-Clean: Marc Khouzam Tested-by: Marc Khouzam --- .../GDBPatternMatchingExpressions.java | 636 +++++++++---- .../gdb/service/GdbDebugServicesFactory.java | 3 +- .../IGDBPatternMatchingExpressions.java | 20 - .../src/PatternMatchingExpressionsTestApp.cc | 19 + .../GDBPatternMatchingExpressionsTest.java | 858 ++++++++++++++++-- .../ui/viewmodel/DsfCastToTypeSupport.java | 11 +- dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF | 2 +- .../cdt/dsf/debug/service/IExpressions.java | 17 + 8 files changed, 1283 insertions(+), 283 deletions(-) delete mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IGDBPatternMatchingExpressions.java diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBPatternMatchingExpressions.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBPatternMatchingExpressions.java index ce30b9e152f..bee440d140f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBPatternMatchingExpressions.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBPatternMatchingExpressions.java @@ -8,6 +8,7 @@ * Contributors: * Marc Khouzam (Ericsson) - initial API and implementation * Grzegorz Kuligowski - Cannot cast to type that contain commas (bug 393474) + * Marc Khouzam (Ericsson) - Support for glob-expressions for local variables (bug 394408) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -44,7 +45,6 @@ import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMData; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; -import org.eclipse.cdt.dsf.mi.service.IGDBPatternMatchingExpressions; import org.eclipse.cdt.dsf.mi.service.IMIExpressions; import org.eclipse.cdt.dsf.mi.service.MIRegisters.MIRegisterDMC; import org.eclipse.cdt.dsf.service.AbstractDsfService; @@ -57,80 +57,65 @@ import com.ibm.icu.text.MessageFormat; /** * Expressions service added as a layer above the standard Expressions service. - * This layer allows to support group-expressions and glob-pattern matching. - * Group-expressions give the user the ability to create a comma-separated - * list of expressions in a single entry. + * This layer allows to support expression-groups and glob-pattern matching. + * Expression-groups give the user the ability to create a separated list + * of expressions in a single entry. * Glob-patterns are a way to specify a set of expressions that match the * pattern. * @since 4.2 */ -public class GDBPatternMatchingExpressions extends AbstractDsfService implements IGDBPatternMatchingExpressions, ICachingService { +public class GDBPatternMatchingExpressions extends AbstractDsfService implements IMIExpressions, ICachingService { /** * A regex representing each character that can be used to separate - * the different expressions contained in a group-expression. + * n the different expressions contained in an expression-group. * The [] are not part the characters, but are used in the regex format. * Note that we don't allow a space separator because spaces are valid within * an expression (e.g., i + 1). - * We also don't allow a comma because they are used in templates (bug 393474) + * We also don't allow a comma because they are used in C/C++ templates (bug 393474) + * Furthermore, commas are used within array-index matches as well. */ - private final static String GROUP_EXPRESSION_SEPARATORS_REGEXP = "[;]"; //$NON-NLS-1$ + private final static String EXPRESSION_GROUP_SEPARATORS_REGEXP = "[;]"; //$NON-NLS-1$ + + private final static String REGISTER_PREFIX = "$"; //$NON-NLS-1$ + private final static String GLOB_EXPRESSION_PREFIX = "="; //$NON-NLS-1$ + + /** + * This regular expression describes the supported content of an array index range. + * Valid range formats are are numbers, possibly separated by - and/or ,. + * E.g, "23-56" or "32" or "23, 45-67, 12-15" + */ + private static final String ARRAY_INDEX_RANGE_REGEXP = "^*\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$";//$NON-NLS-1$ /** - * A group-expression is an expression that requires expansion into a (potentially empty) - * list of sub-expressions. Using a group-expression allows the user to create groups + * An expression-group is an expression that requires expansion into a (potentially empty) + * list of sub-expressions. Using an expression-group allows the user to create groups * of expressions very quickly. * - * We support two aspects for group-expressions: + * We support two aspects for expression-goups: * 1- The glob syntax (http://www.kernel.org/doc/man-pages/online/pages/man7/glob.7.html) * This allows to user to specify glob-patterns to match different expressions. - * 2- Comma-separated expressions, each potentially using the glob-syntax + * 2- Separated expressions, each potentially using the glob-syntax */ - protected static class GroupExpressionDMC implements IExpressionDMContext { - + protected static class ExpressionGroupDMC implements IExpressionGroupDMContext { /** * The expression context, as created by the main Expression service. * We delegate the handling of the expression to it. */ private IExpressionDMContext fExprDelegate; - - /** - * The set of expressions making up the group expression. - * This list is the result of splitting the original expression - * and then trimming each resulting expression. - */ - private List fExpressionsInGroup = null; - public GroupExpressionDMC(IExpressionDMContext exprDmc) { + public ExpressionGroupDMC(IExpressionDMContext exprDmc) { fExprDelegate = exprDmc; } + protected IExpressionDMContext getExprDelegate() { + return fExprDelegate; + } + @Override public String getExpression() { return fExprDelegate.getExpression(); } - /** - * Returns an array representing the different expressions - * that make up this group-expression. - */ - public List getExpressionsInGroup() { - if (fExpressionsInGroup == null) { - // Split the list - String[] splitExpressions = getExpression().split(GROUP_EXPRESSION_SEPARATORS_REGEXP); - - // Remove any extra whitespace from each resulting expression, - // and ignore any empty expressions. - fExpressionsInGroup = new ArrayList(splitExpressions.length); - for (String expr : splitExpressions) { - expr = expr.trim(); - if (!expr.isEmpty()) { - fExpressionsInGroup.add(expr); - } - } - } - return fExpressionsInGroup; - } - @Override public String getSessionId() { return fExprDelegate.getSessionId(); @@ -150,25 +135,30 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public boolean equals(Object obj) { if (this == obj) return true; - if (!(obj instanceof GroupExpressionDMC)) return false; + if (!(obj instanceof ExpressionGroupDMC)) return false; - return ((GroupExpressionDMC)obj).fExprDelegate.equals(fExprDelegate); + return ((ExpressionGroupDMC)obj).fExprDelegate.equals(fExprDelegate); } @Override public int hashCode() { return fExprDelegate.hashCode(); } + + @Override + public String toString() { + return "Group: " + getExprDelegate().toString(); //$NON-NLS-1$ + } } /** - * The model data interface for group-expressions + * The model data interface for expression-groups */ - protected static class GroupExpressionDMData implements IExpressionDMDataExtension { + protected static class ExpressionGroupDMData implements IExpressionDMDataExtension { private final String fRelativeExpression; private final int fNumChildren; - public GroupExpressionDMData(String expr, int numChildren) { + public ExpressionGroupDMData(String expr, int numChildren) { assert expr != null; fRelativeExpression = expr; @@ -218,8 +208,8 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public boolean equals(Object other) { if (this == other) return true; - if (!(other instanceof GroupExpressionDMData)) return false; - return fRelativeExpression.equals(((GroupExpressionDMData)other).fRelativeExpression); + if (!(other instanceof ExpressionGroupDMData)) return false; + return fRelativeExpression.equals(((ExpressionGroupDMData)other).fRelativeExpression); } @Override @@ -229,18 +219,18 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public String toString() { - return "GroupExpr: " + fRelativeExpression; //$NON-NLS-1$ + return "ExprGroup: " + fRelativeExpression; //$NON-NLS-1$ } } /** - * The base expression service to which we delegate all non-group-expression logic. + * The base expression service to which we delegate all non-expression-group logic. */ private IMIExpressions fDelegate; - public GDBPatternMatchingExpressions(DsfSession session, IExpressions delegate) { + public GDBPatternMatchingExpressions(DsfSession session, IMIExpressions delegate) { super(session); - fDelegate = (IMIExpressions)delegate; + fDelegate = delegate; } @Override @@ -290,8 +280,8 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements public IExpressionDMContext createExpression(IDMContext ctx, String expression) { IExpressionDMContext expressionDmc = fDelegate.createExpression(ctx, expression); - if (isGroupExpression(expression)) { - return new GroupExpressionDMC(expressionDmc); + if (isExpressionGroup(expression)) { + return new ExpressionGroupDMC(expressionDmc); } else { return expressionDmc; } @@ -299,19 +289,19 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public ICastedExpressionDMContext createCastedExpression(IExpressionDMContext context, CastInfo castInfo) { - // Cannot cast a GroupExpression - assert (!(context instanceof GroupExpressionDMC)); + // Cannot cast an expression-group + assert (!(context instanceof IExpressionGroupDMContext)); return fDelegate.createCastedExpression(context, castInfo); } @Override public void getExpressionDataExtension(final IExpressionDMContext dmc, final DataRequestMonitor rm) { - if (dmc instanceof GroupExpressionDMC) { + if (dmc instanceof IExpressionGroupDMContext) { getSubExpressionCount(dmc, new ImmediateDataRequestMonitor(rm) { @Override protected void handleSuccess() { - rm.done(new GroupExpressionDMData(((GroupExpressionDMC)dmc).getExpression(), getData())); + rm.done(new ExpressionGroupDMData(((IExpressionGroupDMContext)dmc).getExpression(), getData())); } }); return; @@ -323,11 +313,11 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void getExpressionData(final IExpressionDMContext dmc, final DataRequestMonitor rm) { - if (dmc instanceof GroupExpressionDMC) { + if (dmc instanceof IExpressionGroupDMContext) { getSubExpressionCount(dmc, new ImmediateDataRequestMonitor(rm) { @Override protected void handleSuccess() { - rm.done(new GroupExpressionDMData(((GroupExpressionDMC)dmc).getExpression(), getData())); + rm.done(new ExpressionGroupDMData(((IExpressionGroupDMContext)dmc).getExpression(), getData())); } }); return; @@ -338,8 +328,8 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void getExpressionAddressData(IExpressionDMContext dmc, DataRequestMonitor rm) { - // A GroupExpression does not have an address - if (dmc instanceof GroupExpressionDMC) { + // An expression-group does not have an address + if (dmc instanceof IExpressionGroupDMContext) { rm.done(new IExpressionDMLocation() { @Override public IAddress getAddress() { @@ -362,8 +352,8 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void getSubExpressions(IExpressionDMContext exprCtx, DataRequestMonitor rm) { - if (exprCtx instanceof GroupExpressionDMC) { - matchGroupExpression((GroupExpressionDMC)exprCtx, -1, -1, rm); + if (exprCtx instanceof IExpressionGroupDMContext) { + matchExpressionGroup((IExpressionGroupDMContext)exprCtx, -1, -1, rm); } else { fDelegate.getSubExpressions(exprCtx, rm); } @@ -371,8 +361,8 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void getSubExpressions(IExpressionDMContext exprCtx, int startIndex, int length, DataRequestMonitor rm) { - if (exprCtx instanceof GroupExpressionDMC) { - matchGroupExpression((GroupExpressionDMC)exprCtx, startIndex, length, rm); + if (exprCtx instanceof IExpressionGroupDMContext) { + matchExpressionGroup((IExpressionGroupDMContext)exprCtx, startIndex, length, rm); } else { fDelegate.getSubExpressions(exprCtx, startIndex, length, rm); } @@ -380,8 +370,8 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void getSubExpressionCount(IExpressionDMContext dmc, final DataRequestMonitor rm) { - if (dmc instanceof GroupExpressionDMC) { - matchGroupExpression((GroupExpressionDMC)dmc, -1, -1, new ImmediateDataRequestMonitor(rm) { + if (dmc instanceof IExpressionGroupDMContext) { + matchExpressionGroup((IExpressionGroupDMContext)dmc, -1, -1, new ImmediateDataRequestMonitor(rm) { @Override protected void handleSuccess() { rm.done(getData().length); @@ -394,10 +384,10 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void getSubExpressionCount(IExpressionDMContext dmc, int maxNumberOfChildren, final DataRequestMonitor rm) { - if (dmc instanceof GroupExpressionDMC) { - // No need to worry about maxNumberOfChildren for the case of a group-expression, since there won't be + if (dmc instanceof IExpressionGroupDMContext) { + // No need to worry about maxNumberOfChildren for the case of an expression-group, since there won't be // a very large amount of them. - matchGroupExpression((GroupExpressionDMC)dmc, -1, -1, new ImmediateDataRequestMonitor(rm) { + matchExpressionGroup((IExpressionGroupDMContext)dmc, -1, -1, new ImmediateDataRequestMonitor(rm) { @Override protected void handleSuccess() { rm.done(getData().length); @@ -415,8 +405,8 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void canWriteExpression(IExpressionDMContext dmc, DataRequestMonitor rm) { - // A GroupExpression's value cannot be modified - if (dmc instanceof GroupExpressionDMC) { + // An expression-group's value cannot be modified + if (dmc instanceof IExpressionGroupDMContext) { rm.done(false); return; } @@ -426,15 +416,15 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void writeExpression(IExpressionDMContext dmc, String expressionValue, String formatId, RequestMonitor rm) { - // A GroupExpression's value cannot be modified - assert !(dmc instanceof GroupExpressionDMC); + // An expression-group's value cannot be modified + assert !(dmc instanceof IExpressionGroupDMContext); fDelegate.writeExpression(dmc, expressionValue, formatId, rm); } @Override public void getAvailableFormats(IFormattedDataDMContext dmc, DataRequestMonitor rm) { - //For a group expression, we only show the NATURAL format - if (dmc instanceof GroupExpressionDMC) { + // For an expression-group, we only show the NATURAL format + if (dmc instanceof IExpressionGroupDMContext) { rm.done(new String[] { IFormattedValues.NATURAL_FORMAT }); return; } @@ -444,15 +434,15 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId) { - // No special handling for GroupExpressions + // No special handling for expression-groups return fDelegate.getFormattedValueContext(dmc, formatId); } @Override public void getFormattedExpressionValue(FormattedValueDMContext dmc, final DataRequestMonitor rm) { - GroupExpressionDMC groupExpr = DMContexts.getAncestorOfType(dmc, GroupExpressionDMC.class); - if (groupExpr != null) { - getSubExpressionCount(groupExpr, new ImmediateDataRequestMonitor(rm) { + IExpressionGroupDMContext exprGroup = DMContexts.getAncestorOfType(dmc, IExpressionGroupDMContext.class); + if (exprGroup != null) { + getSubExpressionCount(exprGroup, new ImmediateDataRequestMonitor(rm) { @Override protected void handleSuccess() { int numChildren = getData(); @@ -475,9 +465,9 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements @Override public void safeToAskForAllSubExpressions(IExpressionDMContext dmc, DataRequestMonitor rm) { - // Always safe to ask for all sub-expression of a group expression, since we don't expect large - // amounts of children - if (dmc instanceof GroupExpressionDMC) { + // Always safe to ask for all sub-expression of an expression-group, + // since we don't expect large amounts of children + if (dmc instanceof IExpressionGroupDMContext) { rm.done(true); return; } @@ -493,81 +483,162 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements } /** - * Verify if we are dealing with a group expression. + * Verify if we are dealing with an expression-group. * @param expr The expression to verify - * @return True if expr is a group expression. A group - * expression is either a comma-separated list of + * @return True if expr is an expression-group. An + * expression-group is either a separated list of * expressions, or an expression using a glob-pattern */ - protected boolean isGroupExpression(String expr) { - // First check for a comma separated list of expression + protected boolean isExpressionGroup(String expr) { + // First check for a separated list of expression // We want to re-use the regex that defines our separators, and we need to check // if the expression contains that regex. I didn't find a method that - // checks if a string contains a regex, so instead we all any character before + // checks if a string contains a regex, so instead we add any character before // and after the regex, which achieves what we want. - // Note that checking if expr.split(regex) is bigger than 1, will not notice + // Note that checking if (expr.split(regex) > 1), will not notice // an expression that has a separator only at the end. - if (expr.matches(".*" + GROUP_EXPRESSION_SEPARATORS_REGEXP +".*")) { //$NON-NLS-1$ //$NON-NLS-2$ - // We are dealing with a group expression. + if (expr.matches(".*" + EXPRESSION_GROUP_SEPARATORS_REGEXP +".*")) { //$NON-NLS-1$ //$NON-NLS-2$ + // We are dealing with a group of expressions. // It may not be a valid one, but it is one nonetheless. return true; } - // Not a comma-separated list. Check if we are dealing with a glob-pattern. - return isGlobPattern(expr); + // Not a list. Check if we are dealing with a glob-pattern. + return isGlobExpression(expr); } - + /** * Verify if we are dealing with a glob-pattern. - * We support the expression * which will match all local variables. - * We support glob-patterns for registers (must start with $) + * We support the expression '*' which will match all local variables + * as well as the expression '$*' which will match all registers. + * We support glob-patterns for any expression starting with '=' + * * @param expr The expression to verify * @return True if expr is a glob-pattern we support. */ - protected boolean isGlobPattern(String expr) { + protected boolean isGlobExpression(String expr) { // Get rid of useless whitespace expr = expr.trim(); // We support the glob-pattern '*' to indicate all local variables - if (expr.equals("*")) { //$NON-NLS-1$ + // and $* for all registers + if (expr.equals("*") || expr.equals("$*")) { //$NON-NLS-1$ //$NON-NLS-2$ return true; } - // We only support glob-expressions for registers at this time - if (expr.startsWith("$")) { //$NON-NLS-1$ - // see: 'man glob' - if (expr.indexOf('*') != -1 || expr.indexOf('?') != -1 || expr.indexOf('[') != -1) { - return true; - } + // Glob-expressions must start with '=' + if (expr.startsWith(GLOB_EXPRESSION_PREFIX)) { + return true; } - + return false; } /** - * Find all expressions that match the specified group-expression. - * This method retains the order of the expressions in the group-expression, to show them - * in the same order as the one specified by the user. The match of each expression in the group - * is sorted alphabetically however. + * Verify if the glob-pattern represents a register. * - * @param groupExprDmc The group-expression context for which we want the matches (sub-expressions) + * @param expr The glob-pattern that may be a register-pattern + * @return True if expr follows the rules of an register-pattern + */ + protected boolean isRegisterPattern(String expr) { + // Get rid of useless whitespace + expr = expr.trim(); + + if (expr.startsWith(REGISTER_PREFIX)) { + return true; + } + return false; + } + + /** + * Verify if the glob-pattern should be handled as an array index range. + * When dealing with variables (on contrast to registers), the [] will + * map to array indices instead of ranges within the array name. + * For example =array[1-2] will map to array[1] and array[2] instead of + * array1 and array2. + * + * If the range contains non-digits, the matching will not be handled + * as array indices. + * + * @param expr The glob-pattern that may be an array-pattern + * @return True if expr follows the rules of an array-pattern + */ + protected boolean isArrayPattern(String expr) { + // Get rid of useless whitespace + expr = expr.trim(); + + int openBracketIndex = expr.indexOf('['); + // There must be an open bracket and it cannot be in the first position + // (as we need some indication of the array name before the brackets) + if (openBracketIndex < 1) { + return false; + } + + // We don't support any characters after the closing bracket + // since we don't support any operations on an expression-group. + if (!expr.endsWith("]")) { //$NON-NLS-1$ + return false; + } + + // We have a match for a variable which uses brackets. + // Check if the indices are integer ranges (using - or commas). + try { + Pattern pattern = Pattern.compile(ARRAY_INDEX_RANGE_REGEXP, Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(expr.substring(openBracketIndex+1, expr.length()-1)); + if (!matcher.find()) { + return false; + } + } catch(Exception e) { + // If the user put an invalid pattern, we just ignore it + return false; + } + + return true; + } + + /** + * Split the expression-group into a list of individual expression strings. + */ + protected List splitExpressionsInGroup(IExpressionGroupDMContext groupDmc) { + // Split the list of expressions + String[] splitExpressions = groupDmc.getExpression().split(EXPRESSION_GROUP_SEPARATORS_REGEXP); + + // Remove any extra whitespace from each resulting expression, + // and ignore any empty expressions. + List expressions = new ArrayList(splitExpressions.length); + for (String expr : splitExpressions) { + expr = expr.trim(); + if (!expr.isEmpty()) { + expressions.add(expr); + } + } + return expressions; + } + + /** + * Find all expressions that match the specified expression-group. + * This method retains the order of the expressions in the expression-group, to show them + * in the same order as the one specified by the user. The matches of each expression within the group + * are sorted alphabetically however. + * + * @param exprGroupDmc The expression-group context for which we want the matches (sub-expressions) * @param startIndex The beginning of the range of matches (-1 means all matches) * @param length The length of the range of matches (-1 means all matches) * @param rm RequestMonitor that will contain the range of found matches. */ - protected void matchGroupExpression(final GroupExpressionDMC groupExprDmc, int startIndex, int length, + protected void matchExpressionGroup(final IExpressionGroupDMContext exprGroupDmc, int startIndex, int length, final DataRequestMonitor rm) { // First separate the group into different expressions. // We need to create a new list, as we will modify it during our processing. - final List exprList = new ArrayList(groupExprDmc.getExpressionsInGroup()); - + final List exprList = new ArrayList(splitExpressionsInGroup(exprGroupDmc)); + // List to store the final result, which is all the sub-expressions of this group final ArrayList subExprList = new ArrayList(); final int startIndex1 = (startIndex < 0) ? 0 : startIndex; final int length1 = (length < 0) ? Integer.MAX_VALUE : length; - matchExpressionList(exprList, subExprList, groupExprDmc, new ImmediateRequestMonitor(rm) { + matchExpressionList(exprList, subExprList, exprGroupDmc, new ImmediateRequestMonitor(rm) { @Override protected void handleSuccess() { // It would be nice to allow identical elements, so that the user @@ -608,9 +679,9 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements // and sort the result alphabetically in that case. String expr = exprList.remove(0); - if (isGlobPattern(expr)) { - IExpressionDMContext exprDmc = createExpression(parentDmc, expr); - matchGlobExpression(exprDmc, new ImmediateDataRequestMonitor>(rm) { + IExpressionDMContext exprDmc = createExpression(parentDmc, expr); + if (exprDmc instanceof IExpressionGroupDMContext) { + matchGlobExpression((IExpressionGroupDMContext)exprDmc, new ImmediateDataRequestMonitor>(rm) { @Override protected void handleSuccess() { List matches = getData(); @@ -629,7 +700,7 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements }); } else { // Just a normal expression - subExprList.add(createExpression(parentDmc, expr)); + subExprList.add(exprDmc); // Match the next expression from the list matchExpressionList(exprList, subExprList, parentDmc, rm); } @@ -639,63 +710,96 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements * Find all expressions that match the specified glob-pattern. * * @param exprDmc The expression context for which we want the matches (sub-expressions) - * @param rm RequestMonitor that will contain the matches. + * @param rm RequestMonitor that will contain the unsorted matches. */ - protected void matchGlobExpression(final IExpressionDMContext exprDmc, final DataRequestMonitor> rm) { - final String fullExpr = exprDmc.getExpression().trim(); + protected void matchGlobExpression(final IExpressionGroupDMContext exprDmc, final DataRequestMonitor> rm) { + String fullExpr = exprDmc.getExpression().trim(); - if (fullExpr.equals("*")) { //$NON-NLS-1$ - matchLocals(exprDmc, rm); - return; + if (fullExpr.startsWith(GLOB_EXPRESSION_PREFIX)) { + // Strip the leading '=' and any extra spaces + fullExpr = fullExpr.substring(1).trim(); } - // Currently, we only support glob-expressions for registers, so - // we only need to match the glob-expression with register names. - // We should not arrive here if we are not handling a register - assert fullExpr.startsWith("$"); //$NON-NLS-1$ - + if (isRegisterPattern(fullExpr)) { + matchRegisters(exprDmc, rm); + } else { + if (!isArrayPattern(fullExpr)) { + matchLocals(exprDmc, rm); + } else { + // If we are dealing with an expression that could represent an array, we must + // try to match arrays and non-arrays. The reason is that a pattern such as + // =a[1-2] can be a valid match for both a[1], a[2] and a1, a2. + matchArrays(exprDmc, new ImmediateDataRequestMonitor>(rm) { + @Override + protected void handleSuccess() { + final List exprList = + getData() != null ? getData() : new ArrayList(); + matchLocals(exprDmc, new ImmediateDataRequestMonitor>(rm) { + @Override + protected void handleSuccess() { + if (getData() != null) { + exprList.addAll(getData()); + } + rm.done(exprList); + } + }); + } + }); + } + } + } + + /** + * Find all registers that match the specified glob-pattern. + * + * @param globDmc The glob-expression context for which we want the matches (sub-expressions) + * @param rm RequestMonitor that will contain the unsorted matches. + */ + protected void matchRegisters(final IExpressionGroupDMContext globDmc, final DataRequestMonitor> rm) { final IRegisters registerService = getServicesTracker().getService(IRegisters.class); if (registerService == null) { rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Register service unavailable", null)); //$NON-NLS-1$ return; } - - registerService.getRegisterGroups(exprDmc, new ImmediateDataRequestMonitor(rm) { + + registerService.getRegisterGroups(globDmc, new ImmediateDataRequestMonitor(rm) { @Override protected void handleSuccess() { registerService.getRegisters( - new CompositeDMContext(new IDMContext[] { getData()[0], exprDmc } ), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleSuccess() { - assert getData() instanceof MIRegisterDMC[]; - ArrayList matches = new ArrayList(); - for (MIRegisterDMC register : (MIRegisterDMC[])getData()) { - String potentialMatch = "$"+register.getName(); //$NON-NLS-1$ - if (globMatches(fullExpr, potentialMatch)) { - matches.add(createExpression(exprDmc, potentialMatch)); - } - } + new CompositeDMContext(new IDMContext[] { getData()[0], globDmc } ), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleSuccess() { + assert getData() instanceof MIRegisterDMC[]; + ArrayList matches = new ArrayList(); + + String fullExpr = globDmc.getExpression().trim(); + if (fullExpr.startsWith(GLOB_EXPRESSION_PREFIX)) { + // Strip the leading '=' and any extra spaces + fullExpr = fullExpr.substring(1).trim(); + } + + for (MIRegisterDMC register : (MIRegisterDMC[])getData()) { + String potentialMatch = REGISTER_PREFIX + register.getName(); + if (globMatches(fullExpr, potentialMatch)) { + matches.add(createExpression(globDmc, potentialMatch)); + } + } - rm.done(matches); - } - }); + rm.done(matches); + } + }); } }); } - /** * Find all local variables that match the specified glob-pattern. - * We currently only support matching all local variables using the '*' pattern. * * @param globDmc The glob-expression context for which we want the matches (sub-expressions) - * @param rm RequestMonitor that will contain the matches. + * @param rm RequestMonitor that will contain the unsorted matches. */ - - protected void matchLocals(final IExpressionDMContext globDmc, final DataRequestMonitor> rm) { - // We only support '*' for local variables at this time - assert globDmc.getExpression().equals("*"); //$NON-NLS-1$ + protected void matchLocals(final IExpressionGroupDMContext globDmc, final DataRequestMonitor> rm) { final IStack stackService = getServicesTracker().getService(IStack.class); if (stackService == null) { @@ -715,45 +819,239 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements IVariableDMContext[] localsDMCs = getData(); final IVariableDMData[] localsDMData = new IVariableDMData[localsDMCs.length]; - final CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm) { + final CountingRequestMonitor varNameCRM = new CountingRequestMonitor(getExecutor(), rm) { @Override public void handleSuccess() { - ArrayList expressionDMCs = new ArrayList(localsDMData.length); + ArrayList matches = new ArrayList(localsDMData.length); + String fullExpr = globDmc.getExpression().trim(); + if (fullExpr.startsWith(GLOB_EXPRESSION_PREFIX)) { + // Strip the leading '=' and any extra spaces + fullExpr = fullExpr.substring(1).trim(); + } + for (IVariableDMData localDMData : localsDMData) { - expressionDMCs.add(createExpression(globDmc, localDMData.getName())); + String potentialMatch = localDMData.getName(); + if (globMatches(fullExpr, potentialMatch)) { + matches.add(createExpression(globDmc, potentialMatch)); + } } - rm.done(expressionDMCs); + + rm.done(matches); } }; - int countRM = 0; + // Get all the names of the variables + int count = 0; for (int index=0; index < localsDMCs.length; index++) { final int finalIndex = index; - stackService.getVariableData(localsDMCs[finalIndex], new ImmediateDataRequestMonitor(crm) { + stackService.getVariableData(localsDMCs[finalIndex], new ImmediateDataRequestMonitor(varNameCRM) { @Override public void handleSuccess() { localsDMData[finalIndex] = getData(); - crm.done(); + varNameCRM.done(); } }); - countRM++; + count++; } - crm.setDoneCount(countRM); + varNameCRM.setDoneCount(count); } }); } + /** + * Find all arrays elements that match the specified glob-pattern. + * + * @param globDmc The glob-expression context for which we want the matches (sub-expressions) + * @param rm RequestMonitor that will contain the unsorted matches. + */ + protected void matchArrays(final IExpressionGroupDMContext globDmc, final DataRequestMonitor> rm) { + final IStack stackService = getServicesTracker().getService(IStack.class); + if (stackService == null) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Stack service unavailable", null)); //$NON-NLS-1$ + return; + } + + IFrameDMContext frameCtx = DMContexts.getAncestorOfType(globDmc, IFrameDMContext.class); + if (frameCtx == null) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Stack frame unavailable", null)); //$NON-NLS-1$ + return; + } + + String fullExpr = globDmc.getExpression().trim(); + if (fullExpr.startsWith(GLOB_EXPRESSION_PREFIX)) { + // Strip the leading '=' and any extra spaces + fullExpr = fullExpr.substring(1).trim(); + } + + // Extract the array name and the array index specification. + // The regex used will remove both [ and ] + String[] arrayExprParts = fullExpr.split("[\\[\\]]"); //$NON-NLS-1$ + assert arrayExprParts != null && arrayExprParts.length == 2; + + if (arrayExprParts == null || arrayExprParts.length < 2) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Error parsing array expression", null)); //$NON-NLS-1$ + return; + } + + final String arrayName = arrayExprParts[0].trim(); + final String arrayIndexSpec = arrayExprParts[1].trim(); + + stackService.getLocals(frameCtx, new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleSuccess() { + IVariableDMContext[] localsDMCs = getData(); + final IVariableDMData[] localsDMData = new IVariableDMData[localsDMCs.length]; + + final CountingRequestMonitor varNameCRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + public void handleSuccess() { + final ArrayList matches = new ArrayList(); + final CountingRequestMonitor elementMatchesCRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + public void handleSuccess() { + rm.done(matches); + } + }; + + int count = 0; + for (IVariableDMData localDMData : localsDMData) { + final String potentialMatch = localDMData.getName(); + + if (globMatches(arrayName, potentialMatch)) { + // We have a variable that matches the name part of the array. + // Let's create the matching elements if that variable is an array. + createPotentialArrayMatches(createExpression(globDmc, potentialMatch), arrayIndexSpec, + new ImmediateDataRequestMonitor>(elementMatchesCRM){ + @Override + protected void handleSuccess() { + if (getData() != null) { + matches.addAll(getData()); + } + elementMatchesCRM.done(); + } + }); + + count++; + } + } + elementMatchesCRM.setDoneCount(count); + } + }; + + // Get all the names of the variables + int count = 0; + for (int index=0; index < localsDMCs.length; index++) { + final int finalIndex = index; + stackService.getVariableData(localsDMCs[finalIndex], new ImmediateDataRequestMonitor(varNameCRM) { + @Override + public void handleSuccess() { + localsDMData[finalIndex] = getData(); + varNameCRM.done(); + } + }); + + count++; + } + varNameCRM.setDoneCount(count); + } + }); + } + + /** + * Creates requested array elements if exprDmc is indeed an array. + * + * @param exprDmc The potential array expression to be used + * @param indexSpec The specification of the element indices + * @param rm The list of created element expressions. + * If exprDmc is not an array, the list will be empty but not null. + */ + protected void createPotentialArrayMatches(final IExpressionDMContext exprDmc, final String indexSpec, + final DataRequestMonitor> rm) { + // We check if the variable is an array or not. If it is an array, + // we create the elements based on the specified indices. + // If it is not an array, we don't need to handle it in this method + getExpressionData(exprDmc, new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + boolean isArray = + isSuccess() && + getData().getBasicType().equals(IExpressionDMData.BasicType.array); + + final ArrayList elements = new ArrayList(); + + if (isArray) { + // we must now create the elements based on the indices + List indicesDmcs = + createArrayIndicesExpression(exprDmc, indexSpec); + if (indicesDmcs != null) { + elements.addAll(indicesDmcs); + } + } + rm.done(elements); + } + }); + } + + /** + * Create all the expressions characterizing the specified arrayDmc and + * indexSpec pattern. + * + * @param arrayDmc The expression context that represents the array itself + * @param indexSpec A string describing the range of indexes to be used. + * Valid range formats are described by {@code ARRAY_INDEX_RANGE_REGEXP} + * The string should not contain the index [] characters. + * @return A list of expression contexts representing all the different + * array elements possible using the name of the array and indexSpec. + * If the indexSpec is invalid (e.g, 3-2) it will be used as-is which + * could be a valid expression (i.e., the index 3-2=1 in this case) + */ + protected List createArrayIndicesExpression(IExpressionDMContext arrayDmc, String indexSpec) { + ArrayList expressionDMCs = new ArrayList(); + String arrayName = arrayDmc.getExpression(); + IDMContext parentDmc = arrayDmc.getParents()[0]; + + // First split the indexRange by comma + String[] ranges = indexSpec.split(","); //$NON-NLS-1$ + + for (String range : ranges) { + // Get rid of any useless spaces + range = range.trim(); + + // Try to split the range with the - separator + String[] rangeNumbers = range.split("-");//$NON-NLS-1$ + if (rangeNumbers.length == 2) { + try { + int lowIndex = Integer.parseInt(rangeNumbers[0]); + int highIndex = Integer.parseInt(rangeNumbers[1]); + + if (lowIndex <= highIndex) { + for (int i = lowIndex; i <= highIndex; i++) { + expressionDMCs.add(createExpression(parentDmc, arrayName + "[" + i + "]")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + continue; + } + } catch (NumberFormatException e) { + // Ignore and fall back on using range as-is below + } + } + + // Leave range as-is, which could be a single digit, or some non-expected expression + expressionDMCs.add(createExpression(parentDmc, arrayName + "[" + range + "]")); //$NON-NLS-1$ //$NON-NLS-2$ + } + return expressionDMCs; + } /** * Verify if the potentialMatch variable matches the glob-pattern. * * @param globPattern The glob-pattern to match * @param potentialMatch The string that must match globPattern. - * @return True of potentialMatch does match globPattern. + * @return True if potentialMatch does match globPattern. */ protected boolean globMatches(String globPattern, String potentialMatch) { - // Convert the glob-pattern into java regex to do the matching + // Convert the glob-pattern into java regex to do the matching boolean inBrackets = false; char[] patternArray = globPattern.toCharArray(); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java index 9b9e772cd0c..c1a2647d4c0 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java @@ -35,6 +35,7 @@ import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_2; import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_4; import org.eclipse.cdt.dsf.mi.service.CSourceLookup; import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.mi.service.IMIExpressions; import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; import org.eclipse.cdt.dsf.mi.service.MIBreakpointsSynchronizer; @@ -165,7 +166,7 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory { // Pass in the original service which will be used as a delegate. // This way of doing things allows to keep the pattern matching aspect isolated // and easy to remove. - IExpressions originialExpressionService = new MIExpressions(session); + IMIExpressions originialExpressionService = new MIExpressions(session); return new GDBPatternMatchingExpressions(session, originialExpressionService); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IGDBPatternMatchingExpressions.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IGDBPatternMatchingExpressions.java deleted file mode 100644 index 9dcd4fa745a..00000000000 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IGDBPatternMatchingExpressions.java +++ /dev/null @@ -1,20 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Ericsson and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Marc Khouzam (Ericsson) - Initial API and implementation - *******************************************************************************/ -package org.eclipse.cdt.dsf.mi.service; - - -/** - * Interface that will indicate that the implementing service supports - * Glob-style expression pattern matching. - * @since 4.2 - */ -public interface IGDBPatternMatchingExpressions extends IMIExpressions { -} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/PatternMatchingExpressionsTestApp.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/PatternMatchingExpressionsTestApp.cc index a5720b1a77e..de2cca25c85 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/PatternMatchingExpressionsTestApp.cc +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/PatternMatchingExpressionsTestApp.cc @@ -2,10 +2,28 @@ int foo(int firstarg, bool secondarg) { bool firstvar = true; int secondvar = 18; + int* ptrvar = &secondvar; + int var = 1; + int var2 = 2; return 0; } +int testArrayMatching() { + int array[20]; + int arrayInt[10]; + bool arrayBool[20]; + int arrayNot,array2,array3; + + array[0] = 20; + array[1] = 21; + array[2] = 22; + array[3] = 23; + array[4] = 24; + array[5] = 25; + return 0; +} + int main (int argc, char *argv[]) { int intvar = 80; @@ -13,6 +31,7 @@ int main (int argc, char *argv[]) char chararray[201]; foo(15, true); + testArrayMatching(); return 0; } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/GDBPatternMatchingExpressionsTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/GDBPatternMatchingExpressionsTest.java index 6427d789bf5..1ee87eecf07 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/GDBPatternMatchingExpressionsTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/GDBPatternMatchingExpressionsTest.java @@ -7,12 +7,13 @@ * * Contributors: * Marc Khouzam (Ericsson) - Initial Implementation + * Marc Khouzam (Ericsson) - Tests for Pattern Matching for variables (Bug 394408) *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.tests; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collections; @@ -41,7 +42,6 @@ import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.mi.service.ClassAccessor.MIExpressionDMCAccessor; -import org.eclipse.cdt.dsf.mi.service.IGDBPatternMatchingExpressions; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIExpressions; import org.eclipse.cdt.dsf.mi.service.MIRegisters.MIRegisterDMC; @@ -84,7 +84,6 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); fExpService = fServicesTracker.getService(IMIExpressions.class); - assertTrue(fExpService instanceof IGDBPatternMatchingExpressions); fRegService = fServicesTracker.getService(IRegisters.class); } @@ -117,6 +116,8 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { return list; } + final static String[] fAllVariables = new String[] { "firstarg", "firstvar", "ptrvar", "secondarg", "secondvar", "var", "var2" }; + protected void checkChildrenCount(final IExpressionDMContext parentDmc, final int expectedCount) throws Throwable { Query query = new Query() { @Override @@ -228,19 +229,20 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { //************************************************************************************** /** - * Test that we can access a single register + * Test that we can access a single register, without using groups or patterns */ @Test - public void testMatchSingleReg() throws Throwable { + public void testSingleReg() throws Throwable { final String regName = "esp"; + final String exprString = "$" + regName; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(frameDmc, IMIExecutionDMContext.class); - final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "$"+regName); + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); checkChildrenCount(exprDmc, 0); // get value of expression and compare with register @@ -251,55 +253,101 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { * Test that we can access a single variable, without using groups or patterns */ @Test - public void testMatchSingleLocal() throws Throwable { - SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); - - IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); - - final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "secondvar"); - checkChildrenCount(exprDmc, 0); - - // get value of expression and compare with register - assertEquals(getExpressionValue(exprDmc), "0x12"); - } - - /** - * Test that we can create the all-register match - */ - @Test - public void testMatchAllRegs() throws Throwable { - final String exprString = "$*"; - List regList = get_X86_REGS(); - Collections.sort(regList); - final String[] children = regList.toArray(new String[0]); + public void testSingleLocal() throws Throwable { + final String exprString = "secondvar"; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildrenCount(exprDmc, 0); + assertEquals(getExpressionValue(exprDmc), "0x12"); + } + /** + * Test that we can match a single register + */ + @Test + public void testMatchSingleReg() throws Throwable { + final String exprString = "=$esp"; + final String[] children = new String[] { "$esp" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can match a single variable + */ + @Test + public void testMatchSingleLocal() throws Throwable { + final String exprString = "=secondvar"; + final String[] children = new String[] { "secondvar" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); checkChildren(exprDmc, -1, -1, children); checkChildrenCount(exprDmc, children.length); } /** - * Test that we can create the all-locals match + * Test that we can create the all-register match and that + * the '$*' form is treated just like '=$*' + */ + @Test + public void testMatchAllRegs() throws Throwable { + final String exprString = "$*"; + final String exprString2 = "=$*"; + List regList = get_X86_REGS(); + Collections.sort(regList); + final String[] children = regList.toArray(new String[0]); + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + + exprDmc = SyncUtil.createExpression(frameDmc, exprString2); + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can create the all-locals match and that + * the '*' form is treated just like '=*' */ @Test public void testMatchAllLocals() throws Throwable { final String exprString = "*"; - final String[] children = new String[] { "firstarg", "firstvar", "secondarg", "secondvar" }; + final String exprString2 = "=*"; + final String[] children = fAllVariables; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); - final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + exprDmc = SyncUtil.createExpression(frameDmc, exprString2); checkChildren(exprDmc, -1, -1, children); checkChildrenCount(exprDmc, children.length); } @@ -313,7 +361,7 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { final String[] children = new String[] { "argc", "argv", "boolvar", "chararray", "intvar" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 1); @@ -328,11 +376,11 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { */ @Test public void testMatchRegWithStar() throws Throwable { - final String exprString = "$e*"; + final String exprString = "=$e*"; final String[] children = new String[] { "$eax","$ebp","$ebx","$ecx","$edi","$edx","$eflags","$eip","$es", "$esi","$esp" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -342,16 +390,149 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { checkChildrenCount(exprDmc, children.length); } + /** + * Test that registers can be multiplied using '*' + * without conflicting with glob-expressions + */ + @Test + public void testMultiplyReg() throws Throwable { + final String exprString = "$eax*0"; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildrenCount(exprDmc, 0); + + assertEquals(getExpressionValue(exprDmc), "0x0"); + } + + /** + * Test that variables can be matched using '*' at the start + * not to be confused with dereferencing + */ + @Test + public void testMatchVarWithStarBefore() throws Throwable { + final String exprString = "=*ptrvar"; + final String[] children = new String[] { "ptrvar" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that variables can be dereferenced using '*' + * without conflicting with glob-expressions + */ + @Test + public void testDerefVar() throws Throwable { + final String exprString = "*ptrvar"; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildrenCount(exprDmc, 0); + + assertEquals(getExpressionValue(exprDmc), "0x12"); + } + + /** + * Test that variables can be matched using '*' at the end + * not to be confused with multiplication + */ + @Test + public void testMatchVarWithStarAfter() throws Throwable { + final String exprString = "=var*2"; + final String[] children = new String[] { "var2" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that variables can be multiplied using '*' + * without conflicting with glob-expressions + */ + @Test + public void testMultiplyVar() throws Throwable { + final String exprString = "var*0"; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildrenCount(exprDmc, 0); + + assertEquals(getExpressionValue(exprDmc), "0x0"); + } + /** * Test that registers can be matched using '?' */ @Test public void testMatchRegWithQuestionMark() throws Throwable { - final String exprString = "$e??"; + final String exprString = "=$e??"; final String[] children = new String[] { "$eax","$ebp","$ebx","$ecx","$edi","$edx","$eip", "$esi","$esp" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that conditional operator can be used for registers + */ + @Test + public void testRegWithConditionalOperator() throws Throwable { + final String exprString = "$eax?0x16:0x11"; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildrenCount(exprDmc, 0); + + assertEquals(getExpressionValue(exprDmc), "0x16"); + } + /** + * Test that variables can be matched using '?' + */ + @Test + public void testMatchVarWithQuestionMark() throws Throwable { + final String exprString = "=?ar?"; + final String[] children = new String[] { "var2" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -362,15 +543,34 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { } /** - * Test that registers can be matched using [] with a single index + * Test that conditional operator can be used with variables */ @Test - public void testMatchRegWithBracketsOneDigit() throws Throwable { - final String exprString = "$st[4]"; + public void testVarWithConditionalOperator() throws Throwable { + final String exprString = "var?0x16:0x11"; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildrenCount(exprDmc, 0); + + assertEquals(getExpressionValue(exprDmc), "0x16"); + } + + /** + * Test that registers can be matched using [] with a single number. + * There should be no confusion about array index for registers. + */ + @Test + public void testMatchRegWithOneDigitRange() throws Throwable { + final String exprString = "=$st[4]"; final String[] children = new String[] { "$st4" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -381,15 +581,16 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { } /** - * Test that registers can be matched using '[]' using a range + * Test that registers can be matched using [] with a single letter. + * There should be no confusion about array index for registers. */ @Test - public void testMatchRegWithBracketsRange() throws Throwable { - final String exprString = "$st[2-5]"; - final String[] children = new String[] { "$st2","$st3", "$st4","$st5" }; + public void testMatchRegWithOneLetterRange() throws Throwable { + final String exprString = "=$ea[x]"; + final String[] children = new String[] { "$eax" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -399,6 +600,419 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { checkChildrenCount(exprDmc, children.length); } + /** + * Test that registers can be matched using [] using a range of numbers + * There should be no confusion about array index for registers. + */ + @Test + public void testMatchRegWithNumberRange() throws Throwable { + final String exprString = "=$st[2-5]"; + final String[] children = new String[] { "$st2","$st3", "$st4","$st5" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that registers can be matched using [] using a range of letters + * There should be no confusion about array index for registers. + */ + @Test + public void testMatchRegWithLetterRange() throws Throwable { + final String exprString = "=$eb[a-z]"; + final String[] children = new String[] { "$ebp", "$ebx" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that registers can be matched using [] using a range of letters + * and a * pattern + */ + @Test + public void testMatchRegWithComplexLetterRange() throws Throwable { + final String exprString = "=$e[b-c]*"; + final String[] children = new String[] { "$ebp", "$ebx", "$ecx" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that arrays can be matched using [] using a range of numbers. + * In this case, we want to show the user the range of array elements. + */ + @Test + public void testMatchArrayWithNumberRange() throws Throwable { + final String exprString = "=array[2-5]"; + final String[] children = new String[] { "array2", "array3", "array[2]","array[3]", "array[4]","array[5]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that arrays can be matched using [] using a single number range. + */ + @Test + public void testMatchArrayWithSingleNumberRange() throws Throwable { + final String exprString = "=array[2]"; + final String[] children = new String[] { "array2", "array[2]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that arrays can be matched using [] using a number range + * using the same number. + */ + @Test + public void testMatchArrayWithSameNumberRange() throws Throwable { + final String exprString = "=array[2-2]"; + final String[] children = new String[] { "array2", "array[2]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that arrays can be accessed using subtraction. + * No matching should be performed here, and the result should + * be the array element based on the subtraction result. + */ + @Test + public void testArrayWithSubtraction() throws Throwable { + final String exprString = "array[5-3]"; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + checkChildrenCount(exprDmc, 0); + assertEquals(getExpressionValue(exprDmc), "0x16"); + } + + /** + * Test that arrays can be matched using [] and a wildcard. + */ + @Test + public void testMatchArrayWithWildCardAndNumberRange() throws Throwable { + final String exprString = "=ar*[2-3]"; + final String[] children = new String[] { + "array2", "array3", + "arrayBool[2]", "arrayBool[3]", + "arrayInt[2]", "arrayInt[3]", + "array[2]", "array[3]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that arrays can be matched using [] and a ?. + */ + @Test + public void testMatchArrayWithQuestionMarkAndNumberRange() throws Throwable { + final String exprString = "=ar?a?[2-4]"; + final String[] children = new String[] { "array2", "array3", "array[2]","array[3]", "array[4]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can match all arrays using []. + * In this case, we want to show the user the range of array elements. + */ + @Test + public void testMatchAllArraysAndNumberRange() throws Throwable { + final String exprString = "=*[2-3]"; + final String[] children = new String[] { + "array2", "array3", + "arrayBool[2]", "arrayBool[3]", + "arrayInt[2]", "arrayInt[3]", + "array[2]", "array[3]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can match arrays using [] and comma-separated indices. + */ + @Test + public void testMatchArraysWithCommaSeparatedIndices() throws Throwable { + final String exprString = "=array[2,5,8]"; + final String[] children = new String[] { + "array2", "array[2]", "array[5]", "array[8]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can match arrays using [] and comma-separated ranges. + */ + @Test + public void testMatchArraysWithCommaSeparatedNumberRanges() throws Throwable { + final String exprString = "=array[2-3, 5, 7-8]"; + final String[] children = new String[] { + "array2", "array3", + "array[2]", "array[3]", + "array[5]", + "array[7]", "array[8]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can match arrays using [] and comma-separated ranges + * with an overlap. + */ + @Test + public void testMatchArraysWithCommaSeparatedOverlappingRanges() throws Throwable { + final String exprString = "=array[2-3, 5, 4-6]"; + final String[] children = new String[] { + "array2", "array3", "array[2]", "array[3]", "array[4]", "array[5]", "array[6]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can match arrays using [] and comma-separated ranges + * that are not sorted. + */ + @Test + public void testMatchArraysWithCommaSeparatedUnsortedRanges() throws Throwable { + final String exprString = "=array[5-6, 3, 0-1]"; + final String[] children = new String[] { + "array3", "array[0]", "array[1]", "array[3]", "array[5]", "array[6]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can match arrays using [] and comma-separated ranges + * containing invalid ranges. Invalid ranges are not accepted by + * regular expressions and therefore will be ignored for the name. + * I.e., array2 and array3 will not be matches, but only array indices + * will be matched. + */ + @Test + public void testMatchArraysWithCommaSeparatedInvalidRanges() throws Throwable { + final String exprString = "=array[2-3, 5, 6-4]"; + final String[] children = new String[] { + "array[2]", "array[3]", "array[5]", "array[6-4]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we properly handle a non-arrays when using [] + */ + @Test + public void testMatchNonArrayWithNumberRange() throws Throwable { + final String exprString = "=arrayNot[2-3]"; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildrenCount(exprDmc, 0); + } + + /** + * Test that arrays can be accessed using [] with a single letter range. + * In this case, since letters do not indicate an array index, + * we match a letter range within the _name_ of the array. + */ + @Test + public void testMatchArrayWithSingleLetterRange() throws Throwable { + final String exprString = "=array[B]*"; + final String[] children = new String[] { "arrayBool" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that arrays can be accessed using [] with a letter range. + * In this case, since letters do not indicate an array index, + * we match a letter range within the _name_ of the array. + */ + @Test + public void testMatchArrayWithLetterRange() throws Throwable { + final String exprString = "=array[B-I]*"; + final String[] children = new String[] { "arrayBool", "arrayInt" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that arrays can be accessed using [] with a letter range. + * In this case, since letters do not indicate an array index, + * we match a letter range within the _name_ of the array. + */ + @Test + public void testMatchArrayWithLetterRange2() throws Throwable { + final String exprString = "=ar*[B-I]*"; + final String[] children = new String[] { "arrayBool", "arrayInt" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that arrays can be accessed using [] with an invalid range. + * In this case, the range is used as-is to create the expression. + */ + @Test + public void testMatchArrayWithInvalidNumberRange() throws Throwable { + final String exprString = "=array[5-2]"; + final String[] children = new String[] { "array[5-2]" }; + + SyncUtil.runToLocation("testArrayMatching"); + MIStoppedEvent stoppedEvent = SyncUtil.step(6, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + /** * Test that all registers and all locals can be matched at the same time */ @@ -407,11 +1021,11 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { final String exprString = "$*; *"; List list = get_X86_REGS(); Collections.sort(list); - list.addAll(Arrays.asList(new String[] { "firstarg", "firstvar", "secondarg", "secondvar" })); + list.addAll(Arrays.asList(fAllVariables)); final String[] children = list.toArray(new String[list.size()]); SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -430,11 +1044,11 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { final String exprString = "*; $*"; List list = get_X86_REGS(); Collections.sort(list); - list.addAll(0, Arrays.asList(new String[] { "firstarg", "firstvar", "secondarg", "secondvar" })); + list.addAll(0, Arrays.asList(fAllVariables)); final String[] children = list.toArray(new String[list.size()]); SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -450,10 +1064,9 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { @Test public void testGroupSubExprRange() throws Throwable { final String exprString = "$eax; $es; *"; - final String[] children = new String[] { "$es", "firstarg", "firstvar", "secondarg" }; - + final String[] children = new String[] { "$es", "firstarg", "firstvar", "ptrvar" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -475,7 +1088,26 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { final String[] children = list.toArray(new String[list.size()]); SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, exprString); + + checkChildren(exprDmc, -1, -1, children); + checkChildrenCount(exprDmc, children.length); + } + + /** + * Test that we can group a local with a pattern for variables + */ + @Test + public void testGroupOneLocalMatchedLocals() throws Throwable { + final String exprString = "*ptrvar; =var*"; + final String[] children = new String[] { "*ptrvar", "var", "var2" }; + + SyncUtil.runToLocation("foo"); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -492,11 +1124,11 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { */ @Test public void testUniqueWhenOverlapReg() throws Throwable { - final String exprString = "$eax; $e?x; $eb?"; + final String exprString = "=$eax; =$e?x; =$eb?"; final String[] children = new String[] { "$eax","$ebx","$ecx","$edx", "$ebp" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -514,10 +1146,9 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { @Test public void testUniqueWhenOverlapLocal() throws Throwable { final String exprString = "firstvar;*;firstvar"; - final String[] children = new String[] { "firstvar", "firstarg", "secondarg", "secondvar" }; - + final String[] children = new String[] { "firstvar", "firstarg", "ptrvar", "secondarg", "secondvar", "var", "var2" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -538,7 +1169,7 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { final String[] children = regList.toArray(new String[0]); SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -554,12 +1185,12 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { @Test public void testSortedAllLocals() throws Throwable { final String exprString = "*"; - List list = Arrays.asList(new String[] { "firstarg", "firstvar", "secondarg", "secondvar" }); + List list = Arrays.asList(fAllVariables); Collections.sort(list); final String[] children = list.toArray(new String[list.size()]); SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -580,13 +1211,13 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { final String exprString = "$*; *"; List list = get_X86_REGS(); Collections.sort(list); - List localsList = Arrays.asList(new String[] { "firstarg", "firstvar", "secondarg", "secondvar" }); + List localsList = Arrays.asList(fAllVariables); Collections.sort(localsList); list.addAll(localsList); final String[] children = list.toArray(new String[list.size()]); SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -606,7 +1237,7 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { // final String[] children = new String[] { "firstvar","$eax" }; // // SyncUtil.runToLocation("foo"); -// MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); +// MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); // // IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // @@ -625,7 +1256,7 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { final String[] children = new String[] { "firstvar","$eax" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -635,6 +1266,56 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { checkChildrenCount(exprDmc, children.length); } + + /** + * Test that a valid expression that contains a comma will not be converted + * to a group expression (bug 393474) + */ + @Test + public void testExpressionWithCommand() throws Throwable { + // The following expression is a valid one for a program using templates. + // We will check that it does not get split into two expressions. + final String exprStringComma = "((((((class std::_Vector_base >) v))._M_impl))._M_start)"; + // The following expression is not valid. However, it is identical to the previous + // one except it uses a semi-colon. We use it to confirm that such an expression + // will be treated as a group-exrepssion and get split into two children. + // This is a way to confirm that the test is valid for the above expression that + // is separated by a command. + final String exprStringSemiColon = exprStringComma.replace(',', ';'); + + assertFalse("The two strings for this test should not be the same", exprStringComma.equals(exprStringSemiColon)); + + MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("foo"); + + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmcSemiColon = SyncUtil.createExpression(frameDmc, exprStringSemiColon); + final IExpressionDMContext exprDmcComma = SyncUtil.createExpression(frameDmc, exprStringComma); + + try { + // This should get split into two children and not + // sent to GDB at all, so we should not see a failure, + // even though the expression is not valid for the + // program we are debugging. + checkChildrenCount(exprDmcSemiColon, 2); + } catch (Exception e) { + assertFalse("Expected two children for when using a semi-colon", true); + } + + try { + // Should throw an exception because this expression is not + // valid and since it does not get split into children, + // we'll be sending to GDB and seeing the failure. + checkChildrenCount(exprDmcComma, 0); + } catch (Exception e) { + // Valid and expected + return; + } + // Should not get here + assertFalse("Should have seen an expression thrown", true); + + } + // Cannot use comma separator because of templates (bug 393474) // /** // * Test that group-expression can use a comma and a semi-colon as a @@ -646,7 +1327,7 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { // final String[] children = new String[] { "firstvar","$eax","$es" }; // // SyncUtil.runToLocation("foo"); -// MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); +// MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); // // IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // @@ -666,7 +1347,7 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { // final String[] children = new String[] { "firstvar","$eax" }; // // SyncUtil.runToLocation("foo"); -// MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); +// MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); // // IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // @@ -685,7 +1366,7 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { final String[] children = new String[] { "firstvar","$eax" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -700,11 +1381,11 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { */ @Test public void testExtraSpaces() throws Throwable { - final String exprString = " firstvar ; $eax ; ; "; - final String[] children = new String[] { "firstvar","$eax" }; + final String exprString = " firstvar ; $eax ; ; =var? ; = second* "; + final String[] children = new String[] { "firstvar","$eax","var2","secondarg","secondvar" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -720,10 +1401,9 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { @Test public void testGroupExpressionData() throws Throwable { final String exprString = "$eax;*"; -// final String[] children = new String[] { "$eax", "firstarg", "firstvar", "secondarg", "secondvar" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -749,7 +1429,7 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { IExpressionDMDataExtension dataExt = query2.get(); // Make sure the two different ways to get the group-expression data return - // teh same thing, to make sure we didn't forget to update one of the two. + // the same thing, to make sure we didn't forget to update one of the two. assertEquals(data, dataExt); assertEquals(exprString, dataExt.getName()); @@ -765,10 +1445,9 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { @Test public void testGroupExpressionAddressData() throws Throwable { final String exprString = "$eax;*"; -// final String[] children = new String[] { "$eax", "firstarg", "firstvar", "secondarg", "secondvar" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -797,10 +1476,13 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { @Test public void testGroupGetSubExpressions() throws Throwable { final String exprString = "$eax;*"; - final String[] children = new String[] { "$eax", "firstarg", "firstvar", "secondarg", "secondvar" }; + List list = new LinkedList(); + list.add("$eax"); + list.addAll(Arrays.asList(fAllVariables)); + final String[] children = list.toArray(new String[list.size()]); SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -839,10 +1521,9 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { @Test public void testGroupExpressionNotModifiable() throws Throwable { final String exprString = "$eax;*"; -// final String[] children = new String[] { "$eax", "firstarg", "firstvar", "secondarg", "secondvar" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -867,10 +1548,9 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { @Test public void testGroupExpressionAvailableFormats() throws Throwable { final String exprString = "$eax;*"; -// final String[] children = new String[] { "$eax", "firstarg", "firstvar", "secondarg", "secondvar" }; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); @@ -895,12 +1575,12 @@ public class GDBPatternMatchingExpressionsTest extends BaseTestCase { */ @Test public void testGroupExpressionValue() throws Throwable { - final String noMatchExpr = "$zzz*"; - final String singleMatchExpr = "$eax;"; - final String doubleMatchExpr = "$eax;$ebx"; + final String noMatchExpr = "=$zzz*"; + final String singleMatchExpr = "=$eax;"; + final String doubleMatchExpr = "=$eax;=$ebx"; SyncUtil.runToLocation("foo"); - MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); + MIStoppedEvent stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/DsfCastToTypeSupport.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/DsfCastToTypeSupport.java index 458f1829e1c..380e2ed68cf 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/DsfCastToTypeSupport.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/DsfCastToTypeSupport.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010 Nokia and others. + * Copyright (c) 2010, 2012 Nokia and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,6 +7,8 @@ * * Contributors: * Nokia - Initial API and implementation + * Marc Khouzam (Ericsson) - Turn off casting for expression-group or + * pattern expressions (bug 394408) *******************************************************************************/ package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel; @@ -25,6 +27,7 @@ import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMData; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionGroupDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions.IIndexedPartitionDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions2; import org.eclipse.cdt.dsf.debug.service.IExpressions2.CastInfo; @@ -91,9 +94,11 @@ public class DsfCastToTypeSupport { } private boolean isValid() { - if (exprDMC instanceof IIndexedPartitionDMContext) + if (exprDMC instanceof IIndexedPartitionDMContext || + exprDMC instanceof IExpressionGroupDMContext) { return false; - + } + TestExpressions2Query query = new TestExpressions2Query(); dmvmProvider.getSession().getExecutor().execute(query); diff --git a/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF index 3baade410e9..91aa88e256d 100644 --- a/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF +++ b/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.cdt.dsf;singleton:=true -Bundle-Version: 2.3.0.qualifier +Bundle-Version: 2.4.0.qualifier Bundle-Activator: org.eclipse.cdt.dsf.internal.DsfPlugin Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime, diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java index fac33c4a85c..a076ca5804a 100644 --- a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java @@ -8,6 +8,7 @@ * Contributors: * Wind River Systems - initial API and implementation * Ericsson - Update for GDB/MI + * Marc Khouzam (Ericsson) - Added expression-groups (bug 394408) *******************************************************************************/ package org.eclipse.cdt.dsf.debug.service; @@ -67,6 +68,22 @@ public interface IExpressions extends IFormattedValues { public int getLength(); } + /** + * Represents a group of expressions. A group of expressions is a list of + * possibly unrelated expressions which are somehow described by an + * expression-group string. + * + * Examples of expression-groups that the service could choose to support are: + * "myVar1; myVar2" + * "=myVar*" + * + * The sub-expressions of an expression-group are the individual expressions + * making up this group. + * + * @since 2.4 + */ + public interface IExpressionGroupDMContext extends IExpressionDMContext {} + /** * The address and size of an expression. */