1 /*
   2  * Copyright (c) 2002, 2012, 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.utilities;
  26 
  27 import java.lang.reflect.Modifier;
  28 import java.util.*;
  29 import java.util.stream.*;
  30 import sun.jvm.hotspot.debugger.*;
  31 import sun.jvm.hotspot.oops.*;
  32 import sun.jvm.hotspot.runtime.*;
  33 import sun.jvm.hotspot.utilities.*;
  34 
  35 /**
  36  * ObjectReader can "deserialize" objects from debuggee.
  37  *
  38  * Class Loading:
  39  *
  40  * ObjectReader loads classes using the given class loader. If no
  41  * class loader is supplied, it uses a ProcImageClassLoader, which
  42  * loads classes from debuggee core or process.
  43 
  44  * Object creation:
  45  *
  46  * This class uses no-arg constructor to construct objects. But if
  47  * there is no no-arg constructor in a given class, then it tries to
  48  * use other constructors with 'default' values - null for object
  49  * types, 0, 0.0, false etc. for primitives.  If this process fails to
  50  * construct an instance (because of null checking by constructor or 0
  51  * being invalid for an int arg etc.), then null is returned. While
  52  * constructing complete object graph 'null' is inserted silently on
  53  * failure and the deserialization continues to construct best-effort
  54  * object graph.
  55  *
  56  * Debug messages:
  57  *
  58  * The flag sun.jvm.hotspot.utilities.ObjectReader.DEBUG may be set to
  59  * non-null to get debug error messages and stack traces.
  60  *
  61  * JDK version:
  62  *
  63  * JDK classes are loaded by bootstrap class loader and not by the
  64  * supplied class loader or ProcImageClassLoader. This may create
  65  * problems if a JDK class evolves. i.e., if SA runs a JDK version
  66  * different from that of the debuggee, there is a possibility of
  67  * schema change. It is recommended that the matching JDK version be
  68  * used to run SA for proper object deserialization.
  69  *
  70  */
  71 
  72 public class ObjectReader {
  73 
  74    private static final boolean DEBUG;
  75    static {
  76       DEBUG = System.getProperty("sun.jvm.hotspot.utilities.ObjectReader.DEBUG") != null;
  77    }
  78 
  79    public ObjectReader(ClassLoader cl) {
  80       this.cl = cl;
  81       this.oopToObjMap = new HashMap();
  82       this.fieldMap = new HashMap();
  83    }
  84 
  85    public ObjectReader() {
  86       this(new ProcImageClassLoader());
  87    }
  88 
  89    static void debugPrintln(String msg) {
  90       if (DEBUG) {
  91          System.err.println("DEBUG>" + msg);
  92       }
  93    }
  94 
  95    static void debugPrintStackTrace(Exception exp) {
  96       if (DEBUG) {
  97          StackTraceElement[] els = exp.getStackTrace();
  98          for (int i = 0; i < els.length; i++) {
  99             System.err.println("DEBUG>" + els[i].toString());
 100          }
 101       }
 102    }
 103 
 104    public Object readObject(Oop oop) throws ClassNotFoundException {
 105       if (oop instanceof Instance) {
 106          return readInstance((Instance) oop);
 107       } else if (oop instanceof TypeArray){
 108          return readPrimitiveArray((TypeArray)oop);
 109       } else if (oop instanceof ObjArray){
 110          return readObjectArray((ObjArray)oop);
 111       } else {
 112          return null;
 113       }
 114    }
 115 
 116    protected final Object getDefaultPrimitiveValue(Class clz) {
 117       if (clz == Boolean.TYPE) {
 118          return Boolean.FALSE;
 119       } else if (clz == Character.TYPE) {
 120          return new Character(' ');
 121       } else if (clz == Byte.TYPE) {
 122          return new Byte((byte) 0);
 123       } else if (clz == Short.TYPE) {
 124          return new Short((short) 0);
 125       } else if (clz == Integer.TYPE) {
 126          return new Integer(0);
 127       } else if (clz == Long.TYPE) {
 128          return new Long(0L);
 129       } else if (clz == Float.TYPE) {
 130          return new Float(0.0f);
 131       } else if (clz == Double.TYPE) {
 132          return new Double(0.0);
 133       } else {
 134          throw new RuntimeException("should not reach here!");
 135       }
 136    }
 137 
 138    protected Symbol javaLangString;
 139    protected Symbol javaUtilHashtableEntry;
 140    protected Symbol javaUtilHashtable;
 141    protected Symbol javaUtilProperties;
 142 
 143    protected Symbol getVMSymbol(String name) {
 144       return VM.getVM().getSymbolTable().probe(name);
 145    }
 146 
 147    protected Symbol javaLangString() {
 148       if (javaLangString == null) {
 149          javaLangString = getVMSymbol("java/lang/String");
 150       }
 151       return javaLangString;
 152    }
 153 
 154    protected Symbol javaUtilHashtableEntry() {
 155       if (javaUtilHashtableEntry == null) {
 156          javaUtilHashtableEntry = getVMSymbol("java/util/Hashtable$Entry");
 157       }
 158       return javaUtilHashtableEntry;
 159    }
 160 
 161    protected Symbol javaUtilHashtable() {
 162       if (javaUtilHashtable == null) {
 163          javaUtilHashtable = getVMSymbol("java/util/Hashtable");
 164       }
 165       return javaUtilHashtable;
 166    }
 167 
 168    protected Symbol javaUtilProperties() {
 169       if (javaUtilProperties == null) {
 170          javaUtilProperties = getVMSymbol("java/util/Properties");
 171       }
 172       return javaUtilProperties;
 173    }
 174 
 175    private void setHashtableEntry(java.util.Hashtable p, Oop oop) {
 176       InstanceKlass ik = (InstanceKlass)oop.getKlass();
 177       OopField keyField = (OopField)ik.findField("key", "Ljava/lang/Object;");
 178       OopField valueField = (OopField)ik.findField("value", "Ljava/lang/Object;");
 179       OopField nextField = (OopField)ik.findField("next", "Ljava/util/Hashtable$Entry;");
 180       if (DEBUG) {
 181          if (Assert.ASSERTS_ENABLED) {
 182             Assert.that(ik.getName().equals(javaUtilHashtableEntry()), "Not a Hashtable$Entry?");
 183             Assert.that(keyField != null && valueField != null && nextField != null, "Invalid fields!");
 184          }
 185       }
 186 
 187       Object key = null;
 188       Object value = null;
 189       Oop next = null;
 190       try {
 191          key = readObject(keyField.getValue(oop));
 192          value = readObject(valueField.getValue(oop));
 193          next =  (Oop)nextField.getValue(oop);
 194          // For Properties, should use setProperty(k, v). Since it only runs in SA
 195          // using put(k, v) should be OK.
 196          p.put(key, value);
 197          if (next != null) {
 198             setHashtableEntry(p, next);
 199          }
 200       } catch (ClassNotFoundException ce) {
 201          if( DEBUG) {
 202             debugPrintln("Class not found " + ce);
 203             debugPrintStackTrace(ce);
 204          }
 205       }
 206    }
 207 
 208    private void setPropertiesEntry(java.util.Properties p, Oop oop) {
 209       InstanceKlass ik = (InstanceKlass)oop.getKlass();
 210       OopField keyField = (OopField)ik.findField("key", "Ljava/lang/Object;");
 211       OopField valueField = (OopField)ik.findField("val", "Ljava/lang/Object;");
 212 
 213       try {
 214          p.setProperty((String)readObject(keyField.getValue(oop)),
 215                        (String)readObject(valueField.getValue(oop)));
 216       } catch (ClassNotFoundException ce) {
 217          if (DEBUG) {
 218             debugPrintStackTrace(ce);
 219          }
 220       }
 221    }
 222 
 223    protected Object getHashtable(Instance oop) {
 224       InstanceKlass k = (InstanceKlass)oop.getKlass();
 225       OopField tableField = (OopField)k.findField("table", "[Ljava/util/Hashtable$Entry;");
 226       if (tableField == null) {
 227          debugPrintln("Could not find field of [Ljava/util/Hashtable$Entry;");
 228          return null;
 229       }
 230       java.util.Hashtable table = new java.util.Hashtable();
 231       ObjArray kvs = (ObjArray)tableField.getValue(oop);
 232       long size = kvs.getLength();
 233       debugPrintln("Hashtable$Entry Size = " + size);
 234       for (long i=0; i<size; i++) {
 235          Oop entry = kvs.getObjAt(i);
 236          if (entry != null && entry.isInstance()) {
 237             setHashtableEntry(table, entry);
 238          }
 239       }
 240       return table;
 241    }
 242 
 243    private Properties getProperties(Instance oop) {
 244       InstanceKlass k = (InstanceKlass)oop.getKlass();
 245       OopField mapField = (OopField)k.findField("map", "Ljava/util/concurrent/ConcurrentHashMap;");
 246       if (mapField == null) {
 247          debugPrintln("Could not find field of Ljava/util/concurrent/ConcurrentHashMap");
 248          return null;
 249       }
 250 
 251       Instance mapObj = (Instance)mapField.getValue(oop);
 252       if (mapObj == null) {
 253          debugPrintln("Could not get map field from java.util.Properties");
 254          return null;
 255       }
 256 
 257       InstanceKlass mk = (InstanceKlass)mapObj.getKlass();
 258       OopField tableField = (OopField)mk.findField("table", "[Ljava/util/concurrent/ConcurrentHashMap$Node;");
 259       if (tableField == null) {
 260          debugPrintln("Could not find field of [Ljava/util/concurrent/ConcurrentHashMap$Node");
 261          return null;
 262       }
 263 
 264       java.util.Properties props = new java.util.Properties();
 265       ObjArray kvs = (ObjArray)tableField.getValue(mapObj);
 266       long size = kvs.getLength();
 267       debugPrintln("ConcurrentHashMap$Node Size = " + size);
 268       LongStream.range(0, size)
 269                 .mapToObj(kvs::getObjAt)
 270                 .filter(o -> o != null)
 271                 .forEach(o -> setPropertiesEntry(props, o));
 272 
 273       return props;
 274    }
 275 
 276    public Object readInstance(Instance oop) throws ClassNotFoundException {
 277       Object result = getFromObjTable(oop);
 278       if (result == null) {
 279          InstanceKlass kls = (InstanceKlass) oop.getKlass();
 280          // Handle java.lang.String instances differently. As part of JSR-133, fields of immutable
 281          // classes have been made final. The algorithm below will not be able to read Strings from
 282          // debuggee (can't use reflection to set final fields). But, need to read Strings is very
 283          // important.
 284          // Same for Hashtable, key and hash are final, could not be set in the algorithm too.
 285          // FIXME: need a framework to handle many other special cases.
 286          if (kls.getName().equals(javaLangString())) {
 287             return OopUtilities.stringOopToString(oop);
 288          }
 289 
 290          if (kls.getName().equals(javaUtilHashtable())) {
 291             return getHashtable(oop);
 292          }
 293 
 294          if (kls.getName().equals(javaUtilProperties())) {
 295             return getProperties(oop);
 296          }
 297 
 298          Class clz = readClass(kls);
 299          try {
 300             result = clz.newInstance();
 301          } catch (Exception ex) {
 302             // no-arg constructor failed to create object. Let us try
 303             // to call constructors one-by-one with default arguments
 304             // (null for objects, 0/0.0 etc. for primitives) till we
 305             // succeed or fail on all constructors.
 306 
 307             java.lang.reflect.Constructor[] ctrs = clz.getDeclaredConstructors();
 308             for (int n = 0; n < ctrs.length; n++) {
 309                java.lang.reflect.Constructor c = ctrs[n];
 310                Class[] paramTypes = c.getParameterTypes();
 311                Object[] params = new Object[paramTypes.length];
 312                for (int i = 0; i < params.length; i++) {
 313                   if (paramTypes[i].isPrimitive()) {
 314                      params[i] = getDefaultPrimitiveValue(paramTypes[i]);
 315                   }
 316                }
 317                try {
 318                   c.setAccessible(true);
 319                   result = c.newInstance(params);
 320                   break;
 321                } catch (Exception exp) {
 322                   if (DEBUG) {
 323                      debugPrintln("Can't create object using " + c);
 324                      debugPrintStackTrace(exp);
 325                   }
 326                }
 327             }
 328          }
 329 
 330          if (result != null) {
 331             putIntoObjTable(oop, result);
 332             oop.iterate(new FieldSetter(result), false);
 333          }
 334       }
 335       return result;
 336    }
 337 
 338    public Object readPrimitiveArray(final TypeArray array) {
 339 
 340       Object result = getFromObjTable(array);
 341       if (result == null) {
 342          int length = (int) array.getLength();
 343          TypeArrayKlass klass = (TypeArrayKlass) array.getKlass();
 344          int type = (int) klass.getElementType();
 345          switch (type) {
 346             case TypeArrayKlass.T_BOOLEAN: {
 347                final boolean[] arrayObj = new boolean[length];
 348                array.iterate(new DefaultOopVisitor() {
 349                                 public void doBoolean(BooleanField field, boolean isVMField) {
 350                                    IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 351                                    arrayObj[ifd.getIndex()] = field.getValue(array);
 352                                 }
 353                             }, false);
 354                result = arrayObj;
 355                }
 356                break;
 357 
 358             case TypeArrayKlass.T_CHAR: {
 359                final char[] arrayObj = new char[length];
 360                array.iterate(new DefaultOopVisitor() {
 361                                 public void doChar(CharField field, boolean isVMField) {
 362                                    IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 363                                    arrayObj[ifd.getIndex()] = field.getValue(array);
 364                                 }
 365                             }, false);
 366                result = arrayObj;
 367                }
 368                break;
 369 
 370             case TypeArrayKlass.T_FLOAT: {
 371                final float[] arrayObj = new float[length];
 372                array.iterate(new DefaultOopVisitor() {
 373                                 public void doFloat(FloatField field, boolean isVMField) {
 374                                    IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 375                                    arrayObj[ifd.getIndex()] = field.getValue(array);
 376                                 }
 377                             }, false);
 378                result = arrayObj;
 379                }
 380                break;
 381 
 382             case TypeArrayKlass.T_DOUBLE: {
 383                final double[] arrayObj = new double[length];
 384                array.iterate(new DefaultOopVisitor() {
 385                                 public void doDouble(DoubleField field, boolean isVMField) {
 386                                    IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 387                                    arrayObj[ifd.getIndex()] = field.getValue(array);
 388                                 }
 389                             }, false);
 390                result = arrayObj;
 391                }
 392                break;
 393 
 394             case TypeArrayKlass.T_BYTE: {
 395                final byte[] arrayObj = new byte[length];
 396                array.iterate(new DefaultOopVisitor() {
 397                                 public void doByte(ByteField field, boolean isVMField) {
 398                                    IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 399                                    arrayObj[ifd.getIndex()] = field.getValue(array);
 400                                 }
 401                             }, false);
 402                result = arrayObj;
 403                }
 404                break;
 405 
 406             case TypeArrayKlass.T_SHORT: {
 407                final short[] arrayObj = new short[length];
 408                array.iterate(new DefaultOopVisitor() {
 409                                 public void doShort(ShortField field, boolean isVMField) {
 410                                    IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 411                                    arrayObj[ifd.getIndex()] = field.getValue(array);
 412                                 }
 413                             }, false);
 414                result = arrayObj;
 415                }
 416                break;
 417 
 418             case TypeArrayKlass.T_INT: {
 419                final int[] arrayObj = new int[length];
 420                array.iterate(new DefaultOopVisitor() {
 421                                 public void doInt(IntField field, boolean isVMField) {
 422                                    IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 423                                    arrayObj[ifd.getIndex()] = field.getValue(array);
 424                                 }
 425                             }, false);
 426                result = arrayObj;
 427                }
 428                break;
 429 
 430             case TypeArrayKlass.T_LONG: {
 431                final long[] arrayObj = new long[length];
 432                array.iterate(new DefaultOopVisitor() {
 433                                 public void doLong(LongField field, boolean isVMField) {
 434                                    IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 435                                    arrayObj[ifd.getIndex()] = field.getValue(array);
 436                                 }
 437                             }, false);
 438                result = arrayObj;
 439                }
 440                break;
 441 
 442             default:
 443                throw new RuntimeException("should not reach here!");
 444          }
 445 
 446          putIntoObjTable(array, result);
 447       }
 448       return result;
 449    }
 450 
 451    protected final boolean isRobust(OopHandle handle) {
 452       return RobustOopDeterminator.oopLooksValid(handle);
 453    }
 454 
 455    public Object readObjectArray(final ObjArray array) throws ClassNotFoundException {
 456        Object result = getFromObjTable(array);
 457        if (result == null) {
 458           int length = (int) array.getLength();
 459           ObjArrayKlass klass = (ObjArrayKlass) array.getKlass();
 460           Klass bottomKls = klass.getBottomKlass();
 461           Class bottomCls = null;
 462           final int dimension = (int) klass.getDimension();
 463           int[] dimArray = null;
 464           if (bottomKls instanceof InstanceKlass) {
 465              bottomCls = readClass((InstanceKlass) bottomKls);
 466              dimArray = new int[dimension];
 467           } else { // instanceof TypeArrayKlass
 468              TypeArrayKlass botKls = (TypeArrayKlass) bottomKls;
 469              dimArray = new int[dimension -1];
 470           }
 471           // initialize the length
 472           dimArray[0] = length;
 473           final Object[] arrayObj = (Object[]) java.lang.reflect.Array.newInstance(bottomCls, dimArray);
 474           putIntoObjTable(array, arrayObj);
 475           result = arrayObj;
 476           array.iterate(new DefaultOopVisitor() {
 477                                public void doOop(OopField field, boolean isVMField) {
 478                                   OopHandle handle = field.getValueAsOopHandle(getObj());
 479                                   if (! isRobust(handle)) {
 480                                      return;
 481                                   }
 482 
 483                                   IndexableFieldIdentifier ifd = (IndexableFieldIdentifier) field.getID();
 484                                   try {
 485                                      arrayObj[ifd.getIndex()] = readObject(field.getValue(getObj()));
 486                                   } catch (Exception e) {
 487                                      if (DEBUG) {
 488                                         debugPrintln("Array element set failed for " + ifd);
 489                                         debugPrintStackTrace(e);
 490                                      }
 491                                   }
 492                                }
 493                         }, false);
 494        }
 495        return result;
 496    }
 497 
 498    protected class FieldSetter extends DefaultOopVisitor {
 499       protected Object obj;
 500 
 501       public FieldSetter(Object obj) {
 502          this.obj = obj;
 503       }
 504 
 505       private void printFieldSetError(java.lang.reflect.Field f, Exception ex) {
 506          if (DEBUG) {
 507             if (f != null) debugPrintln("Field set failed for " + f);
 508             debugPrintStackTrace(ex);
 509          }
 510       }
 511 
 512       // Callback methods for each field type in an object
 513       public void doOop(OopField field, boolean isVMField) {
 514          OopHandle handle = field.getValueAsOopHandle(getObj());
 515          if (! isRobust(handle) ) {
 516             return;
 517          }
 518 
 519          java.lang.reflect.Field f = null;
 520          try {
 521             f = readField(field);
 522             if (Modifier.isFinal(f.getModifiers())) return;
 523             f.setAccessible(true);
 524             f.set(obj, readObject(field.getValue(getObj())));
 525          } catch (Exception ex) {
 526             printFieldSetError(f, ex);
 527          }
 528       }
 529 
 530       public void doByte(ByteField field, boolean isVMField) {
 531          java.lang.reflect.Field f = null;
 532          try {
 533             f = readField(field);
 534             if (Modifier.isFinal(f.getModifiers())) return;
 535             f.setAccessible(true);
 536             f.setByte(obj, field.getValue(getObj()));
 537          } catch (Exception ex) {
 538             printFieldSetError(f, ex);
 539          }
 540       }
 541 
 542       public void doChar(CharField field, boolean isVMField) {
 543          java.lang.reflect.Field f = null;
 544          try {
 545             f = readField(field);
 546             if (Modifier.isFinal(f.getModifiers())) return;
 547             f.setAccessible(true);
 548             f.setChar(obj, field.getValue(getObj()));
 549          } catch (Exception ex) {
 550             printFieldSetError(f, ex);
 551          }
 552       }
 553 
 554       public void doBoolean(BooleanField field, boolean isVMField) {
 555          java.lang.reflect.Field f = null;
 556          try {
 557             f = readField(field);
 558             if (Modifier.isFinal(f.getModifiers())) return;
 559             f.setAccessible(true);
 560             f.setBoolean(obj, field.getValue(getObj()));
 561          } catch (Exception ex) {
 562             printFieldSetError(f, ex);
 563          }
 564       }
 565 
 566       public void doShort(ShortField field, boolean isVMField) {
 567          java.lang.reflect.Field f = null;
 568          try {
 569             f = readField(field);
 570             if (Modifier.isFinal(f.getModifiers())) return;
 571             f.setAccessible(true);
 572             f.setShort(obj, field.getValue(getObj()));
 573          } catch (Exception ex) {
 574             printFieldSetError(f, ex);
 575          }
 576       }
 577 
 578       public void doInt(IntField field, boolean isVMField) {
 579          java.lang.reflect.Field f = null;
 580          try {
 581             f = readField(field);
 582             if (Modifier.isFinal(f.getModifiers())) return;
 583             f.setAccessible(true);
 584             f.setInt(obj, field.getValue(getObj()));
 585          } catch (Exception ex) {
 586             printFieldSetError(f, ex);
 587          }
 588       }
 589 
 590       public void doLong(LongField field, boolean isVMField) {
 591          java.lang.reflect.Field f = null;
 592          try {
 593             f = readField(field);
 594             if (Modifier.isFinal(f.getModifiers())) return;
 595             f.setAccessible(true);
 596             f.setLong(obj, field.getValue(getObj()));
 597          } catch (Exception ex) {
 598             printFieldSetError(f, ex);
 599          }
 600       }
 601 
 602       public void doFloat(FloatField field, boolean isVMField) {
 603          java.lang.reflect.Field f = null;
 604          try {
 605             f = readField(field);
 606             if (Modifier.isFinal(f.getModifiers())) return;
 607             f.setAccessible(true);
 608             f.setFloat(obj, field.getValue(getObj()));
 609          } catch (Exception ex) {
 610             printFieldSetError(f, ex);
 611          }
 612       }
 613 
 614       public void doDouble(DoubleField field, boolean isVMField) {
 615          java.lang.reflect.Field f = null;
 616          try {
 617             f = readField(field);
 618             if (Modifier.isFinal(f.getModifiers())) return;
 619             f.setAccessible(true);
 620             f.setDouble(obj, field.getValue(getObj()));
 621          } catch (Exception ex) {
 622             printFieldSetError(f, ex);
 623          }
 624       }
 625 
 626       public void doCInt(CIntField field, boolean isVMField) {
 627          throw new RuntimeException("should not reach here!");
 628       }
 629    }
 630 
 631    public Class readClass(InstanceKlass kls) throws ClassNotFoundException {
 632       Class cls = (Class) getFromObjTable(kls);
 633       if (cls == null) {
 634          cls = Class.forName(kls.getName().asString().replace('/', '.'), true, cl);
 635          putIntoObjTable(kls, cls);
 636       }
 637       return cls;
 638    }
 639 
 640    public Object readMethodOrConstructor(sun.jvm.hotspot.oops.Method m)
 641                      throws NoSuchMethodException, ClassNotFoundException {
 642       String name = m.getName().asString();
 643       if (name.equals("<init>")) {
 644          return readConstructor(m);
 645       } else {
 646          return readMethod(m);
 647       }
 648    }
 649 
 650    public java.lang.reflect.Method readMethod(sun.jvm.hotspot.oops.Method m)
 651             throws NoSuchMethodException, ClassNotFoundException {
 652       java.lang.reflect.Method result = (java.lang.reflect.Method) getFromObjTable(m);
 653       if (result == null) {
 654          Class clz = readClass((InstanceKlass)m.getMethodHolder());
 655          String name = m.getName().asString();
 656          Class[] paramTypes = getParamTypes(m.getSignature());
 657          result = clz.getMethod(name, paramTypes);
 658          putIntoObjTable(m, result);
 659       }
 660       return result;
 661    }
 662 
 663    public java.lang.reflect.Constructor readConstructor(sun.jvm.hotspot.oops.Method m)
 664             throws NoSuchMethodException, ClassNotFoundException {
 665       java.lang.reflect.Constructor result = (java.lang.reflect.Constructor) getFromObjTable(m);
 666       if (result == null) {
 667          Class clz = readClass((InstanceKlass)m.getMethodHolder());
 668          String name = m.getName().asString();
 669          Class[] paramTypes = getParamTypes(m.getSignature());
 670          result = clz.getDeclaredConstructor(paramTypes);
 671          putIntoObjTable(m, result);
 672       }
 673       return result;
 674    }
 675 
 676    public java.lang.reflect.Field readField(sun.jvm.hotspot.oops.Field f)
 677             throws NoSuchFieldException, ClassNotFoundException {
 678       java.lang.reflect.Field result = (java.lang.reflect.Field) fieldMap.get(f);
 679       if (result == null) {
 680          FieldIdentifier fieldId = f.getID();
 681          Class clz = readClass((InstanceKlass) f.getFieldHolder());
 682          String name = fieldId.getName();
 683          try {
 684             result = clz.getField(name);
 685          } catch (NoSuchFieldException nsfe) {
 686             result = clz.getDeclaredField(name);
 687          }
 688          fieldMap.put(f, result);
 689       }
 690       return result;
 691    }
 692 
 693    protected final ClassLoader cl;
 694    protected Map   oopToObjMap; // Map<Oop, Object>
 695    protected Map   fieldMap;    // Map<sun.jvm.hotspot.oops.Field, java.lang.reflect.Field>
 696 
 697    protected void putIntoObjTable(Oop oop, Object obj) {
 698       oopToObjMap.put(oop, obj);
 699    }
 700 
 701    protected Object getFromObjTable(Oop oop) {
 702       return oopToObjMap.get(oop);
 703    }
 704 
 705    protected void putIntoObjTable(Metadata oop, Object obj) {
 706       oopToObjMap.put(oop, obj);
 707    }
 708 
 709    protected Object getFromObjTable(Metadata oop) {
 710       return oopToObjMap.get(oop);
 711    }
 712 
 713    protected class SignatureParser extends SignatureIterator {
 714       protected Vector tmp = new Vector(); // Vector<Class>
 715 
 716       public SignatureParser(Symbol s) {
 717          super(s);
 718       }
 719 
 720       public void doBool  () { tmp.add(Boolean.TYPE);    }
 721       public void doChar  () { tmp.add(Character.TYPE);  }
 722       public void doFloat () { tmp.add(Float.TYPE);      }
 723       public void doDouble() { tmp.add(Double.TYPE);     }
 724       public void doByte  () { tmp.add(Byte.TYPE);       }
 725       public void doShort () { tmp.add(Short.TYPE);      }
 726       public void doInt   () { tmp.add(Integer.TYPE);    }
 727       public void doLong  () { tmp.add(Long.TYPE);       }
 728       public void doVoid  () {
 729          if(isReturnType()) {
 730             tmp.add(Void.TYPE);
 731          } else {
 732             throw new RuntimeException("should not reach here");
 733          }
 734       }
 735 
 736       public void doObject(int begin, int end) {
 737          tmp.add(getClass(begin, end));
 738       }
 739 
 740       public void doArray (int begin, int end) {
 741         int inner = arrayInnerBegin(begin);
 742         Class elemCls = null;
 743         switch (_signature.getByteAt(inner)) {
 744         case 'B': elemCls = Boolean.TYPE; break;
 745         case 'C': elemCls = Character.TYPE; break;
 746         case 'D': elemCls = Double.TYPE; break;
 747         case 'F': elemCls = Float.TYPE; break;
 748         case 'I': elemCls = Integer.TYPE; break;
 749         case 'J': elemCls = Long.TYPE; break;
 750         case 'S': elemCls = Short.TYPE; break;
 751         case 'Z': elemCls = Boolean.TYPE; break;
 752         case 'L': elemCls = getClass(inner + 1, end); break;
 753         default: break;
 754         }
 755 
 756         int dimension = inner - begin;
 757         // create 0 x 0 ... array and get class from that
 758         int[] dimArray = new int[dimension];
 759         tmp.add(java.lang.reflect.Array.newInstance(elemCls, dimArray).getClass());
 760       }
 761 
 762       protected Class getClass(int begin, int end) {
 763          String className = getClassName(begin, end);
 764          try {
 765             return Class.forName(className, true, cl);
 766          } catch (Exception e) {
 767             if (DEBUG) {
 768                debugPrintln("Can't load class " + className);
 769             }
 770             throw new RuntimeException(e);
 771          }
 772       }
 773 
 774       protected String getClassName(int begin, int end) {
 775          StringBuffer buf = new StringBuffer();
 776          for (int i = begin; i < end; i++) {
 777             char c = (char) (_signature.getByteAt(i) & 0xFF);
 778             if (c == '/') {
 779                buf.append('.');
 780             } else {
 781                buf.append(c);
 782             }
 783          }
 784          return buf.toString();
 785       }
 786 
 787       protected int arrayInnerBegin(int begin) {
 788          while (_signature.getByteAt(begin) == '[') {
 789            ++begin;
 790          }
 791          return begin;
 792       }
 793 
 794       public int getNumParams() {
 795          return tmp.size();
 796       }
 797 
 798       public Enumeration getParamTypes() {
 799          return tmp.elements();
 800       }
 801    }
 802 
 803    protected Class[] getParamTypes(Symbol signature) {
 804       SignatureParser sp = new SignatureParser(signature);
 805       sp.iterateParameters();
 806       Class result[] = new Class[sp.getNumParams()];
 807       Enumeration e = sp.getParamTypes();
 808       int i = 0;
 809       while (e.hasMoreElements()) {
 810          result[i] = (Class) e.nextElement();
 811          i++;
 812       }
 813       return result;
 814    }
 815 }