blob: aeed5a6fe72bf0c15195d5b2977b54768f733770 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.formatter;
import java.math.BigInteger;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.IArrayDimensionType;
import org.eclipse.cdt.debug.edc.internal.formatter.FormatExtensionManager;
import org.eclipse.cdt.debug.edc.internal.services.dsf.Expressions;
import org.eclipse.cdt.debug.edc.internal.services.dsf.Memory;
import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
import org.eclipse.cdt.debug.edc.internal.symbols.IInheritance;
import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
import org.eclipse.cdt.debug.edc.internal.symbols.ITypedef;
import org.eclipse.cdt.debug.edc.services.IEDCExpression;
import org.eclipse.cdt.debug.edc.services.IEDCMemory;
import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
import org.eclipse.cdt.debug.edc.symbols.IType;
import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
import org.eclipse.cdt.dsf.debug.service.IExpressions;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData;
import org.eclipse.cdt.utils.Addr32;
import org.eclipse.cdt.utils.Addr64;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.model.MemoryByte;
/**
* Utilities for generating formatters
*
* Use of non-api IType in this class is provisional. IType will later move to a public package.
*/
public class FormatUtils {
/** The Constant CLASS. */
private final static String CLASS = "class " ; //$NON-NLS-1$
/** The Constant STRUCT. */
private final static String STRUCT = "struct "; //$NON-NLS-1$
/**
* Check type by name.
*
* @param type the type
* @param baseName the base name
* @return true, if successful
*/
public static boolean checkTypeByName(IType type, String baseName) {
if (type == null)
return false;
// we want to preserve typedefs to determine whether this is a type we support with a formatter
IType baseType = TypeUtils.getBaseTypePreservingTypedef(type);
// check for someone making a typedef of what we're looking for
while (baseType != null && baseType instanceof ITypedef) {
if (baseType.getName().equals(baseName))
return true;
baseType = TypeUtils.getBaseTypePreservingTypedef(baseType.getType());
}
if (baseType == null)
return false;
return checkName(baseType.getName(), baseName);
}
/**
* Check name.
*
* @param typeName the type name
* @param baseName the base name
* @return true, if successful
*/
public static boolean checkName(String typeName, String baseName) {
String checkName = typeName;
if (typeName.startsWith(CLASS))
checkName = typeName.substring(CLASS.length()).trim();
else if (typeName.startsWith(STRUCT))
checkName = typeName.substring(STRUCT.length()).trim();
return checkName.equals(baseName);
}
/**
* Check if the name of a class/struct, or one of the classes/structs it
* derives from, matches a given name.
*
* @param type type of class/struct
* @param name type name to match against
* @return true if class/struct or inherited class/struct matches name,
* or null if no match
*/
public static boolean checkClassOrInheritanceByName(IType type, String name) {
// strip off typedefs and type qualifiers, to look for classes and structs
type = TypeUtils.getBaseType(type);
if (!(type instanceof ICompositeType))
return false;
ICompositeType composite = (ICompositeType)type;
String baseName = composite.getBaseName();
if (baseName.equals(name))
return true;
// if base name ends with a template size (e.g., "<15>"),
// match ignoring the value in the braces
if (baseName.indexOf('<') != -1) //$NON-NLS-1$
if (baseName.matches(name + "<.*>$")) //$NON-NLS-1$
return true;
// check classes and structs it derives from
for (IInheritance inheritance : composite.getInheritances()) {
if (checkClassOrInheritanceByName(inheritance.getType(), name))
return true;
}
return false;
}
/**
* Creates the sub expression.
*
* @param variable the variable
* @param name the name
* @param subExpressionStr the sub expression str
* @return the IExpressionDMContext for the sub expression.
*/
public static IExpressionDMContext createSubExpression(IExpressionDMContext variable, String name, String subExpressionStr) {
IEDCExpression parentExpr = (IEDCExpression) variable;
IExpressions expressions = parentExpr.getExpressionsService();
if (expressions == null)
return null;
String expressionStr = parentExpr.getExpression() + subExpressionStr;
IEDCExpression subExpression = (IEDCExpression) expressions.createExpression(parentExpr, expressionStr);
subExpression.setName(name);
return subExpression;
}
/**
* Gets the formatted string.
*
* @param variable the variable
* @param address the address
* @param length the length
* @param charSize the char size
* @return the formatted string
* @throws CoreException the core exception
*/
public static String getFormattedString(IExpressionDMContext variable, IAddress address, int length, int charSize)
throws CoreException {
IEDCExpression expression = (IEDCExpression) variable;
StackFrameDMC frame = (StackFrameDMC) expression.getFrame();
IEDCMemory memory = frame.getEDCServicesTracker().getService(Memory.class);
StringBuilder sb = new StringBuilder();
ArrayList<MemoryByte> buffer = new ArrayList<MemoryByte>();
IStatus status = memory.getMemory(frame.getExecutionDMC(), address, buffer, length * charSize, 1);
if (status.isOK()) {
for (int i = 0; i < length * charSize; i++) {
// make sure each byte is okay
if (!buffer.get(i).isReadable())
throw EDCDebugger.newCoreException(
MessageFormat.format(EDCFormatterMessages.FormatUtils_CannotReadMemory,
address.add(i).getValue().toString(16)));
char c = (char) (buffer.get(i).getValue() & 0xff);
if (charSize > 1) {
char c2 = (char) (buffer.get(++i).getValue() << 8);
c |= c2;
}
sb.append(c);
}
}
return sb.toString();
}
/**
* Gets the formatted null term string.
*
* @param variable the variable
* @param address the address
* @param charSize the char size
* @param maximumLength the maximum length
* @return the formatted null term string
* @throws CoreException the core exception
*/
public static String getFormattedNullTermString(IExpressionDMContext variable,
IAddress address, int charSize,
int maximumLength) throws CoreException {
IEDCExpression expression = (IEDCExpression) variable;
StackFrameDMC frame = (StackFrameDMC) expression.getFrame();
IEDCMemory memory = frame.getEDCServicesTracker().getService(Memory.class);
StringBuilder sb = new StringBuilder();
ArrayList<MemoryByte> buffer = new ArrayList<MemoryByte>(64);// typical size of cache block
if (maximumLength == 0)
maximumLength = 16384; // somewhat arbitrary; if the user really wants more, the value can always be set higher
OUTER:while (maximumLength > 0) {
int amount = Math.min(maximumLength, 64);// typical size of cache block
IStatus status = memory.getMemory(frame.getExecutionDMC(), address, buffer, amount, charSize);
if (status.isOK()) {
// make sure each byte is okay
for (int i = 0; i < buffer.size() && maximumLength > 0; ++i, --maximumLength) {
if (!buffer.get(i).isReadable())
{
if (i == 0) // partial memory read success
throw EDCDebugger.newCoreException(
MessageFormat.format(EDCFormatterMessages.FormatUtils_CannotReadMemory,
address.add(i).getValue().toString(16)));
maximumLength = 0;
break OUTER;
}
char c = (char) buffer.get(i).getValue();
if (charSize > 1) {
char c2 = (char) (buffer.get(++i).getValue() << 8);
c |= c2;
}
if (c == '\0')
break OUTER;
sb.append(c);
address = address.add(charSize);
}
} else if (amount > 1) {
maximumLength = Math.min(maximumLength, 64) / 2;
} else {
// Error in reading memory, bail out. If we got more than one character,
// use ellipsis, else fail.
if (sb.length() == 0)
throw EDCDebugger.newCoreException(
MessageFormat.format(EDCFormatterMessages.FormatUtils_CannotReadMemory,
address.getValue().toString(16)));
maximumLength = 0;
break;
}
buffer.clear();
}
if (maximumLength <= 0)
sb.append("..."); //$NON-NLS-1$
return sb.toString();
}
/**
* Find in collection by name.
*
* @param collection the collection
* @param name the name
* @return the i expression dm context
*/
public static IExpressionDMContext findInCollectionByName(Collection<IExpressionDMContext> collection, String name) {
for (IExpressionDMContext context : collection) {
if (((IEDCExpression) context).getName().equals(name))
return context;
}
return null;
}
/**
* Gets the all child expressions.
*
* @param variable the variable
* @return the all child expressions
*/
public static List<IExpressionDMContext> getAllChildExpressions(IExpressionDMContext variable) {
IEDCExpression variableDMC = (IEDCExpression) variable;
Expressions expressions = (Expressions) variableDMC.getExpressionsService();
if (expressions == null)
return Collections.emptyList();
List<IExpressionDMContext> kids = Arrays.<IExpressionDMContext>asList(
expressions.getLogicalSubExpressions(variableDMC));
return kids;
}
/**
* Gets the field accessor.
*
* @param type the type
* @return the field accessor
*/
public static String getFieldAccessor(IType type) {
if (type instanceof IPointerType)
return "->"; //$NON-NLS-1$
return "."; //$NON-NLS-1$
}
/**
* Gets the member value.
*
* @param variable the variable
* @param type the type
* @param memberName the member name
* @return the member value
*/
public static String getMemberValue(IExpressionDMContext variable, IType type, String memberName) {
return getMemberValue(variable, type, memberName, IExpressions.NATURAL_FORMAT);
}
/**
* Gets the member value.
*
* @param variable the variable
* @param type the type
* @param memberName the member name
* @param format the format
* @return the member value
*/
public static String getMemberValue(IExpressionDMContext variable, IType type, String memberName, String format) {
IExpressions expressions = ((IEDCExpression)variable).getExpressionsService();
if (expressions == null)
return ""; //$NON-NLS-1$
IEDCExpression expression =
(IEDCExpression) expressions.createExpression(variable, variable.getExpression()
+ FormatUtils.getFieldAccessor(type) + memberName);
FormattedValueDMContext fvc = expressions.getFormattedValueContext(expression, format);
return expression.getFormattedValue(fvc).getFormattedValue();
}
/**
* Gets the variable value.
*
* @param variable the variable
* @return the variable value
* @since 2.0
*/
public static String getVariableValue(IExpressionDMContext variable) {
return getVariableValue(variable, IExpressions.NATURAL_FORMAT);
}
/**
* Gets the variable value.
*
* @param variable the variable
* @param format the format
* @return the variable value
* @since 2.0
*/
public static String getVariableValue(IExpressionDMContext variable, String format) {
IExpressions expressions = ((IEDCExpression)variable).getExpressionsService();
FormattedValueDMContext fvc =
expressions.getFormattedValueContext(variable, format);
FormattedValueDMData formattedValue = ((IEDCExpression) variable).getFormattedValue(fvc);
return formattedValue.getFormattedValue();
}
/**
* Gets the unqualified type remove pointers.
*
* @param type the type
* @return the unqualified type remove pointers
*/
public static IType getUnqualifiedTypeRemovePointers(IType type) {
IType unqualifiedType = TypeUtils.getStrippedType(type);
while (unqualifiedType instanceof IPointerType)
unqualifiedType = TypeUtils.getStrippedType(unqualifiedType.getType());
return unqualifiedType;
}
/**
* Gets the custom value converter.
*
* @param variable the variable
* @return the custom value converter
*/
public static IVariableValueConverter getCustomValueConverter(IExpressionDMContext variable) {
IEDCExpression variableDMC = (IEDCExpression) variable;
variableDMC.evaluateExpression();
IType type = TypeUtils.getUnRefStrippedType(variableDMC.getEvaluatedType());
if (type instanceof IArrayDimensionType)
type = ((IArrayDimensionType)type).getArrayType();
return FormatExtensionManager.instance().getVariableValueConverter(type);
}
/**
* Get an address from an expression representing a pointer.
* @param value the evaluated value of an IEDCExpression
* @return the pointer address or <code>null</code>
*/
public static IAddress getPointerValue(Number value) {
IAddress address = null;
if (value instanceof BigInteger) {
address = new Addr64((BigInteger) value);
} else {
address = new Addr32(value.longValue());
}
return address;
}
/**
* Gets the template type name.
*
* @param typeName the type name
* @param type the type
* @return the template type name
* @since 2.0
*/
public static String getTemplateTypeName(String typeName, IType type) {
// TODO Fix this when type gives template information Bug 11443
ICompositeType composite = (ICompositeType) TypeUtils.getBaseType(type);
String baseName = composite.getBaseName();
Matcher m = Pattern.compile(typeName + "<(.+)>").matcher(baseName);
if (m.matches())
return m.group(1);
// check classes and structs it derives from
for (IInheritance inheritance : composite.getInheritances()) {
String templateTypeName = getTemplateTypeName(typeName, inheritance.getType());
if (templateTypeName != null)
return templateTypeName;
}
return null;
}
/**
* Gets the formatted value.
*
* @param variable the variable
* @return the formatted value
* @throws CoreException the core exception
* @since 2.0
*/
public static String getFormattedValue(IExpressionDMContext variable) throws CoreException {
IVariableValueConverter valueConverter = getCustomValueConverter(variable);
if (valueConverter != null) {
return valueConverter.getValue(variable);
}
else
return getVariableValue(variable);
}
/**
* Gets the max number of children.
*
* @return the max number of children
* @since 2.0
*/
public static int getMaxNumberOfChildren() {
return 200; // this seems like a good default
}
/**
* Evaluates the expression and throws a CoreException if there is an evaluation error.
*
* @param expression the expression
* @throws CoreException the core exception
* @since 2.0
*/
public static void evaluateExpression(IEDCExpression expression) throws CoreException {
expression.evaluateExpression();
IStatus status = expression.getEvaluationError();
if ((status != null && !status.isOK()) || expression.getEvaluatedValue() == null) {
Throwable t = status != null ? status.getException() : null;
throw EDCDebugger.newDebugException("Error evaluating expression: " + expression.getExpression(), t);
}
}
}