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