ObjectReader.java revision 9883:903a2e023ffb
1/*
2 * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25package sun.jvm.hotspot.utilities;
26
27import java.lang.reflect.Modifier;
28import java.util.*;
29import sun.jvm.hotspot.debugger.*;
30import sun.jvm.hotspot.oops.*;
31import sun.jvm.hotspot.runtime.*;
32import 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
71public 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}
768