import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* Utility functions that do not really belong to any class in particular.
- *
- * @version $Id: Utility.java,v 1.8 2009/09/09 19:56:20 aclement Exp $
- * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
*
- * modified: Andy Clement 2-mar-05 Removed unnecessary static and optimized
+ * @version $Id: Utility.java,v 1.9 2009/09/09 22:18:20 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 {
- /* 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'.
- *
- * @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: 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
- */
- 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 ClassFormatException("Byte code error: " + e);
- }
-
- return buf.toString();
- }
-
-
-
- /**
- * Disassemble a stream of byte codes and return the
- * string representation.
- */
- public static final String codeToString(byte[] code, ConstantPool constant_pool, int index, int length) {
- return codeToString(code, constant_pool, index, length, true);
- }
-
-
-
- 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)) {
- String result = str.substring(len);
- if ( result.indexOf('.') == -1) {
- str = result;
- }
- }
- }
- 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);
- }
-
- 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);
- }
-
- /**
- * This method converts such a string into a Java type declaration like
- * 'void main(String[])' and throws a 'ClassFormatException' when the parsed
- * type is invalid.
- *
- * @param signature Method signature
- * @param name Method name
- * @param access Method access rights
- * @return Java type declaration
- * @throws ClassFormatException
- */
- 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);
-
- 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);
-
- if(vars != null) {
- LocalVariable l = vars.getLocalVariable(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++;
-
- buf.append(", ");
- index += rh.getConsumedChars();
- }
-
- index++;
-
- // Read return type after `)'
- type = signatureToString(signature.substring(index), chopit);
-
- } catch(StringIndexOutOfBoundsException e) { // Should never occur
- throw new ClassFormatException("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();
- }
-
-
-
- /**
- * 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 {
- 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();
- }
- } catch(StringIndexOutOfBoundsException e) {
- 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);
- }
-
-
- public static final String signatureToString(String signature,boolean chopit) {
- ResultHolder rh = signatureToStringInternal(signature,chopit);
- return rh.getResult();
- }
-
- /**
- * This method converts this string into a Java type declaration such as
- * '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
- * @return Java type declaration
- */
- public static final ResultHolder signatureToStringInternal(String signature,boolean chopit) {
- int processedChars = 1; // This is the default, read just one char
- try {
- switch(signature.charAt(0)) {
- case 'B' : return ResultHolder.BYTE;
- case 'C' : return ResultHolder.CHAR;
- case 'D' : return ResultHolder.DOUBLE;
- case 'F' : return ResultHolder.FLOAT;
- case 'I' : return ResultHolder.INT;
- case 'J' : return ResultHolder.LONG;
-
- case 'L' : { // Full class name
- int index = signature.indexOf(';'); // Look for closing `;'
- // Jump to the correct ';'
- if (index!=-1 &&
- signature.length()>index+1 &&
- signature.charAt(index+1)=='>') index = index+2;
-
- if (index < 0)
- throw new ClassFormatException("Invalid signature: " + signature);
-
- int genericStart = signature.indexOf('<');
- int genericEnd = signature.indexOf('>');
- if (genericStart !=-1) {
- // FIXME asc going to need a lot more work in here for generics
- ResultHolder rh = signatureToStringInternal(signature.substring(genericStart+1,genericEnd),chopit);
- ResultHolder retval = new ResultHolder(compactClassName(signature.substring(1,genericStart)+"<"+
- rh.getResult()+">",chopit),genericEnd+1);
- return retval;
- } else {
- processedChars = index + 1; // "Lblabla;" `L' and `;' are removed
- ResultHolder retval = new ResultHolder(compactClassName(signature.substring(1, index), chopit),processedChars);
- return retval;
- }
- }
-
- case 'S' : return ResultHolder.SHORT;
- case 'Z' : return ResultHolder.BOOLEAN;
-
- case '[' : { // Array declaration
- StringBuffer brackets;
- int consumedChars,n;
-
- brackets = new StringBuffer(); // Accumulate []'s
- // Count opening brackets and look for optional size argument
- for(n=0; signature.charAt(n) == '['; n++)
- brackets.append("[]");
- consumedChars = n;
- // The rest of the string denotes a `<field_type>'
- ResultHolder restOfIt = signatureToStringInternal(signature.substring(n),chopit);
-
- // type = signatureToString(signature.substring(n), chopit);
-
- consumedChars+= restOfIt.getConsumedChars();
- return new ResultHolder(restOfIt.getResult() + brackets.toString(),consumedChars);
- }
-
- case 'V' : return ResultHolder.VOID;
-
- default : throw new ClassFormatException("Invalid signature: `" +
- signature + "'");
- }
- } catch(StringIndexOutOfBoundsException e) { // Should never occur
- throw new ClassFormatException("Invalid signature: " + e + ":" + signature);
- }
- }
-
-
-
-
- /**
- * 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 ClassFormatException {
- int index;
-
- try {
- if (signature.charAt(0) != '(')
- throw new ClassFormatException("Invalid method signature: " + signature);
- index = signature.lastIndexOf(')') + 1;
- return typeOfSignature(signature.substring(index));
- } catch(StringIndexOutOfBoundsException e) {
- throw new ClassFormatException("Invalid method signature: " + signature);
- }
- }
-
- /**
- * 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);
-
- // Just one digit, so prepend 0
- if (b < 0x10) 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;
- }
-
- /**
- * Escape all occurences of newline chars '\n', quotes \", etc.
- */
- public static final String convertString(String label) {
- char[] ch = label.toCharArray();
- StringBuffer buf = new StringBuffer();
-
- for(int i=0; i < ch.length; i++) {
- switch(ch[i]) {
- case '\n':
- buf.append("\\n"); break;
- case '\r':
- buf.append("\\r"); break;
- case '\"':
- buf.append("\\\""); break;
- case '\'':
- buf.append("\\'"); break;
- case '\\':
- buf.append("\\\\"); break;
- default:
- buf.append(ch[i]); break;
- }
- }
-
- return buf.toString();
- }
-
- /**
- * 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
- */
- public static Attribute[] getAnnotationAttributes(ConstantPool cp,List<AnnotationGen> vec) {
-
- if (vec.size()==0) return null;
-
- try {
- int countVisible = 0;
- int countInvisible = 0;
-
- // put the annotations in the right output stream
- for (int i=0; i<vec.size(); i++) {
- AnnotationGen a = vec.get(i);
- if (a.isRuntimeVisible()) countVisible++;
- else countInvisible++;
- }
-
- ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
- ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
- DataOutputStream rvaDos = new DataOutputStream(rvaBytes);
- DataOutputStream riaDos = new DataOutputStream(riaBytes);
-
- rvaDos.writeShort(countVisible);
- riaDos.writeShort(countInvisible);
-
- // put the annotations in the right output stream
- for (int i=0; i<vec.size(); i++) {
- AnnotationGen a = vec.get(i);
- if (a.isRuntimeVisible()) a.dump(rvaDos);
- else a.dump(riaDos);
- }
-
- rvaDos.close();
- riaDos.close();
-
- byte[] rvaData = rvaBytes.toByteArray();
- byte[] riaData = riaBytes.toByteArray();
-
- int rvaIndex = -1;
- int riaIndex = -1;
-
- if (rvaData.length>2) rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations");
- if (riaData.length>2) riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations");
-
- List<RuntimeAnnotations> newAttributes = new ArrayList<RuntimeAnnotations>();
- if (rvaData.length>2) {
- newAttributes.add(
- new RuntimeVisibleAnnotations(rvaIndex,rvaData.length,rvaData,cp));
- }
- if (riaData.length>2) {
- newAttributes.add(
- new RuntimeInvisibleAnnotations(riaIndex,riaData.length,riaData,cp));
- }
-
- return newAttributes.toArray(new Attribute[]{});
- } catch (IOException e) {
- System.err.println("IOException whilst processing annotations");
- e.printStackTrace();
+ /*
+ * 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'.
+ *
+ * @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: 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
+ */
+ public static final String classOrInterface(int access_flags) {
+ return ((access_flags & Constants.ACC_INTERFACE) != 0) ? "interface" : "class";
}
- return null;
- }
-
- /**
- * Annotations against a class are stored in one of four attribute kinds:
- * - RuntimeVisibleParameterAnnotations
- * - RuntimeInvisibleParameterAnnotations
- */
- // OPTIMIZE looks heavyweight?
- public static Attribute[] getParameterAnnotationAttributes(ConstantPool cp,List[] /*Array of lists, array size depends on #params */ vec) {
-
- int visCount[] = new int[vec.length];
- int totalVisCount = 0;
- int invisCount[] = new int[vec.length];
- int totalInvisCount = 0;
- try {
-
- for (int i=0; i<vec.length; i++) {
- List l = vec[i];
- if (l!=null) {
- for (Iterator iter = l.iterator(); iter.hasNext();) {
- AnnotationGen element = (AnnotationGen) iter.next();
- if (element.isRuntimeVisible()) {visCount[i]++;totalVisCount++;}
- else {invisCount[i]++;totalInvisCount++;}
- }
- }
- }
-
- // Lets do the visible ones
- ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
- DataOutputStream rvaDos = new DataOutputStream(rvaBytes);
- rvaDos.writeByte(vec.length); // First goes number of parameters
-
- for (int i=0; i<vec.length; i++) {
- rvaDos.writeShort(visCount[i]);
- if (visCount[i]>0) {
- List l = vec[i];
- for (Iterator iter = l.iterator(); iter.hasNext();) {
- AnnotationGen element = (AnnotationGen) iter.next();
- if (element.isRuntimeVisible()) element.dump(rvaDos);
+
+ /**
+ * 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');
}
- }
- }
- rvaDos.close();
-
- // Lets do the invisible ones
- ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
- DataOutputStream riaDos = new DataOutputStream(riaBytes);
- riaDos.writeByte(vec.length); // First goes number of parameters
-
- for (int i=0; i<vec.length; i++) {
- riaDos.writeShort(invisCount[i]);
- if (invisCount[i]>0) {
- List l = vec[i];
- for (Iterator iter = l.iterator(); iter.hasNext();) {
- AnnotationGen element = (AnnotationGen) iter.next();
- if (!element.isRuntimeVisible()) element.dump(riaDos);
+ }
+ } catch (IOException e) {
+ System.out.println(buf.toString());
+ e.printStackTrace();
+ throw new ClassFormatException("Byte code error: " + e);
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Disassemble a stream of byte codes and return the string representation.
+ */
+ public static final String codeToString(byte[] code, ConstantPool constant_pool, int index, int length) {
+ return codeToString(code, constant_pool, index, length, true);
+ }
+
+ 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)) {
+ String result = str.substring(len);
+ if (result.indexOf('.') == -1) {
+ str = result;
}
- }
- }
- riaDos.close();
-
- byte[] rvaData = rvaBytes.toByteArray();
- byte[] riaData = riaBytes.toByteArray();
-
- int rvaIndex = -1;
- int riaIndex = -1;
-
- if (totalVisCount>0) rvaIndex = cp.addUtf8("RuntimeVisibleParameterAnnotations");
- if (totalInvisCount>0) riaIndex = cp.addUtf8("RuntimeInvisibleParameterAnnotations");
-
- List<RuntimeParameterAnnotations> newAttributes = new ArrayList<RuntimeParameterAnnotations>();
-
- if (totalVisCount>0) {
- newAttributes.add(
- new RuntimeVisibleParameterAnnotations(rvaIndex,rvaData.length,rvaData,cp));
- }
-
-
- if (totalInvisCount>0) {
- newAttributes.add(
- new RuntimeInvisibleParameterAnnotations(riaIndex,riaData.length,riaData,cp));
- }
-
- return newAttributes.toArray(new Attribute[]{});
- } catch (IOException e) {
- System.err.println("IOException whilst processing parameter annotations");
- e.printStackTrace();
+ }
+ }
+ 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);
+ }
+
+ 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);
+ }
+
+ /**
+ * This method converts such a string into a Java type declaration like 'void main(String[])' and throws a
+ * 'ClassFormatException' when the parsed type is invalid.
+ *
+ * @param signature Method signature
+ * @param name Method name
+ * @param access Method access rights
+ * @return Java type declaration
+ * @throws ClassFormatException
+ */
+ 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);
+
+ 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);
+
+ if (vars != null) {
+ LocalVariable l = vars.getLocalVariable(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++;
+
+ buf.append(", ");
+ index += rh.getConsumedChars();
+ }
+
+ index++;
+
+ // Read return type after `)'
+ type = signatureToString(signature.substring(index), chopit);
+
+ } catch (StringIndexOutOfBoundsException e) { // Should never occur
+ throw new ClassFormatException("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();
+ }
+
+ /**
+ * 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 {
+ 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();
+ }
+ } catch (StringIndexOutOfBoundsException e) {
+ 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);
+ }
+
+ public static final String signatureToString(String signature, boolean chopit) {
+ ResultHolder rh = signatureToStringInternal(signature, chopit);
+ return rh.getResult();
+ }
+
+ /**
+ * This method converts this string into a Java type declaration such as '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
+ * @return Java type declaration
+ */
+ public static final ResultHolder signatureToStringInternal(String signature, boolean chopit) {
+ int processedChars = 1; // This is the default, read just one char
+ try {
+ switch (signature.charAt(0)) {
+ case 'B':
+ return ResultHolder.BYTE;
+ case 'C':
+ return ResultHolder.CHAR;
+ case 'D':
+ return ResultHolder.DOUBLE;
+ case 'F':
+ return ResultHolder.FLOAT;
+ case 'I':
+ return ResultHolder.INT;
+ case 'J':
+ return ResultHolder.LONG;
+
+ case 'L': { // Full class name
+ int index = signature.indexOf(';'); // Look for closing `;'
+ // Jump to the correct ';'
+ if (index != -1 && signature.length() > index + 1 && signature.charAt(index + 1) == '>')
+ index = index + 2;
+
+ if (index < 0)
+ throw new ClassFormatException("Invalid signature: " + signature);
+
+ int genericStart = signature.indexOf('<');
+ int genericEnd = signature.indexOf('>');
+ if (genericStart != -1) {
+ // FIXME asc going to need a lot more work in here for generics
+ ResultHolder rh = signatureToStringInternal(signature.substring(genericStart + 1, genericEnd), chopit);
+ ResultHolder retval = new ResultHolder(compactClassName(signature.substring(1, genericStart) + "<"
+ + rh.getResult() + ">", chopit), genericEnd + 1);
+ return retval;
+ } else {
+ processedChars = index + 1; // "Lblabla;" `L' and `;' are removed
+ ResultHolder retval = new ResultHolder(compactClassName(signature.substring(1, index), chopit), processedChars);
+ return retval;
+ }
+ }
+
+ case 'S':
+ return ResultHolder.SHORT;
+ case 'Z':
+ return ResultHolder.BOOLEAN;
+
+ case '[': { // Array declaration
+ StringBuffer brackets;
+ int consumedChars, n;
+
+ brackets = new StringBuffer(); // Accumulate []'s
+ // Count opening brackets and look for optional size argument
+ for (n = 0; signature.charAt(n) == '['; n++)
+ brackets.append("[]");
+ consumedChars = n;
+ // The rest of the string denotes a `<field_type>'
+ ResultHolder restOfIt = signatureToStringInternal(signature.substring(n), chopit);
+
+ // type = signatureToString(signature.substring(n), chopit);
+
+ consumedChars += restOfIt.getConsumedChars();
+ return new ResultHolder(restOfIt.getResult() + brackets.toString(), consumedChars);
+ }
+
+ case 'V':
+ return ResultHolder.VOID;
+
+ default:
+ throw new ClassFormatException("Invalid signature: `" + signature + "'");
+ }
+ } catch (StringIndexOutOfBoundsException e) { // Should never occur
+ throw new ClassFormatException("Invalid signature: " + e + ":" + signature);
+ }
+ }
+
+ /**
+ * 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 ClassFormatException {
+ int index;
+
+ try {
+ if (signature.charAt(0) != '(')
+ throw new ClassFormatException("Invalid method signature: " + signature);
+ index = signature.lastIndexOf(')') + 1;
+ return typeOfSignature(signature.substring(index));
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new ClassFormatException("Invalid method signature: " + signature);
+ }
+ }
+
+ /**
+ * 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);
+
+ // Just one digit, so prepend 0
+ if (b < 0x10)
+ 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;
+ }
+
+ /**
+ * Escape all occurences of newline chars '\n', quotes \", etc.
+ */
+ public static final String convertString(String label) {
+ char[] ch = label.toCharArray();
+ StringBuffer buf = new StringBuffer();
+
+ for (int i = 0; i < ch.length; i++) {
+ switch (ch[i]) {
+ case '\n':
+ buf.append("\\n");
+ break;
+ case '\r':
+ buf.append("\\r");
+ break;
+ case '\"':
+ buf.append("\\\"");
+ break;
+ case '\'':
+ buf.append("\\'");
+ break;
+ case '\\':
+ buf.append("\\\\");
+ break;
+ default:
+ buf.append(ch[i]);
+ break;
+ }
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * 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
+ */
+ public static Collection<RuntimeAnnotations> getAnnotationAttributes(ConstantPool cp, List<AnnotationGen> vec) {
+
+ if (vec.size() == 0)
+ return null;
+
+ try {
+ int countVisible = 0;
+ int countInvisible = 0;
+
+ // put the annotations in the right output stream
+ for (int i = 0; i < vec.size(); i++) {
+ AnnotationGen a = vec.get(i);
+ if (a.isRuntimeVisible())
+ countVisible++;
+ else
+ countInvisible++;
+ }
+
+ ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
+ ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
+ DataOutputStream rvaDos = new DataOutputStream(rvaBytes);
+ DataOutputStream riaDos = new DataOutputStream(riaBytes);
+
+ rvaDos.writeShort(countVisible);
+ riaDos.writeShort(countInvisible);
+
+ // put the annotations in the right output stream
+ for (int i = 0; i < vec.size(); i++) {
+ AnnotationGen a = vec.get(i);
+ if (a.isRuntimeVisible())
+ a.dump(rvaDos);
+ else
+ a.dump(riaDos);
+ }
+
+ rvaDos.close();
+ riaDos.close();
+
+ byte[] rvaData = rvaBytes.toByteArray();
+ byte[] riaData = riaBytes.toByteArray();
+
+ int rvaIndex = -1;
+ int riaIndex = -1;
+
+ if (rvaData.length > 2)
+ rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations");
+ if (riaData.length > 2)
+ riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations");
+
+ List<RuntimeAnnotations> newAttributes = new ArrayList<RuntimeAnnotations>();
+ if (rvaData.length > 2) {
+ newAttributes.add(new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, rvaData, cp));
+ }
+ if (riaData.length > 2) {
+ newAttributes.add(new RuntimeInvisibleAnnotations(riaIndex, riaData.length, riaData, cp));
+ }
+
+ return newAttributes;
+ } catch (IOException e) {
+ System.err.println("IOException whilst processing annotations");
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Annotations against a class are stored in one of four attribute kinds: - RuntimeVisibleParameterAnnotations -
+ * RuntimeInvisibleParameterAnnotations
+ */
+ // OPTIMIZE looks heavyweight?
+ public static Attribute[] getParameterAnnotationAttributes(ConstantPool cp, List[] /*
+ * Array of lists, array size depends on
+ * #params
+ */vec) {
+
+ int visCount[] = new int[vec.length];
+ int totalVisCount = 0;
+ int invisCount[] = new int[vec.length];
+ int totalInvisCount = 0;
+ try {
+
+ for (int i = 0; i < vec.length; i++) {
+ List l = vec[i];
+ if (l != null) {
+ for (Iterator iter = l.iterator(); iter.hasNext();) {
+ AnnotationGen element = (AnnotationGen) iter.next();
+ if (element.isRuntimeVisible()) {
+ visCount[i]++;
+ totalVisCount++;
+ } else {
+ invisCount[i]++;
+ totalInvisCount++;
+ }
+ }
+ }
+ }
+
+ // Lets do the visible ones
+ ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
+ DataOutputStream rvaDos = new DataOutputStream(rvaBytes);
+ rvaDos.writeByte(vec.length); // First goes number of parameters
+
+ for (int i = 0; i < vec.length; i++) {
+ rvaDos.writeShort(visCount[i]);
+ if (visCount[i] > 0) {
+ List l = vec[i];
+ for (Iterator iter = l.iterator(); iter.hasNext();) {
+ AnnotationGen element = (AnnotationGen) iter.next();
+ if (element.isRuntimeVisible())
+ element.dump(rvaDos);
+ }
+ }
+ }
+ rvaDos.close();
+
+ // Lets do the invisible ones
+ ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
+ DataOutputStream riaDos = new DataOutputStream(riaBytes);
+ riaDos.writeByte(vec.length); // First goes number of parameters
+
+ for (int i = 0; i < vec.length; i++) {
+ riaDos.writeShort(invisCount[i]);
+ if (invisCount[i] > 0) {
+ List l = vec[i];
+ for (Iterator iter = l.iterator(); iter.hasNext();) {
+ AnnotationGen element = (AnnotationGen) iter.next();
+ if (!element.isRuntimeVisible())
+ element.dump(riaDos);
+ }
+ }
+ }
+ riaDos.close();
+
+ byte[] rvaData = rvaBytes.toByteArray();
+ byte[] riaData = riaBytes.toByteArray();
+
+ int rvaIndex = -1;
+ int riaIndex = -1;
+
+ if (totalVisCount > 0)
+ rvaIndex = cp.addUtf8("RuntimeVisibleParameterAnnotations");
+ if (totalInvisCount > 0)
+ riaIndex = cp.addUtf8("RuntimeInvisibleParameterAnnotations");
+
+ List<RuntimeParameterAnnotations> newAttributes = new ArrayList<RuntimeParameterAnnotations>();
+
+ if (totalVisCount > 0) {
+ newAttributes.add(new RuntimeVisibleParameterAnnotations(rvaIndex, rvaData.length, rvaData, cp));
+ }
+
+ if (totalInvisCount > 0) {
+ newAttributes.add(new RuntimeInvisibleParameterAnnotations(riaIndex, riaData.length, riaData, cp));
+ }
+
+ return newAttributes.toArray(new Attribute[] {});
+ } catch (IOException e) {
+ System.err.println("IOException whilst processing parameter annotations");
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static class ResultHolder {
+ private String result;
+ private int consumed;
+
+ public static final ResultHolder BYTE = new ResultHolder("byte", 1);
+ public static final ResultHolder CHAR = new ResultHolder("char", 1);
+ public static final ResultHolder DOUBLE = new ResultHolder("double", 1);
+ public static final ResultHolder FLOAT = new ResultHolder("float", 1);
+ public static final ResultHolder INT = new ResultHolder("int", 1);
+ public static final ResultHolder LONG = new ResultHolder("long", 1);
+ public static final ResultHolder SHORT = new ResultHolder("short", 1);
+ public static final ResultHolder BOOLEAN = new ResultHolder("boolean", 1);
+ public static final ResultHolder VOID = new ResultHolder("void", 1);
+
+ public ResultHolder(String s, int c) {
+ result = s;
+ consumed = c;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ public int getConsumedChars() {
+ return consumed;
+ }
+ }
+
+ /**
+ * 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 byte typeOfSignature(char c) throws ClassFormatException {
+ switch (c) {
+ 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 type of signature: " + c);
+ }
+ }
+
+ /**
+ * 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.iLen[opcode] - 1) > 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;
}
- return null;
- }
-
- public static class ResultHolder {
- private String result;
- private int consumed;
-
- public static final ResultHolder BYTE = new ResultHolder("byte",1);
- public static final ResultHolder CHAR = new ResultHolder("char",1);
- public static final ResultHolder DOUBLE = new ResultHolder("double",1);
- public static final ResultHolder FLOAT = new ResultHolder("float",1);
- public static final ResultHolder INT = new ResultHolder("int",1);
- public static final ResultHolder LONG = new ResultHolder("long",1);
- public static final ResultHolder SHORT = new ResultHolder("short",1);
- public static final ResultHolder BOOLEAN = new ResultHolder("boolean",1);
- public static final ResultHolder VOID = new ResultHolder("void",1);
-
- public ResultHolder(String s,int c) {
- result = s;
- consumed = c;
- }
-
- public String getResult() { return result;}
- public int getConsumedChars() { return consumed; }
- }
-
-
- /**
- * 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 byte typeOfSignature(char c) throws ClassFormatException {
- switch(c) {
- 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 type of signature: " + c);
- }
- }
-
- /**
- * 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.iLen[opcode]-1) > 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;
- }
}
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.aspectj.apache.bcel.Constants;
-import org.aspectj.apache.bcel.classfile.FieldOrMethod;
-import org.aspectj.apache.bcel.classfile.Modifiers;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.Field;
+import org.aspectj.apache.bcel.classfile.FieldOrMethod;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
+import org.aspectj.apache.bcel.classfile.Modifiers;
import org.aspectj.apache.bcel.classfile.SourceFile;
import org.aspectj.apache.bcel.classfile.Utility;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeInvisibleAnnotations;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisibleAnnotations;
-/**
- * Template class for building up a java class. May be initialized with an
- * existing java class (file).
- *
+/**
+ * Template class for building up a java class. May be initialized with an existing java class (file).
+ *
* @see JavaClass
- * @version $Id: ClassGen.java,v 1.11 2009/09/09 19:56:20 aclement Exp $
- * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
- *
- * Upgraded, Andy Clement 9th Mar 06 - calculates SUID
+ * @version $Id: ClassGen.java,v 1.12 2009/09/09 22:18:20 aclement Exp $
+ * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
+ *
+ * Upgraded, Andy Clement 9th Mar 06 - calculates SUID
*/
public class ClassGen extends Modifiers implements Cloneable {
- private String class_name, super_class_name, file_name;
- private int class_name_index = -1, superclass_name_index = -1;
- private int major = Constants.MAJOR_1_1, minor = Constants.MINOR_1_1;
-
- private ConstantPool cp;
-
- private ArrayList<Field> field_vec = new ArrayList<Field>();
- private ArrayList<Method> method_vec = new ArrayList<Method>();
- private ArrayList<Attribute> attributesList = new ArrayList<Attribute>();
- private ArrayList<String> interface_vec = new ArrayList<String>();
- private ArrayList<AnnotationGen> annotation_vec= new ArrayList<AnnotationGen>();
-
- /** Convenience constructor to set up some important values initially.
- *
- * @param class_name fully qualified class name
- * @param super_class_name fully qualified superclass name
- * @param file_name source file name
- * @param access_flags access qualifiers
- * @param interfaces implemented interfaces
- * @param cp constant pool to use
- */
- public ClassGen(String class_name, String super_class_name, String file_name,
- int access_flags, String[] interfaces, ConstantPool cp) {
- this.class_name = class_name;
- this.super_class_name = super_class_name;
- this.file_name = file_name;
- this.modifiers = access_flags;
- this.cp = cp;
-
- // Put everything needed by default into the constant pool and the vectors
- if (file_name != null) {
- addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(file_name), cp));
- }
-
- class_name_index = cp.addClass(class_name);
- superclass_name_index = cp.addClass(super_class_name);
-
- if (interfaces != null) {
- for(int i=0; i < interfaces.length; i++)
- addInterface(interfaces[i]);
- }
- }
-
- /** Convenience constructor to set up some important values initially.
- *
- * @param class_name fully qualified class name
- * @param super_class_name fully qualified superclass name
- * @param file_name source file name
- * @param access_flags access qualifiers
- * @param interfaces implemented interfaces
- */
- public ClassGen(String class_name, String super_class_name, String file_name,
- int access_flags, String[] interfaces) {
- this(class_name, super_class_name, file_name, access_flags, interfaces,
- new ConstantPool());
- }
-
- /**
- * Initialize with existing class.
- * @param clazz JavaClass object (e.g. read from file)
- */
- public ClassGen(JavaClass clazz) {
- class_name_index = clazz.getClassNameIndex();
- superclass_name_index = clazz.getSuperclassNameIndex();
- class_name = clazz.getClassName();
- super_class_name = clazz.getSuperclassName();
- file_name = clazz.getSourceFileName();
- modifiers = clazz.getModifiers();
- cp = clazz.getConstantPool().copy();
- major = clazz.getMajor();
- minor = clazz.getMinor();
-
- Attribute[] attributes = clazz.getAttributes();
- // J5TODO: Could make unpacking lazy, done on first reference
- AnnotationGen[] annotations = unpackAnnotations(attributes);
- Method[] methods = clazz.getMethods();
- Field[] fields = clazz.getFields();
- String[] interfaces = clazz.getInterfaceNames();
-
- for(int i=0; i < interfaces.length; i++)
- addInterface(interfaces[i]);
-
- //Attribute[] attrs = attributes.getAttributes();
- for(int i=0; i < attributes.length; i++) {
- // Dont add attributes for annotations as those will have been unpacked
- if (annotations.length==0) {
- addAttribute(attributes[i]);
- } else if (!attributes[i].getName().equals("RuntimeVisibleAnnotations") &&
- !attributes[i].getName().equals("RuntimeInvisibleAnnotations")) {
- addAttribute(attributes[i]);
- }
- }
- for(int i=0; i < annotations.length; i++)
- addAnnotation(annotations[i]);
-
- for(int i=0; i < methods.length; i++) {
- Method m = methods[i];
- addMethod(m);
- }
-
- for(int i=0; i < fields.length; i++)
- addField(fields[i]);
- }
-
- /**
- * Look for attributes representing annotations and unpack them.
- */
- private AnnotationGen[] unpackAnnotations(Attribute[] attrs) {
- List /*AnnotationGen*/<AnnotationGen> annotationGenObjs = new ArrayList<AnnotationGen>();
- for (int i = 0; i < attrs.length; i++) {
- Attribute attr = attrs[i];
- if (attr instanceof RuntimeVisibleAnnotations) {
- RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations)attr;
- List<AnnotationGen> annos = rva.getAnnotations();
- for (Iterator<AnnotationGen> iter = annos.iterator(); iter.hasNext();) {
- AnnotationGen a = iter.next();
- annotationGenObjs.add(new AnnotationGen(a,getConstantPool(),false));
+ private String class_name, super_class_name, file_name;
+ private int class_name_index = -1, superclass_name_index = -1;
+ private int major = Constants.MAJOR_1_1, minor = Constants.MINOR_1_1;
+
+ private ConstantPool cp;
+
+ private List<Field> field_vec = new ArrayList<Field>();
+ private List<Method> method_vec = new ArrayList<Method>();
+ private List<Attribute> attributesList = new ArrayList<Attribute>();
+ private List<String> interface_vec = new ArrayList<String>();
+ private List<AnnotationGen> annotationsList = new ArrayList<AnnotationGen>();
+
+ /**
+ * Convenience constructor to set up some important values initially.
+ *
+ * @param class_name fully qualified class name
+ * @param super_class_name fully qualified superclass name
+ * @param file_name source file name
+ * @param access_flags access qualifiers
+ * @param interfaces implemented interfaces
+ * @param cp constant pool to use
+ */
+ public ClassGen(String class_name, String super_class_name, String file_name, int access_flags, String[] interfaces,
+ ConstantPool cp) {
+ this.class_name = class_name;
+ this.super_class_name = super_class_name;
+ this.file_name = file_name;
+ this.modifiers = access_flags;
+ this.cp = cp;
+
+ // Put everything needed by default into the constant pool and the vectors
+ if (file_name != null) {
+ addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(file_name), cp));
+ }
+
+ class_name_index = cp.addClass(class_name);
+ superclass_name_index = cp.addClass(super_class_name);
+
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; i++)
+ addInterface(interfaces[i]);
+ }
+ }
+
+ /**
+ * Convenience constructor to set up some important values initially.
+ *
+ * @param class_name fully qualified class name
+ * @param super_class_name fully qualified superclass name
+ * @param file_name source file name
+ * @param access_flags access qualifiers
+ * @param interfaces implemented interfaces
+ */
+ public ClassGen(String class_name, String super_class_name, String file_name, int access_flags, String[] interfaces) {
+ this(class_name, super_class_name, file_name, access_flags, interfaces, new ConstantPool());
+ }
+
+ /**
+ * Initialize with existing class.
+ *
+ * @param clazz JavaClass object (e.g. read from file)
+ */
+ public ClassGen(JavaClass clazz) {
+ class_name_index = clazz.getClassNameIndex();
+ superclass_name_index = clazz.getSuperclassNameIndex();
+ class_name = clazz.getClassName();
+ super_class_name = clazz.getSuperclassName();
+ file_name = clazz.getSourceFileName();
+ modifiers = clazz.getModifiers();
+ cp = clazz.getConstantPool().copy();
+ major = clazz.getMajor();
+ minor = clazz.getMinor();
+
+ Attribute[] attributes = clazz.getAttributes();
+ // J5TODO: Could make unpacking lazy, done on first reference
+ AnnotationGen[] annotations = unpackAnnotations(attributes);
+ Method[] methods = clazz.getMethods();
+ Field[] fields = clazz.getFields();
+ String[] interfaces = clazz.getInterfaceNames();
+
+ for (int i = 0; i < interfaces.length; i++)
+ addInterface(interfaces[i]);
+
+ // Attribute[] attrs = attributes.getAttributes();
+ for (int i = 0; i < attributes.length; i++) {
+ // Dont add attributes for annotations as those will have been unpacked
+ if (annotations.length == 0) {
+ addAttribute(attributes[i]);
+ } else if (!attributes[i].getName().equals("RuntimeVisibleAnnotations")
+ && !attributes[i].getName().equals("RuntimeInvisibleAnnotations")) {
+ addAttribute(attributes[i]);
}
- } else if (attr instanceof RuntimeInvisibleAnnotations) {
- RuntimeInvisibleAnnotations ria = (RuntimeInvisibleAnnotations)attr;
- List<AnnotationGen> annos = ria.getAnnotations();
- for (Iterator<AnnotationGen> iter = annos.iterator(); iter.hasNext();) {
- AnnotationGen a = iter.next();
- annotationGenObjs.add(new AnnotationGen(a,getConstantPool(),false));
+ }
+ for (int i = 0; i < annotations.length; i++)
+ addAnnotation(annotations[i]);
+
+ for (int i = 0; i < methods.length; i++) {
+ Method m = methods[i];
+ addMethod(m);
+ }
+
+ for (int i = 0; i < fields.length; i++)
+ addField(fields[i]);
+ }
+
+ /**
+ * Look for attributes representing annotations and unpack them.
+ */
+ private AnnotationGen[] unpackAnnotations(Attribute[] attrs) {
+ List /* AnnotationGen */<AnnotationGen> annotationGenObjs = new ArrayList<AnnotationGen>();
+ for (int i = 0; i < attrs.length; i++) {
+ Attribute attr = attrs[i];
+ if (attr instanceof RuntimeVisibleAnnotations) {
+ RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations) attr;
+ List<AnnotationGen> annos = rva.getAnnotations();
+ for (Iterator<AnnotationGen> iter = annos.iterator(); iter.hasNext();) {
+ AnnotationGen a = iter.next();
+ annotationGenObjs.add(new AnnotationGen(a, getConstantPool(), false));
+ }
+ } else if (attr instanceof RuntimeInvisibleAnnotations) {
+ RuntimeInvisibleAnnotations ria = (RuntimeInvisibleAnnotations) attr;
+ List<AnnotationGen> annos = ria.getAnnotations();
+ for (Iterator<AnnotationGen> iter = annos.iterator(); iter.hasNext();) {
+ AnnotationGen a = iter.next();
+ annotationGenObjs.add(new AnnotationGen(a, getConstantPool(), false));
+ }
}
}
+ return annotationGenObjs.toArray(new AnnotationGen[] {});
}
- return annotationGenObjs.toArray(new AnnotationGen[]{});
- }
-
- /**
- * @return the (finally) built up Java class object.
- */
- public JavaClass getJavaClass() {
- int[] interfaces = getInterfaces();
- Field[] fields = getFields();
- Method[] methods = getMethods();
-
- // OPTIMIZE sort out this mangling of arrays and collections
- Attribute[] attributes = null;
- if (annotation_vec.size()==0) {
- attributes = getAttributes();
- } else {
- // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations'
- Attribute[] annAttributes = Utility.getAnnotationAttributes(cp,annotation_vec);
- attributes = new Attribute[attributesList.size()+annAttributes.length];
- attributesList.toArray(attributes);
- System.arraycopy(annAttributes,0,attributes,attributesList.size(),annAttributes.length);
- }
-
- // Must be last since the above calls may still add something to it
- ConstantPool cp = this.cp.getFinalConstantPool();
-
- return new JavaClass(class_name_index, superclass_name_index,
- file_name, major, minor, modifiers,
- cp, interfaces, fields, methods, attributes);
- }
-
- /**
- * Add an interface to this class, i.e., this class has to implement it.
- * @param name interface to implement (fully qualified class name)
- */
- public void addInterface(String name) {
- interface_vec.add(name);
- }
-
- /**
- * Remove an interface from this class.
- * @param name interface to remove (fully qualified name)
- */
- public void removeInterface(String name) {
- interface_vec.remove(name);
- }
-
- /**
- * @return major version number of class file
- */
- public int getMajor() { return major; }
-
- /** Set major version number of class file, default value is 45 (JDK 1.1)
- * @param major major version number
- */
- public void setMajor(int major) {
- this.major = major;
- }
-
- /** Set minor version number of class file, default value is 3 (JDK 1.1)
- * @param minor minor version number
- */
- public void setMinor(int minor) {
- this.minor = minor;
- }
-
- /**
- * @return minor version number of class file
- */
- public int getMinor() { return minor; }
-
- /**
- * Add an attribute to this class.
- * @param a attribute to add
- */
- public void addAttribute(Attribute a) { attributesList.add(a); }
-
- public void addAnnotation(AnnotationGen a) { annotation_vec.add(a); }
- /**
- * Add a method to this class.
- * @param m method to add
- */
- public void addMethod(Method m) { method_vec.add(m); }
-
- /**
- * Convenience method.
- *
- * Add an empty constructor to this class that does nothing but calling super().
- * @param access rights for constructor
- */
- public void addEmptyConstructor(int access_flags) {
- InstructionList il = new InstructionList();
- il.append(InstructionConstants.THIS); // Push `this'
- il.append(new InvokeInstruction(Constants.INVOKESPECIAL,cp.addMethodref(super_class_name,"<init>", "()V")));
- il.append(InstructionConstants.RETURN);
-
- MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null,
- "<init>", class_name, il, cp);
- mg.setMaxStack(1);
- mg.setMaxLocals();
- addMethod(mg.getMethod());
- }
-
- /**
- * Add a field to this class.
- * @param f field to add
- */
- public void addField(Field f) { field_vec.add(f); }
-
- public boolean containsField(Field f) { return field_vec.contains(f); }
-
- /** @return field object with given name, or null
- */
- public Field containsField(String name) {
- for(Iterator<Field> e=field_vec.iterator(); e.hasNext(); ) {
- Field f = e.next();
- if(f.getName().equals(name))
- return f;
- }
-
- return null;
- }
-
- /** @return method object with given name and signature, or null
- */
- public Method containsMethod(String name, String signature) {
- for(Iterator<Method> e=method_vec.iterator(); e.hasNext();) {
- Method m = e.next();
- if(m.getName().equals(name) && m.getSignature().equals(signature))
- return m;
- }
-
- return null;
- }
-
- /**
- * Remove an attribute from this class.
- * @param a attribute to remove
- */
- public void removeAttribute(Attribute a) { attributesList.remove(a); }
- public void removeAnnotation(AnnotationGen a) {annotation_vec.remove(a);}
-
- /**
- * Remove a method from this class.
- * @param m method to remove
- */
- public void removeMethod(Method m) { method_vec.remove(m); }
-
- /** Replace given method with new one. If the old one does not exist
- * add the new_ method to the class anyway.
- */
- public void replaceMethod(Method old, Method new_) {
- if(new_ == null)
- throw new ClassGenException("Replacement method must not be null");
-
- int i = method_vec.indexOf(old);
-
- if (i < 0)
- method_vec.add(new_);
- else
- method_vec.set(i, new_);
- }
-
- /** Replace given field with new one. If the old one does not exist
- * add the new_ field to the class anyway.
- */
- public void replaceField(Field old, Field new_) {
- if(new_ == null)
- throw new ClassGenException("Replacement method must not be null");
-
- int i = field_vec.indexOf(old);
-
- if(i < 0)
- field_vec.add(new_);
- else
- field_vec.set(i, new_);
- }
-
- /**
- * Remove a field to this class.
- * @param f field to remove
- */
- public void removeField(Field f) { field_vec.remove(f); }
-
- public String getClassName() { return class_name; }
- public String getSuperclassName() { return super_class_name; }
- public String getFileName() { return file_name; }
-
- public void setClassName(String name) {
- class_name = name.replace('/', '.');
- class_name_index = cp.addClass(name);
- }
-
- public void setSuperclassName(String name) {
- super_class_name = name.replace('/', '.');
- superclass_name_index = cp.addClass(name);
- }
-
- public Method[] getMethods() {
- Method[] methods = new Method[method_vec.size()];
- method_vec.toArray(methods);
- return methods;
- }
-
- public void setMethods(Method[] methods) {
- method_vec.clear();
- for(int m=0; m<methods.length; m++)
- addMethod(methods[m]);
- }
-
- public void setFields(Field[] fs) {
- field_vec.clear();
- for(int m=0; m<fs.length; m++)
- addField(fs[m]);
- }
-
- public void setMethodAt(Method method, int pos) {
- method_vec.set(pos, method);
- }
-
- public Method getMethodAt(int pos) {
- return method_vec.get(pos);
- }
-
- public String[] getInterfaceNames() {
- int size = interface_vec.size();
- String[] interfaces = new String[size];
-
- interface_vec.toArray(interfaces);
- return interfaces;
- }
-
- public int[] getInterfaces() {
- int size = interface_vec.size();
- int[] interfaces = new int[size];
-
- for(int i=0; i < size; i++)
- interfaces[i] = cp.addClass(interface_vec.get(i));
-
- return interfaces;
- }
-
- public Field[] getFields() {
- Field[] fields = new Field[field_vec.size()];
- field_vec.toArray(fields);
- return fields;
- }
-
- public Attribute[] getAttributes() {
- Attribute[] attributes = new Attribute[attributesList.size()];
- attributesList.toArray(attributes);
- return attributes;
- }
-
- // J5TODO: Should we make calling unpackAnnotations() lazy and put it in here?
- public AnnotationGen[] getAnnotations() {
- AnnotationGen[] annotations = new AnnotationGen[annotation_vec.size()];
- annotation_vec.toArray(annotations);
- return annotations;
- }
-
- public ConstantPool getConstantPool() { return cp; }
- public void setConstantPool(ConstantPool constant_pool) {
- cp = constant_pool;
- }
-
- public void setClassNameIndex(int class_name_index) {
- this.class_name_index = class_name_index;
- class_name = cp.
- getConstantString(class_name_index, Constants.CONSTANT_Class).replace('/', '.');
- }
-
- public void setSuperclassNameIndex(int superclass_name_index) {
- this.superclass_name_index = superclass_name_index;
- super_class_name = cp.
- getConstantString(superclass_name_index, Constants.CONSTANT_Class).replace('/', '.');
- }
-
- public int getSuperclassNameIndex() { return superclass_name_index; }
-
- public int getClassNameIndex() { return class_name_index; }
-
- public Object clone() {
- try {
- return super.clone();
- } catch(CloneNotSupportedException e) {
- System.err.println(e);
- return null;
- }
- }
-
- // J5SUPPORT:
-
- /**
- * Returns true if this class represents an annotation type
- */
- public final boolean isAnnotation() {
- return (modifiers & Constants.ACC_ANNOTATION) != 0;
- }
-
- /**
- * Returns true if this class represents an enum type
- */
- public final boolean isEnum() {
- return (modifiers & Constants.ACC_ENUM) != 0;
- }
-
- /**
- * Calculate the SerialVersionUID for a class.
- */
- public long getSUID() {
- try {
- Field[] fields = getFields();
- Method[] methods = getMethods();
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DataOutputStream dos = new DataOutputStream(baos);
-
- // 1. classname
- dos.writeUTF(getClassName());
-
- // 2. classmodifiers: ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, and ACC_ABSTRACT
- int classmods = 0;
- classmods|=(isPublic()?Constants.ACC_PUBLIC:0);
- classmods|=(isFinal()?Constants.ACC_FINAL:0);
- classmods|=(isInterface()?Constants.ACC_INTERFACE:0);
-
- if (isAbstract()) {
- // if an interface then abstract is only set if it has methods
- if (isInterface()) {
- if (methods.length>0) classmods|=Constants.ACC_ABSTRACT;
- } else {
- classmods|=Constants.ACC_ABSTRACT;
- }
- }
-
- dos.writeInt(classmods);
-
- // 3. ordered list of interfaces
- List<FieldOrMethod> list = new ArrayList<FieldOrMethod>();
- String[] names = getInterfaceNames();
- if (names!=null) {
- Arrays.sort(names);
- for (int i = 0; i < names.length; i++) dos.writeUTF(names[i]);
- }
-
- // 4. ordered list of fields (ignoring private static and private transient fields):
- // (relevant modifiers are ACC_PUBLIC, ACC_PRIVATE,
- // ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE,
- // ACC_TRANSIENT)
- list.clear();
- for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
- if (!(field.isPrivate() && field.isStatic()) &&
- !(field.isPrivate() && field.isTransient())) list.add(field);
+
+ /**
+ * @return the (finally) built up Java class object.
+ */
+ public JavaClass getJavaClass() {
+ int[] interfaces = getInterfaces();
+ Field[] fields = getFields();
+ Method[] methods = getMethods();
+
+ Collection<Attribute> attributes = null;
+ if (annotationsList.size() == 0) {
+ attributes = attributesList;
+ } else {
+ // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations'
+ attributes = new ArrayList<Attribute>();
+ attributes.addAll(Utility.getAnnotationAttributes(cp, annotationsList));
+ attributes.addAll(attributesList);
}
- Collections.sort(list,new FieldComparator());
- int relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED |
- Constants.ACC_STATIC | Constants.ACC_FINAL | Constants.ACC_VOLATILE | Constants.ACC_TRANSIENT;
- for (Iterator<FieldOrMethod> iter = list.iterator(); iter.hasNext();) {
- Field f = (Field) iter.next();
- dos.writeUTF(f.getName());
- dos.writeInt(relevantFlags&f.getModifiers());
- dos.writeUTF(f.getType().getSignature());
+
+ // Must be last since the above calls may still add something to it
+ ConstantPool cp = this.cp.getFinalConstantPool();
+
+ return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor, modifiers, cp, interfaces, fields,
+ methods, attributes.toArray(new Attribute[attributes.size()]));// OPTIMIZE avoid toArray()?
+ }
+
+ /**
+ * Add an interface to this class, i.e., this class has to implement it.
+ *
+ * @param name interface to implement (fully qualified class name)
+ */
+ public void addInterface(String name) {
+ interface_vec.add(name);
+ }
+
+ /**
+ * Remove an interface from this class.
+ *
+ * @param name interface to remove (fully qualified name)
+ */
+ public void removeInterface(String name) {
+ interface_vec.remove(name);
+ }
+
+ /**
+ * @return major version number of class file
+ */
+ public int getMajor() {
+ return major;
+ }
+
+ /**
+ * Set major version number of class file, default value is 45 (JDK 1.1)
+ *
+ * @param major major version number
+ */
+ public void setMajor(int major) {
+ this.major = major;
+ }
+
+ /**
+ * Set minor version number of class file, default value is 3 (JDK 1.1)
+ *
+ * @param minor minor version number
+ */
+ public void setMinor(int minor) {
+ this.minor = minor;
+ }
+
+ /**
+ * @return minor version number of class file
+ */
+ public int getMinor() {
+ return minor;
+ }
+
+ /**
+ * Add an attribute to this class.
+ *
+ * @param a attribute to add
+ */
+ public void addAttribute(Attribute a) {
+ attributesList.add(a);
+ }
+
+ public void addAnnotation(AnnotationGen a) {
+ annotationsList.add(a);
+ }
+
+ /**
+ * Add a method to this class.
+ *
+ * @param m method to add
+ */
+ public void addMethod(Method m) {
+ method_vec.add(m);
+ }
+
+ /**
+ * Convenience method.
+ *
+ * Add an empty constructor to this class that does nothing but calling super().
+ *
+ * @param access rights for constructor
+ */
+ public void addEmptyConstructor(int access_flags) {
+ InstructionList il = new InstructionList();
+ il.append(InstructionConstants.THIS); // Push `this'
+ il.append(new InvokeInstruction(Constants.INVOKESPECIAL, cp.addMethodref(super_class_name, "<init>", "()V")));
+ il.append(InstructionConstants.RETURN);
+
+ MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>", class_name, il, cp);
+ mg.setMaxStack(1);
+ mg.setMaxLocals();
+ addMethod(mg.getMethod());
+ }
+
+ /**
+ * Add a field to this class.
+ *
+ * @param f field to add
+ */
+ public void addField(Field f) {
+ field_vec.add(f);
+ }
+
+ public boolean containsField(Field f) {
+ return field_vec.contains(f);
+ }
+
+ /**
+ * @return field object with given name, or null
+ */
+ public Field containsField(String name) {
+ for (Iterator<Field> e = field_vec.iterator(); e.hasNext();) {
+ Field f = e.next();
+ if (f.getName().equals(name))
+ return f;
}
- // some up front method processing: discover clinit, init and ordinary methods of interest:
- list.clear(); // now used for methods
- List<Method> ctors = new ArrayList<Method>();
- boolean hasClinit = false;
- for (int i = 0; i < methods.length; i++) {
- Method m = methods[i];
- boolean couldBeInitializer = m.getName().charAt(0)=='<';
- if (couldBeInitializer && m.getName().equals("<clinit>")) {
- hasClinit=true;
- } else if (couldBeInitializer && m.getName().equals("<init>")) {
- if (!m.isPrivate()) ctors.add(m);
- } else {
- if (!m.isPrivate()) list.add(m);
- }
+ return null;
+ }
+
+ /**
+ * @return method object with given name and signature, or null
+ */
+ public Method containsMethod(String name, String signature) {
+ for (Iterator<Method> e = method_vec.iterator(); e.hasNext();) {
+ Method m = e.next();
+ if (m.getName().equals(name) && m.getSignature().equals(signature))
+ return m;
}
- Collections.sort(ctors, new ConstructorComparator());
- Collections.sort(list, new MethodComparator());
-
-
- // 5. If a class initializer exists, write out the following:
- // 1. The name of the method, <clinit>.
- // 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, written as a 32-bit integer.
- // 3. The descriptor of the method, ()V.
- if (hasClinit) {
- dos.writeUTF("<clinit>");
- dos.writeInt(Modifier.STATIC);
- dos.writeUTF("()V");
- }
-
- // for methods and constructors:
- // ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED,
- // ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT
- relevantFlags =
- Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED |
- Constants.ACC_STATIC | Constants.ACC_FINAL | Constants.ACC_SYNCHRONIZED |
- Constants.ACC_NATIVE | Constants.ACC_ABSTRACT | Constants.ACC_STRICT;
-
- // 6. sorted non-private constructors
- for (Iterator<Method> iter = ctors.iterator(); iter.hasNext();) {
- Method m = iter.next();
- dos.writeUTF(m.getName()); // <init>
- dos.writeInt(relevantFlags & m.getModifiers());
- dos.writeUTF(m.getSignature().replace('/','.'));
+
+ return null;
+ }
+
+ /**
+ * Remove an attribute from this class.
+ *
+ * @param a attribute to remove
+ */
+ public void removeAttribute(Attribute a) {
+ attributesList.remove(a);
+ }
+
+ public void removeAnnotation(AnnotationGen a) {
+ annotationsList.remove(a);
+ }
+
+ /**
+ * Remove a method from this class.
+ *
+ * @param m method to remove
+ */
+ public void removeMethod(Method m) {
+ method_vec.remove(m);
+ }
+
+ /**
+ * Replace given method with new one. If the old one does not exist add the new_ method to the class anyway.
+ */
+ public void replaceMethod(Method old, Method new_) {
+ if (new_ == null)
+ throw new ClassGenException("Replacement method must not be null");
+
+ int i = method_vec.indexOf(old);
+
+ if (i < 0)
+ method_vec.add(new_);
+ else
+ method_vec.set(i, new_);
+ }
+
+ /**
+ * Replace given field with new one. If the old one does not exist add the new_ field to the class anyway.
+ */
+ public void replaceField(Field old, Field new_) {
+ if (new_ == null)
+ throw new ClassGenException("Replacement method must not be null");
+
+ int i = field_vec.indexOf(old);
+
+ if (i < 0)
+ field_vec.add(new_);
+ else
+ field_vec.set(i, new_);
+ }
+
+ /**
+ * Remove a field to this class.
+ *
+ * @param f field to remove
+ */
+ public void removeField(Field f) {
+ field_vec.remove(f);
+ }
+
+ public String getClassName() {
+ return class_name;
+ }
+
+ public String getSuperclassName() {
+ return super_class_name;
+ }
+
+ public String getFileName() {
+ return file_name;
+ }
+
+ public void setClassName(String name) {
+ class_name = name.replace('/', '.');
+ class_name_index = cp.addClass(name);
+ }
+
+ public void setSuperclassName(String name) {
+ super_class_name = name.replace('/', '.');
+ superclass_name_index = cp.addClass(name);
+ }
+
+ public Method[] getMethods() {
+ Method[] methods = new Method[method_vec.size()];
+ method_vec.toArray(methods);
+ return methods;
+ }
+
+ public void setMethods(Method[] methods) {
+ method_vec.clear();
+ for (int m = 0; m < methods.length; m++)
+ addMethod(methods[m]);
+ }
+
+ public void setFields(Field[] fs) {
+ field_vec.clear();
+ for (int m = 0; m < fs.length; m++)
+ addField(fs[m]);
+ }
+
+ public void setMethodAt(Method method, int pos) {
+ method_vec.set(pos, method);
+ }
+
+ public Method getMethodAt(int pos) {
+ return method_vec.get(pos);
+ }
+
+ public String[] getInterfaceNames() {
+ int size = interface_vec.size();
+ String[] interfaces = new String[size];
+
+ interface_vec.toArray(interfaces);
+ return interfaces;
+ }
+
+ public int[] getInterfaces() {
+ int size = interface_vec.size();
+ int[] interfaces = new int[size];
+
+ for (int i = 0; i < size; i++)
+ interfaces[i] = cp.addClass(interface_vec.get(i));
+
+ return interfaces;
+ }
+
+ public Field[] getFields() {
+ Field[] fields = new Field[field_vec.size()];
+ field_vec.toArray(fields);
+ return fields;
+ }
+
+ public Collection<Attribute> getAttributes() {
+ return attributesList;
+ }
+
+ // J5TODO: Should we make calling unpackAnnotations() lazy and put it in here?
+ public AnnotationGen[] getAnnotations() {
+ AnnotationGen[] annotations = new AnnotationGen[annotationsList.size()];
+ annotationsList.toArray(annotations);
+ return annotations;
+ }
+
+ public ConstantPool getConstantPool() {
+ return cp;
+ }
+
+ public void setConstantPool(ConstantPool constant_pool) {
+ cp = constant_pool;
+ }
+
+ public void setClassNameIndex(int class_name_index) {
+ this.class_name_index = class_name_index;
+ class_name = cp.getConstantString(class_name_index, Constants.CONSTANT_Class).replace('/', '.');
+ }
+
+ public void setSuperclassNameIndex(int superclass_name_index) {
+ this.superclass_name_index = superclass_name_index;
+ super_class_name = cp.getConstantString(superclass_name_index, Constants.CONSTANT_Class).replace('/', '.');
+ }
+
+ public int getSuperclassNameIndex() {
+ return superclass_name_index;
+ }
+
+ public int getClassNameIndex() {
+ return class_name_index;
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ System.err.println(e);
+ return null;
}
+ }
+
+ // J5SUPPORT:
+
+ /**
+ * Returns true if this class represents an annotation type
+ */
+ public final boolean isAnnotation() {
+ return (modifiers & Constants.ACC_ANNOTATION) != 0;
+ }
+
+ /**
+ * Returns true if this class represents an enum type
+ */
+ public final boolean isEnum() {
+ return (modifiers & Constants.ACC_ENUM) != 0;
+ }
+
+ /**
+ * Calculate the SerialVersionUID for a class.
+ */
+ public long getSUID() {
+ try {
+ Field[] fields = getFields();
+ Method[] methods = getMethods();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+
+ // 1. classname
+ dos.writeUTF(getClassName());
+
+ // 2. classmodifiers: ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, and ACC_ABSTRACT
+ int classmods = 0;
+ classmods |= (isPublic() ? Constants.ACC_PUBLIC : 0);
+ classmods |= (isFinal() ? Constants.ACC_FINAL : 0);
+ classmods |= (isInterface() ? Constants.ACC_INTERFACE : 0);
+
+ if (isAbstract()) {
+ // if an interface then abstract is only set if it has methods
+ if (isInterface()) {
+ if (methods.length > 0)
+ classmods |= Constants.ACC_ABSTRACT;
+ } else {
+ classmods |= Constants.ACC_ABSTRACT;
+ }
+ }
+
+ dos.writeInt(classmods);
+
+ // 3. ordered list of interfaces
+ List<FieldOrMethod> list = new ArrayList<FieldOrMethod>();
+ String[] names = getInterfaceNames();
+ if (names != null) {
+ Arrays.sort(names);
+ for (int i = 0; i < names.length; i++)
+ dos.writeUTF(names[i]);
+ }
+
+ // 4. ordered list of fields (ignoring private static and private transient fields):
+ // (relevant modifiers are ACC_PUBLIC, ACC_PRIVATE,
+ // ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE,
+ // ACC_TRANSIENT)
+ list.clear();
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ if (!(field.isPrivate() && field.isStatic()) && !(field.isPrivate() && field.isTransient()))
+ list.add(field);
+ }
+ Collections.sort(list, new FieldComparator());
+ int relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
+ | Constants.ACC_FINAL | Constants.ACC_VOLATILE | Constants.ACC_TRANSIENT;
+ for (Iterator<FieldOrMethod> iter = list.iterator(); iter.hasNext();) {
+ Field f = (Field) iter.next();
+ dos.writeUTF(f.getName());
+ dos.writeInt(relevantFlags & f.getModifiers());
+ dos.writeUTF(f.getType().getSignature());
+ }
+
+ // some up front method processing: discover clinit, init and ordinary methods of interest:
+ list.clear(); // now used for methods
+ List<Method> ctors = new ArrayList<Method>();
+ boolean hasClinit = false;
+ for (int i = 0; i < methods.length; i++) {
+ Method m = methods[i];
+ boolean couldBeInitializer = m.getName().charAt(0) == '<';
+ if (couldBeInitializer && m.getName().equals("<clinit>")) {
+ hasClinit = true;
+ } else if (couldBeInitializer && m.getName().equals("<init>")) {
+ if (!m.isPrivate())
+ ctors.add(m);
+ } else {
+ if (!m.isPrivate())
+ list.add(m);
+ }
+ }
+ Collections.sort(ctors, new ConstructorComparator());
+ Collections.sort(list, new MethodComparator());
+
+ // 5. If a class initializer exists, write out the following:
+ // 1. The name of the method, <clinit>.
+ // 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, written as a 32-bit integer.
+ // 3. The descriptor of the method, ()V.
+ if (hasClinit) {
+ dos.writeUTF("<clinit>");
+ dos.writeInt(Modifier.STATIC);
+ dos.writeUTF("()V");
+ }
- // 7. sorted non-private methods
- for (Iterator<FieldOrMethod> iter = list.iterator(); iter.hasNext();) {
- Method m = (Method) iter.next();
- dos.writeUTF(m.getName());
- dos.writeInt(relevantFlags & m.getModifiers());
- dos.writeUTF(m.getSignature().replace('/','.'));
+ // for methods and constructors:
+ // ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED,
+ // ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT
+ relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
+ | Constants.ACC_FINAL | Constants.ACC_SYNCHRONIZED | Constants.ACC_NATIVE | Constants.ACC_ABSTRACT
+ | Constants.ACC_STRICT;
+
+ // 6. sorted non-private constructors
+ for (Iterator<Method> iter = ctors.iterator(); iter.hasNext();) {
+ Method m = iter.next();
+ dos.writeUTF(m.getName()); // <init>
+ dos.writeInt(relevantFlags & m.getModifiers());
+ dos.writeUTF(m.getSignature().replace('/', '.'));
+ }
+
+ // 7. sorted non-private methods
+ for (Iterator<FieldOrMethod> iter = list.iterator(); iter.hasNext();) {
+ Method m = (Method) iter.next();
+ dos.writeUTF(m.getName());
+ dos.writeInt(relevantFlags & m.getModifiers());
+ dos.writeUTF(m.getSignature().replace('/', '.'));
+ }
+ dos.flush();
+ dos.close();
+ byte[] bs = baos.toByteArray();
+ MessageDigest md = MessageDigest.getInstance("SHA");
+ byte[] result = md.digest(bs);
+
+ long suid = 0L;
+ int pos = result.length > 8 ? 7 : result.length - 1; // use the bytes we have
+ while (pos >= 0) {
+ suid = suid << 8 | ((long) result[pos--] & 0xff);
+ }
+
+ // if it was definetly 8 everytime...
+ // long suid = ((long)(sha[0]&0xff) | (long)(sha[1]&0xff) << 8 |
+ // (long)(sha[2]&0xff) << 16 | (long)(sha[3]&0xff) << 24 |
+ // (long)(sha[4]&0xff) << 32 | (long)(sha[5]&0xff) << 40 |
+ // (long)(sha[6]&0xff) << 48 | (long)(sha[7]&0xff) << 56);
+ return suid;
+ } catch (Exception e) {
+ System.err.println("Unable to calculate suid for " + getClassName());
+ e.printStackTrace();
+ throw new RuntimeException("Unable to calculate suid for " + getClassName() + ": " + e.toString());
}
- dos.flush();
- dos.close();
- byte[] bs = baos.toByteArray();
- MessageDigest md = MessageDigest.getInstance("SHA");
- byte[] result = md.digest(bs);
-
- long suid = 0L;
- int pos = result.length>8?7:result.length-1; // use the bytes we have
- while (pos>=0) {
- suid = suid<<8 | ((long)result[pos--]&0xff);
- }
-
- // if it was definetly 8 everytime...
- // long suid = ((long)(sha[0]&0xff) | (long)(sha[1]&0xff) << 8 |
- // (long)(sha[2]&0xff) << 16 | (long)(sha[3]&0xff) << 24 |
- // (long)(sha[4]&0xff) << 32 | (long)(sha[5]&0xff) << 40 |
- // (long)(sha[6]&0xff) << 48 | (long)(sha[7]&0xff) << 56);
- return suid;
- } catch (Exception e) {
- System.err.println("Unable to calculate suid for "+getClassName());
- e.printStackTrace();
- throw new RuntimeException("Unable to calculate suid for "+getClassName()+": "+e.toString());
- }
- }
-
- private static class FieldComparator implements Comparator {
- public int compare(Object arg0, Object arg1) {
- return ((Field)arg0).getName().compareTo(((Field)arg1).getName());
+ }
+
+ private static class FieldComparator implements Comparator {
+ public int compare(Object arg0, Object arg1) {
+ return ((Field) arg0).getName().compareTo(((Field) arg1).getName());
}
- }
- private static class ConstructorComparator implements Comparator {
- public int compare(Object arg0, Object arg1) {
+ }
+
+ private static class ConstructorComparator implements Comparator {
+ public int compare(Object arg0, Object arg1) {
// can ignore the name...
- return ((Method)arg0).getSignature().compareTo(((Method)arg1).getSignature());
+ return ((Method) arg0).getSignature().compareTo(((Method) arg1).getSignature());
}
- }
- private static class MethodComparator implements Comparator {
- public int compare(Object arg0, Object arg1) {
- Method m1 = (Method)arg0;
- Method m2 = (Method)arg1;
+ }
+
+ private static class MethodComparator implements Comparator {
+ public int compare(Object arg0, Object arg1) {
+ Method m1 = (Method) arg0;
+ Method m2 = (Method) arg1;
int result = m1.getName().compareTo(m2.getName());
- if (result!=0) return result;
+ if (result != 0)
+ return result;
return m1.getSignature().compareTo(m2.getSignature());
}
- }
-
- public boolean hasAttribute(String attributeName) {
- for (Iterator<Attribute> iter = attributesList.iterator(); iter.hasNext();) {
- Attribute attr = iter.next();
- if (attr.getName().equals(attributeName)) return true;
- }
- return false;
- }
-
- public Attribute getAttribute(String attributeName) {
- for (Iterator<Attribute> iter = attributesList.iterator(); iter.hasNext();) {
+ }
+
+ public boolean hasAttribute(String attributeName) {
+ for (Attribute attr : attributesList) {
+ if (attr.getName().equals(attributeName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Attribute getAttribute(String attributeName) {
+ for (Iterator<Attribute> iter = attributesList.iterator(); iter.hasNext();) {
Attribute attr = iter.next();
- if (attr.getName().equals(attributeName)) return attr;
- }
+ if (attr.getName().equals(attributeName))
+ return attr;
+ }
return null;
- }
+ }
}