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