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