1 /*
   2  * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.types.basic;
  26 
  27 import java.util.*;
  28 import sun.jvm.hotspot.debugger.*;
  29 import sun.jvm.hotspot.types.*;
  30 import sun.jvm.hotspot.runtime.VM;
  31 
  32 /** <P> This is a basic implementation of the TypeDataBase interface.
  33     It allows an external type database builder to add types to be
  34     consumed by a client through the Type interfaces. It has no
  35     knowledge of symbol lookup; for example, the builder is
  36     responsible for providing the addresses of static fields. </P>
  37 
  38     <P> Among other things, the database builder is responsible for
  39     providing the Types for the Java primitive types, as well as their
  40     sizes. </P>
  41 */
  42 
  43 public class BasicTypeDataBase implements TypeDataBase {
  44   private MachineDescription machDesc;
  45   private VtblAccess vtblAccess;
  46   /** Maps strings to Type objects. This does not contain the primitive types. */
  47   private Map nameToTypeMap = new HashMap();
  48   /** Maps strings to Integers, used for enums, etc. */
  49   private Map nameToIntConstantMap = new HashMap();
  50   /** Maps strings to Longs, used for 32/64-bit constants, etc. */
  51   private Map nameToLongConstantMap = new HashMap();
  52   /** Primitive types. */
  53   private Type jbooleanType;
  54   private Type jbyteType;
  55   private Type jcharType;
  56   private Type jdoubleType;
  57   private Type jfloatType;
  58   private Type jintType;
  59   private Type jlongType;
  60   private Type jshortType;
  61 
  62   /** For debugging */
  63   private static final boolean DEBUG;
  64   static {
  65     DEBUG = System.getProperty("sun.jvm.hotspot.types.basic.BasicTypeDataBase.DEBUG") != null;
  66   }
  67 
  68   public BasicTypeDataBase(MachineDescription machDesc, VtblAccess vtblAccess) {
  69     this.machDesc   = machDesc;
  70     this.vtblAccess = vtblAccess;
  71   }
  72 
  73   public Type lookupType(String cTypeName) {
  74     return lookupType(cTypeName, true);
  75   }
  76 
  77   public Type lookupType(String cTypeName, boolean throwException) {
  78     Type type = (Type) nameToTypeMap.get(cTypeName);
  79     if (type == null && throwException) {
  80       throw new RuntimeException("No type named \"" + cTypeName + "\" in database");
  81     }
  82     return type;
  83   }
  84 
  85   public Integer lookupIntConstant(String constantName) {
  86     return lookupIntConstant(constantName, true);
  87   }
  88 
  89   public Integer lookupIntConstant(String constantName, boolean throwException) {
  90     Integer i = (Integer) nameToIntConstantMap.get(constantName);
  91     if (i == null) {
  92       if (throwException) {
  93         throw new RuntimeException("No integer constant named \"" + constantName + "\" present in type database");
  94       }
  95     }
  96     return i;
  97   }
  98 
  99   public Long lookupLongConstant(String constantName) {
 100     return lookupLongConstant(constantName, true);
 101   }
 102 
 103   public Long lookupLongConstant(String constantName, boolean throwException) {
 104     Long i = (Long) nameToLongConstantMap.get(constantName);
 105     if (i == null) {
 106       if (throwException) {
 107         throw new RuntimeException("No long constant named \"" + constantName + "\" present in type database");
 108       }
 109     }
 110     return i;
 111   }
 112 
 113   public Type getJBooleanType() {
 114     return jbooleanType;
 115   }
 116 
 117   public Type getJByteType() {
 118     return jbyteType;
 119   }
 120 
 121   public Type getJCharType() {
 122     return jcharType;
 123   }
 124 
 125   public Type getJDoubleType() {
 126     return jdoubleType;
 127   }
 128 
 129   public Type getJFloatType() {
 130     return jfloatType;
 131   }
 132 
 133   public Type getJIntType() {
 134     return jintType;
 135   }
 136 
 137   public Type getJLongType() {
 138     return jlongType;
 139   }
 140 
 141   public Type getJShortType() {
 142     return jshortType;
 143   }
 144 
 145   public long getAddressSize() {
 146     return machDesc.getAddressSize();
 147   }
 148 
 149   public long getOopSize() {
 150     return VM.getVM().getOopSize();
 151   }
 152 
 153   static HashMap typeToVtbl = new HashMap();
 154 
 155   private Address vtblForType(Type type) {
 156     Address vtblAddr = (Address)typeToVtbl.get(type);
 157     if (vtblAddr == null) {
 158       vtblAddr = vtblAccess.getVtblForType(type);
 159       if (vtblAddr != null) {
 160         typeToVtbl.put(type, vtblAddr);
 161       }
 162     }
 163     return vtblAddr;
 164   }
 165 
 166   public boolean addressTypeIsEqualToType(Address addr, Type type) {
 167     if (addr == null) {
 168       return false;
 169     }
 170 
 171     // This implementation should be suitably platform-independent; we
 172     // search nearby memory for the vtbl value of the given type.
 173 
 174     Address vtblAddr = vtblForType(type);
 175 
 176     if (vtblAddr == null) {
 177       // Type was not polymorphic, or an error occurred during lookup
 178       if (DEBUG) {
 179         System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: vtblAddr == null");
 180       }
 181 
 182       return false;
 183     }
 184 
 185     // The first implementation searched three locations for this vtbl
 186     // value; scanning through the entire object was considered, but
 187     // we thought we knew where we were looking, and looking only in
 188     // these specific locations should reduce the probability of
 189     // mistaking random bits as a pointer (although, realistically
 190     // speaking, the likelihood of finding a match between the bit
 191     // pattern of, for example, a double and the vtbl is vanishingly
 192     // small.)
 193     //    1. The first word of the object (should handle MSVC++ as
 194     //    well as the SparcWorks compilers with compatibility set to
 195     //    v5.0 or greater)
 196     //    2. and 3. The last two Address-aligned words of the part of
 197     //    the object defined by its topmost polymorphic superclass.
 198     //    This should handle the SparcWorks compilers, v4.2 or
 199     //    earlier, as well as any other compilers which place the vptr
 200     //    at the end of the user-defined fields of the first base
 201     //    class with virtual functions.
 202     //
 203     // Unfortunately this algorithm did not work properly for the
 204     // specific case of the ThreadShadow/Thread inheritance situation,
 205     // because the Solaris compiler seems to cleverly eliminate the
 206     // vtbl for ThreadShadow since the only virtual is empty. (We
 207     // should get rid of the ThreadShadow and fix the include
 208     // databases, but need to postpone this for the present.) The
 209     // current solution performs the three-location check for this
 210     // class and all of its known superclasses rather than just the
 211     // topmost polymorphic one.
 212 
 213     Type curType = type;
 214 
 215     try {
 216       while (curType != null) {
 217         // Using the size information we have for this type, check the
 218         // three locations described above.
 219 
 220         // (1)
 221         if (vtblAddr.equals(addr.getAddressAt(0))) {
 222           return true;
 223         }
 224 
 225         // (2)
 226         long offset = curType.getSize();
 227         // I don't think this should be misaligned under any
 228         // circumstances, but I'm not sure (FIXME: also not sure which
 229         // way to go here, up or down -- assuming down)
 230         offset -= (offset % getAddressSize());
 231         if (offset <= 0) {
 232           return false;
 233         }
 234         if (vtblAddr.equals(addr.getAddressAt(offset))) {
 235           return true;
 236         }
 237         offset -= getAddressSize();
 238         if (offset <= 0) {
 239           return false;
 240         }
 241         if (vtblAddr.equals(addr.getAddressAt(offset))) {
 242           return true;
 243         }
 244 
 245         curType = curType.getSuperclass();
 246       }
 247     }
 248     catch (Exception e) {
 249       // Any UnmappedAddressExceptions, etc. are a good indication
 250       // that the pointer is not of the specified type
 251       if (DEBUG) {
 252         System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: exception occurred during lookup:");
 253         e.printStackTrace();
 254       }
 255 
 256       return false;
 257     }
 258 
 259     if (DEBUG) {
 260       System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: all vptr tests failed for type " +
 261                          type.getName());
 262     }
 263 
 264     return false;
 265   }
 266 
 267   public Type findDynamicTypeForAddress(Address addr, Type baseType) {
 268     // This implementation should be suitably platform-independent; we
 269     // search nearby memory for the vtbl value of the given type.
 270 
 271     if (vtblForType(baseType) == null) {
 272       // Type was not polymorphic which is an error of some sort
 273       throw new InternalError(baseType + " does not appear to be polymorphic");
 274     }
 275 
 276     // This is a more restricted version of guessTypeForAddress since
 277     // that function has some limitations since it doesn't really know
 278     // where in the hierarchy a virtual type starts and just poking
 279     // around in memory is likely to trip over some vtable address,
 280     // resulting in false positives.  Eventually all uses should
 281     // switch to this logic but in the interests of stability it will
 282     // be separate for the moment.
 283 
 284     // Assuming that the base type is truly the first polymorphic type
 285     // then the vtbl for all subclasss should be at several defined
 286     // locations so only those locations will be checked.  It's also
 287     // required that the caller knows that the static type is at least
 288     // baseType.  See the notes in guessTypeForAddress for the logic of
 289     // the locations searched.
 290 
 291     Address loc1 = addr.getAddressAt(0);
 292     Address loc2 = null;
 293     Address loc3 = null;
 294     long offset2 = baseType.getSize();
 295     // I don't think this should be misaligned under any
 296     // circumstances, but I'm not sure (FIXME: also not sure which
 297     // way to go here, up or down -- assuming down)
 298     offset2 = offset2 - (offset2 % getAddressSize()) - getAddressSize();
 299     if (offset2 > 0) {
 300       loc2 = addr.getAddressAt(offset2);
 301     }
 302     long offset3 = offset2 - getAddressSize();
 303     if (offset3 > 0) {
 304       loc3 = addr.getAddressAt(offset3);
 305     }
 306 
 307     Type loc2Match = null;
 308     Type loc3Match = null;
 309     for (Iterator iter = getTypes(); iter.hasNext(); ) {
 310       Type type = (Type) iter.next();
 311       Type superClass = type;
 312       while (superClass != baseType && superClass != null) {
 313         superClass = superClass.getSuperclass();
 314       }
 315       if (superClass == null) continue;
 316       Address vtblAddr = vtblForType(type);
 317       if (vtblAddr == null) {
 318         // This occurs sometimes for intermediate types that are never
 319         // instantiated.
 320         if (DEBUG) {
 321           System.err.println("null vtbl for " + type);
 322         }
 323         continue;
 324       }
 325       // Prefer loc1 match
 326       if (vtblAddr.equals(loc1)) return type;
 327       if (loc2 != null && loc2Match == null && vtblAddr.equals(loc2)) {
 328           loc2Match = type;
 329       }
 330       if (loc3 != null && loc3Match == null && vtblAddr.equals(loc3)) {
 331           loc3Match = type;
 332       }
 333     }
 334     if (loc2Match != null) return loc2Match;
 335     if (loc3Match != null) return loc3Match;
 336     return null;
 337   }
 338 
 339   public Type guessTypeForAddress(Address addr) {
 340     for (Iterator iter = getTypes(); iter.hasNext(); ) {
 341       Type t = (Type) iter.next();
 342       if (addressTypeIsEqualToType(addr, t)) {
 343         return t;
 344       }
 345     }
 346     return null;
 347   }
 348 
 349   public long cIntegerTypeMaxValue(long sizeInBytes, boolean isUnsigned) {
 350     return machDesc.cIntegerTypeMaxValue(sizeInBytes, isUnsigned);
 351   }
 352 
 353   public long cIntegerTypeMinValue(long sizeInBytes, boolean isUnsigned) {
 354     return machDesc.cIntegerTypeMinValue(sizeInBytes, isUnsigned);
 355   }
 356 
 357   public Iterator getTypes() {
 358     return nameToTypeMap.values().iterator();
 359   }
 360 
 361   public Iterator getIntConstants() {
 362     return nameToIntConstantMap.keySet().iterator();
 363   }
 364 
 365   public Iterator getLongConstants() {
 366     return nameToLongConstantMap.keySet().iterator();
 367   }
 368 
 369   //--------------------------------------------------------------------------------
 370   // Public routines only for use by the database builder
 371   //
 372 
 373   /** This method should only be called by the builder of the
 374       TypeDataBase and at most once */
 375   public void setJBooleanType(Type type) {
 376     jbooleanType = type;
 377   }
 378 
 379   /** This method should only be called by the builder of the
 380       TypeDataBase and at most once */
 381   public void setJByteType(Type type) {
 382     jbyteType = type;
 383   }
 384 
 385   /** This method should only be called by the builder of the
 386       TypeDataBase and at most once */
 387   public void setJCharType(Type type) {
 388     jcharType = type;
 389   }
 390 
 391   /** This method should only be called by the builder of the
 392       TypeDataBase and at most once */
 393   public void setJDoubleType(Type type) {
 394     jdoubleType = type;
 395   }
 396 
 397   /** This method should only be called by the builder of the
 398       TypeDataBase and at most once */
 399   public void setJFloatType(Type type) {
 400     jfloatType = type;
 401   }
 402 
 403   /** This method should only be called by the builder of the
 404       TypeDataBase and at most once */
 405   public void setJIntType(Type type) {
 406     jintType = type;
 407   }
 408 
 409   /** This method should only be called by the builder of the
 410       TypeDataBase and at most once */
 411   public void setJLongType(Type type) {
 412     jlongType = type;
 413   }
 414 
 415   /** This method should only be called by the builder of the
 416       TypeDataBase and at most once */
 417   public void setJShortType(Type type) {
 418     jshortType = type;
 419   }
 420 
 421   /** This method should only be used by the builder of the
 422       TypeDataBase. Throws a RuntimeException if a class with this
 423       name was already present. */
 424   public void addType(Type type) {
 425     if (nameToTypeMap.get(type.getName()) != null) {
 426       throw new RuntimeException("type of name \"" + type.getName() + "\" already present");
 427     }
 428 
 429     nameToTypeMap.put(type.getName(), type);
 430   }
 431 
 432   /** This method should only be used by the builder of the
 433       TypeDataBase. Throws a RuntimeException if this class was not
 434       present. */
 435   public void removeType(Type type) {
 436     Type curType = (Type) nameToTypeMap.get(type.getName());
 437     if (curType == null) {
 438       throw new RuntimeException("type of name \"" + type.getName() + "\" not present");
 439     }
 440 
 441     if (!curType.equals(type)) {
 442       throw new RuntimeException("a different type of name \"" + type.getName() + "\" was present");
 443     }
 444 
 445     nameToTypeMap.remove(type.getName());
 446   }
 447 
 448   /** This method should only be used by the builder of the
 449       TypeDataBase. Throws a RuntimeException if an integer constant
 450       with this name was already present. */
 451   public void addIntConstant(String name, int value) {
 452     if (nameToIntConstantMap.get(name) != null) {
 453       throw new RuntimeException("int constant of name \"" + name + "\" already present");
 454     }
 455 
 456     nameToIntConstantMap.put(name, new Integer(value));
 457   }
 458 
 459   /** This method should only be used by the builder of the
 460       TypeDataBase. Throws a RuntimeException if an integer constant
 461       with this name was not present. */
 462   public void removeIntConstant(String name) {
 463     Integer curConstant = (Integer) nameToIntConstantMap.get(name);
 464     if (curConstant == null) {
 465       throw new RuntimeException("int constant of name \"" + name + "\" not present");
 466     }
 467 
 468     nameToIntConstantMap.remove(name);
 469   }
 470 
 471   /** This method should only be used by the builder of the
 472       TypeDataBase. Throws a RuntimeException if a long constant with
 473       this name was already present. */
 474   public void addLongConstant(String name, long value) {
 475     if (nameToLongConstantMap.get(name) != null) {
 476       throw new RuntimeException("long constant of name \"" + name + "\" already present");
 477     }
 478 
 479     nameToLongConstantMap.put(name, new Long(value));
 480   }
 481 
 482   /** This method should only be used by the builder of the
 483       TypeDataBase. Throws a RuntimeException if a long constant with
 484       this name was not present. */
 485   public void removeLongConstant(String name) {
 486     Long curConstant = (Long) nameToLongConstantMap.get(name);
 487     if (curConstant == null) {
 488       throw new RuntimeException("long constant of name \"" + name + "\" not present");
 489     }
 490 
 491     nameToLongConstantMap.remove(name);
 492   }
 493 }