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