1   package org.apache.bcel.classfile;
2   
3   /* ====================================================================
4    * The Apache Software License, Version 1.1
5    *
6    * Copyright (c) 2001 The Apache Software Foundation.  All rights
7    * reserved.
8    *
9    * Redistribution and use in source and binary forms, with or without
10   * modification, are permitted provided that the following conditions
11   * are met:
12   *
13   * 1. Redistributions of source code must retain the above copyright
14   *    notice, this list of conditions and the following disclaimer.
15   *
16   * 2. Redistributions in binary form must reproduce the above copyright
17   *    notice, this list of conditions and the following disclaimer in
18   *    the documentation and/or other materials provided with the
19   *    distribution.
20   *
21   * 3. The end-user documentation included with the redistribution,
22   *    if any, must include the following acknowledgment:
23   *       "This product includes software developed by the
24   *        Apache Software Foundation (http://www.apache.org/)."
25   *    Alternately, this acknowledgment may appear in the software itself,
26   *    if and wherever such third-party acknowledgments normally appear.
27   *
28   * 4. The names "Apache" and "Apache Software Foundation" and
29   *    "Apache BCEL" must not be used to endorse or promote products
30   *    derived from this software without prior written permission. For
31   *    written permission, please contact apache@apache.org.
32   *
33   * 5. Products derived from this software may not be called "Apache",
34   *    "Apache BCEL", nor may "Apache" appear in their name, without
35   *    prior written permission of the Apache Software Foundation.
36   *
37   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48   * SUCH DAMAGE.
49   * ====================================================================
50   *
51   * This software consists of voluntary contributions made by many
52   * individuals on behalf of the Apache Software Foundation.  For more
53   * information on the Apache Software Foundation, please see
54   * <http://www.apache.org/>.
55   */
56  
57  import org.apache.bcel.Constants;
58  import org.apache.bcel.util.ByteSequence;
59  import java.io.*;
60  import java.util.ArrayList;
61  import java.util.zip.*;
62  
63  /***
64   * Utility functions that do not really belong to any class in particular.
65   *
66   * @version $Id: Utility.java,v 1.5 2002/04/24 11:01:30 mdahm Exp $
67   * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
68   */
69  public abstract class Utility {
70    private static int consumed_chars; /* How many chars have been consumed
71  				      * during parsing in signatureToString().
72  				      * Read by methodSignatureToString().
73  				      * Set by side effect,but only internally.
74  				      */
75    private static boolean wide=false; /* The `WIDE' instruction is used in the
76  				      * byte code to allow 16-bit wide indices
77  				      * for local variables. This opcode
78  				      * precedes an `ILOAD', e.g.. The opcode
79  				      * immediately following takes an extra
80  				      * byte which is combined with the
81  				      * following byte to form a
82  				      * 16-bit value.
83  				      */
84    /***
85     * Convert bit field of flags into string such as `static final'.
86     *
87     * @param  access_flags Access flags
88     * @return String representation of flags
89     */
90    public static final String accessToString(int access_flags) {
91      return accessToString(access_flags, false);
92    }
93  
94    /***
95     * Convert bit field of flags into string such as `static final'.
96     *
97     * Special case: Classes compiled with new compilers and with the
98     * `ACC_SUPER' flag would be said to be "synchronized". This is
99     * because SUN used the same value for the flags `ACC_SUPER' and
100    * `ACC_SYNCHRONIZED'. 
101    *
102    * @param  access_flags Access flags
103    * @param  for_class access flags are for class qualifiers ?
104    * @return String representation of flags
105    */
106   public static final String accessToString(int access_flags, 
107 					    boolean for_class)
108   {
109     StringBuffer buf = new StringBuffer();
110 
111     int p = 0;
112     for(int i=0; p < Constants.MAX_ACC_FLAG; i++) { // Loop through known flags
113       p = pow2(i);
114 
115       if((access_flags & p) != 0) {
116 	/* Special case: Classes compiled with new compilers and with the
117 	 * `ACC_SUPER' flag would be said to be "synchronized". This is
118 	 * because SUN used the same value for the flags `ACC_SUPER' and
119 	 * `ACC_SYNCHRONIZED'.
120 	 */
121 	if(for_class && ((p == Constants.ACC_SUPER) || (p == Constants.ACC_INTERFACE)))
122 	  continue;	    
123 
124 	buf.append(Constants.ACCESS_NAMES[i] + " ");
125       }
126     }
127 
128     return buf.toString().trim();
129   }
130 
131   /***
132    * @return "class" or "interface", depending on the ACC_INTERFACE flag
133    */
134   public static final String classOrInterface(int access_flags) {
135     return ((access_flags & Constants.ACC_INTERFACE) != 0)? "interface" : "class";
136   }
137 
138   /***
139    * Disassemble a byte array of JVM byte codes starting from code line 
140    * `index' and return the disassembled string representation. Decode only
141    * `num' opcodes (including their operands), use -1 if you want to
142    * decompile everything.
143    *
144    * @param  code byte code array
145    * @param  constant_pool Array of constants
146    * @param  index offset in `code' array
147    * <EM>(number of opcodes, not bytes!)</EM>
148    * @param  length number of opcodes to decompile, -1 for all
149    * @param  verbose be verbose, e.g. print constant pool index
150    * @return String representation of byte codes
151    */
152   public static final String codeToString(byte[] code, 
153 					  ConstantPool constant_pool, 
154 					  int index, int length, boolean verbose)
155   {
156     StringBuffer buf    = new StringBuffer(code.length * 20); // Should be sufficient
157     ByteSequence stream = new ByteSequence(code);
158 
159     try {
160       for(int i=0; i < index; i++) // Skip `index' lines of code
161 	codeToString(stream, constant_pool, verbose);
162 
163       for(int i=0; stream.available() > 0; i++) {
164 	if((length < 0) || (i < length)) {
165 	  String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
166 	  buf.append(indices + codeToString(stream, constant_pool, verbose) + '\n');
167 	}
168       }
169     } catch(IOException e) {
170       System.out.println(buf.toString());
171       e.printStackTrace();
172       throw new ClassFormatException("Byte code error: " + e);
173     }
174 
175     return buf.toString();
176   }
177 
178   public static final String codeToString(byte[] code, 
179 					  ConstantPool constant_pool, 
180 					  int index, int length) {
181     return codeToString(code, constant_pool, index, length, true);
182   }
183 
184   /***
185    * Disassemble a stream of byte codes and return the
186    * string representation.
187    *
188    * @param  bytes stream of bytes
189    * @param  constant_pool Array of constants
190    * @param  verbose be verbose, e.g. print constant pool index
191    * @return String representation of byte code
192    */
193   public static final String codeToString(ByteSequence bytes,
194 					  ConstantPool constant_pool, boolean verbose)
195        throws IOException
196   {
197     short        opcode = (short)bytes.readUnsignedByte();
198     int          default_offset=0, low, high, npairs;
199     int          index, vindex, constant;
200     int[]        match, jump_table;
201     int          no_pad_bytes=0, offset;
202     StringBuffer buf = new StringBuffer(Constants.OPCODE_NAMES[opcode]);
203 
204     /* Special case: Skip (0-3) padding bytes, i.e., the
205      * following bytes are 4-byte-aligned
206      */
207     if((opcode == Constants.TABLESWITCH) || (opcode == Constants.LOOKUPSWITCH)) {
208       int remainder = bytes.getIndex() % 4;
209       no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
210 
211       for(int i=0; i < no_pad_bytes; i++) {
212 	byte b;
213 
214 	if((b=bytes.readByte()) != 0)
215 	  System.err.println("Warning: Padding byte != 0 in " +
216 			     Constants.OPCODE_NAMES[opcode] + ":" + b);
217       }
218 
219       // Both cases have a field default_offset in common
220       default_offset = bytes.readInt();
221     }
222 
223     switch(opcode) {
224       /* Table switch has variable length arguments.
225        */
226     case Constants.TABLESWITCH:
227       low  = bytes.readInt();
228       high = bytes.readInt();
229 
230       offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
231       default_offset += offset;
232 
233       buf.append("\tdefault = " + default_offset + ", low = " + low + 
234 		 ", high = " + high + "(");
235 
236       jump_table = new int[high - low + 1];
237       for(int i=0; i < jump_table.length; i++) {
238 	jump_table[i] = offset + bytes.readInt();
239 	buf.append(jump_table[i]);
240 
241 	if(i < jump_table.length - 1)
242 	  buf.append(", ");
243       }
244       buf.append(")");
245 
246       break;
247 
248       /* Lookup switch has variable length arguments.
249        */
250     case Constants.LOOKUPSWITCH: {
251 
252       npairs = bytes.readInt();
253       offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
254 	  
255       match      = new int[npairs];
256       jump_table = new int[npairs];
257       default_offset += offset;
258 
259       buf.append("\tdefault = " + default_offset + ", npairs = " + npairs +
260 		 " (");
261 
262       for(int i=0; i < npairs; i++) {
263 	match[i]      = bytes.readInt();
264 
265 	jump_table[i] = offset + bytes.readInt();
266 
267 	buf.append("(" + match[i] + ", " + jump_table[i] + ")");
268 
269 	if(i < npairs - 1)
270 	  buf.append(", ");
271       }
272       buf.append(")");
273     }
274     break;
275 
276     /* Two address bytes + offset from start of byte stream form the
277      * jump target
278      */
279     case Constants.GOTO:      case Constants.IFEQ:      case Constants.IFGE:      case Constants.IFGT:
280     case Constants.IFLE:      case Constants.IFLT:      case Constants.JSR: case Constants.IFNE:
281     case Constants.IFNONNULL: case Constants.IFNULL:    case Constants.IF_ACMPEQ:
282     case Constants.IF_ACMPNE: case Constants.IF_ICMPEQ: case Constants.IF_ICMPGE: case Constants.IF_ICMPGT:
283     case Constants.IF_ICMPLE: case Constants.IF_ICMPLT: case Constants.IF_ICMPNE:
284       buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readShort()));
285       break;
286 	  
287       /* 32-bit wide jumps
288        */
289     case Constants.GOTO_W: case Constants.JSR_W:
290       buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readInt()));
291       break;
292 
293       /* Index byte references local variable (register)
294        */
295     case Constants.ALOAD:  case Constants.ASTORE: case Constants.DLOAD:  case Constants.DSTORE: case Constants.FLOAD:
296     case Constants.FSTORE: case Constants.ILOAD:  case Constants.ISTORE: case Constants.LLOAD:  case Constants.LSTORE:
297     case Constants.RET: 
298       if(wide) {
299 	vindex = bytes.readUnsignedShort();
300 	wide=false; // Clear flag
301       }
302       else
303 	vindex = bytes.readUnsignedByte();
304 
305       buf.append("\t\t%" + vindex);
306       break;
307 
308       /*
309        * Remember wide byte which is used to form a 16-bit address in the
310        * following instruction. Relies on that the method is called again with
311        * the following opcode.
312        */
313     case Constants.WIDE:
314       wide      = true;
315       buf.append("\t(wide)");
316       break;
317 
318       /* Array of basic type.
319        */
320     case Constants.NEWARRAY:
321       buf.append("\t\t<" + Constants.TYPE_NAMES[bytes.readByte()] + ">");
322       break;
323 
324       /* Access object/class fields.
325        */
326     case Constants.GETFIELD: case Constants.GETSTATIC: case Constants.PUTFIELD: case Constants.PUTSTATIC:
327       index = bytes.readUnsignedShort();
328       buf.append("\t\t" +
329 		 constant_pool.constantToString(index, Constants.CONSTANT_Fieldref) +
330 		 (verbose? " (" + index + ")" : ""));
331       break;
332 	  
333       /* Operands are references to classes in constant pool
334        */
335     case Constants.NEW:
336     case Constants.CHECKCAST:
337       buf.append("\t");
338     case Constants.INSTANCEOF:
339       index = bytes.readUnsignedShort();
340       buf.append("\t<" + constant_pool.constantToString(index,
341 							Constants.CONSTANT_Class) +
342 		 ">" + (verbose? " (" + index + ")" : ""));
343       break;
344 
345       /* Operands are references to methods in constant pool
346        */
347     case Constants.INVOKESPECIAL: case Constants.INVOKESTATIC: case Constants.INVOKEVIRTUAL:
348       index = bytes.readUnsignedShort();
349       buf.append("\t" + constant_pool.constantToString(index,
350 						       Constants.CONSTANT_Methodref) +
351 		 (verbose? " (" + index + ")" : ""));
352       break;
353 
354     case Constants.INVOKEINTERFACE:
355       index = bytes.readUnsignedShort();
356       int nargs = bytes.readUnsignedByte(); // historical, redundant
357       buf.append("\t" + 
358 		 constant_pool.constantToString(index,
359 						Constants.CONSTANT_InterfaceMethodref) +
360 		 (verbose? " (" + index + ")\t" : "") + nargs + "\t" + 
361 		 bytes.readUnsignedByte()); // Last byte is a reserved space
362       break;
363 	
364       /* Operands are references to items in constant pool
365        */
366     case Constants.LDC_W: case Constants.LDC2_W:
367       index = bytes.readUnsignedShort();
368 
369       buf.append("\t\t" + constant_pool.constantToString
370 		 (index, constant_pool.getConstant(index).getTag()) +
371 		 (verbose? " (" + index + ")" : ""));
372       break;
373 
374     case Constants.LDC:
375       index = bytes.readUnsignedByte();
376 
377       buf.append("\t\t" + 
378 		 constant_pool.constantToString
379 		 (index, constant_pool.getConstant(index).getTag()) +
380 		 (verbose? " (" + index + ")" : ""));
381       break;
382 	
383       /* Array of references.
384        */
385     case Constants.ANEWARRAY:
386       index = bytes.readUnsignedShort();
387 	  
388       buf.append("\t\t<" + compactClassName(constant_pool.getConstantString
389 					  (index, Constants.CONSTANT_Class), false) +
390 		 ">" + (verbose? " (" + index + ")": ""));
391       break;
392 	
393       /* Multidimensional array of references.
394        */
395     case Constants.MULTIANEWARRAY: {
396       index          = bytes.readUnsignedShort();
397       int dimensions = bytes.readUnsignedByte();
398 
399       buf.append("\t<" + compactClassName(constant_pool.getConstantString
400 					  (index, Constants.CONSTANT_Class), false) +
401 		 ">\t" + dimensions + (verbose? " (" + index + ")" : ""));
402     }
403     break;
404 
405     /* Increment local variable.
406      */
407     case Constants.IINC:
408       if(wide) {
409 	vindex   = bytes.readUnsignedShort();
410 	constant = bytes.readShort();
411 	wide     = false;
412       }
413       else {
414 	vindex   = bytes.readUnsignedByte();
415 	constant = bytes.readByte();
416       }
417       buf.append("\t\t%" + vindex + "\t" + constant);
418       break;
419 
420     default:
421       if(Constants.NO_OF_OPERANDS[opcode] > 0) {
422 	for(int i=0; i < Constants.TYPE_OF_OPERANDS[opcode].length; i++) {
423 	  buf.append("\t\t");
424 	  switch(Constants.TYPE_OF_OPERANDS[opcode][i]) {
425 	  case Constants.T_BYTE:  buf.append(bytes.readByte()); break;
426 	  case Constants.T_SHORT: buf.append(bytes.readShort());       break;
427 	  case Constants.T_INT:   buf.append(bytes.readInt());         break;
428 					      
429 	  default: // Never reached
430 	    System.err.println("Unreachable default case reached!");
431 	    System.exit(-1);
432 	  }
433 	}
434       }
435     }
436 
437     return buf.toString();
438   }
439 
440   public static final String codeToString(ByteSequence bytes, ConstantPool constant_pool)
441     throws IOException
442   {
443     return codeToString(bytes, constant_pool, true);
444   }
445 
446   /***
447    * Shorten long class names, <em>java/lang/String</em> becomes 
448    * <em>String</em>.
449    *
450    * @param str The long class name
451    * @return Compacted class name
452    */
453   public static final String compactClassName(String str) {
454     return compactClassName(str, true);
455   }
456  
457   /***
458    * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>,
459    * if the
460    * class name starts with this string and the flag <em>chopit</em> is true.
461    * Slashes <em>/</em> are converted to dots <em>.</em>.
462    *
463    * @param str The long class name
464    * @param prefix The prefix the get rid off
465    * @param chopit Flag that determines whether chopping is executed or not
466    * @return Compacted class name
467    */
468   public static final String compactClassName(String str, 
469 					      String prefix,
470 					      boolean chopit)
471   {
472     int len = prefix.length();
473 
474     str = str.replace('/', '.'); // Is `/' on all systems, even DOS
475 
476     if(chopit) {
477       // If string starts with `prefix' and contains no further dots
478       if(str.startsWith(prefix) &&
479 	 (str.substring(len).indexOf('.') == -1))
480 	str = str.substring(len);
481     }
482 	
483     return str;
484   }
485 
486   /***
487    * Shorten long class names, <em>java/lang/String</em> becomes 
488    * <em>java.lang.String</em>,
489    * e.g.. If <em>chopit</em> is <em>true</em> the prefix <em>java.lang</em>
490    * is also removed.
491    *
492    * @param str The long class name
493    * @param chopit Flag that determines whether chopping is executed or not
494    * @return Compacted class name
495    */
496   public static final String compactClassName(String str, boolean chopit) {
497     return compactClassName(str, "java.lang.", chopit);
498   }    
499 
500   private static final boolean is_digit(char ch) {
501     return (ch >= '0') && (ch <= '9');
502   }    
503   
504   private static final boolean is_space(char ch) {
505     return (ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n');
506   }    
507 
508   /***
509    * @return `flag' with bit `i' set to 1
510    */
511   public static final int setBit(int flag, int i) {
512     return flag | pow2(i); 
513   }
514 
515   /***
516    * @return `flag' with bit `i' set to 0
517    */
518   public static final int clearBit(int flag, int i) {
519     int bit = pow2(i); 
520     return (flag & bit) == 0? flag : flag ^ bit; 
521   }
522    
523   /***
524    * @return true, if bit `i' in `flag' is set
525    */
526   public static final boolean isSet(int flag, int i) {
527     return (flag & pow2(i)) != 0;
528   }
529 
530   /***
531    * Converts string containing the method return and argument types 
532    * to a byte code method signature.
533    *
534    * @param  ret Return type of method
535    * @param  argv Types of method arguments
536    * @return Byte code representation of method signature
537    */
538   public final static String methodTypeToSignature(String ret, String[] argv)
539     throws ClassFormatException
540   {
541     StringBuffer buf = new StringBuffer("(");
542     String       str;
543 
544     if(argv != null)
545       for(int i=0; i < argv.length; i++) {
546 	str = getSignature(argv[i]);
547 
548 	if(str.endsWith("V")) // void can't be a method argument
549 	  throw new ClassFormatException("Invalid type: " + argv[i]);
550 
551 	buf.append(str);
552       }
553 
554     str = getSignature(ret);
555 
556     buf.append(")" + str);
557 
558     return buf.toString();
559   }
560 
561   /***
562    * @param  signature    Method signature
563    * @return Array of argument types
564    * @throws  ClassFormatException  
565    */
566   public static final String[] methodSignatureArgumentTypes(String signature)
567     throws ClassFormatException 
568   {
569     return methodSignatureArgumentTypes(signature, true);
570   }    
571 
572   /***
573    * @param  signature    Method signature
574    * @param chopit Shorten class names ?
575    * @return Array of argument types
576    * @throws  ClassFormatException  
577    */
578   public static final String[] methodSignatureArgumentTypes(String signature,
579 							    boolean chopit)
580     throws ClassFormatException
581   {
582     ArrayList vec = new ArrayList();
583     int       index;
584     String[]  types;
585 
586     try { // Read all declarations between for `(' and `)'
587       if(signature.charAt(0) != '(')
588 	throw new ClassFormatException("Invalid method signature: " + signature);
589 
590       index = 1; // current string position
591 
592       while(signature.charAt(index) != ')') {
593 	vec.add(signatureToString(signature.substring(index), chopit));
594 	index += consumed_chars; // update position
595       }
596     } catch(StringIndexOutOfBoundsException e) { // Should never occur
597       throw new ClassFormatException("Invalid method signature: " + signature);
598     }
599 	
600     types = new String[vec.size()];
601     vec.toArray(types);
602     return types;
603   }      
604   /***
605    * @param  signature    Method signature
606    * @return return type of method
607    * @throws  ClassFormatException  
608    */
609   public static final String methodSignatureReturnType(String signature)
610        throws ClassFormatException 
611   {
612     return methodSignatureReturnType(signature, true);
613   }    
614   /***
615    * @param  signature    Method signature
616    * @param chopit Shorten class names ?
617    * @return return type of method
618    * @throws  ClassFormatException  
619    */
620   public static final String methodSignatureReturnType(String signature,
621 						       boolean chopit)
622        throws ClassFormatException
623   {
624     int    index;
625     String type;
626 
627     try {
628       // Read return type after `)'
629       index = signature.lastIndexOf(')') + 1; 
630       type = signatureToString(signature.substring(index), chopit);
631     } catch(StringIndexOutOfBoundsException e) { // Should never occur
632       throw new ClassFormatException("Invalid method signature: " + signature);
633     }
634 
635     return type;
636   }
637 
638   /***
639    * Converts method signature to string with all class names compacted.
640    *
641    * @param signature to convert
642    * @param name of method
643    * @param access flags of method
644    * @return Human readable signature
645    */
646   public static final String methodSignatureToString(String signature,
647 						     String name,
648 						     String access) {
649     return methodSignatureToString(signature, name, access, true);
650   }
651 
652   public static final String methodSignatureToString(String signature,
653 						     String name,
654 						     String access,
655 						     boolean chopit) {
656     return methodSignatureToString(signature, name, access, chopit, null);
657   }
658 
659   /***
660    * A returntype signature represents the return value from a method.
661    * It is a series of bytes in the following grammar:
662    *
663    * <return_signature> ::= <field_type> | V
664    *
665    * The character V indicates that the method returns no value. Otherwise, the
666    * signature indicates the type of the return value.
667    * An argument signature represents an argument passed to a method:
668    *
669    * <argument_signature> ::= <field_type>
670    *
671    * A method signature represents the arguments that the method expects, and
672    * the value that it returns.
673    * <method_signature> ::= (<arguments_signature>) <return_signature>
674    * <arguments_signature>::= <argument_signature>*
675    *
676    * This method converts such a string into a Java type declaration like
677    * `void main(String[])' and throws a `ClassFormatException' when the parsed 
678    * type is invalid.
679    *
680    * @param  signature    Method signature
681    * @param  name         Method name
682    * @param  access       Method access rights
683    * @return Java type declaration
684    * @throws  ClassFormatException  
685    */
686   public static final String methodSignatureToString(String signature,
687 						     String name,
688 						     String access,
689 						     boolean chopit,
690 						     LocalVariableTable vars)
691     throws ClassFormatException
692   {
693     StringBuffer buf = new StringBuffer("(");
694     String       type;
695     int          index;
696     int          var_index = (access.indexOf("static") >= 0)? 0 : 1;
697 
698     try { // Read all declarations between for `(' and `)'
699       if(signature.charAt(0) != '(')
700 	throw new ClassFormatException("Invalid method signature: " + signature);
701 
702       index = 1; // current string position
703 
704       while(signature.charAt(index) != ')') {
705 	String param_type = signatureToString(signature.substring(index), chopit);
706 	buf.append(param_type);
707 
708 	if(vars != null) {
709 	  LocalVariable l = vars.getLocalVariable(var_index);
710 
711 	  if(l != null)
712 	    buf.append(" " + l.getName());
713 	} else
714 	  buf.append(" arg" + var_index);
715 
716 	if("double".equals(param_type) || "long".equals(param_type))
717 	  var_index += 2;
718 	else
719 	  var_index++;
720 
721 	buf.append(", ");
722 	index += consumed_chars; // update position
723       }
724 
725       index++; // update position
726 
727       // Read return type after `)'
728       type = signatureToString(signature.substring(index), chopit);
729 
730     } catch(StringIndexOutOfBoundsException e) { // Should never occur
731       throw new ClassFormatException("Invalid method signature: " + signature);
732     }
733 
734     if(buf.length() > 1) // Tack off the extra ", "
735       buf.setLength(buf.length() - 2);
736 
737     buf.append(")");
738 
739     return access + ((access.length() > 0)? " " : "") + // May be an empty string
740       type + " " + name + buf.toString();
741   }
742 
743   // Guess what this does
744   private static final int pow2(int n) {
745     return 1 << n;
746   }
747     
748   /***
749    * Replace all occurences of <em>old</em> in <em>str</em> with <em>new</em>.
750    *
751    * @param str String to permute
752    * @param old String to be replaced
753    * @param new Replacement string
754    * @return new String object
755    */
756   public static final String replace(String str, String old, String new_) {
757     int          index, old_index;
758     StringBuffer buf = new StringBuffer();
759 
760     try {
761       if((index = str.indexOf(old)) != -1) { // `old' found in str
762 	old_index = 0;                       // String start offset
763 	  
764 	// While we have something to replace
765 	while((index = str.indexOf(old, old_index)) != -1) {
766 	  buf.append(str.substring(old_index, index)); // append prefix
767 	  buf.append(new_);                            // append replacement
768 	      
769 	  old_index = index + old.length(); // Skip `old'.length chars
770 	}
771 
772 	buf.append(str.substring(old_index)); // append rest of string
773 	str = buf.toString();	
774       }
775     } catch(StringIndexOutOfBoundsException e) { // Should not occur
776       System.err.println(e);
777     }
778 
779     return str;
780   }
781 
782   /***
783    * Converts signature to string with all class names compacted.
784    *
785    * @param signature to convert
786    * @return Human readable signature
787    */
788   public static final String signatureToString(String signature) {
789     return signatureToString(signature, true);
790   }    
791 
792   /***
793    * The field signature represents the value of an argument to a function or 
794    * the value of a variable. It is a series of bytes generated by the 
795    * following grammar:
796    *
797    * <PRE>
798    * <field_signature> ::= <field_type>
799    * <field_type>      ::= <base_type>|<object_type>|<array_type>
800    * <base_type>       ::= B|C|D|F|I|J|S|Z
801    * <object_type>     ::= L<fullclassname>;
802    * <array_type>      ::= [<field_type>
803    *
804    * The meaning of the base types is as follows:
805    * B byte signed byte
806    * C char character
807    * D double double precision IEEE float
808    * F float single precision IEEE float
809    * I int integer
810    * J long long integer
811    * L<fullclassname>; ... an object of the given class
812    * S short signed short
813    * Z boolean true or false
814    * [<field sig> ... array
815    * </PRE>
816    *
817    * This method converts this string into a Java type declaration such as
818    * `String[]' and throws a `ClassFormatException' when the parsed type is 
819    * invalid.
820    *
821    * @param  signature  Class signature
822    * @param chopit Flag that determines whether chopping is executed or not
823    * @return Java type declaration
824    * @throws ClassFormatException
825    */
826   public static final String signatureToString(String signature,
827 					       boolean chopit)
828   {
829     consumed_chars = 1; // This is the default, read just one char like `B'
830 
831     try {
832       switch(signature.charAt(0)) {
833       case 'B' : return "byte";
834       case 'C' : return "char";
835       case 'D' : return "double";
836       case 'F' : return "float";
837       case 'I' : return "int";
838       case 'J' : return "long";
839 
840       case 'L' : { // Full class name
841 	int    index = signature.indexOf(';'); // Look for closing `;'
842 
843 	if(index < 0)
844 	  throw new ClassFormatException("Invalid signature: " + signature);
845 	
846 	consumed_chars = index + 1; // "Lblabla;" `L' and `;' are removed
847 
848 	return compactClassName(signature.substring(1, index), chopit);
849       }
850 
851       case 'S' : return "short";
852       case 'Z' : return "boolean";
853 
854       case '[' : { // Array declaration
855 	int          n;
856 	StringBuffer buf, brackets;
857 	String       type;
858 	char         ch;
859 	int          consumed_chars; // Shadows global var
860 
861 	brackets = new StringBuffer(); // Accumulate []'s
862 
863 	// Count opening brackets and look for optional size argument
864 	for(n=0; signature.charAt(n) == '['; n++)
865 	  brackets.append("[]");
866 
867 	consumed_chars = n; // Remember value
868 
869 	// The rest of the string denotes a `<field_type>'
870 	type = signatureToString(signature.substring(n), chopit);
871 	
872 	Utility.consumed_chars += consumed_chars;
873 	return type + brackets.toString();
874       }
875 
876       case 'V' : return "void";
877 
878       default  : throw new ClassFormatException("Invalid signature: `" +
879 					    signature + "'");
880       }
881     } catch(StringIndexOutOfBoundsException e) { // Should never occur
882       throw new ClassFormatException("Invalid signature: " + e + ":" + signature);
883     }
884   }
885 
886   /*** Parse Java type such as "char", or "java.lang.String[]" and return the
887    * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" respectively.
888    *
889    * @param  type Java type
890    * @return byte code signature
891    */
892   public static String getSignature(String type) {
893     StringBuffer buf        = new StringBuffer();
894     char[]       chars      = type.toCharArray();
895     boolean      char_found = false, delim = false;
896     int          index      = -1;
897 
898   loop:
899     for(int i=0; i < chars.length; i++) {
900       switch(chars[i]) {
901       case ' ': case '\t': case '\n': case '\r': case '\f':
902 	if(char_found)
903 	  delim = true;
904 	break;
905 
906       case '[':
907 	if(!char_found)
908 	  throw new RuntimeException("Illegal type: " + type);
909 
910 	index = i;
911 	break loop;
912 
913       default:
914 	char_found = true;
915 	if(!delim)
916 	  buf.append(chars[i]);
917       }
918     }
919 
920     int brackets = 0;
921 
922     if(index > 0)
923       brackets = countBrackets(type.substring(index));
924 
925     type = buf.toString();
926     buf.setLength(0);
927 
928     for(int i=0; i < brackets; i++)
929       buf.append('[');
930 
931     boolean found = false;
932 
933     for(int i=Constants.T_BOOLEAN; (i <= Constants.T_VOID) && !found; i++) {
934       if(Constants.TYPE_NAMES[i].equals(type)) {
935 	found = true;
936 	buf.append(Constants.SHORT_TYPE_NAMES[i]);
937       }
938     }
939     
940     if(!found) // Class name
941       buf.append('L' + type.replace('.', '/') + ';');
942 
943     return buf.toString();
944   }
945 
946   private static int countBrackets(String brackets) {
947     char[]  chars = brackets.toCharArray();
948     int     count = 0;
949     boolean open  = false;
950 
951     for(int i=0; i<chars.length; i++) {
952       switch(chars[i]) {
953       case '[':
954 	if(open)
955 	  throw new RuntimeException("Illegally nested brackets:" + brackets);
956 	open = true;
957 	break;
958 
959       case ']':
960 	if(!open)
961 	  throw new RuntimeException("Illegally nested brackets:" + brackets);
962 	open = false;
963 	count++;
964 	break;
965 
966       default:
967 	// Don't care
968       }
969     }
970 
971     if(open)
972       throw new RuntimeException("Illegally nested brackets:" + brackets);
973 
974     return count;
975   }
976 
977   /***
978    * Return type of method signature as a byte value as defined in <em>Constants</em>
979    *
980    * @param  signature in format described above
981    * @return type of method signature
982    * @see    Constants
983    */
984   public static final byte typeOfMethodSignature(String signature)
985     throws ClassFormatException
986   {
987     int index;
988 
989     try {
990       if(signature.charAt(0) != '(')
991 	throw new ClassFormatException("Invalid method signature: " + signature);
992 
993       index = signature.lastIndexOf(')') + 1;
994       return typeOfSignature(signature.substring(index));
995     } catch(StringIndexOutOfBoundsException e) {
996       throw new ClassFormatException("Invalid method signature: " + signature);
997     }
998   }
999 
1000   /***
1001    * Return type of signature as a byte value as defined in <em>Constants</em>
1002    *
1003    * @param  signature in format described above
1004    * @return type of signature
1005    * @see    Constants
1006    */
1007   public static final byte typeOfSignature(String signature)
1008     throws ClassFormatException
1009   {
1010     try {
1011       switch(signature.charAt(0)) {
1012       case 'B' : return Constants.T_BYTE;
1013       case 'C' : return Constants.T_CHAR;
1014       case 'D' : return Constants.T_DOUBLE;
1015       case 'F' : return Constants.T_FLOAT;
1016       case 'I' : return Constants.T_INT;
1017       case 'J' : return Constants.T_LONG;
1018       case 'L' : return Constants.T_REFERENCE;
1019       case '[' : return Constants.T_ARRAY;
1020       case 'V' : return Constants.T_VOID;
1021       case 'Z' : return Constants.T_BOOLEAN;
1022       case 'S' : return Constants.T_SHORT;
1023       default:  
1024 	throw new ClassFormatException("Invalid method signature: " + signature);
1025       }
1026     } catch(StringIndexOutOfBoundsException e) {
1027       throw new ClassFormatException("Invalid method signature: " + signature);
1028     }
1029   }
1030 
1031   /*** Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload"
1032    */
1033   public static short searchOpcode(String name) {
1034     name = name.toLowerCase();
1035 
1036     for(short i=0; i < Constants.OPCODE_NAMES.length; i++)
1037       if(Constants.OPCODE_NAMES[i].equals(name))
1038 	return i;
1039     
1040     return -1;
1041   }
1042 
1043   /***
1044    * Convert (signed) byte to (unsigned) short value, i.e., all negative
1045    * values become positive.
1046    */
1047   private static final short byteToShort(byte b) {
1048     return (b < 0)? (short)(256 + b) : (short)b;
1049   }
1050 
1051   /*** Convert bytes into hexidecimal string
1052    *
1053    * @return bytes as hexidecimal string, e.g. 00 FA 12 ...
1054    */
1055   public static final String toHexString(byte[] bytes) {
1056     StringBuffer buf = new StringBuffer();
1057 
1058     for(int i=0; i < bytes.length; i++) {
1059       short  b   = byteToShort(bytes[i]);
1060       String hex = Integer.toString(b, 0x10);
1061 
1062       if(b < 0x10) // just one digit, prepend '0'
1063 	buf.append('0');
1064 
1065       buf.append(hex);
1066 
1067       if(i < bytes.length - 1)
1068 	buf.append(' ');
1069     }
1070 
1071     return buf.toString();
1072   }
1073 
1074   /***
1075    * Return a string for an integer justified left or right and filled up with
1076    * `fill' characters if necessary.
1077    *
1078    * @param i integer to format
1079    * @param length length of desired string
1080    * @param left_justify format left or right
1081    * @param fill fill character
1082    * @return formatted int
1083    */
1084   public static final String format(int i, int length, boolean left_justify, char fill) {
1085     return fillup(Integer.toString(i), length, left_justify, fill);
1086   }
1087 
1088   /***
1089    * Fillup char with up to length characters with char `fill' and justify it left or right.
1090    *
1091    * @param str string to format
1092    * @param length length of desired string
1093    * @param left_justify format left or right
1094    * @param fill fill character
1095    * @return formatted string
1096    */
1097   public static final String fillup(String str, int length, boolean left_justify, char fill) {
1098     int    len = length - str.length();
1099     char[] buf = new char[(len < 0)? 0 : len];
1100 
1101     for(int j=0; j < buf.length; j++)
1102       buf[j] = fill;
1103 
1104     if(left_justify)
1105       return str + new String(buf);    
1106     else
1107       return new String(buf) + str;
1108   }
1109 
1110   static final boolean equals(byte[] a, byte[] b) {
1111     int size;
1112 
1113     if((size=a.length) != b.length)
1114       return false;
1115 
1116     for(int i=0; i < size; i++)
1117       if(a[i] != b[i])
1118 	return false;
1119 
1120     return true;
1121   }
1122 
1123   public static final void printArray(PrintStream out, Object[] obj) {
1124     out.println(printArray(obj, true));
1125   }
1126 
1127   public static final void printArray(PrintWriter out, Object[] obj) {
1128     out.println(printArray(obj, true));
1129   }
1130 
1131   public static final String printArray(Object[] obj) {
1132     return printArray(obj, true);
1133   }
1134 
1135   public static final String printArray(Object[] obj, boolean braces) {
1136     return printArray(obj, braces, false);
1137   }
1138 
1139   public static final String printArray(Object[] obj, boolean braces,
1140 					boolean quote) {
1141     if(obj == null)
1142       return null;
1143 
1144     StringBuffer buf = new StringBuffer();
1145     if(braces)
1146       buf.append('{');
1147 
1148     for(int i=0; i < obj.length; i++) {
1149       if(obj[i] != null) {
1150 	buf.append((quote? "\"" : "") + obj[i].toString() + (quote? "\"" : ""));
1151       } else {
1152 	buf.append("null");
1153       }
1154 
1155       if(i < obj.length - 1) {
1156 	buf.append(", ");
1157       }
1158     }
1159 
1160     if(braces)
1161       buf.append('}');
1162 
1163     return buf.toString();
1164   }
1165 
1166   /*** @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _)
1167    */
1168   public static boolean isJavaIdentifierPart(char ch) {
1169     return ((ch >= 'a') && (ch <= 'z')) ||
1170       ((ch >= 'A') && (ch <= 'Z')) ||
1171       ((ch >= '0') && (ch <= '9')) ||
1172       (ch == '_');
1173   }
1174 
1175   /*** Encode byte array it into Java identifier string, i.e., a string
1176    * that only contains the following characters: (a, ... z, A, ... Z,
1177    * 0, ... 9, _, $).  The encoding algorithm itself is not too
1178    * clever: if the current byte's ASCII value already is a valid Java
1179    * identifier part, leave it as it is. Otherwise it writes the
1180    * escape character($) followed by <p><ul><li> the ASCII value as a
1181    * hexadecimal string, if the value is not in the range
1182    * 200..247</li> <li>a Java identifier char not used in a lowercase
1183    * hexadecimal string, if the value is in the range
1184    * 200..247</li><ul></p>
1185    *
1186    * <p>This operation inflates the original byte array by roughly 40-50%</p>
1187    *
1188    * @param bytes the byte array to convert
1189    * @param compress use gzip to minimize string
1190    */
1191   public static String encode(byte[] bytes, boolean compress) throws IOException {
1192     if(compress) {
1193       ByteArrayOutputStream baos = new ByteArrayOutputStream();
1194       GZIPOutputStream      gos  = new GZIPOutputStream(baos);
1195 
1196       gos.write(bytes, 0, bytes.length);
1197       gos.close();
1198       baos.close();
1199 
1200       bytes = baos.toByteArray();
1201     }
1202 
1203     CharArrayWriter caw = new CharArrayWriter();
1204     JavaWriter      jw  = new JavaWriter(caw);
1205 
1206     for(int i=0; i < bytes.length; i++) {
1207       int in = bytes[i] & 0x000000ff; // Normalize to unsigned
1208       jw.write(in);
1209     }
1210 
1211     return caw.toString();
1212   }
1213 
1214   /*** Decode a string back to a byte array.
1215    *
1216    * @param bytes the byte array to convert
1217    * @param uncompress use gzip to uncompress the stream of bytes
1218    */
1219   public static byte[] decode(String s, boolean uncompress) throws IOException {
1220     char[] chars = s.toCharArray();
1221 
1222     CharArrayReader car = new CharArrayReader(chars);
1223     JavaReader      jr  = new JavaReader(car);
1224 
1225     ByteArrayOutputStream bos = new ByteArrayOutputStream();
1226 
1227     int ch;
1228 
1229     while((ch = jr.read()) >= 0) {
1230       bos.write(ch);
1231     }
1232 
1233     bos.close();
1234     car.close();
1235     jr.close();
1236 
1237     byte[] bytes = bos.toByteArray();
1238 
1239     if(uncompress) {
1240       GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
1241 
1242       byte[] tmp   = new byte[bytes.length * 3]; // Rough estimate
1243       int    count = 0;
1244       int    b;
1245 
1246       while((b = gis.read()) >= 0)
1247 	tmp[count++] = (byte)b;
1248 
1249       bytes = new byte[count];
1250       System.arraycopy(tmp, 0, bytes, 0, count);
1251     }
1252 
1253     return bytes;
1254   }
1255 
1256   // A-Z, g-z, _, $
1257   private static final int   FREE_CHARS  = 48;
1258   private static       int[] CHAR_MAP    = new int[FREE_CHARS];
1259   private static       int[] MAP_CHAR    = new int[256]; // Reverse map
1260   private static final char  ESCAPE_CHAR = '$';
1261 
1262   static {
1263     int j = 0, k = 0;
1264     for(int i='A'; i <= 'Z'; i++) {
1265       CHAR_MAP[j] = i;
1266       MAP_CHAR[i] = j;
1267       j++;
1268     }
1269 
1270     for(int i='g'; i <= 'z'; i++) {
1271       CHAR_MAP[j] = i;
1272       MAP_CHAR[i] = j;
1273       j++;
1274     }
1275 
1276     CHAR_MAP[j]   = '$';
1277     MAP_CHAR['$'] = j;
1278     j++;
1279 
1280     CHAR_MAP[j]   = '_';
1281     MAP_CHAR['_'] = j;
1282   }
1283 
1284   /*** Decode characters into bytes.
1285    * Used by <a href="Utility.html#decode(java.lang.String, boolean)">decode()</a>
1286    */
1287   private static class JavaReader extends FilterReader {
1288     public JavaReader(Reader in) {
1289       super(in);
1290     }
1291 
1292     public int read() throws IOException {
1293       int b = in.read();
1294 
1295       if(b != ESCAPE_CHAR) {
1296 	return b;
1297       } else {
1298 	int i = in.read();
1299 
1300 	if(i < 0)
1301 	  return -1;
1302 
1303 	if(((i >= '0') && (i <= '9')) || ((i >= 'a') && (i <= 'f'))) { // Normal escape
1304 	  int j = in.read();
1305 
1306 	  if(j < 0)
1307 	    return -1;
1308 
1309 	  char[] tmp = { (char)i, (char)j };
1310 	  int    s   = Integer.parseInt(new String(tmp), 16);
1311 
1312 	  return s;
1313 	} else { // Special escape
1314 	  return MAP_CHAR[i];
1315 	}
1316       }
1317     }
1318 
1319     public int read(char[] cbuf, int off, int len) throws IOException {
1320       for(int i=0; i < len; i++)
1321 	cbuf[off + i] = (char)read();
1322 
1323       return len;
1324     }
1325   }
1326 
1327   /*** Encode bytes into valid java identifier characters.
1328    * Used by <a href="Utility.html#encode(byte[], boolean)">encode()</a>
1329    */
1330   private static class JavaWriter extends FilterWriter {
1331     public JavaWriter(Writer out) {
1332       super(out);
1333     }
1334 
1335     public void write(int b) throws IOException {
1336       if(isJavaIdentifierPart((char)b) && (b != ESCAPE_CHAR)) {
1337 	out.write(b);
1338       } else {
1339 	out.write(ESCAPE_CHAR); // Escape character
1340 
1341 	// Special escape
1342 	if(b >= 0 && b < FREE_CHARS) {
1343 	  out.write(CHAR_MAP[b]);
1344 	} else { // Normal escape
1345 	  char[] tmp = Integer.toHexString(b).toCharArray();
1346 
1347 	  if(tmp.length == 1) {
1348 	    out.write('0');
1349 	    out.write(tmp[0]);
1350 	  } else {
1351 	    out.write(tmp[0]);
1352 	    out.write(tmp[1]);
1353 	  }
1354 	}
1355       }
1356     }
1357 
1358     public void write(char[] cbuf, int off, int len) throws IOException {
1359       for(int i=0; i < len; i++)
1360 	write(cbuf[off + i]);
1361     }
1362 
1363     public void write(String str, int off, int len) throws IOException {
1364       write(str.toCharArray(), off, len);
1365     }
1366   }
1367 
1368   /***
1369    * Escape all occurences of newline chars '\n', quotes \", etc.
1370    */
1371   public static final String convertString(String label) {
1372     char[]       ch  = label.toCharArray();
1373     StringBuffer buf = new StringBuffer();
1374 
1375     for(int i=0; i < ch.length; i++) {
1376       switch(ch[i]) {
1377       case '\n':
1378 	buf.append("//n"); break;
1379       case '\r':
1380 	buf.append("//r"); break;
1381       case '\"':
1382 	buf.append("//\""); break;
1383       case '\'':
1384 	buf.append("//'"); break;
1385       case '//':
1386 	buf.append("////"); break;
1387       default:
1388 	buf.append(ch[i]); break;
1389       }
1390     }
1391 
1392     return buf.toString();
1393   }
1394 }
This page was automatically generated by Maven