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