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