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