- package com.sun.org.apache.bcel.internal.classfile;
-
- /* ====================================================================
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 2001 The Apache Software Foundation. All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- * if any, must include the following acknowledgment:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowledgment may appear in the software itself,
- * if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" and
- * "Apache BCEL" must not be used to endorse or promote products
- * derived from this software without prior written permission. For
- * written permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- * "Apache BCEL", nor may "Apache" appear in their name, without
- * prior written permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- */
-
- import com.sun.org.apache.bcel.internal.Constants;
- import com.sun.org.apache.bcel.internal.util.ByteSequence;
- import java.io.*;
- import java.util.ArrayList;
- import java.util.zip.*;
-
- /**
- * Utility functions that do not really belong to any class in particular.
- *
- * @version $Id: Utility.java,v 1.1.1.1 2001/10/29 20:00:05 jvanzyl Exp $
- * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
- */
- public abstract class Utility {
- private static int consumed_chars; /* How many chars have been consumed
- * during parsing in signatureToString().
- * Read by methodSignatureToString().
- * Set by side effect,but only internally.
- */
- private static boolean wide=false; /* The `WIDE' instruction is used in the
- * byte code to allow 16-bit wide indices
- * for local variables. This opcode
- * precedes an `ILOAD', e.g.. The opcode
- * immediately following takes an extra
- * byte which is combined with the
- * following byte to form a
- * 16-bit value.
- */
- /**
- * Convert bit field of flags into string such as `static final'.
- *
- * @param access_flags Access flags
- * @return String representation of flags
- */
- public static final String accessToString(int access_flags) {
- return accessToString(access_flags, false);
- }
-
- /**
- * Convert bit field of flags into string such as `static final'.
- *
- * Special case: Classes compiled with new compilers and with the
- * `ACC_SUPER' flag would be said to be "synchronized". This is
- * because SUN used the same value for the flags `ACC_SUPER' and
- * `ACC_SYNCHRONIZED'.
- *
- * @param access_flags Access flags
- * @param for_class access flags are for class qualifiers ?
- * @return String representation of flags
- */
- public static final String accessToString(int access_flags,
- boolean for_class)
- {
- StringBuffer buf = new StringBuffer();
-
- int p = 0;
- for(int i=0; p < Constants.MAX_ACC_FLAG; i++) { // Loop through known flags
- p = pow2(i);
-
- if((access_flags & p) != 0) {
- /* Special case: Classes compiled with new compilers and with the
- * `ACC_SUPER' flag would be said to be "synchronized". This is
- * because SUN used the same value for the flags `ACC_SUPER' and
- * `ACC_SYNCHRONIZED'.
- */
- if(for_class && ((p == Constants.ACC_SUPER) || (p == Constants.ACC_INTERFACE)))
- continue;
-
- buf.append(Constants.ACCESS_NAMES[i] + " ");
- }
- }
-
- return buf.toString().trim();
- }
-
- /**
- * @return "class" or "interface", depending on the ACC_INTERFACE flag
- */
- public static final String classOrInterface(int access_flags) {
- return ((access_flags & Constants.ACC_INTERFACE) != 0)? "interface" : "class";
- }
-
- /**
- * Disassemble a byte array of JVM byte codes starting from code line
- * `index' and return the disassembled string representation. Decode only
- * `num' opcodes (including their operands), use -1 if you want to
- * decompile everything.
- *
- * @param code byte code array
- * @param constant_pool Array of constants
- * @param index offset in `code' array
- * <EM>(number of opcodes, not bytes!)</EM>
- * @param length number of opcodes to decompile, -1 for all
- * @param verbose be verbose, e.g. print constant pool index
- * @return String representation of byte codes
- */
- public static final String codeToString(byte[] code,
- ConstantPool constant_pool,
- int index, int length, boolean verbose)
- {
- StringBuffer buf = new StringBuffer(code.length * 20); // Should be sufficient
- ByteSequence stream = new ByteSequence(code);
-
- try {
- for(int i=0; i < index; i++) // Skip `index' lines of code
- codeToString(stream, constant_pool, verbose);
-
- for(int i=0; stream.available() > 0; i++) {
- if((length < 0) || (i < length)) {
- String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
- buf.append(indices + codeToString(stream, constant_pool, verbose) + '\n');
- }
- }
- } catch(IOException e) {
- System.out.println(buf.toString());
- e.printStackTrace();
- throw new ClassFormatError("Byte code error: " + e);
- }
-
- return buf.toString();
- }
-
- public static final String codeToString(byte[] code,
- ConstantPool constant_pool,
- int index, int length) {
- return codeToString(code, constant_pool, index, length, true);
- }
-
- /**
- * Disassemble a stream of byte codes and return the
- * string representation.
- *
- * @param bytes stream of bytes
- * @param constant_pool Array of constants
- * @param verbose be verbose, e.g. print constant pool index
- * @return String representation of byte code
- */
- public static final String codeToString(ByteSequence bytes,
- ConstantPool constant_pool, boolean verbose)
- throws IOException
- {
- short opcode = (short)bytes.readUnsignedByte();
- int default_offset=0, low, high, npairs;
- int index, vindex, constant;
- int[] match, jump_table;
- int no_pad_bytes=0, offset;
- StringBuffer buf = new StringBuffer(Constants.OPCODE_NAMES[opcode]);
-
- /* Special case: Skip (0-3) padding bytes, i.e., the
- * following bytes are 4-byte-aligned
- */
- if((opcode == Constants.TABLESWITCH) || (opcode == Constants.LOOKUPSWITCH)) {
- int remainder = bytes.getIndex() % 4;
- no_pad_bytes = (remainder == 0)? 0 : 4 - remainder;
-
- for(int i=0; i < no_pad_bytes; i++) {
- byte b;
-
- if((b=bytes.readByte()) != 0)
- System.err.println("Warning: Padding byte != 0 in " +
- Constants.OPCODE_NAMES[opcode] + ":" + b);
- }
-
- // Both cases have a field default_offset in common
- default_offset = bytes.readInt();
- }
-
- switch(opcode) {
- /* Table switch has variable length arguments.
- */
- case Constants.TABLESWITCH:
- low = bytes.readInt();
- high = bytes.readInt();
-
- offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
- default_offset += offset;
-
- buf.append("\tdefault = " + default_offset + ", low = " + low +
- ", high = " + high + "(");
-
- jump_table = new int[high - low + 1];
- for(int i=0; i < jump_table.length; i++) {
- jump_table[i] = offset + bytes.readInt();
- buf.append(jump_table[i]);
-
- if(i < jump_table.length - 1)
- buf.append(", ");
- }
- buf.append(")");
-
- break;
-
- /* Lookup switch has variable length arguments.
- */
- case Constants.LOOKUPSWITCH: {
-
- npairs = bytes.readInt();
- offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
-
- match = new int[npairs];
- jump_table = new int[npairs];
- default_offset += offset;
-
- buf.append("\tdefault = " + default_offset + ", npairs = " + npairs +
- " (");
-
- for(int i=0; i < npairs; i++) {
- match[i] = bytes.readInt();
-
- jump_table[i] = offset + bytes.readInt();
-
- buf.append("(" + match[i] + ", " + jump_table[i] + ")");
-
- if(i < npairs - 1)
- buf.append(", ");
- }
- buf.append(")");
- }
- break;
-
- /* Two address bytes + offset from start of byte stream form the
- * jump target
- */
- case Constants.GOTO: case Constants.IFEQ: case Constants.IFGE: case Constants.IFGT:
- case Constants.IFLE: case Constants.IFLT: case Constants.JSR: case Constants.IFNE:
- case Constants.IFNONNULL: case Constants.IFNULL: case Constants.IF_ACMPEQ:
- case Constants.IF_ACMPNE: case Constants.IF_ICMPEQ: case Constants.IF_ICMPGE: case Constants.IF_ICMPGT:
- case Constants.IF_ICMPLE: case Constants.IF_ICMPLT: case Constants.IF_ICMPNE:
- buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readShort()));
- break;
-
- /* 32-bit wide jumps
- */
- case Constants.GOTO_W: case Constants.JSR_W:
- buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readInt()));
- break;
-
- /* Index byte references local variable (register)
- */
- case Constants.ALOAD: case Constants.ASTORE: case Constants.DLOAD: case Constants.DSTORE: case Constants.FLOAD:
- case Constants.FSTORE: case Constants.ILOAD: case Constants.ISTORE: case Constants.LLOAD: case Constants.LSTORE:
- case Constants.RET:
- if(wide) {
- vindex = bytes.readUnsignedShort();
- wide=false; // Clear flag
- }
- else
- vindex = bytes.readUnsignedByte();
-
- buf.append("\t\t%" + vindex);
- break;
-
- /*
- * Remember wide byte which is used to form a 16-bit address in the
- * following instruction. Relies on that the method is called again with
- * the following opcode.
- */
- case Constants.WIDE:
- wide = true;
- buf.append("\t(wide)");
- break;
-
- /* Array of basic type.
- */
- case Constants.NEWARRAY:
- buf.append("\t\t<" + Constants.TYPE_NAMES[bytes.readByte()] + ">");
- break;
-
- /* Access object/class fields.
- */
- case Constants.GETFIELD: case Constants.GETSTATIC: case Constants.PUTFIELD: case Constants.PUTSTATIC:
- index = bytes.readUnsignedShort();
- buf.append("\t\t" +
- constant_pool.constantToString(index, Constants.CONSTANT_Fieldref) +
- (verbose? " (" + index + ")" : ""));
- break;
-
- /* Operands are references to classes in constant pool
- */
- case Constants.NEW:
- case Constants.CHECKCAST:
- buf.append("\t");
- case Constants.INSTANCEOF:
- index = bytes.readUnsignedShort();
- buf.append("\t<" + constant_pool.constantToString(index,
- Constants.CONSTANT_Class) +
- ">" + (verbose? " (" + index + ")" : ""));
- break;
-
- /* Operands are references to methods in constant pool
- */
- case Constants.INVOKESPECIAL: case Constants.INVOKESTATIC: case Constants.INVOKEVIRTUAL:
- index = bytes.readUnsignedShort();
- buf.append("\t" + constant_pool.constantToString(index,
- Constants.CONSTANT_Methodref) +
- (verbose? " (" + index + ")" : ""));
- break;
-
- case Constants.INVOKEINTERFACE:
- index = bytes.readUnsignedShort();
- int nargs = bytes.readUnsignedByte(); // historical, redundant
- buf.append("\t" +
- constant_pool.constantToString(index,
- Constants.CONSTANT_InterfaceMethodref) +
- (verbose? " (" + index + ")\t" : "") + nargs + "\t" +
- bytes.readUnsignedByte()); // Last byte is a reserved space
- break;
-
- /* Operands are references to items in constant pool
- */
- case Constants.LDC_W: case Constants.LDC2_W:
- index = bytes.readUnsignedShort();
-
- buf.append("\t\t" + constant_pool.constantToString
- (index, constant_pool.getConstant(index).getTag()) +
- (verbose? " (" + index + ")" : ""));
- break;
-
- case Constants.LDC:
- index = bytes.readUnsignedByte();
-
- buf.append("\t\t" +
- constant_pool.constantToString
- (index, constant_pool.getConstant(index).getTag()) +
- (verbose? " (" + index + ")" : ""));
- break;
-
- /* Array of references.
- */
- case Constants.ANEWARRAY:
- index = bytes.readUnsignedShort();
-
- buf.append("\t\t<" + compactClassName(constant_pool.getConstantString
- (index, Constants.CONSTANT_Class), false) +
- ">" + (verbose? " (" + index + ")": ""));
- break;
-
- /* Multidimensional array of references.
- */
- case Constants.MULTIANEWARRAY: {
- index = bytes.readUnsignedShort();
- int dimensions = bytes.readUnsignedByte();
-
- buf.append("\t<" + compactClassName(constant_pool.getConstantString
- (index, Constants.CONSTANT_Class), false) +
- ">\t" + dimensions + (verbose? " (" + index + ")" : ""));
- }
- break;
-
- /* Increment local variable.
- */
- case Constants.IINC:
- if(wide) {
- vindex = bytes.readUnsignedShort();
- constant = bytes.readShort();
- wide = false;
- }
- else {
- vindex = bytes.readUnsignedByte();
- constant = bytes.readByte();
- }
- buf.append("\t\t%" + vindex + "\t" + constant);
- break;
-
- default:
- if(Constants.NO_OF_OPERANDS[opcode] > 0) {
- for(int i=0; i < Constants.TYPE_OF_OPERANDS[opcode].length; i++) {
- buf.append("\t\t");
- switch(Constants.TYPE_OF_OPERANDS[opcode][i]) {
- case Constants.T_BYTE: buf.append(bytes.readByte()); break;
- case Constants.T_SHORT: buf.append(bytes.readShort()); break;
- case Constants.T_INT: buf.append(bytes.readInt()); break;
-
- default: // Never reached
- System.err.println("Unreachable default case reached!");
- System.exit(-1);
- }
- }
- }
- }
-
- return buf.toString();
- }
-
- public static final String codeToString(ByteSequence bytes, ConstantPool constant_pool)
- throws IOException
- {
- return codeToString(bytes, constant_pool, true);
- }
-
- /**
- * Shorten long class names, <em>java/lang/String</em> becomes
- * <em>String</em>.
- *
- * @param str The long class name
- * @return Compacted class name
- */
- public static final String compactClassName(String str) {
- return compactClassName(str, true);
- }
-
- /**
- * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>,
- * if the
- * class name starts with this string and the flag <em>chopit</em> is true.
- * Slashes <em>/</em> are converted to dots <em>.</em>.
- *
- * @param str The long class name
- * @param prefix The prefix the get rid off
- * @param chopit Flag that determines whether chopping is executed or not
- * @return Compacted class name
- */
- public static final String compactClassName(String str,
- String prefix,
- boolean chopit)
- {
- int len = prefix.length();
-
- str = str.replace('/', '.'); // Is `/' on all systems, even DOS
-
- if(chopit) {
- // If string starts with `prefix' and contains no further dots
- if(str.startsWith(prefix) &&
- (str.substring(len).indexOf('.') == -1))
- str = str.substring(len);
- }
-
- return str;
- }
-
- /**
- * Shorten long class names, <em>java/lang/String</em> becomes
- * <em>java.lang.String</em>,
- * e.g.. If <em>chopit</em> is <em>true</em> the prefix <em>java.lang</em>
- * is also removed.
- *
- * @param str The long class name
- * @param chopit Flag that determines whether chopping is executed or not
- * @return Compacted class name
- */
- public static final String compactClassName(String str, boolean chopit) {
- return compactClassName(str, "java.lang.", chopit);
- }
-
- private static final boolean is_digit(char ch) {
- return (ch >= '0') && (ch <= '9');
- }
-
- private static final boolean is_space(char ch) {
- return (ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n');
- }
-
- /**
- * @return `flag' with bit `i' set to 1
- */
- public static final int setBit(int flag, int i) {
- return flag | pow2(i);
- }
-
- /**
- * @return `flag' with bit `i' set to 0
- */
- public static final int clearBit(int flag, int i) {
- int bit = pow2(i);
- return (flag & bit) == 0? flag : flag ^ bit;
- }
-
- /**
- * @return true, if bit `i' in `flag' is set
- */
- public static final boolean isSet(int flag, int i) {
- return (flag & pow2(i)) != 0;
- }
-
- /**
- * Converts string containing the method return and argument types
- * to a byte code method signature.
- *
- * @param ret Return type of method
- * @param argv Types of method arguments
- * @return Byte code representation of method signature
- */
- public final static String methodTypeToSignature(String ret, String[] argv)
- throws ClassFormatError
- {
- StringBuffer buf = new StringBuffer("(");
- String str;
-
- if(argv != null)
- for(int i=0; i < argv.length; i++) {
- str = getSignature(argv[i]);
-
- if(str.endsWith("V")) // void can't be a method argument
- throw new ClassFormatError("Invalid type: " + argv[i]);
-
- buf.append(str);
- }
-
- str = getSignature(ret);
-
- buf.append(")" + str);
-
- return buf.toString();
- }
-
- /**
- * @param signature Method signature
- * @return Array of argument types
- * @throw ClassFormatError
- */
- public static final String[] methodSignatureArgumentTypes(String signature)
- throws ClassFormatError
- {
- return methodSignatureArgumentTypes(signature, true);
- }
-
- /**
- * @param signature Method signature
- * @param chopit Shorten class names ?
- * @return Array of argument types
- * @throw ClassFormatError
- */
- public static final String[] methodSignatureArgumentTypes(String signature,
- boolean chopit)
- throws ClassFormatError
- {
- ArrayList vec = new ArrayList();
- int index;
- String[] types;
-
- try { // Read all declarations between for `(' and `)'
- if(signature.charAt(0) != '(')
- throw new ClassFormatError("Invalid method signature: " + signature);
-
- index = 1; // current string position
-
- while(signature.charAt(index) != ')') {
- vec.add(signatureToString(signature.substring(index), chopit));
- index += consumed_chars; // update position
- }
- } catch(StringIndexOutOfBoundsException e) { // Should never occur
- throw new ClassFormatError("Invalid method signature: " + signature);
- }
-
- types = new String[vec.size()];
- vec.toArray(types);
- return types;
- }
- /**
- * @param signature Method signature
- * @return return type of method
- * @throw ClassFormatError
- */
- public static final String methodSignatureReturnType(String signature)
- throws ClassFormatError
- {
- return methodSignatureReturnType(signature, true);
- }
- /**
- * @param signature Method signature
- * @param chopit Shorten class names ?
- * @return return type of method
- * @throw ClassFormatError
- */
- public static final String methodSignatureReturnType(String signature,
- boolean chopit)
- throws ClassFormatError
- {
- int index;
- String type;
-
- try {
- // Read return type after `)'
- index = signature.lastIndexOf(')') + 1;
- type = signatureToString(signature.substring(index), chopit);
- } catch(StringIndexOutOfBoundsException e) { // Should never occur
- throw new ClassFormatError("Invalid method signature: " + signature);
- }
-
- return type;
- }
-
- /**
- * Converts method signature to string with all class names compacted.
- *
- * @param signature to convert
- * @param name of method
- * @param access flags of method
- * @return Human readable signature
- */
- public static final String methodSignatureToString(String signature,
- String name,
- String access) {
- return methodSignatureToString(signature, name, access, true);
- }
-
- public static final String methodSignatureToString(String signature,
- String name,
- String access,
- boolean chopit) {
- return methodSignatureToString(signature, name, access, chopit, null);
- }
-
- /**
- * A returnÿtype signature represents the return value from a method.
- * It is a series of bytes in the following grammar:
- *
- * <return_signature> ::= <field_type> | V
- *
- * The character V indicates that the method returns no value. Otherwise, the
- * signature indicates the type of the return value.
- * An argument signature represents an argument passed to a method:
- *
- * <argument_signature> ::= <field_type>
- *
- * A method signature represents the arguments that the method expects, and
- * the value that it returns.
- * <method_signature> ::= (<arguments_signature>) <return_signature>
- * <arguments_signature>::= <argument_signature>*
- *
- * This method converts such a string into a Java type declaration like
- * `void main(String[])' and throws a `ClassFormatError' when the parsed
- * type is invalid.
- *
- * @param signature Method signature
- * @param name Method name
- * @param access Method access rights
- * @return Java type declaration
- * @throw ClassFormatError
- */
- public static final String methodSignatureToString(String signature,
- String name,
- String access,
- boolean chopit,
- LocalVariableTable vars)
- throws ClassFormatError
- {
- StringBuffer buf = new StringBuffer("(");
- String type;
- int index;
- int var_index = (access.indexOf("static") >= 0)? 0 : 1;
-
- try { // Read all declarations between for `(' and `)'
- if(signature.charAt(0) != '(')
- throw new ClassFormatError("Invalid method signature: " + signature);
-
- index = 1; // current string position
-
- while(signature.charAt(index) != ')') {
- buf.append(signatureToString(signature.substring(index), chopit));
-
- if(vars != null) {
- LocalVariable l = vars.getLocalVariable(var_index);
-
- if(l != null)
- buf.append(" " + l.getName());
- } else
- buf.append(" arg" + var_index);
-
- var_index++;
- buf.append(", ");
- index += consumed_chars; // update position
- }
-
- index++; // update position
-
- // Read return type after `)'
- type = signatureToString(signature.substring(index), chopit);
-
- } catch(StringIndexOutOfBoundsException e) { // Should never occur
- throw new ClassFormatError("Invalid method signature: " + signature);
- }
-
- if(buf.length() > 1) // Tack off the extra ", "
- buf.setLength(buf.length() - 2);
-
- buf.append(")");
-
- return access + ((access.length() > 0)? " " : "") + // May be an empty string
- type + " " + name + buf.toString();
- }
-
- // Guess what this does
- private static final int pow2(int n) {
- return 1 << n;
- }
-
- /**
- * Replace all occurences of <em>old</em> in <em>str</em> with <em>new</em>.
- *
- * @param str String to permute
- * @param old String to be replaced
- * @param new Replacement string
- * @return new String object
- */
- public static final String replace(String str, String old, String new_) {
- int index, old_index;
- StringBuffer buf = new StringBuffer();
-
- try {
- if((index = str.indexOf(old)) != -1) { // `old' found in str
- old_index = 0; // String start offset
-
- // While we have something to replace
- while((index = str.indexOf(old, old_index)) != -1) {
- buf.append(str.substring(old_index, index)); // append prefix
- buf.append(new_); // append replacement
-
- old_index = index + old.length(); // Skip `old'.length chars
- }
-
- buf.append(str.substring(old_index)); // append rest of string
- str = buf.toString();
- }
- } catch(StringIndexOutOfBoundsException e) { // Should not occur
- System.err.println(e);
- }
-
- return str;
- }
-
- /**
- * Converts signature to string with all class names compacted.
- *
- * @param signature to convert
- * @return Human readable signature
- */
- public static final String signatureToString(String signature) {
- return signatureToString(signature, true);
- }
-
- /**
- * The field signature represents the value of an argument to a function or
- * the value of a variable. It is a series of bytes generated by the
- * following grammar:
- *
- * <PRE>
- * <field_signature> ::= <field_type>
- * <field_type> ::= <base_type>|<object_type>|<array_type>
- * <base_type> ::= B|C|D|F|I|J|S|Z
- * <object_type> ::= L<fullclassname>
- * <array_type> ::= [<field_type>
- *
- * The meaning of the base types is as follows:
- * B byte signed byte
- * C char character
- * D double double precision IEEE float
- * F float single precision IEEE float
- * I int integer
- * J long long integer
- * L<fullclassname> ... an object of the given class
- * S short signed short
- * Z boolean true or false
- * [<field sig> ... array
- * </PRE>
- *
- * This method converts this string into a Java type declaration such as
- * `String[]' and throws a `ClassFormatError' when the parsed type is
- * invalid.
- *
- * @param signature Class signature
- * @param chopit Flag that determines whether chopping is executed or not
- * @return Java type declaration
- * @throws ClassFormatError
- */
- public static final String signatureToString(String signature,
- boolean chopit)
- {
- consumed_chars = 1; // This is the default, read just one char like `B'
-
- try {
- switch(signature.charAt(0)) {
- case 'B' : return "byte";
- case 'C' : return "char";
- case 'D' : return "double";
- case 'F' : return "float";
- case 'I' : return "int";
- case 'J' : return "long";
-
- case 'L' : { // Full class name
- int index = signature.indexOf(';'); // Look for closing `;'
-
- if(index < 0)
- throw new ClassFormatError("Invalid signature: " + signature);
-
- consumed_chars = index + 1; // "Lblabla;" `L' and `;' are removed
-
- return compactClassName(signature.substring(1, index), chopit);
- }
-
- case 'S' : return "short";
- case 'Z' : return "boolean";
-
- case '[' : { // Array declaration
- int n;
- StringBuffer buf, brackets;
- String type;
- char ch;
- int consumed_chars; // Shadows global var
-
- brackets = new StringBuffer(); // Accumulate []'s
-
- // Count opening brackets and look for optional size argument
- for(n=0; signature.charAt(n) == '['; n++)
- brackets.append("[]");
-
- consumed_chars = n; // Remember value
-
- // The rest of the string denotes a `<field_type>'
- type = signatureToString(signature.substring(n), chopit);
-
- Utility.consumed_chars += consumed_chars;
- return type + brackets.toString();
- }
-
- case 'V' : return "void";
-
- default : throw new ClassFormatError("Invalid signature: `" +
- signature + "'");
- }
- } catch(StringIndexOutOfBoundsException e) { // Should never occur
- throw new ClassFormatError("Invalid signature: " + e + ":" + signature);
- }
- }
-
- /** Parse Java type such as "char", or "java.lang.String[]" and return the
- * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" respectively.
- *
- * @param type Java type
- * @return byte code signature
- */
- public static String getSignature(String type) {
- StringBuffer buf = new StringBuffer();
- char[] chars = type.toCharArray();
- boolean char_found = false, delim = false;
- int index = -1;
-
- loop:
- for(int i=0; i < chars.length; i++) {
- switch(chars[i]) {
- case ' ': case '\t': case '\n': case '\r': case '\f':
- if(char_found)
- delim = true;
- break;
-
- case '[':
- if(!char_found)
- throw new RuntimeException("Illegal type: " + type);
-
- index = i;
- break loop;
-
- default:
- char_found = true;
- if(!delim)
- buf.append(chars[i]);
- }
- }
-
- int brackets = 0;
-
- if(index > 0)
- brackets = countBrackets(type.substring(index));
-
- type = buf.toString();
- buf.setLength(0);
-
- for(int i=0; i < brackets; i++)
- buf.append('[');
-
- boolean found = false;
-
- for(int i=Constants.T_BOOLEAN; (i <= Constants.T_VOID) && !found; i++) {
- if(Constants.TYPE_NAMES[i].equals(type)) {
- found = true;
- buf.append(Constants.SHORT_TYPE_NAMES[i]);
- }
- }
-
- if(!found) // Class name
- buf.append('L' + type.replace('.', '/') + ';');
-
- return buf.toString();
- }
-
- private static int countBrackets(String brackets) {
- char[] chars = brackets.toCharArray();
- int count = 0;
- boolean open = false;
-
- for(int i=0; i<chars.length; i++) {
- switch(chars[i]) {
- case '[':
- if(open)
- throw new RuntimeException("Illegally nested brackets:" + brackets);
- open = true;
- break;
-
- case ']':
- if(!open)
- throw new RuntimeException("Illegally nested brackets:" + brackets);
- open = false;
- count++;
- break;
-
- default:
- // Don't care
- }
- }
-
- if(open)
- throw new RuntimeException("Illegally nested brackets:" + brackets);
-
- return count;
- }
-
- /**
- * Return type of method signature as a byte value as defined in <em>Constants</em>
- *
- * @param signature in format described above
- * @return type of method signature
- * @see Constants
- */
- public static final byte typeOfMethodSignature(String signature)
- throws ClassFormatError
- {
- int index;
-
- try {
- if(signature.charAt(0) != '(')
- throw new ClassFormatError("Invalid method signature: " + signature);
-
- index = signature.lastIndexOf(')') + 1;
- return typeOfSignature(signature.substring(index));
- } catch(StringIndexOutOfBoundsException e) {
- throw new ClassFormatError("Invalid method signature: " + signature);
- }
- }
-
- /**
- * Return type of signature as a byte value as defined in <em>Constants</em>
- *
- * @param signature in format described above
- * @return type of signature
- * @see Constants
- */
- public static final byte typeOfSignature(String signature)
- throws ClassFormatError
- {
- try {
- switch(signature.charAt(0)) {
- case 'B' : return Constants.T_BYTE;
- case 'C' : return Constants.T_CHAR;
- case 'D' : return Constants.T_DOUBLE;
- case 'F' : return Constants.T_FLOAT;
- case 'I' : return Constants.T_INT;
- case 'J' : return Constants.T_LONG;
- case 'L' : return Constants.T_REFERENCE;
- case '[' : return Constants.T_ARRAY;
- case 'V' : return Constants.T_VOID;
- case 'Z' : return Constants.T_BOOLEAN;
- case 'S' : return Constants.T_SHORT;
- default:
- throw new ClassFormatError("Invalid method signature: " + signature);
- }
- } catch(StringIndexOutOfBoundsException e) {
- throw new ClassFormatError("Invalid method signature: " + signature);
- }
- }
-
- /** Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload"
- */
- public static short searchOpcode(String name) {
- name = name.toLowerCase();
-
- for(short i=0; i < Constants.OPCODE_NAMES.length; i++)
- if(Constants.OPCODE_NAMES[i].equals(name))
- return i;
-
- return -1;
- }
-
- /**
- * Convert (signed) byte to (unsigned) short value, i.e., all negative
- * values become positive.
- */
- private static final short byteToShort(byte b) {
- return (b < 0)? (short)(256 + b) : (short)b;
- }
-
- /** Convert bytes into hexidecimal string
- *
- * @return bytes as hexidecimal string, e.g. 00 FA 12 ...
- */
- public static final String toHexString(byte[] bytes) {
- StringBuffer buf = new StringBuffer();
-
- for(int i=0; i < bytes.length; i++) {
- short b = byteToShort(bytes[i]);
- String hex = Integer.toString(b, 0x10);
-
- if(b < 0x10) // just one digit, prepend '0'
- buf.append('0');
-
- buf.append(hex);
-
- if(i < bytes.length - 1)
- buf.append(' ');
- }
-
- return buf.toString();
- }
-
- /**
- * Return a string for an integer justified left or right and filled up with
- * `fill' characters if necessary.
- *
- * @param i integer to format
- * @param length length of desired string
- * @param left_justify format left or right
- * @param fill fill character
- * @return formatted int
- */
- public static final String format(int i, int length, boolean left_justify, char fill) {
- return fillup(Integer.toString(i), length, left_justify, fill);
- }
-
- /**
- * Fillup char with up to length characters with char `fill' and justify it left or right.
- *
- * @param str string to format
- * @param length length of desired string
- * @param left_justify format left or right
- * @param fill fill character
- * @return formatted string
- */
- public static final String fillup(String str, int length, boolean left_justify, char fill) {
- int len = length - str.length();
- char[] buf = new char[(len < 0)? 0 : len];
-
- for(int j=0; j < buf.length; j++)
- buf[j] = fill;
-
- if(left_justify)
- return str + new String(buf);
- else
- return new String(buf) + str;
- }
-
- static final boolean equals(byte[] a, byte[] b) {
- int size;
-
- if((size=a.length) != b.length)
- return false;
-
- for(int i=0; i < size; i++)
- if(a[i] != b[i])
- return false;
-
- return true;
- }
-
- public static final void printArray(PrintStream out, Object[] obj) {
- out.println(printArray(obj, true));
- }
-
- public static final void printArray(PrintWriter out, Object[] obj) {
- out.println(printArray(obj, true));
- }
-
- public static final String printArray(Object[] obj) {
- return printArray(obj, true);
- }
-
- public static final String printArray(Object[] obj, boolean braces) {
- if(obj == null)
- return null;
-
- StringBuffer buf = new StringBuffer();
- if(braces)
- buf.append('{');
-
- for(int i=0; i < obj.length; i++) {
- if(obj[i] != null)
- buf.append(obj[i].toString());
- else
- buf.append("null");
-
- if(i < obj.length - 1)
- buf.append(", ");
- }
-
- if(braces)
- buf.append('}');
-
- return buf.toString();
- }
-
- /** @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _)
- */
- public static boolean isJavaIdentifierPart(char ch) {
- return ((ch >= 'a') && (ch <= 'z')) ||
- ((ch >= 'A') && (ch <= 'Z')) ||
- ((ch >= '0') && (ch <= '9')) ||
- (ch == '_');
- }
-
- /** Encode byte array it into Java identifier string, i.e., a string
- * that only contains the following characters: (a, ... z, A, ... Z,
- * 0, ... 9, _, $). The encoding algorithm itself is not too
- * clever: if the current byte's ASCII value already is a valid Java
- * identifier part, leave it as it is. Otherwise it writes the
- * escape character($) followed by <p><ul><li> the ASCII value as a
- * hexadecimal string, if the value is not in the range
- * 200..247</li> <li>a Java identifier char not used in a lowercase
- * hexadecimal string, if the value is in the range
- * 200..247</li><ul></p>
- *
- * <p>This operation inflates the original byte array by roughly 40-50%</p>
- *
- * @param bytes the byte array to convert
- * @param compress use gzip to minimize string
- */
- public static String encode(byte[] bytes, boolean compress) throws IOException {
- if(compress) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- GZIPOutputStream gos = new GZIPOutputStream(baos);
-
- gos.write(bytes, 0, bytes.length);
- gos.close();
- baos.close();
-
- bytes = baos.toByteArray();
- }
-
- CharArrayWriter caw = new CharArrayWriter();
- JavaWriter jw = new JavaWriter(caw);
-
- for(int i=0; i < bytes.length; i++) {
- int in = bytes[i] & 0x000000ff; // Normalize to unsigned
- jw.write(in);
- }
-
- return caw.toString();
- }
-
- /** Decode a string back to a byte array.
- *
- * @param bytes the byte array to convert
- * @param uncompress use gzip to uncompress the stream of bytes
- */
- public static byte[] decode(String s, boolean uncompress) throws IOException {
- char[] chars = s.toCharArray();
-
- CharArrayReader car = new CharArrayReader(chars);
- JavaReader jr = new JavaReader(car);
-
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- int ch;
-
- while((ch = jr.read()) >= 0) {
- bos.write(ch);
- }
-
- bos.close();
- car.close();
- jr.close();
-
- byte[] bytes = bos.toByteArray();
-
- if(uncompress) {
- GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
-
- byte[] tmp = new byte[bytes.length * 3]; // Rough estimate
- int count = 0;
- int b;
-
- while((b = gis.read()) >= 0)
- tmp[count++] = (byte)b;
-
- bytes = new byte[count];
- System.arraycopy(tmp, 0, bytes, 0, count);
- }
-
- return bytes;
- }
-
- // A-Z, g-z, _, $
- private static final int FREE_CHARS = 48;
- private static int[] CHAR_MAP = new int[FREE_CHARS];
- private static int[] MAP_CHAR = new int[256]; // Reverse map
- private static final char ESCAPE_CHAR = '$';
-
- static {
- int j = 0, k = 0;
- for(int i='A'; i <= 'Z'; i++) {
- CHAR_MAP[j] = i;
- MAP_CHAR[i] = j;
- j++;
- }
-
- for(int i='g'; i <= 'z'; i++) {
- CHAR_MAP[j] = i;
- MAP_CHAR[i] = j;
- j++;
- }
-
- CHAR_MAP[j] = '$';
- MAP_CHAR['$'] = j;
- j++;
-
- CHAR_MAP[j] = '_';
- MAP_CHAR['_'] = j;
- }
-
- /** Decode characters into bytes.
- * Used by <a href="Utility.html#decode(java.lang.String, boolean)">decode()</a>
- */
- private static class JavaReader extends FilterReader {
- public JavaReader(Reader in) {
- super(in);
- }
-
- public int read() throws IOException {
- int b = in.read();
-
- if(b != ESCAPE_CHAR) {
- return b;
- } else {
- int i = in.read();
-
- if(i < 0)
- return -1;
-
- if(((i >= '0') && (i <= '9')) || ((i >= 'a') && (i <= 'f'))) { // Normal escape
- int j = in.read();
-
- if(j < 0)
- return -1;
-
- char[] tmp = { (char)i, (char)j };
- int s = Integer.parseInt(new String(tmp), 16);
-
- return s;
- } else { // Special escape
- return MAP_CHAR[i];
- }
- }
- }
-
- public int read(char[] cbuf, int off, int len) throws IOException {
- for(int i=0; i < len; i++)
- cbuf[off + i] = (char)read();
-
- return len;
- }
- }
-
- /** Encode bytes into valid java identifier characters.
- * Used by <a href="Utility.html#encode(byte[], boolean)">encode()</a>
- */
- private static class JavaWriter extends FilterWriter {
- public JavaWriter(Writer out) {
- super(out);
- }
-
- public void write(int b) throws IOException {
- if(isJavaIdentifierPart((char)b) && (b != ESCAPE_CHAR)) {
- out.write(b);
- } else {
- out.write(ESCAPE_CHAR); // Escape character
-
- // Special escape
- if(b >= 0 && b < FREE_CHARS) {
- out.write(CHAR_MAP[b]);
- } else { // Normal escape
- char[] tmp = Integer.toHexString(b).toCharArray();
-
- if(tmp.length == 1) {
- out.write('0');
- out.write(tmp[0]);
- } else {
- out.write(tmp[0]);
- out.write(tmp[1]);
- }
- }
- }
- }
-
- public void write(char[] cbuf, int off, int len) throws IOException {
- for(int i=0; i < len; i++)
- write(cbuf[off + i]);
- }
-
- public void write(String str, int off, int len) throws IOException {
- write(str.toCharArray(), off, len);
- }
- }
- }