| /* |
| * 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); |
| } |
| } |