diff options
author | aclement <aclement> | 2005-06-01 14:56:57 +0000 |
---|---|---|
committer | aclement <aclement> | 2005-06-01 14:56:57 +0000 |
commit | d21465f01441f77a6998f3470092316b15c35761 (patch) | |
tree | 1f58ad30c452ef242c4ac422af79cbbf6d39e282 /bcel-builder | |
parent | a2face5f02864185f69466416034c1530af8a275 (diff) | |
download | aspectj-d21465f01441f77a6998f3470092316b15c35761.tar.gz aspectj-d21465f01441f77a6998f3470092316b15c35761.zip |
GenericsWork: Lots of changes in here, tidying stuff up, moving it around. Have made some of the signature processing generics aware (still work in progress) - also started adding new parsing code for the more complex signatures we are likely to encounter, this is tested but not currently used anywhere. See GenericSignatureParsingTest
Diffstat (limited to 'bcel-builder')
-rw-r--r-- | bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java | 1139 |
1 files changed, 577 insertions, 562 deletions
diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java index 188309c14..3f10cd731 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java @@ -86,24 +86,21 @@ import org.aspectj.apache.bcel.util.ByteSequence; /** * Utility functions that do not really belong to any class in particular. * - * @version $Id: Utility.java,v 1.3 2005/03/10 12:14:08 aclement Exp $ + * @version $Id: Utility.java,v 1.4 2005/06/01 14:56:57 aclement Exp $ * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> * * modified: Andy Clement 2-mar-05 Removed unnecessary static and optimized */ public abstract class Utility { - 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. - */ + /* 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. + */ + private static boolean wide = false; + /** - * Convert bit field of flags into string such as `static final'. + * Convert bit field of flags into string such as 'static final'. * * @param access_flags Access flags * @return String representation of flags @@ -112,43 +109,36 @@ public abstract class Utility { return accessToString(access_flags, false); } + /** - * Convert bit field of flags into string such as `static final'. + * 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'. + * '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) - { + 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 + 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]).append(" "); + if ((access_flags & p) != 0) { + // Special case: see comment at top of class... + if (for_class && ((p == Constants.ACC_SUPER) || (p == Constants.ACC_INTERFACE))) + continue; + buf.append(Constants.ACCESS_NAMES[i]).append(" "); } } - return buf.toString().trim(); } + /** * @return "class" or "interface", depending on the ACC_INTERFACE flag */ @@ -156,10 +146,11 @@ public abstract class Utility { 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 + * '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 @@ -178,14 +169,14 @@ public abstract class Utility { 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'); - } + 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()); @@ -196,277 +187,25 @@ public abstract class Utility { 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; + public static final String codeToString(byte[] code, ConstantPool constant_pool, int index, int length) { + return codeToString(code, constant_pool, index, length, true); + } - 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 - { + throws IOException { return codeToString(bytes, constant_pool, true); } /** - * Shorten long class names, <em>java/lang/String</em> becomes - * <em>String</em>. + * Shorten long class names, <em>java/lang/String</em> becomes <em>String</em>. * * @param str The long class name * @return Compacted class name @@ -492,18 +231,19 @@ public abstract class Utility { { int len = prefix.length(); - str = str.replace('/', '.'); // Is `/' on all systems, even DOS + 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); + 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>, @@ -518,23 +258,17 @@ public abstract class Utility { 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'); - } + // bit modification /** - * @return `flag' with bit `i' set to 1 + * @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 + * @return 'flag' with bit 'i' set to 0 */ public static final int clearBit(int flag, int i) { int bit = pow2(i); @@ -542,51 +276,50 @@ public abstract class Utility { } /** - * @return true, if bit `i' in `flag' is set + * @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 + * @param returnType Return type of method (e.g. "char" or "java.lang.String[]") + * @param methodArgs Types of method arguments * @return Byte code representation of method signature */ - public final static String methodTypeToSignature(String ret, String[] argv) - throws ClassFormatException - { + public final static String methodTypeToSignature(String returnType, String[] methodArgs) throws ClassFormatException { + StringBuffer buf = new StringBuffer("("); - String str; - if(argv != null) - for(int i=0; i < argv.length; i++) { - str = getSignature(argv[i]); + if (methodArgs != null) { + for (int i=0; i < methodArgs.length; i++) { + String str = getSignature(methodArgs[i]); - if(str.endsWith("V")) // void can't be a method argument - throw new ClassFormatException("Invalid type: " + argv[i]); + if (str.equals("V")) // void can't be a method argument + throw new ClassFormatException("Invalid type: " + methodArgs[i]); - buf.append(str); + buf.append(str); } - - str = getSignature(ret); - - buf.append(")" + str); + } + + buf.append(")" + getSignature(returnType)); return buf.toString(); } + + /** * @param signature Method signature * @return Array of argument types * @throws ClassFormatException */ - public static final String[] methodSignatureArgumentTypes(String signature) - throws ClassFormatException - { + public static final String[] methodSignatureArgumentTypes(String signature) throws ClassFormatException { return methodSignatureArgumentTypes(signature, true); } @@ -594,10 +327,12 @@ public abstract class Utility { /** + * For some method signature (class file format) like '([Ljava/lang/String;Z)V' this returns an array + * of strings representing the arguments in their 'normal' form, e.g. '{java.lang.String[],boolean}' + * * @param signature Method signature - * @param chopit Shorten class names ? + * @param chopit Shorten class names * @return Array of argument types - * @throws ClassFormatException */ public static final String[] methodSignatureArgumentTypes(String signature,boolean chopit) throws ClassFormatException { ArrayList vec = new ArrayList(); @@ -605,17 +340,17 @@ public abstract class Utility { String[] types; try { // Read all declarations between for `(' and `)' - if(signature.charAt(0) != '(') - throw new ClassFormatException("Invalid method signature: " + signature); + if (signature.charAt(0) != '(') + throw new ClassFormatException("Invalid method signature: " + signature); index = 1; // current string position while(signature.charAt(index) != ')') { ResultHolder rh = signatureToStringInternal(signature.substring(index),chopit); vec.add(rh.getResult()); - index += rh.getConsumedChars(); // update position + index += rh.getConsumedChars(); } - } catch(StringIndexOutOfBoundsException e) { // Should never occur + } catch(StringIndexOutOfBoundsException e) { throw new ClassFormatException("Invalid method signature: " + signature); } @@ -626,79 +361,53 @@ public abstract class Utility { /** + * For some method signature (class file format) like '([Ljava/lang/String;)Z' this returns + * the string representing the return type its 'normal' form, e.g. 'boolean' + * * @param signature Method signature * @return return type of method * @throws ClassFormatException */ - public static final String methodSignatureReturnType(String signature) - throws ClassFormatException - { + public static final String methodSignatureReturnType(String signature) throws ClassFormatException { return methodSignatureReturnType(signature, true); } + + /** + * For some method signature (class file format) like '([Ljava/lang/String;)Z' this returns + * the string representing the return type its 'normal' form, e.g. 'boolean' + * * @param signature Method signature - * @param chopit Shorten class names ? + * @param chopit Shorten class names * @return return type of method - * @throws ClassFormatException */ - public static final String methodSignatureReturnType(String signature, - boolean chopit) - throws ClassFormatException - { + public static final String methodSignatureReturnType(String signature,boolean chopit) throws ClassFormatException { 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 + } catch (StringIndexOutOfBoundsException e) { throw new ClassFormatException("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) { + + + 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) { + public static final String methodSignatureToString(String signature,String name, + String access,boolean chopit) { return methodSignatureToString(signature, name, access, chopit, null); } /** - * A returntype 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 `ClassFormatException' when the parsed + * 'void main(String[])' and throws a 'ClassFormatException' when the parsed * type is invalid. * * @param signature Method signature @@ -707,47 +416,64 @@ public abstract class Utility { * @return Java type declaration * @throws ClassFormatException */ - public static final String methodSignatureToString(String signature, - String name, - String access, - boolean chopit, - LocalVariableTable vars) - throws ClassFormatException - { - StringBuffer buf = new StringBuffer("("); + public static final String methodSignatureToString(String signature, String name, + String access, boolean chopit, LocalVariableTable vars) throws ClassFormatException { +// +// +// if (signature.charAt(0)!='(') +// throw new ClassFormatException("Invalid method signature: " + signature); +// +// // Break the signature into two pieces: ([PARAMS])[RETURNTYPE] +// int lastBracketPos = signature.lastIndexOf(")"); +// String parameters = signature.substring(1,lastBracketPos); +// String returnType = signature.substring(lastBracketPos+1); +// +// // e.g. parameters="Ljava/util/List<Ljava/lang/String;>;" +// // returnType="V" +// +// // Break signature into its parts +// // dont want lots of substringing so lets use an index +// int posn=0; +// StringBuffer piece; +// while (posn<parameters.length()) { +// piece = new StringBuffer(); +// posn+=getSignatureFrom(parameters,piece); +// } +// + 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 ClassFormatException("Invalid method signature: " + signature); + if (signature.charAt(0) != '(') + throw new ClassFormatException("Invalid method signature: " + signature); index = 1; // current string position while(signature.charAt(index) != ')') { ResultHolder rh = signatureToStringInternal(signature.substring(index), chopit); - String param_type = rh.getResult(); - buf.append(param_type); + String param_type = rh.getResult(); + buf.append(param_type); - if(vars != null) { - LocalVariable l = vars.getLocalVariable(var_index); + if(vars != null) { + LocalVariable l = vars.getLocalVariable(var_index); - if(l != null) - buf.append(" " + l.getName()); - } else - buf.append(" arg" + var_index); + if(l != null) + buf.append(" " + l.getName()); + } else + buf.append(" arg" + var_index); - if("double".equals(param_type) || "long".equals(param_type)) - var_index += 2; - else - var_index++; + if("double".equals(param_type) || "long".equals(param_type)) + var_index += 2; + else + var_index++; - buf.append(", "); - index += rh.getConsumedChars(); // update position + buf.append(", "); + index += rh.getConsumedChars(); } - index++; // update position + index++; // Read return type after `)' type = signatureToString(signature.substring(index), chopit); @@ -764,11 +490,8 @@ public abstract class Utility { 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>. @@ -783,21 +506,21 @@ public abstract class Utility { StringBuffer buf = new StringBuffer(); try { - if((index = str.indexOf(old)) != -1) { // `old' found in str - old_index = 0; // String start offset + index = str.indexOf(old); + if ( index != -1) { + old_index = 0; - // 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(); + // 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 + } catch(StringIndexOutOfBoundsException e) { System.err.println(e); } @@ -821,42 +544,16 @@ public abstract class Utility { } /** - * 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 `ClassFormatException' when the parsed type is + * 'String[]' and throws a `ClassFormatException' when the parsed type is * invalid. * * @param signature Class signature - * @param chopit Flag that determines whether chopping is executed or not + * @param chopit Flag that determines whether chopping is executed or not * @return Java type declaration - * @throws ClassFormatException */ - // J5TODO: This will have problems with nest generic types...(but then I think we all will) public static final ResultHolder signatureToStringInternal(String signature,boolean chopit) { - int processedChars = 1; // This is the default, read just one char like `B' + int processedChars = 1; // This is the default, read just one char try { switch(signature.charAt(0)) { case 'B' : return ResultHolder.BYTE; @@ -921,8 +618,12 @@ public abstract class Utility { throw new ClassFormatException("Invalid signature: " + e + ":" + signature); } } + + + - /** Parse Java type such as "char", or "java.lang.String[]" and return the + /** + * 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 @@ -934,84 +635,50 @@ public abstract class Utility { 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]); + 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)); + if(index > 0) brackets = countBrackets(type.substring(index)); type = buf.toString(); buf.setLength(0); - for(int i=0; i < brackets; i++) - buf.append('['); + 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 (Constants.TYPE_NAMES[i].equals(type)) { + found = true; + buf.append(Constants.SHORT_TYPE_NAMES[i]); } } - if(!found) // Class name - buf.append('L' + type.replace('.', '/') + ';'); + // Class name + if (!found) 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> @@ -1020,15 +687,12 @@ public abstract class Utility { * @return type of method signature * @see Constants */ - public static final byte typeOfMethodSignature(String signature) - throws ClassFormatException - { + public static final byte typeOfMethodSignature(String signature) throws ClassFormatException { int index; try { - if(signature.charAt(0) != '(') - throw new ClassFormatException("Invalid method signature: " + signature); - + if (signature.charAt(0) != '(') + throw new ClassFormatException("Invalid method signature: " + signature); index = signature.lastIndexOf(')') + 1; return typeOfSignature(signature.substring(index)); } catch(StringIndexOutOfBoundsException e) { @@ -1036,45 +700,17 @@ public abstract class Utility { } } - /** - * 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 ClassFormatException - { - 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 ClassFormatException("Invalid method signature: " + signature); - } - } catch(StringIndexOutOfBoundsException e) { - throw new ClassFormatException("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; + for (short i=0; i < Constants.OPCODE_NAMES.length; i++) { + if (Constants.OPCODE_NAMES[i].equals(name)) + return i; + } return -1; } @@ -1087,7 +723,8 @@ public abstract class Utility { return (b < 0)? (short)(256 + b) : (short)b; } - /** Convert bytes into hexidecimal string + /** + * Convert bytes into hexidecimal string * * @return bytes as hexidecimal string, e.g. 00 FA 12 ... */ @@ -1098,13 +735,12 @@ public abstract class Utility { short b = byteToShort(bytes[i]); String hex = Integer.toString(b, 0x10); - if(b < 0x10) // just one digit, prepend '0' - buf.append('0'); + // Just one digit, so prepend 0 + if (b < 0x10) buf.append('0'); buf.append(hex); - if(i < bytes.length - 1) - buf.append(' '); + if (i < bytes.length - 1) buf.append(' '); } return buf.toString(); @@ -1112,7 +748,7 @@ public abstract class Utility { /** * Return a string for an integer justified left or right and filled up with - * `fill' characters if necessary. + * 'fill' characters if necessary. * * @param i integer to format * @param length length of desired string @@ -1211,7 +847,8 @@ public abstract class Utility { (ch == '_'); } - /** Encode byte array it into Java identifier string, i.e., a string + /** + * 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 @@ -1441,9 +1078,11 @@ public abstract class Utility { return names; } + /** * Converts a list of AnnotationGen objects into a set of attributes * that can be attached to the class file. + * * @param cp The constant pool gen where we can create the necessary name refs * @param vec A list of AnnotationGen objects */ @@ -1595,6 +1234,15 @@ public abstract class Utility { return null; } + + 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'); + } + private static class ResultHolder { private String result; private int consumed; @@ -1618,4 +1266,371 @@ public abstract class Utility { public int getConsumedChars() { return consumed; } } + // code below here I am happy with .... + + /** + * 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 ClassFormatException { + 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 ClassFormatException("Invalid method signature: " + signature); + } + } catch(StringIndexOutOfBoundsException e) { + throw new ClassFormatException("Invalid method signature: " + signature); + } + } + + public static final String readClassTypeSignatureFrom(String signature) { + StringBuffer sb = new StringBuffer(); + readClassTypeSignatureFrom(signature,0,sb,false); + return sb.toString(); + } + + /** + * Takes a string and consumes a single complete signature from it, returning + * how many chars it consumed. The chopit flag indicates whether to shorten + * type references ( java/lang/String => String ) + * + * FIXME asc this should also create some kind of object you can query for information about whether its parameterized, what the bounds are, etc... + */ + public static final int readClassTypeSignatureFrom(String signature, int posn, StringBuffer result, boolean chopit) { + int idx = posn; + try { + switch (signature.charAt(idx)) { + case 'B' : result.append("byte"); return 1; + case 'C' : result.append("char"); return 1; + case 'D' : result.append("double"); return 1; + case 'F' : result.append("float"); return 1; + case 'I' : result.append("int"); return 1; + case 'J' : result.append("long"); return 1; + case 'S' : result.append("short"); return 1; + case 'Z' : result.append("boolean");return 1; + case 'V' : result.append("void"); return 1; + + + //FIXME ASC Need a state machine to check we are parsing the right stuff here ! + case 'T' : + idx++; + int nextSemiIdx = signature.indexOf(';',idx); + result.append(signature.substring(idx,nextSemiIdx)); + return nextSemiIdx+1-posn; + + case '+' : + result.append("? extends "); + return readClassTypeSignatureFrom(signature,idx+1,result,chopit)+1; + + case '-' : + result.append("? super "); + return readClassTypeSignatureFrom(signature,idx+1,result,chopit)+1; + + case '*' : + result.append("?"); + return 1; + + case 'L' : // Full class name + boolean parameterized = false; + int idxSemicolon = signature.indexOf(';',idx); // Look for closing ';' or '<' + int idxAngly = signature.indexOf('<',idx); + int endOfSig = idxSemicolon; + if ((idxAngly!=-1) && idxAngly<endOfSig) { endOfSig = idxAngly; parameterized = true; } + + String p = signature.substring(idx+1,endOfSig); + String t = compactClassName(p,chopit); + + result.append(t); + idx=endOfSig; + // we might have finished now, depending on whether this is a parameterized type... + if (parameterized) { + idx++; + result.append("<"); + while (signature.charAt(idx)!='>') { + idx+=readClassTypeSignatureFrom(signature,idx,result,chopit); + if (signature.charAt(idx)!='>') result.append(","); + } + result.append(">");idx++; + } + if (signature.charAt(idx)!=';') throw new RuntimeException("Did not find ';' at end of signature, found "+signature.charAt(idx)); + idx++; + return idx-posn; + + + case '[' : // Array declaration + int dim = 0; + while (signature.charAt(idx)=='[') {dim++;idx++;} + idx+=readClassTypeSignatureFrom(signature,idx,result,chopit); + while (dim>0) {result.append("[]");dim--;} + return idx-posn; + + default : throw new ClassFormatException("Invalid signature: `" + + signature + "'"); + } + } catch(StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid signature: " + e + ":" + signature); + } + } + + 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: + } + } + + if (open) throw new RuntimeException("Illegally nested brackets:" + brackets); + + return count; + } + + /** + * 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 = bytes.readByte(); + if (b != 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(); + } + + // private helpers + private static final int pow2(int n) { + return 1 << n; + } } |