blob: 910e15e0f3772dc21baa1986d1b81bde00cc54fb [file] [log] [blame]
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package sun.jvm.hotspot.types.basic;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.debugger.MachineDescription;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.types.Type;
import sun.jvm.hotspot.types.TypeDataBase;
/** <P> This is a basic implementation of the TypeDataBase interface.
It allows an external type database builder to add types to be
consumed by a client through the Type interfaces. It has no
knowledge of symbol lookup; for example, the builder is
responsible for providing the addresses of static fields. </P>
<P> Among other things, the database builder is responsible for
providing the Types for the Java primitive types, as well as their
sizes. </P>
*/
public class BasicTypeDataBase implements TypeDataBase {
private MachineDescription machDesc;
private VtblAccess vtblAccess;
/** Maps strings to Type objects. This does not contain the primitive types. */
private Map nameToTypeMap = new HashMap();
/** Maps strings to Integers, used for enums, etc. */
private Map nameToIntConstantMap = new HashMap();
/** Maps strings to Longs, used for 32/64-bit constants, etc. */
private Map nameToLongConstantMap = new HashMap();
/** Primitive types. */
private Type jbooleanType;
private Type jbyteType;
private Type jcharType;
private Type jdoubleType;
private Type jfloatType;
private Type jintType;
private Type jlongType;
private Type jshortType;
/** For debugging */
private static final boolean DEBUG;
static {
DEBUG = System.getProperty("sun.jvm.hotspot.types.basic.BasicTypeDataBase.DEBUG") != null;
}
public BasicTypeDataBase(MachineDescription machDesc, VtblAccess vtblAccess) {
this.machDesc = machDesc;
this.vtblAccess = vtblAccess;
}
public Type lookupType(String cTypeName) {
return lookupType(cTypeName, true);
}
public Type lookupType(String cTypeName, boolean throwException) {
Type type = (Type) nameToTypeMap.get(cTypeName);
if (type == null && throwException) {
throw new RuntimeException("No type named \"" + cTypeName + "\" in database");
}
return type;
}
public Integer lookupIntConstant(String constantName) {
return lookupIntConstant(constantName, true);
}
public Integer lookupIntConstant(String constantName, boolean throwException) {
Integer i = (Integer) nameToIntConstantMap.get(constantName);
if (i == null) {
if (throwException) {
throw new RuntimeException("No integer constant named \"" + constantName + "\" present in type database");
}
}
return i;
}
public Long lookupLongConstant(String constantName) {
return lookupLongConstant(constantName, true);
}
public Long lookupLongConstant(String constantName, boolean throwException) {
Long i = (Long) nameToLongConstantMap.get(constantName);
if (i == null) {
if (throwException) {
throw new RuntimeException("No long constant named \"" + constantName + "\" present in type database");
}
}
return i;
}
public Type getJBooleanType() {
return jbooleanType;
}
public Type getJByteType() {
return jbyteType;
}
public Type getJCharType() {
return jcharType;
}
public Type getJDoubleType() {
return jdoubleType;
}
public Type getJFloatType() {
return jfloatType;
}
public Type getJIntType() {
return jintType;
}
public Type getJLongType() {
return jlongType;
}
public Type getJShortType() {
return jshortType;
}
public long getAddressSize() {
return machDesc.getAddressSize();
}
public long getOopSize() {
return VM.getVM().getOopSize();
}
HashMap typeToVtbl = new HashMap();
private Address vtblForType(Type type) {
Address vtblAddr = (Address)typeToVtbl.get(type);
if (vtblAddr == null) {
vtblAddr = vtblAccess.getVtblForType(type);
if (vtblAddr != null) {
typeToVtbl.put(type, vtblAddr);
}
}
return vtblAddr;
}
public boolean addressTypeIsEqualToType(Address addr, Type type) {
if (addr == null) {
return false;
}
// This implementation should be suitably platform-independent; we
// search nearby memory for the vtbl value of the given type.
Address vtblAddr = vtblForType(type);
if (vtblAddr == null) {
// Type was not polymorphic, or an error occurred during lookup
if (DEBUG) {
System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: vtblAddr == null");
}
return false;
}
// The first implementation searched three locations for this vtbl
// value; scanning through the entire object was considered, but
// we thought we knew where we were looking, and looking only in
// these specific locations should reduce the probability of
// mistaking random bits as a pointer (although, realistically
// speaking, the likelihood of finding a match between the bit
// pattern of, for example, a double and the vtbl is vanishingly
// small.)
// 1. The first word of the object (should handle MSVC++ as
// well as the SparcWorks compilers with compatibility set to
// v5.0 or greater)
// 2. and 3. The last two Address-aligned words of the part of
// the object defined by its topmost polymorphic superclass.
// This should handle the SparcWorks compilers, v4.2 or
// earlier, as well as any other compilers which place the vptr
// at the end of the user-defined fields of the first base
// class with virtual functions.
//
// Unfortunately this algorithm did not work properly for the
// specific case of the ThreadShadow/Thread inheritance situation,
// because the Solaris compiler seems to cleverly eliminate the
// vtbl for ThreadShadow since the only virtual is empty. (We
// should get rid of the ThreadShadow and fix the include
// databases, but need to postpone this for the present.) The
// current solution performs the three-location check for this
// class and all of its known superclasses rather than just the
// topmost polymorphic one.
Type curType = type;
try {
while (curType != null) {
// Using the size information we have for this type, check the
// three locations described above.
// (1)
if (vtblAddr.equals(addr.getAddressAt(0))) {
return true;
}
// (2)
long offset = curType.getSize();
// I don't think this should be misaligned under any
// circumstances, but I'm not sure (FIXME: also not sure which
// way to go here, up or down -- assuming down)
offset -= (offset % getAddressSize());
if (offset <= 0) {
return false;
}
if (vtblAddr.equals(addr.getAddressAt(offset))) {
return true;
}
offset -= getAddressSize();
if (offset <= 0) {
return false;
}
if (vtblAddr.equals(addr.getAddressAt(offset))) {
return true;
}
curType = curType.getSuperclass();
}
}
catch (Exception e) {
// Any UnmappedAddressExceptions, etc. are a good indication
// that the pointer is not of the specified type
if (DEBUG) {
System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: exception occurred during lookup:");
e.printStackTrace();
}
return false;
}
if (DEBUG) {
System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: all vptr tests failed for type " +
type.getName());
}
return false;
}
public Type findDynamicTypeForAddress(Address addr, Type baseType) {
// This implementation should be suitably platform-independent; we
// search nearby memory for the vtbl value of the given type.
if (vtblForType(baseType) == null) {
// Type was not polymorphic which is an error of some sort
throw new InternalError(baseType + " does not appear to be polymorphic");
}
// This is a more restricted version of guessTypeForAddress since
// that function has some limitations since it doesn't really know
// where in the hierarchy a virtual type starts and just poking
// around in memory is likely to trip over some vtable address,
// resulting in false positives. Eventually all uses should
// switch to this logic but in the interests of stability it will
// be separate for the moment.
// Assuming that the base type is truly the first polymorphic type
// then the vtbl for all subclasss should be at several defined
// locations so only those locations will be checked. It's also
// required that the caller knows that the static type is at least
// baseType. See the notes in guessTypeForAddress for the logic of
// the locations searched.
Address loc1 = addr.getAddressAt(0);
Address loc2 = null;
Address loc3 = null;
long offset2 = baseType.getSize();
// I don't think this should be misaligned under any
// circumstances, but I'm not sure (FIXME: also not sure which
// way to go here, up or down -- assuming down)
offset2 = offset2 - (offset2 % getAddressSize()) - getAddressSize();
if (offset2 > 0) {
loc2 = addr.getAddressAt(offset2);
}
long offset3 = offset2 - getAddressSize();
if (offset3 > 0) {
loc3 = addr.getAddressAt(offset3);
}
Type loc2Match = null;
Type loc3Match = null;
for (Iterator iter = getTypes(); iter.hasNext(); ) {
Type type = (Type) iter.next();
Type superClass = type;
while (superClass != baseType && superClass != null) {
superClass = superClass.getSuperclass();
}
if (superClass == null) continue;
Address vtblAddr = vtblForType(type);
if (vtblAddr == null) {
// This occurs sometimes for intermediate types that are never
// instantiated.
if (DEBUG) {
System.err.println("null vtbl for " + type);
}
continue;
}
// Prefer loc1 match
if (vtblAddr.equals(loc1)) return type;
if (loc2 != null && loc2Match == null && vtblAddr.equals(loc2)) {
loc2Match = type;
}
if (loc3 != null && loc3Match == null && vtblAddr.equals(loc3)) {
loc3Match = type;
}
}
if (loc2Match != null) return loc2Match;
if (loc3Match != null) return loc3Match;
return null;
}
public Type guessTypeForAddress(Address addr) {
for (Iterator iter = getTypes(); iter.hasNext(); ) {
Type t = (Type) iter.next();
if (addressTypeIsEqualToType(addr, t)) {
return t;
}
}
return null;
}
public long cIntegerTypeMaxValue(long sizeInBytes, boolean isUnsigned) {
return machDesc.cIntegerTypeMaxValue(sizeInBytes, isUnsigned);
}
public long cIntegerTypeMinValue(long sizeInBytes, boolean isUnsigned) {
return machDesc.cIntegerTypeMinValue(sizeInBytes, isUnsigned);
}
public Iterator getTypes() {
return nameToTypeMap.values().iterator();
}
public Iterator getIntConstants() {
return nameToIntConstantMap.keySet().iterator();
}
public Iterator getLongConstants() {
return nameToLongConstantMap.keySet().iterator();
}
//--------------------------------------------------------------------------------
// Public routines only for use by the database builder
//
/** This method should only be called by the builder of the
TypeDataBase and at most once */
public void setJBooleanType(Type type) {
jbooleanType = type;
}
/** This method should only be called by the builder of the
TypeDataBase and at most once */
public void setJByteType(Type type) {
jbyteType = type;
}
/** This method should only be called by the builder of the
TypeDataBase and at most once */
public void setJCharType(Type type) {
jcharType = type;
}
/** This method should only be called by the builder of the
TypeDataBase and at most once */
public void setJDoubleType(Type type) {
jdoubleType = type;
}
/** This method should only be called by the builder of the
TypeDataBase and at most once */
public void setJFloatType(Type type) {
jfloatType = type;
}
/** This method should only be called by the builder of the
TypeDataBase and at most once */
public void setJIntType(Type type) {
jintType = type;
}
/** This method should only be called by the builder of the
TypeDataBase and at most once */
public void setJLongType(Type type) {
jlongType = type;
}
/** This method should only be called by the builder of the
TypeDataBase and at most once */
public void setJShortType(Type type) {
jshortType = type;
}
/** This method should only be used by the builder of the
TypeDataBase. Throws a RuntimeException if a class with this
name was already present. */
public void addType(Type type) {
if (nameToTypeMap.get(type.getName()) != null) {
throw new RuntimeException("type of name \"" + type.getName() + "\" already present");
}
nameToTypeMap.put(type.getName(), type);
}
/** This method should only be used by the builder of the
TypeDataBase. Throws a RuntimeException if this class was not
present. */
public void removeType(Type type) {
Type curType = (Type) nameToTypeMap.get(type.getName());
if (curType == null) {
throw new RuntimeException("type of name \"" + type.getName() + "\" not present");
}
if (!curType.equals(type)) {
throw new RuntimeException("a different type of name \"" + type.getName() + "\" was present");
}
nameToTypeMap.remove(type.getName());
}
/** This method should only be used by the builder of the
TypeDataBase. Throws a RuntimeException if an integer constant
with this name was already present. */
public void addIntConstant(String name, int value) {
if (nameToIntConstantMap.get(name) != null) {
throw new RuntimeException("int constant of name \"" + name + "\" already present");
}
nameToIntConstantMap.put(name, new Integer(value));
}
/** This method should only be used by the builder of the
TypeDataBase. Throws a RuntimeException if an integer constant
with this name was not present. */
public void removeIntConstant(String name) {
Integer curConstant = (Integer) nameToIntConstantMap.get(name);
if (curConstant == null) {
throw new RuntimeException("int constant of name \"" + name + "\" not present");
}
nameToIntConstantMap.remove(name);
}
/** This method should only be used by the builder of the
TypeDataBase. Throws a RuntimeException if a long constant with
this name was already present. */
public void addLongConstant(String name, long value) {
if (nameToLongConstantMap.get(name) != null) {
throw new RuntimeException("long constant of name \"" + name + "\" already present");
}
nameToLongConstantMap.put(name, new Long(value));
}
/** This method should only be used by the builder of the
TypeDataBase. Throws a RuntimeException if a long constant with
this name was not present. */
public void removeLongConstant(String name) {
Long curConstant = (Long) nameToLongConstantMap.get(name);
if (curConstant == null) {
throw new RuntimeException("long constant of name \"" + name + "\" not present");
}
nameToLongConstantMap.remove(name);
}
}