You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/08/30 22:49:57 UTC
[commons-bcel] branch master updated: Update signature scanning in
Utility to support TypeParameters. (#32)
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git
The following commit(s) were added to refs/heads/master by this push:
new 04a21a2 Update signature scanning in Utility to support TypeParameters. (#32)
04a21a2 is described below
commit 04a21a2dfb1c95563224b166c21d746d7dda18dc
Author: Mark Roberts <ma...@users.noreply.github.com>
AuthorDate: Fri Aug 30 15:49:53 2019 -0700
Update signature scanning in Utility to support TypeParameters. (#32)
* Update signature scanning in Utility to support TypeParameters.
* Add @since tag to new public method
---
.../org/apache/bcel/classfile/LocalVariable.java | 11 +-
.../java/org/apache/bcel/classfile/Utility.java | 369 +++++++++++++++------
src/main/java/org/apache/bcel/generic/Type.java | 19 +-
.../org/apache/bcel/classfile/UtilityTestCase.java | 24 ++
4 files changed, 303 insertions(+), 120 deletions(-)
diff --git a/src/main/java/org/apache/bcel/classfile/LocalVariable.java b/src/main/java/org/apache/bcel/classfile/LocalVariable.java
index 1d68dd8..5c8fa72 100644
--- a/src/main/java/org/apache/bcel/classfile/LocalVariable.java
+++ b/src/main/java/org/apache/bcel/classfile/LocalVariable.java
@@ -26,15 +26,24 @@ import org.apache.bcel.Constants;
/**
* This class represents a local variable within a method. It contains its
- * scope, name, signature and index on the method's frame.
+ * scope, name, signature and index on the method's frame. It is used both
+ * to represent an element of the LocalVariableTable as well as an element
+ * of the LocalVariableTypeTable. The nomenclature used here may be a bit confusing;
+ * while the two items have the same layout in a class file, a LocalVariableTable
+ * attribute contains a descriptor_index, not a signature_index. The
+ * LocalVariableTypeTable attribute does have a signature_index.
+ * @see org.apache.bcel.classfile.Utility for more details on the difference.
*
* @see LocalVariableTable
+ * @see LocalVariableTypeTable
*/
public final class LocalVariable implements Cloneable, Node, Constants {
private int start_pc; // Range in which the variable is valid
private int length;
private int name_index; // Index in constant pool of variable name
+ // Technically, a decscriptor_index for a local variable table entry
+ // and a signature_index for a local variable type table entry.
private int signature_index; // Index of variable signature
private int index; /* Variable is `index'th local variable on
* this method's frame.
diff --git a/src/main/java/org/apache/bcel/classfile/Utility.java b/src/main/java/org/apache/bcel/classfile/Utility.java
index 4fca43f..5072c34 100644
--- a/src/main/java/org/apache/bcel/classfile/Utility.java
+++ b/src/main/java/org/apache/bcel/classfile/Utility.java
@@ -48,23 +48,23 @@ public abstract class Utility {
return tl.get().intValue();
}
-
private static void wrap( final ThreadLocal<Integer> tl, final int value ) {
tl.set(Integer.valueOf(value));
}
+ /* How many chars have been consumed
+ * during parsing in typeSignatureToString().
+ * Read by methodSignatureToString().
+ * Set by side effect, but only internally.
+ */
private static ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() {
-
@Override
protected Integer initialValue() {
return Integer.valueOf(0);
}
- };/* How many chars have been consumed
- * during parsing in signatureToString().
- * Read by methodSignatureToString().
- * Set by side effect,but only internally.
- */
- private static boolean wide = false; /* The `WIDE' instruction is used in the
+ };
+
+ /* 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
@@ -73,6 +73,7 @@ public abstract class Utility {
* following byte to form a
* 16-bit value.
*/
+ private static boolean wide = false;
/**
@@ -465,6 +466,21 @@ public abstract class Utility {
/**
+ * 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 String compactClassName( final String str, final boolean chopit ) {
+ return compactClassName(str, "java.lang.", chopit);
+ }
+
+
+ /**
* 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.
@@ -472,7 +488,7 @@ public abstract class Utility {
*
* @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
+ * @param chopit flag that determines whether chopping is executed or not
* @return Compacted class name
*/
public static String compactClassName( String str, final String prefix, final boolean chopit ) {
@@ -489,21 +505,6 @@ public abstract class Utility {
/**
- * 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 String compactClassName( final String str, final boolean chopit ) {
- return compactClassName(str, "java.lang.", chopit);
- }
-
-
- /**
* @return `flag' with bit `i' set to 1
*/
public static int setBit( final int flag, final int i ) {
@@ -558,9 +559,11 @@ public abstract class Utility {
/**
+ * Converts argument list portion of method signature to string with all class names compacted.
+ *
* @param signature Method signature
- * @return Array of argument types
- * @throws ClassFormatException
+ * @return String Array of argument types
+ * @throws ClassFormatException
*/
public static String[] methodSignatureArgumentTypes( final String signature )
throws ClassFormatException {
@@ -569,22 +572,25 @@ public abstract class Utility {
/**
+ * Converts argument list portion of method signature to string.
+ *
* @param signature Method signature
- * @param chopit Shorten class names ?
- * @return Array of argument types
- * @throws ClassFormatException
+ * @param chopit flag that determines whether chopping is executed or not
+ * @return String Array of argument types
+ * @throws ClassFormatException
*/
public static String[] methodSignatureArgumentTypes( final String signature, final boolean chopit )
throws ClassFormatException {
final List<String> vec = new ArrayList<>();
int index;
- try { // Read all declarations between for `(' and `)'
- if (signature.charAt(0) != '(') {
+ try {
+ // Skip any type arguments to read argument declarations between `(' and `)'
+ index = signature.indexOf('(') + 1;
+ if (index <= 0) {
throw new ClassFormatException("Invalid method signature: " + signature);
}
- index = 1; // current string position
while (signature.charAt(index) != ')') {
- vec.add(signatureToString(signature.substring(index), chopit));
+ vec.add(typeSignatureToString(signature.substring(index), chopit));
//corrected concurrent private static field acess
index += unwrap(consumed_chars); // update position
}
@@ -596,9 +602,11 @@ public abstract class Utility {
/**
+ * Converts return type portion of method signature to string with all class names compacted.
+ *
* @param signature Method signature
- * @return return type of method
- * @throws ClassFormatException
+ * @return String representation of method return type
+ * @throws ClassFormatException
*/
public static String methodSignatureReturnType( final String signature ) throws ClassFormatException {
return methodSignatureReturnType(signature, true);
@@ -606,10 +614,12 @@ public abstract class Utility {
/**
+ * Converts return type portion of method signature to string.
+ *
* @param signature Method signature
- * @param chopit Shorten class names ?
- * @return return type of method
- * @throws ClassFormatException
+ * @param chopit flag that determines whether chopping is executed or not
+ * @return String representation of method return type
+ * @throws ClassFormatException
*/
public static String methodSignatureReturnType( final String signature, final boolean chopit ) throws ClassFormatException {
int index;
@@ -617,7 +627,10 @@ public abstract class Utility {
try {
// Read return type after `)'
index = signature.lastIndexOf(')') + 1;
- type = signatureToString(signature.substring(index), chopit);
+ if (index <= 0) {
+ throw new ClassFormatException("Invalid method signature: " + signature);
+ }
+ type = typeSignatureToString(signature.substring(index), chopit);
} catch (final StringIndexOutOfBoundsException e) { // Should never occur
throw new ClassFormatException("Invalid method signature: " + signature, e);
}
@@ -628,9 +641,9 @@ public abstract class Utility {
/**
* Converts method signature to string with all class names compacted.
*
- * @param signature to convert
- * @param name of method
- * @param access flags of method
+ * @param signature to convert
+ * @param name of method
+ * @param access flags of method
* @return Human readable signature
*/
public static String methodSignatureToString( final String signature, final String name, final String access ) {
@@ -638,45 +651,32 @@ public abstract class Utility {
}
+ /**
+ * Converts method signature to string.
+ *
+ * @param signature to convert
+ * @param name of method
+ * @param access flags of method
+ * @param chopit flag that determines whether chopping is executed or not
+ * @return Human readable signature
+ */
public static String methodSignatureToString( final String signature, final String name, final String access, final boolean chopit ) {
return methodSignatureToString(signature, name, access, chopit, null);
}
/**
- * A returntype signature represents the return value from a method.
- * It is a series of bytes in the following grammar:
- *
- * <pre>
- * <return_signature> ::= <field_type> | V
- * </pre>
- *
- * The character V indicates that the method returns no value. Otherwise, the
- * signature indicates the type of the return value.
- * An argument signature represents an argument passed to a method:
- *
- * <pre>
- * <argument_signature> ::= <field_type>
- * </pre>
- *
- * A method signature represents the arguments that the method expects, and
- * the value that it returns.
- * <pre>
- * <method_signature> ::= (<arguments_signature>) <return_signature>
- * <arguments_signature>::= <argument_signature>*
- * </pre>
- *
- * This method converts such a string into a Java type declaration like
+ * This method converts a method signature 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
- * @param chopit
- * @param vars
+ * @param chopit flag that determines whether chopping is executed or not
+ * @param vars the LocalVariableTable for the method
* @return Java type declaration
- * @throws ClassFormatException
+ * @throws ClassFormatException
*/
public static String methodSignatureToString( final String signature, final String name,
final String access, final boolean chopit, final LocalVariableTable vars ) throws ClassFormatException {
@@ -684,13 +684,14 @@ public abstract class Utility {
String type;
int index;
int var_index = access.contains("static") ? 0 : 1;
- try { // Read all declarations between for `(' and `)'
- if (signature.charAt(0) != '(') {
+ try {
+ // Skip any type arguments to read argument declarations between `(' and `)'
+ index = signature.indexOf('(') + 1;
+ if (index <= 0) {
throw new ClassFormatException("Invalid method signature: " + signature);
}
- index = 1; // current string position
while (signature.charAt(index) != ')') {
- final String param_type = signatureToString(signature.substring(index), chopit);
+ final String param_type = typeSignatureToString(signature.substring(index), chopit);
buf.append(param_type);
if (vars != null) {
final LocalVariable l = vars.getLocalVariable(var_index, 0);
@@ -711,10 +712,11 @@ public abstract class Utility {
}
index++; // update position
// Read return type after `)'
- type = signatureToString(signature.substring(index), chopit);
+ type = typeSignatureToString(signature.substring(index), chopit);
} catch (final StringIndexOutOfBoundsException e) { // Should never occur
throw new ClassFormatException("Invalid method signature: " + signature, e);
}
+ // ignore any throws information in the signature
if (buf.length() > 1) {
buf.setLength(buf.length() - 2);
}
@@ -724,7 +726,6 @@ public abstract class Utility {
}
- // Guess what this does
private static int pow2( final int n ) {
return 1 << n;
}
@@ -762,10 +763,40 @@ public abstract class Utility {
/**
- * Converts signature to string with all class names compacted.
+ * WARNING:
*
- * @param signature to convert
- * @return Human readable signature
+ * There is some nomenclature confusion through much of the BCEL code base with
+ * respect to the terms Descriptor and Signature. For the offical definitions see:
+ *
+ * @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3">
+ * Descriptors in The Java Virtual Machine Specification</a>
+ *
+ * @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1">
+ * Signatures in The Java Virtual Machine Specification</a>
+ *
+ * In brief, a descriptor is a string representing the type of a field or method.
+ * Signatures are similar, but more complex. Signatures are used to encode declarations
+ * written in the Java programming language that use types outside the type system of the
+ * Java Virtual Machine. They are used to describe the type of any class, interface,
+ * constructor, method or field whose declaration uses type variables or parameterized types.
+ *
+ * To parse a descriptor, call typeSignatureToString.
+ * To parse a signature, call signatureToString.
+ *
+ * Note that if the signature string is a single, non-generic item, the call to
+ * signatureToString reduces to a call to typeSignatureToString.
+ * Also note, that if you only wish to parse the first item in a longer signature
+ * string, you should call typeSignatureToString directly.
+ */
+
+
+ /**
+ * Converts a signature to a string with all class names compacted.
+ * Class, Method and Type signatures are supported.
+ * Enum and Interface signatures are not supported.
+ *
+ * @param signature signature to convert
+ * @return String containg human readable signature
*/
public static String signatureToString( final String signature ) {
return signatureToString(signature, true);
@@ -773,40 +804,158 @@ public abstract class Utility {
/**
- * The field signature represents the value of an argument to a function or
- * the value of a variable. It is a series of bytes generated by the
- * following grammar:
+ * Converts a signature to a string.
+ * Class, Method and Type signatures are supported.
+ * Enum and Interface signatures are not supported.
+ *
+ * @param signature signature to convert
+ * @param chopit flag that determines whether chopping is executed or not
+ * @return String containg human readable signature
+ */
+ public static String signatureToString( final String signature, final boolean chopit ) {
+ String type = "";
+ String typeParams = "";
+ int index = 0;
+ if (signature.charAt(0) == '<') {
+ // we have type paramters
+ typeParams = typeParamTypesToString(signature, chopit);
+ index += unwrap(consumed_chars); // update position
+ }
+ if (signature.charAt(index) == '(') {
+ // We have a Method signature.
+ // add types of arguments
+ type = typeParams + typeSignaturesToString(signature.substring(index), chopit, ')');
+ index += unwrap(consumed_chars); // update position
+ // add return type
+ type = type + typeSignatureToString(signature.substring(index), chopit);
+ index += unwrap(consumed_chars); // update position
+ // ignore any throws information in the signature
+ return type;
+ } else {
+ // Could be Class or Type...
+ type = typeSignatureToString(signature.substring(index), chopit);
+ index += unwrap(consumed_chars); // update position
+ if ((typeParams.length() == 0) && (index == signature.length())) {
+ // We have a Type signature.
+ return type;
+ }
+ // We have a Class signature.
+ StringBuilder typeClass = new StringBuilder(typeParams);
+ typeClass.append(" extends ");
+ typeClass.append(type);
+ if (index < signature.length()) {
+ typeClass.append(" implements ");
+ typeClass.append(typeSignatureToString(signature.substring(index), chopit));
+ index += unwrap(consumed_chars); // update position
+ }
+ while (index < signature.length()) {
+ typeClass.append(", ");
+ typeClass.append(typeSignatureToString(signature.substring(index), chopit));
+ index += unwrap(consumed_chars); // update position
+ }
+ return typeClass.toString();
+ }
+ }
+
+
+ /**
+ * Converts a type parameter list signature to a string.
*
- * <PRE>
- * <field_signature> ::= <field_type>
- * <field_type> ::= <base_type>|<object_type>|<array_type>
- * <base_type> ::= B|C|D|F|I|J|S|Z
- * <object_type> ::= L<fullclassname>;
- * <array_type> ::= [<field_type>
+ * @param signature signature to convert
+ * @param chopit flag that determines whether chopping is executed or not
+ * @return String containg human readable signature
+ */
+ private static String typeParamTypesToString( final String signature, final boolean chopit ) {
+ // The first character is guranteed to be '<'
+ StringBuilder typeParams = new StringBuilder("<");
+ int index = 1; // skip the '<'
+ // get the first TypeParameter
+ typeParams.append(typeParamTypeToString(signature.substring(index), chopit));
+ index += unwrap(consumed_chars); // update position
+ // are there more TypeParameters?
+ while (signature.charAt(index) != '>') {
+ typeParams.append(", ");
+ typeParams.append(typeParamTypeToString(signature.substring(index), chopit));
+ index += unwrap(consumed_chars); // update position
+ }
+ wrap(consumed_chars, index + 1); // account for the '>' char
+ return typeParams.append(">").toString();
+ }
+
+
+ /**
+ * Converts a type parameter signature to a string.
*
- * The meaning of the base types is as follows:
- * B byte signed byte
- * C char character
- * D double double precision IEEE float
- * F float single precision IEEE float
- * I int integer
- * J long long integer
- * L<fullclassname>; ... an object of the given class
- * S short signed short
- * Z boolean true or false
- * [<field sig> ... array
- * </PRE>
+ * @param signature signature to convert
+ * @param chopit flag that determines whether chopping is executed or not
+ * @return String containg human readable signature
+ */
+ private static String typeParamTypeToString( final String signature, final boolean chopit ) {
+ int index = signature.indexOf(':');
+ if (index <= 0) {
+ throw new ClassFormatException("Invalid type parameter signature: " + signature);
+ }
+ // get the TypeParameter identifier
+ StringBuilder typeParam = new StringBuilder(signature.substring(0, index));
+ index++; // account for the ':'
+ if (signature.charAt(index) != ':') {
+ // we have a class bound
+ typeParam.append(" extends ");
+ typeParam.append(typeSignatureToString(signature.substring(index), chopit));
+ index += unwrap(consumed_chars); // update position
+ }
+ // look for interface bounds
+ while (signature.charAt(index) == ':') {
+ index++; // skip over the ':'
+ typeParam.append(" & ");
+ typeParam.append(typeSignatureToString(signature.substring(index), chopit));
+ index += unwrap(consumed_chars); // update position
+ }
+ wrap(consumed_chars, index);
+ return typeParam.toString();
+ }
+
+
+ /**
+ * Converts a list of type signatures to a string.
*
- * 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 signature to convert
+ * @param chopit flag that determines whether chopping is executed or not
+ * @param term character indicating the end of the list
+ * @return String containg human readable signature
+ */
+ private static String typeSignaturesToString( final String signature, final boolean chopit, final char term ) {
+ // The first character will be an 'open' that matches the 'close' contained in term.
+ StringBuilder typeList = new StringBuilder(signature.substring(0, 1));
+ int index = 1; // skip the 'open' character
+ // get the first Type in the list
+ if (signature.charAt(index) != term) {
+ typeList.append(typeSignatureToString(signature.substring(index), chopit));
+ index += unwrap(consumed_chars); // update position
+ }
+ // are there more types in the list?
+ while (signature.charAt(index) != term) {
+ typeList.append(", ");
+ typeList.append(typeSignatureToString(signature.substring(index), chopit));
+ index += unwrap(consumed_chars); // update position
+ }
+ wrap(consumed_chars, index + 1); // account for the term char
+ return typeList.append(term).toString();
+ }
+
+
+ /**
*
- * @param signature Class signature
- * @param chopit Flag that determines whether chopping is executed or not
- * @return Java type declaration
+ * This method converts a type signature string into a Java type declaration such as
+ * `String[]' and throws a `ClassFormatException' when the parsed type is invalid.
+ *
+ * @param signature type signature
+ * @param chopit flag that determines whether chopping is executed or not
+ * @return string containing human readable type signature
* @throws ClassFormatException
+ * @since 6.4.0
*/
- public static String signatureToString( final String signature, final boolean chopit ) {
+ public static String typeSignatureToString( final String signature, final boolean chopit ) throws ClassFormatException {
//corrected concurrent private static field acess
wrap(consumed_chars, 1); // This is the default, read just one char like `B'
try {
@@ -826,7 +975,7 @@ public abstract class Utility {
case 'T': { // TypeVariableSignature
final int index = signature.indexOf(';'); // Look for closing `;'
if (index < 0) {
- throw new ClassFormatException("Invalid signature: " + signature);
+ throw new ClassFormatException("Invalid type variable signature: " + signature);
}
//corrected concurrent private static field acess
wrap(consumed_chars, index + 1); // "Tblabla;" `T' and `;' are removed
@@ -886,7 +1035,7 @@ public abstract class Utility {
type.append("?");
consumed_chars++;
} else {
- type.append(signatureToString(signature.substring(consumed_chars), chopit));
+ type.append(typeSignatureToString(signature.substring(consumed_chars), chopit));
// update our consumed count by the number of characters the for type argument
consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars;
wrap(Utility.consumed_chars, consumed_chars);
@@ -907,7 +1056,7 @@ public abstract class Utility {
type.append("?");
consumed_chars++;
} else {
- type.append(signatureToString(signature.substring(consumed_chars), chopit));
+ type.append(typeSignatureToString(signature.substring(consumed_chars), chopit));
// update our consumed count by the number of characters the for type argument
consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars;
wrap(Utility.consumed_chars, consumed_chars);
@@ -923,7 +1072,7 @@ public abstract class Utility {
type.append(".");
// convert SimpleClassTypeSignature to fake ClassTypeSignature
// and then recurse to parse it
- type.append(signatureToString("L" + signature.substring(consumed_chars+1), chopit));
+ type.append(typeSignatureToString("L" + signature.substring(consumed_chars+1), chopit));
// update our consumed count by the number of characters the for type argument
// note that this count includes the "L" we added, but that is ok
// as it accounts for the "." we didn't consume
@@ -953,7 +1102,7 @@ public abstract class Utility {
}
consumed_chars = n; // Remember value
// The rest of the string denotes a `<field_type>'
- type = signatureToString(signature.substring(n), chopit);
+ type = typeSignatureToString(signature.substring(n), chopit);
//corrected concurrent private static field acess
//Utility.consumed_chars += consumed_chars; is replaced by:
final int _temp = unwrap(Utility.consumed_chars) + consumed_chars;
diff --git a/src/main/java/org/apache/bcel/generic/Type.java b/src/main/java/org/apache/bcel/generic/Type.java
index d6e1fb8..3b9e604 100644
--- a/src/main/java/org/apache/bcel/generic/Type.java
+++ b/src/main/java/org/apache/bcel/generic/Type.java
@@ -211,9 +211,8 @@ public abstract class Type {
wrap(consumed_chars, _temp);
return new ArrayType(t, dim);
} else { // type == T_REFERENCE
- // Utility.signatureToString understands how to parse
- // generic types.
- final String parsedSignature = Utility.signatureToString(signature, false);
+ // Utility.typeSignatureToString understands how to parse generic types.
+ final String parsedSignature = Utility.typeSignatureToString(signature, false);
wrap(consumed_chars, parsedSignature.length() + 2); // "Lblabla;" `L' and `;' are removed
return ObjectType.getInstance(parsedSignature.replace('/', '.'));
}
@@ -246,11 +245,12 @@ public abstract class Type {
final List<Type> vec = new ArrayList<>();
int index;
Type[] types;
- try { // Read all declarations between for `(' and `)'
- if (signature.charAt(0) != '(') {
+ try {
+ // Skip any type arguments to read argument declarations between `(' and `)'
+ index = signature.indexOf('(') + 1;
+ if (index <= 0) {
throw new ClassFormatException("Invalid method signature: " + signature);
}
- index = 1; // current string position
while (signature.charAt(index) != ')') {
vec.add(getType(signature.substring(index)));
//corrected concurrent private static field acess
@@ -348,11 +348,12 @@ public abstract class Type {
static int getArgumentTypesSize( final String signature ) {
int res = 0;
int index;
- try { // Read all declarations between for `(' and `)'
- if (signature.charAt(0) != '(') {
+ try {
+ // Skip any type arguments to read argument declarations between `(' and `)'
+ index = signature.indexOf('(') + 1;
+ if (index <= 0) {
throw new ClassFormatException("Invalid method signature: " + signature);
}
- index = 1; // current string position
while (signature.charAt(index) != ')') {
final int coded = getTypeSize(signature.substring(index));
res += size(coded);
diff --git a/src/test/java/org/apache/bcel/classfile/UtilityTestCase.java b/src/test/java/org/apache/bcel/classfile/UtilityTestCase.java
index 8058657..44b923f 100644
--- a/src/test/java/org/apache/bcel/classfile/UtilityTestCase.java
+++ b/src/test/java/org/apache/bcel/classfile/UtilityTestCase.java
@@ -32,6 +32,7 @@ public class UtilityTestCase extends TestCase {
assertEquals("generic signature",
"java.nio.file.attribute.FileAttribute<?>[]",
Utility.signatureToString("[Ljava/nio/file/attribute/FileAttribute<*>;"));
+
// tests for BCEL-286
assertEquals("generic signature",
"boofcv.alg.tracker.tld.TldTracker<boofcv.struct.image.ImageGray<boofcv.struct.image.GrayU8>, boofcv.struct.image.GrayI<boofcv.struct.image.GrayU8>>",
@@ -42,5 +43,28 @@ public class UtilityTestCase extends TestCase {
assertEquals("generic signature",
"com.jme3.util.IntMap<T>.IntMapIterator",
Utility.signatureToString("Lcom/jme3/util/IntMap<TT;>.IntMapIterator;"));
+
+ // tests for BCEL-279
+ assertEquals("type parameters signature",
+ "<T extends java.lang.Object>(com.google.common.io.ByteProcessor<T>, int)T",
+ Utility.signatureToString("<T:Ljava/lang/Object;>(Lcom/google/common/io/ByteProcessor<TT;>;I)TT;", false));
+ assertEquals("type parameters signature",
+ "<T extends Object>(com.google.common.io.ByteProcessor<T>, int)T",
+ Utility.signatureToString("<T:Ljava/lang/Object;>(Lcom/google/common/io/ByteProcessor<TT;>;I)TT;", true));
+ assertEquals("type parameters signature",
+ "<M extends java.lang.reflect.AccessibleObject & java.lang.reflect.Member>(M)void",
+ Utility.signatureToString("<M:Ljava/lang/reflect/AccessibleObject;:Ljava/lang/reflect/Member;>(TM;)V"));
+ assertEquals("type parameters signature",
+ "<K1 extends K, V1 extends V>()com.google.common.cache.Weigher<K1, V1>",
+ Utility.signatureToString("<K1:TK;V1:TV;>()Lcom/google/common/cache/Weigher<TK1;TV1;>;"));
+ assertEquals("type parameters signature",
+ "<K1 extends K, V1 extends V>(com.google.common.cache.Weigher<? super K1, ? super V1>)com.google.common.cache.CacheBuilder<K1, V1>",
+ Utility.signatureToString("<K1:TK;V1:TV;>(Lcom/google/common/cache/Weigher<-TK1;-TV1;>;)Lcom/google/common/cache/CacheBuilder<TK1;TV1;>;"));
+ assertEquals("class signature",
+ "<N extends java.lang.Object, E extends java.lang.Object> extends java.lang.Object implements com.google.common.graph.Network<N, E>",
+ Utility.signatureToString("<N:Ljava/lang/Object;E:Ljava/lang/Object;>Ljava/lang/Object;Lcom/google/common/graph/Network<TN;TE;>;", false));
+ assertEquals("class signature",
+ "<K extends Object, V extends Object> extends Object",
+ Utility.signatureToString("<K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;"));
}
}