blob: df05a867ef0af34b5e1bc1982752d8a9d573a385 [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.symbols;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTProblemTypeId;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTArrayDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.PushLongOrBigInteger;
import org.eclipse.cdt.debug.edc.internal.symbols.ArrayBoundType;
import org.eclipse.cdt.debug.edc.internal.symbols.ArrayType;
import org.eclipse.cdt.debug.edc.internal.symbols.CPPBasicType;
import org.eclipse.cdt.debug.edc.internal.symbols.IArrayBoundType;
import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
import org.eclipse.cdt.debug.edc.internal.symbols.PointerType;
import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
import org.eclipse.core.runtime.CoreException;
/**
* This class manages the {@link IType} instances relevant to a given target.
* @noextend This class is not intended to be subclassed by clients.
*/
@SuppressWarnings("deprecation")
public class TypeEngine {
private Map<Integer, Integer> typeSizeMap;
private Map<Object, IType> typeMap = new HashMap<Object, IType>();
private int addressSize;
private Map<IType, String> typeNameMap = new HashMap<IType, String>();
private final IDebugInfoProvider debugInfoProvider;
/** map of type signature to IType or CoreException */
private Map<String, Object> typeIdToTypeMap = new HashMap<String, Object>();
private Map<String, IArrayType> typeToArrayTypeMap = new HashMap<String, IArrayType>();
private boolean charIsSigned;
/**
* @since 2.0
*/
public TypeEngine(ITargetEnvironment targetEnvironment, IDebugInfoProvider debugInfoProvider) {
this.debugInfoProvider = debugInfoProvider;
if (targetEnvironment != null) {
typeSizeMap = targetEnvironment.getBasicTypeSizes();
addressSize = targetEnvironment.getPointerSize();
charIsSigned = targetEnvironment.isCharSigned();
} else {
typeSizeMap = Collections.emptyMap();
addressSize = 4;
charIsSigned = true;
}
}
/**
* Get the target's basic type for an integer of the given size
* @param size
* @param isSigned
* @return type or <code>null</code> if no match
*/
public IType getIntegerTypeOfSize(int size, boolean isSigned) {
int basicType;
int flags;
if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_CHAR) == size) {
basicType = ICPPBasicType.t_char;
flags = ICPPBasicType.IS_SIGNED;
} else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_CHAR) == size) {
basicType = ICPPBasicType.t_char;
flags = ICPPBasicType.IS_UNSIGNED;
} else if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_SHORT) == size) {
basicType = ICPPBasicType.t_int;
flags = ICPPBasicType.IS_SHORT + ICPPBasicType.IS_SIGNED;
} else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_SHORT) == size) {
basicType = ICPPBasicType.t_int;
flags = ICPPBasicType.IS_SHORT + ICPPBasicType.IS_UNSIGNED;
} else if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_INT) == size) {
basicType = ICPPBasicType.t_int;
flags = ICPPBasicType.IS_SIGNED;
} else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_INT) == size) {
basicType = ICPPBasicType.t_int;
flags = ICPPBasicType.IS_UNSIGNED;
} else if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG) == size) {
basicType = ICPPBasicType.t_int;
flags = ICPPBasicType.IS_LONG + ICPPBasicType.IS_SIGNED;
} else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG) == size) {
basicType = ICPPBasicType.t_int;
flags = ICPPBasicType.IS_LONG + ICPPBasicType.IS_UNSIGNED;
} else if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG_LONG) == size) {
basicType = ICPPBasicType.t_int;
flags = ICPPBasicType.IS_LONG_LONG + ICPPBasicType.IS_SIGNED;
} else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG_LONG) == size) {
basicType = ICPPBasicType.t_int;
flags = ICPPBasicType.IS_LONG_LONG + ICPPBasicType.IS_UNSIGNED;
} else {
return null;
}
return getBasicType(basicType, flags, size);
}
/**
* Get the target's basic type for an integer of the given kind
* @param typeUtilsType from TypeUtils#BASIC_TYPE
* @param isSigned
* @return type or <code>null</code> if no match
*/
public IType getIntegerTypeFor(int typeUtilsBasicType, boolean isSigned) {
return getIntegerTypeOfSize(typeSizeMap.get(typeUtilsBasicType), isSigned);
}
/**
* Get a cached ICPPBasicType instance
* @param basicType
* @param flags
* @param size
* @return IType
*/
public IType getBasicType(int basicType, int flags, int size) {
return getBasicType(null, basicType, flags, size);
}
/**
* Get a cached ICPPBasicType instance, using a custom name.
* @param name
* @param basicType
* @param flags
* @param size
* @return IType
*/
public IType getBasicType(String name, int basicType, int flags, int size) {
Object typeCode = (flags << 16) + (basicType << 8) + size;
if (name != null)
typeCode = name + ":" + typeCode; //$NON-NLS-1$
IType type = typeMap.get(typeCode);
if (type == null) {
if (name == null)
name = getBasicTypeName(basicType, flags);
type = new CPPBasicType(name, basicType, flags, size);
typeMap.put(typeCode, type);
}
return type;
}
/**
* @param basicType
* @param flags
* @return
*/
private String getBasicTypeName(int basicType, int flags) {
String name;
switch (basicType) {
case ICPPBasicType.t_bool:
name = "bool"; break; //$NON-NLS-1$
case ICPPBasicType.t_wchar_t:
name = "wchar_t"; break; //$NON-NLS-1$
case ICPPBasicType.t_char:
name = "char"; break; //$NON-NLS-1$
case ICPPBasicType.t_int:
if ((flags & ICPPBasicType.IS_SHORT) != 0)
name = "short"; //$NON-NLS-1$
else if ((flags & ICPPBasicType.IS_LONG) != 0)
name = "long"; //$NON-NLS-1$
else if ((flags & ICPPBasicType.IS_LONG_LONG) != 0)
name = "long long"; //$NON-NLS-1$
else
name = "int"; //$NON-NLS-1$
break;
case ICPPBasicType.t_float:
name = "float"; break; //$NON-NLS-1$
case ICPPBasicType.t_double:
if ((flags & ICPPBasicType.IS_LONG) != 0)
name = "long double"; //$NON-NLS-1$
else
name = "double"; //$NON-NLS-1$
break;
case ICPPBasicType.t_unspecified:
name = "<<unknown>>"; //$NON-NLS-1$
break;
case ICPPBasicType.t_void:
name = "void"; //$NON-NLS-1$
break;
default:
assert(false);
name = ""; //$NON-NLS-1$
break;
}
if ((flags & ICPPBasicType.IS_SIGNED) != 0)
name = "signed " + name; //$NON-NLS-1$
else if ((flags & ICPPBasicType.IS_UNSIGNED) != 0)
name = "unsigned " + name; //$NON-NLS-1$
if ((flags & ICPPBasicType.IS_COMPLEX) != 0)
name = "complex " + name; //$NON-NLS-1$
if ((flags & ICPPBasicType.IS_IMAGINARY) != 0)
name = "imaginary " + name; //$NON-NLS-1$
return name;
}
/**
* Get the target's basic type for a float of the given size
* @param size
* @param isSigned
* @return type or <code>null</code> if no match
*/
public IType getFloatTypeOfSize(int size) {
if (typeSizeMap.get(TypeUtils.BASIC_TYPE_FLOAT) == size) {
return getBasicType(ICPPBasicType.t_float, 0, size);
}
if (typeSizeMap.get(TypeUtils.BASIC_TYPE_DOUBLE) == size) {
return getBasicType(ICPPBasicType.t_double, 0, size);
}
if (typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG_DOUBLE) == size) {
return getBasicType(ICPPBasicType.t_double, ICPPBasicType.IS_LONG, size);
}
return null;
}
/**
* Get the basic type for a character of a given size.
* @param size
* @return IType
*/
public IType getCharacterType(int size) {
if (typeSizeMap.get(TypeUtils.BASIC_TYPE_CHAR) == size) {
return getBasicType(ICPPBasicType.t_char, 0, size);
}
return getBasicType(ICPPBasicType.t_wchar_t, 0, size);
}
/**
* Get the basic type for a character of a given size.
* @param size
* @return IType
*/
public IType getWideCharacterType(int size) {
return getBasicType(ICPPBasicType.t_wchar_t, 0, size);
}
public IType getBooleanType(int size) {
return getBasicType(ICPPBasicType.t_bool, 0, size);
}
public IType getCharArrayType(IType charType, int length) {
IArrayBoundType bounds = new ArrayBoundType(null, length);
IArrayType array = new ArrayType(charType.getName(), null, length, null); //$NON-NLS-1$ //$NON-NLS-2$
array.addBound(bounds);
array.setType(charType);
return array;
}
/**
* Get the integral type the same size as a pointer.
* @return IType or <code>null</code>
*/
public IType getPointerSizeType() {
int size = getPointerSize();
return getBasicType("ptrsize_t", ICPPBasicType.t_int, 0, size); //$NON-NLS-1$
}
private int getPointerSize() {
return addressSize;
}
/**
* Get the byte size of a type
* @param basicType (TypeUtils#BASIC_TYPE_xxx)
* @return type, or 0 if unknown
*/
public int getTypeSize(int basicType) {
Integer size = typeSizeMap.get(basicType);
return size != null ? size.intValue() : 0;
}
/**
* @param valueType
* @return
*/
public String getTypeName(IType valueType) {
if (valueType == null)
return ""; //$NON-NLS-1$
String typeName = null;
synchronized (typeNameMap) {
typeName = typeNameMap.get(valueType);
if (typeName == null) {
typeName = TypeUtils.getFullTypeName(valueType);
typeNameMap.put(valueType, typeName);
}
}
return typeName;
}
/**
* Convert an AST type ID into an EDC IType.
* @param typeId
* @return IType
* @throws CoreException if the IType cannot be created
*/
public IType getTypeForTypeId(IASTTypeId typeId) throws CoreException {
if (typeId == null)
throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_NoTypeToCast);
if (typeId instanceof IASTProblemTypeId)
throw EDCDebugger.newCoreException(((IASTProblemTypeId) typeId).getProblem().getMessage());
String typeSignature = ASTSignatureUtil.getSignature(typeId);
Object obj = typeIdToTypeMap.get(typeSignature);
if (obj instanceof CoreException)
throw (CoreException) obj;
obj = null; //HACK
IType type = null;
if (!(obj instanceof IType)) {
try {
type = createTypeForTypeId(typeId, typeSignature, type);
typeIdToTypeMap.put(typeSignature, type);
} catch (CoreException e) {
typeIdToTypeMap.put(typeSignature, e);
throw e;
}
} else {
type = (IType) obj;
}
return type;
}
/**
* Create an IType mapping to IASTTypeId
* @param typeId
* @param typeSignature
* @param type
* @return new IType
* @throws CoreException
*/
private IType createTypeForTypeId(IASTTypeId typeId, String typeSignature, IType type) throws CoreException {
IASTDeclSpecifier declSpec = typeId.getDeclSpecifier();
if (declSpec instanceof IASTSimpleDeclSpecifier) {
type = getTypeForDeclSpecifier((IASTSimpleDeclSpecifier) declSpec);
} else if (declSpec instanceof IASTNamedTypeSpecifier || declSpec instanceof IASTElaboratedTypeSpecifier) {
String typeName;
int elaboration = -1;
if (declSpec instanceof IASTNamedTypeSpecifier) {
typeName = ((IASTNamedTypeSpecifier) declSpec).getName().toString();
} else /*if (declSpec instanceof IASTElaboratedTypeSpecifier)*/ {
// note: ignore the elaboration (class/struct/etc) since compilers are
// inconsistent with how they do this, and furthermore, only one name
// should be visible at a time, usually, anyway
elaboration = ((IASTElaboratedTypeSpecifier) declSpec).getKind();
typeName = ((IASTElaboratedTypeSpecifier) declSpec).getName().toString();
}
if (debugInfoProvider != null) {
Collection<IType> types;
IType aMatch = null;
types = debugInfoProvider.getTypesByName(typeName);
// try to find one matching struct/class/etc
for (IType aType : types) {
aMatch = aType;
if (elaboration < 0 ||
(type instanceof ICompositeType && ((ICompositeType) type).getKey() == elaboration)) {
type = aType;
break;
}
}
// if no match, just take a matching name
if (type == null && aMatch != null) {
type = aMatch;
}
// fall through to check type != null
}
}
if (type == null) {
throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_CannotResolveType + typeSignature);
}
if (typeId.getAbstractDeclarator() instanceof ICPPASTDeclarator) {
ICPPASTDeclarator declarator = (ICPPASTDeclarator) typeId.getAbstractDeclarator();
for (@SuppressWarnings("unused") IASTPointerOperator pointer : declarator.getPointerOperators()) {
IType ptr = new PointerType(type.getName()+"*", type.getScope(), getPointerSize(), null); //$NON-NLS-1$
ptr.setType(type);
type = ptr;
}
if (declarator instanceof ICPPASTArrayDeclarator) {
ICPPASTArrayDeclarator arrayDeclarator = (ICPPASTArrayDeclarator) declarator;
IArrayType arrayType = new ArrayType(type.getName(), type.getScope(), 0, null); //$NON-NLS-1$
for (IASTArrayModifier arrayMod : arrayDeclarator.getArrayModifiers()) {
long elementCount = 1;
if (arrayMod.getConstantExpression() != null) {
elementCount = getConstantValue(arrayMod.getConstantExpression());
}
IArrayBoundType bound = new ArrayBoundType(arrayType.getScope(), elementCount);
arrayType.addBound(bound);
}
arrayType.setType(type);
type = arrayType;
}
}
return type;
}
private long getConstantValue(IASTExpression constantExpression) throws CoreException {
if (constantExpression instanceof IASTLiteralExpression) {
if (((IASTLiteralExpression) constantExpression).getKind() == IASTLiteralExpression.lk_integer_constant) {
// HACK, use more generic utilities
PushLongOrBigInteger pusher = new PushLongOrBigInteger(constantExpression.toString());
return pusher.getLong();
}
}
throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_ExpectedIntegerConstant +
ASTSignatureUtil.getExpressionString(constantExpression));
}
private IType getTypeForDeclSpecifier(IASTSimpleDeclSpecifier simpleDeclSpec) throws CoreException {
int baseType = 0;
int basicType = 0;
switch (simpleDeclSpec.getType()) {
case IASTSimpleDeclSpecifier.t_bool:
baseType = ICPPBasicType.t_bool;
basicType = TypeUtils.BASIC_TYPE_BOOL;
break;
case IASTSimpleDeclSpecifier.t_char:
baseType = ICPPBasicType.t_char;
basicType = TypeUtils.BASIC_TYPE_CHAR;
break;
case IASTSimpleDeclSpecifier.t_wchar_t:
baseType = ICPPBasicType.t_wchar_t;
basicType = TypeUtils.BASIC_TYPE_WCHAR_T;
break;
case IASTSimpleDeclSpecifier.t_double:
baseType = ICPPBasicType.t_double;
basicType = TypeUtils.BASIC_TYPE_DOUBLE;
break;
case IASTSimpleDeclSpecifier.t_float:
baseType = ICPPBasicType.t_float;
basicType = TypeUtils.BASIC_TYPE_FLOAT;
break;
case IASTSimpleDeclSpecifier.t_int:
baseType = ICPPBasicType.t_int;
basicType = TypeUtils.BASIC_TYPE_INT;
break;
case IASTSimpleDeclSpecifier.t_void:
baseType = ICPPBasicType.t_void;
break;
case IASTSimpleDeclSpecifier.t_typeof:
// we'd need to parse the subexpression then get its type
case IASTSimpleDeclSpecifier.t_decltype:
// not sure about this one
throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_NoDecltypeSupport);
case IASTSimpleDeclSpecifier.t_unspecified:
baseType = ICPPBasicType.t_int;
break;
default:
throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_UnhandledType + simpleDeclSpec);
}
int flags = 0;
if (simpleDeclSpec.isComplex()) flags |= ICPPBasicType.IS_COMPLEX;
if (simpleDeclSpec.isImaginary()) flags |= ICPPBasicType.IS_IMAGINARY;
if (simpleDeclSpec.isLong()) {
flags |= ICPPBasicType.IS_LONG;
if (basicType == 0)
basicType = TypeUtils.BASIC_TYPE_LONG;
}
if (simpleDeclSpec.isLongLong()) {
flags |= ICPPBasicType.IS_LONG_LONG;
if (basicType == 0)
basicType = TypeUtils.BASIC_TYPE_LONG_LONG;
}
if (simpleDeclSpec.isShort()) {
flags |= ICPPBasicType.IS_SHORT;
if (basicType == 0)
basicType = TypeUtils.BASIC_TYPE_SHORT;
}
if (simpleDeclSpec.isUnsigned()) flags |= ICPPBasicType.IS_UNSIGNED;
if (simpleDeclSpec.isSigned()) flags |= ICPPBasicType.IS_SIGNED;
if (!simpleDeclSpec.isUnsigned() && !simpleDeclSpec.isSigned()) {
if (baseType == ICPPBasicType.t_char)
flags |= charIsSigned ? ICPPBasicType.IS_SIGNED : ICPPBasicType.IS_UNSIGNED;
else if (baseType == ICPPBasicType.t_int)
flags |= ICPPBasicType.IS_SIGNED;
}
int size = getTypeSize(basicType);
return getBasicType(baseType, flags, size);
}
/**
* Convert a given type to an array type
* @param exprType
* @return array type
* @throws CoreException if not a sensible conversion
*/
public IType convertToArrayType(IType type, int count) throws CoreException {
String typeSig = getTypeName(type);
IArrayType arrayType = typeToArrayTypeMap.get(typeSig);
if (arrayType == null) {
type = TypeUtils.getStrippedType(type);
IType baseType = type;
if (type instanceof IPointerType || type instanceof IArrayType) {
baseType = type.getType();
}
if (baseType == null)
throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_CannotResolveBaseType + typeSig);
arrayType = new ArrayType(baseType.getName(), baseType.getScope(), //$NON-NLS-1$
baseType.getByteSize() * count, null);
IArrayBoundType bound = new ArrayBoundType(arrayType.getScope(), count);
arrayType.addBound(bound);
arrayType.setType(baseType);
typeToArrayTypeMap.put(typeSig, arrayType);
}
return arrayType;
}
}