1 /*
   2  * Copyright (c) 1997, 2017, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 /*
  28  * The Original Code is HAT. The Initial Developer of the
  29  * Original Code is Bill Foote, with contributions from others
  30  * at JavaSoft/Sun.
  31  */
  32 
  33 package jdk.test.lib.hprof.model;
  34 
  35 import java.io.IOException;
  36 import jdk.test.lib.hprof.parser.ReadBuffer;
  37 
  38 /**
  39  * Represents Java instance
  40  *
  41  * @author      Bill Foote
  42  */
  43 public class JavaObject extends JavaLazyReadObject {
  44 
  45     private Object clazz;       // Number before resolve
  46                                 // JavaClass after resolve
  47     /**
  48      * Construct a new JavaObject.
  49      *
  50      * @param classID id of the class object
  51      * @param offset The offset of field data
  52      */
  53     public JavaObject(long classID, long offset) {
  54         super(offset);
  55         this.clazz = makeId(classID);
  56     }
  57 
  58     public void resolve(Snapshot snapshot) {
  59         if (clazz instanceof JavaClass) {
  60             return;
  61         }
  62         if (clazz instanceof Number) {
  63             long classID = getIdValue((Number)clazz);
  64             clazz = snapshot.findThing(classID);
  65             if (! (clazz instanceof JavaClass)) {
  66                 warn("Class " + Long.toHexString(classID) + " not found, " +
  67                      "adding fake class!");
  68                 int length;
  69                 ReadBuffer buf = snapshot.getReadBuffer();
  70                 int idSize = snapshot.getIdentifierSize();
  71                 long lenOffset = getOffset() + 2*idSize + 4;
  72                 try {
  73                     length = buf.getInt(lenOffset);
  74                 } catch (IOException exp) {
  75                     throw new RuntimeException(exp);
  76                 }
  77                 clazz = snapshot.addFakeInstanceClass(classID, length);
  78             }
  79         } else {
  80             throw new InternalError("should not reach here");
  81         }
  82 
  83         JavaClass cl = (JavaClass) clazz;
  84         cl.resolve(snapshot);
  85 
  86         // while resolving, parse fields in verbose mode.
  87         // but, getFields calls parseFields in non-verbose mode
  88         // to avoid printing warnings repeatedly.
  89         parseFields(true);
  90 
  91         cl.addInstance(this);
  92         super.resolve(snapshot);
  93     }
  94 
  95     /**
  96      * Are we the same type as other?  We are iff our clazz is the
  97      * same type as other's.
  98      */
  99     public boolean isSameTypeAs(JavaThing other) {
 100         if (!(other instanceof JavaObject)) {
 101             return false;
 102         }
 103         JavaObject oo = (JavaObject) other;
 104         return getClazz().equals(oo.getClazz());
 105     }
 106 
 107     /**
 108      * Return our JavaClass object.  This may only be called after resolve.
 109      */
 110     public JavaClass getClazz() {
 111         return (JavaClass) clazz;
 112     }
 113 
 114     public JavaThing[] getFields() {
 115         // pass false to verbose mode so that dereference
 116         // warnings are not printed.
 117         return parseFields(false);
 118     }
 119 
 120     // returns the value of field of given name
 121     public JavaThing getField(String name) {
 122         JavaThing[] flds = getFields();
 123         JavaField[] instFields = getClazz().getFieldsForInstance();
 124         for (int i = 0; i < instFields.length; i++) {
 125             if (instFields[i].getName().equals(name)) {
 126                 return flds[i];
 127             }
 128         }
 129         return null;
 130     }
 131 
 132     public int compareTo(JavaThing other) {
 133         if (other instanceof JavaObject) {
 134             JavaObject oo = (JavaObject) other;
 135             return getClazz().getName().compareTo(oo.getClazz().getName());
 136         }
 137         return super.compareTo(other);
 138     }
 139 
 140     public void visitReferencedObjects(JavaHeapObjectVisitor v) {
 141         super.visitReferencedObjects(v);
 142         JavaThing[] flds = getFields();
 143         for (int i = 0; i < flds.length; i++) {
 144             if (flds[i] != null) {
 145                 if (v.mightExclude()
 146                     && v.exclude(getClazz().getClassForField(i),
 147                                  getClazz().getFieldForInstance(i)))
 148                 {
 149                     // skip it
 150                 } else if (flds[i] instanceof JavaHeapObject) {
 151                     v.visit((JavaHeapObject) flds[i]);
 152                 }
 153             }
 154         }
 155     }
 156 
 157     public boolean refersOnlyWeaklyTo(Snapshot ss, JavaThing other) {
 158         if (ss.getWeakReferenceClass() != null) {
 159             final int referentFieldIndex = ss.getReferentFieldIndex();
 160             if (ss.getWeakReferenceClass().isAssignableFrom(getClazz())) {
 161                 //
 162                 // REMIND:  This introduces a dependency on the JDK
 163                 //      implementation that is undesirable.
 164                 JavaThing[] flds = getFields();
 165                 for (int i = 0; i < flds.length; i++) {
 166                     if (i != referentFieldIndex && flds[i] == other) {
 167                         return false;
 168                     }
 169                 }
 170                 return true;
 171             }
 172         }
 173         return false;
 174     }
 175 
 176     /**
 177      * Describe the reference that this thing has to target.  This will only
 178      * be called if target is in the array returned by getChildrenForRootset.
 179      */
 180     public String describeReferenceTo(JavaThing target, Snapshot ss) {
 181         JavaThing[] flds = getFields();
 182         for (int i = 0; i < flds.length; i++) {
 183             if (flds[i] == target) {
 184                 JavaField f = getClazz().getFieldForInstance(i);
 185                 return "field " + f.getName();
 186             }
 187         }
 188         return super.describeReferenceTo(target, ss);
 189     }
 190 
 191     public String toString() {
 192         if (getClazz().isString()) {
 193             JavaThing value = getField("value");
 194             if (value instanceof JavaValueArray) {
 195                 return ((JavaValueArray)value).valueString();
 196             } else {
 197                 return "null";
 198             }
 199         } else {
 200             return super.toString();
 201         }
 202     }
 203 
 204     // Internals only below this point
 205 
 206     /*
 207      * Java instance record (HPROF_GC_INSTANCE_DUMP) looks as below:
 208      *
 209      *     object ID
 210      *     stack trace serial number (int)
 211      *     class ID
 212      *     data length (int)
 213      *     byte[length]
 214      */
 215     @Override
 216     protected final long readValueLength() throws IOException {
 217         long lengthOffset = getOffset() + 2 * idSize() + 4;
 218         return buf().getInt(lengthOffset);
 219     }
 220 
 221     @Override
 222     protected final JavaThing[] readValue() throws IOException {
 223         return parseFields(false);
 224     }
 225 
 226     private long dataStartOffset() {
 227         return getOffset() + idSize() + 4 + idSize() + 4;
 228     }
 229 
 230     private JavaThing[] parseFields(boolean verbose) {
 231         JavaClass cl = getClazz();
 232         int target = cl.getNumFieldsForInstance();
 233         JavaField[] fields = cl.getFields();
 234         JavaThing[] fieldValues = new JavaThing[target];
 235         Snapshot snapshot = cl.getSnapshot();
 236         int fieldNo = 0;
 237         // In the dump file, the fields are stored in this order:
 238         // fields of most derived class (immediate class) are stored
 239         // first and then the super class and so on. In this object,
 240         // fields are stored in the reverse ("natural") order. i.e.,
 241         // fields of most super class are stored first.
 242 
 243         // target variable is used to compensate for the fact that
 244         // the dump file starts field values from the leaf working
 245         // upwards in the inheritance hierarchy, whereas JavaObject
 246         // starts with the top of the inheritance hierarchy and works down.
 247         target -= fields.length;
 248         JavaClass currClass = cl;
 249         long offset = dataStartOffset();
 250         for (int i = 0; i < fieldValues.length; i++, fieldNo++) {
 251             while (fieldNo >= fields.length) {
 252                 currClass = currClass.getSuperclass();
 253                 fields = currClass.getFields();
 254                 fieldNo = 0;
 255                 target -= fields.length;
 256             }
 257             JavaField f = fields[fieldNo];
 258             char sig = f.getSignature().charAt(0);
 259             try {
 260                 switch (sig) {
 261                     case 'L':
 262                     case '[': {
 263                         long id = objectIdAt(offset);
 264                         offset += idSize();
 265                         JavaObjectRef ref = new JavaObjectRef(id);
 266                         fieldValues[target+fieldNo] = ref.dereference(snapshot, f, verbose);
 267                         break;
 268                     }
 269                     case 'Z': {
 270                         byte value = byteAt(offset);
 271                         offset++;
 272                         fieldValues[target+fieldNo] = new JavaBoolean(value != 0);
 273                         break;
 274                     }
 275                     case 'B': {
 276                         byte value = byteAt(offset);
 277                         offset++;
 278                         fieldValues[target+fieldNo] = new JavaByte(value);
 279                         break;
 280                     }
 281                     case 'S': {
 282                         short value = shortAt(offset);
 283                         offset += 2;
 284                         fieldValues[target+fieldNo] = new JavaShort(value);
 285                         break;
 286                     }
 287                     case 'C': {
 288                         char value = charAt(offset);
 289                         offset += 2;
 290                         fieldValues[target+fieldNo] = new JavaChar(value);
 291                         break;
 292                     }
 293                     case 'I': {
 294                         int value = intAt(offset);
 295                         offset += 4;
 296                         fieldValues[target+fieldNo] = new JavaInt(value);
 297                         break;
 298                     }
 299                     case 'J': {
 300                         long value = longAt(offset);
 301                         offset += 8;
 302                         fieldValues[target+fieldNo] = new JavaLong(value);
 303                         break;
 304                     }
 305                     case 'F': {
 306                         float value = floatAt(offset);
 307                         offset += 4;
 308                         fieldValues[target+fieldNo] = new JavaFloat(value);
 309                         break;
 310                     }
 311                     case 'D': {
 312                         double value = doubleAt(offset);
 313                         offset += 8;
 314                         fieldValues[target+fieldNo] = new JavaDouble(value);
 315                         break;
 316                     }
 317                     default:
 318                         throw new RuntimeException("invalid signature: " + sig);
 319 
 320                 }
 321         } catch (IOException exp) {
 322             System.err.println("lazy read failed at offset " + offset);
 323             exp.printStackTrace();
 324             return Snapshot.EMPTY_JAVATHING_ARRAY;
 325             }
 326         }
 327         return fieldValues;
 328     }
 329 
 330     private void warn(String msg) {
 331         System.out.println("WARNING: " + msg);
 332     }
 333 }