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
25package sun.jvm.hotspot.utilities;
26
27import java.lang.reflect.Modifier;
28import java.util.*;
29import java.util.stream.*;
30import sun.jvm.hotspot.debugger.*;
31import sun.jvm.hotspot.oops.*;
32import sun.jvm.hotspot.runtime.*;
33import 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
72public 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}
816