diff --git a/src/main/java/org/jboss/jandex/GenericSignatureParser.java b/src/main/java/org/jboss/jandex/GenericSignatureParser.java index 5f0c3b03..56ca752b 100644 --- a/src/main/java/org/jboss/jandex/GenericSignatureParser.java +++ b/src/main/java/org/jboss/jandex/GenericSignatureParser.java @@ -1,11 +1,12 @@ package org.jboss.jandex; import java.io.BufferedReader; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @author Jason T. Greene @@ -95,10 +96,11 @@ class GenericSignatureParser { * ReferenceTypeSignature * */ - private static WildcardType UNBOUNDED_WILDCARD = new WildcardType(null, null); + private static WildcardType UNBOUNDED_WILDCARD = new WildcardType(null, true); private String signature; private int pos; private NameTable names; + private Map typeParameters = new HashMap(); GenericSignatureParser() { names = new NameTable(); @@ -243,6 +245,8 @@ private void expect(char c) { MethodSignature parseMethodSignature(String signature) { this.signature = signature; + this.typeParameters.clear(); + this.pos = 0; Type[] typeParameters = parseTypeParameters(); @@ -266,9 +270,18 @@ MethodSignature parseMethodSignature(String signature) { } private Type parseClassTypeSignature() { + int end = scanReferenceTypeEnd(); + String signature = this.signature; + NameTable.Slice slice = names.createSlice(signature, pos, end); + Type type = names.getType(slice); + + if (type != null) { + this.pos = end; + return type; + } + DotName name = parseName(); Type[] types = parseTypeArguments(); - Type type = null; if (types.length > 0) { type = new ParameterizedType(name, types, null); @@ -277,8 +290,8 @@ private Type parseClassTypeSignature() { // Suffix while (signature.charAt(pos) == '.') { int mark = ++pos; - int end = advanceNameEnd(); - name = names.wrap(name, signature.substring(mark, end), true); + int suffixEnd = advanceNameEnd(); + name = names.wrap(name, signature.substring(mark, suffixEnd), true); types = parseTypeArguments(); // A suffix is a parameterized type if it has typeParameters or it's owner is a parameterized type @@ -291,10 +304,34 @@ private Type parseClassTypeSignature() { type = new ParameterizedType(name, types, type); } } - advancePast(';'); - return type != null ? type : new ClassType(name); + this.pos++; // ; + type = type != null ? type : new ClassType(name); + names.storeType(slice, type); + return type; } + private int scanReferenceTypeEnd() { + String signature = this.signature; + int i = pos; + int open = 0; + + while (i < signature.length()) { + switch (signature.charAt(i++)) { + case '<': + open++; + break; + case '>': + open--; + break; + case ';': + if (open == 0) { + return i; + } + } + } + + throw new IllegalArgumentException("Invalid signature, class type is missing terminator"); + } private Type[] parseTypeArguments() { return parseTypeList(true); @@ -310,6 +347,15 @@ private Type[] parseTypeList(boolean argument) { return Type.EMPTY_ARRAY; } pos++; + + int end = scanListEnd(); + NameTable.Slice slice = names.createSlice(signature, pos, end); + Type[] typeList = names.getTypeList(slice); + if (typeList != null) { + this.pos = end; + return typeList; + } + List types = new ArrayList(); for (;;) { Type t = argument ? parseTypeArgument() : parseTypeParameter(); @@ -318,7 +364,29 @@ private Type[] parseTypeList(boolean argument) { } types.add(t); } - return types.toArray(new Type[types.size()]); + return names.storeTypeList(slice, types.toArray(new Type[types.size()])); + } + + private int scanListEnd() { + String signature = this.signature; + int i = pos; + int open = 0; + + while (i < signature.length()) { + switch (signature.charAt(i++)) { + case '<': + open++; + break; + case '>': + if (--open < 0) { + return i; + } + + break; + } + } + + throw new IllegalArgumentException("Invalid signature, class type is missing terminator"); } private Type parseTypeArgument() { @@ -329,12 +397,10 @@ private Type parseTypeArgument() { case '*': return UNBOUNDED_WILDCARD; case '-': { - Type lowerBound = parseReferenceType(); - return new WildcardType(lowerBound, null); + return parseWildCard(false); } case '+': { - Type upperBound = parseReferenceType(); - return new WildcardType(null, upperBound); + return parseWildCard(true); } default: pos--; @@ -342,16 +408,36 @@ private Type parseTypeArgument() { } } + private Type parseWildCard(boolean isExtends) { + int end = scanReferenceTypeEnd(); + NameTable.Slice slice = names.createSlice(signature, pos, end); + Type type = names.getType(slice); + if (type != null) { + pos = end; + return type; + } + + Type bound = parseReferenceType(); + return names.storeType(slice, new WildcardType(bound, isExtends)); + } + private Type parseTypeParameter() { int start = pos; + String signature = this.signature; if (signature.charAt(start) == '>') { pos++; return null; } + int end = scanTypeParameterEnd(); + NameTable.Slice slice = names.createSlice(signature, start, end); + TypeVariable type = (TypeVariable) names.getType(slice); + if (type != null) { + return type; + } + int bound = advancePast(':'); - String signature = this.signature; String name = names.intern(signature.substring(start, bound)); ArrayList bounds = new ArrayList(); @@ -367,7 +453,45 @@ private Type parseTypeParameter() { bounds.add(parseReferenceType()); } - return new TypeVariable(name, bounds.toArray(new Type[bounds.size()])); + type = new TypeVariable(name, bounds.toArray(new Type[bounds.size()])); + names.storeType(slice, type); + typeParameters.put(type.identifier(), type); + return type; + } + + private int scanTypeParameterEnd() { + String signature = this.signature; + int i = pos; + int open = 0; + + while (i < signature.length() - 1) { + char c = signature.charAt(i++); + switch (c) { + case ':': { + char peek = signature.charAt(i); + if (peek != 'T' && peek != 'L' && peek != '[') { + return i; + } + break; + } + case '<': + open++; + break; + case '>': + open--; + break; + case ';': + if (open == 0) { + char peek = signature.charAt(i); + if (peek != ':') { + return i; + } + } + break; + } + } + + throw new IllegalArgumentException("Invalid signature, class type is missing terminator"); } private Type parseReturnType() { @@ -384,18 +508,39 @@ private Type parseReferenceType() { char c = signature.charAt(mark); switch (c) { case 'T': - String name = names.intern(signature.substring(mark + 1, advancePast(';'))); - return new TypeVariable(name); + return parseTypeVariable(); case 'L': return parseClassTypeSignature(); case '[': - int last = advanceNot('['); - return new ArrayType(parseJavaType(), last - mark); + return parseArrayType(); default: return null; } } + private Type parseArrayType() { + int mark = this.pos; + int end = scanReferenceTypeEnd(); + NameTable.Slice slice = names.createSlice(signature, mark, end); + + Type type = names.getType(slice); + if (type != null) { + pos = end; + return type; + } + + int last = advanceNot('['); + type = new ArrayType(parseJavaType(), last - mark); + names.storeType(slice, type); + return type; + } + + private Type parseTypeVariable() { + String name = names.intern(signature.substring(pos + 1, advancePast(';'))); + Type type = typeParameters.get(name); + return type == null ? new TypeVariable(name) : type; + } + private Type parseJavaType() { Type type = PrimitiveType.decode(signature.charAt(pos)); if (type != null) { @@ -450,32 +595,32 @@ private int advanceNameEnd() { public static void main(String[] args) throws IOException { GenericSignatureParser parser = new GenericSignatureParser(); -// MethodSignature sig1 = parser.parseMethodSignature("(Ljava/lang/Class;)Ljava/lang/Class<+TU;>;"); -// MethodSignature sig2 = parser.parseMethodSignature("(Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/Map;"); -// MethodSignature sig3 = parser.parseMethodSignature("(Ljava/util/Collection<-TT;>;[TT;)Z"); -// MethodSignature sig4 = parser.parseMethodSignature("(Ljava/util/Collection<*>;Ljava/util/Collection<*>;)Z"); - //MethodSignature sig7 = parser.parseMethodSignature("()Lcom/sun/xml/internal/bind/v2/model/impl/ElementInfoImpl.PropertyImpl;"); -// ClassSignature sig5 = parser.parseClassSignature(";R:Lio/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel;S:Lio/undertow/server/protocol/framed/AbstractFramedStreamSinkChannel;>Ljava/lang/Object;Lorg/xnio/channels/ConnectedChannel;"); -// ClassSignature sig6 = parser.parseClassSignature("Lcom/apple/laf/AquaUtils$RecyclableSingleton;"); -// System.out.println(sig1); -// System.out.println(sig2); -// System.out.println(sig3); -// System.out.println(sig4); -// System.out.println(sig5); -// System.out.println(sig6); - // System.out.println(sig7); - - BufferedReader reader = new BufferedReader(new FileReader("/Users/jason/sigmethods.txt")); - String line; - while ((line = reader.readLine()) != null) { - try { - System.out.println(parser.parseMethodSignature(line)); - } catch (Exception e) { - System.err.println(line); - e.printStackTrace(System.err); - System.exit(-1); - } - } + MethodSignature sig1 = parser.parseMethodSignature("(Ljava/lang/Class;TU;)Ljava/lang/Class<+TU;>;"); + MethodSignature sig2 = parser.parseMethodSignature("(Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/Map;"); + MethodSignature sig3 = parser.parseMethodSignature("(Ljava/util/Collection<-TT;>;[TT;)Z"); + MethodSignature sig4 = parser.parseMethodSignature("(Ljava/util/Collection<*>;Ljava/util/Collection<*>;)Z"); + MethodSignature sig7 = parser.parseMethodSignature("()Lcom/sun/xml/internal/bind/v2/model/impl/ElementInfoImpl.PropertyImpl;"); + ClassSignature sig5 = parser.parseClassSignature(";R:Lio/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel;S:Lio/undertow/server/protocol/framed/AbstractFramedStreamSinkChannel;>Ljava/lang/Object;Lorg/xnio/channels/ConnectedChannel;"); + ClassSignature sig6 = parser.parseClassSignature("Lcom/apple/laf/AquaUtils$RecyclableSingleton;"); + System.out.println(sig1); + System.out.println(sig2); + System.out.println(sig3); + System.out.println(sig4); + System.out.println(sig5); + System.out.println(sig6); + System.out.println(sig7); + +// BufferedReader reader = new BufferedReader(new FileReader("/Users/jason/sigmethods.txt")); +// String line; +// while ((line = reader.readLine()) != null) { +// try { +// System.out.println(parser.parseMethodSignature(line)); +// } catch (Exception e) { +// System.err.println(line); +// e.printStackTrace(System.err); +// System.exit(-1); +// } +// } } diff --git a/src/main/java/org/jboss/jandex/NameTable.java b/src/main/java/org/jboss/jandex/NameTable.java index a49e30d6..b2eb50e2 100644 --- a/src/main/java/org/jboss/jandex/NameTable.java +++ b/src/main/java/org/jboss/jandex/NameTable.java @@ -18,6 +18,7 @@ package org.jboss.jandex; +import java.security.Signature; import java.util.HashMap; import java.util.Map; @@ -28,6 +29,92 @@ class NameTable { private StrongInternPool stringPool = new StrongInternPool(); private StrongInternPool namePool = new StrongInternPool(); private Map names = new HashMap(); + private Map types = new HashMap(); + private Map typeLists = new HashMap(); + + static class Slice { + private final String string; + private final int start; + private final int end; + + private Slice(String string, int start, int end) { + this.string = string; + this.start = start; + this.end = end; + } + + static Slice create(String string, int start, int end) { + if (start < 0 || start >= end || end < 0 || end > string.length()) { + throw new IllegalArgumentException(); + } + + return new Slice(string, start, end); + } + + public int hashCode() { + int hash = 0; + int start = this.start; + int end = this.end; + + for (int i = start; i < end; i++) { + hash = 31 * hash + string.charAt(i); + } + + return hash; + } + + public boolean equals(Object other) { + if (!(other instanceof Slice)) { + return false; + } + + Slice otherSlice = (Slice) other; + int otherStart = otherSlice.start; + int otherEnd = otherSlice.end; + int start = this.start; + int end = this.end; + String otherString = otherSlice.string; + String string = this.string; + + if (otherEnd - otherStart != end - start) { + return false; + } + + while (start < end) { + if (string.charAt(start++) != otherString.charAt(otherStart++)) { + return false; + } + } + + return true; + } + + public String toString() { + return string.substring(start, end); + } + } + + Slice createSlice(String string, int start,int end) { + return Slice.create(string, start, end); + } + + Type getType(Slice slice) { + return types.get(slice); + } + + Type storeType(Slice slice, Type type) { + types.put(slice, type); + return type; + } + + Type[] getTypeList(Slice slice) { + return typeLists.get(slice); + } + + Type[] storeTypeList(Slice slice, Type[] typeList) { + typeLists.put(slice, typeList); + return typeList; + } DotName convertToName(String name) { return convertToName(name, '.'); @@ -66,11 +153,11 @@ DotName wrap(DotName prefix, String local, boolean inner) { return intern(name, '.'); } - public String intern(String string) { + String intern(String string) { return stringPool.intern(string); } - public DotName intern(DotName dotName, char delim) { + DotName intern(DotName dotName, char delim) { String name = dotName.toString(delim); DotName old = names.get(name); if (old == null) { diff --git a/src/main/java/org/jboss/jandex/WildcardType.java b/src/main/java/org/jboss/jandex/WildcardType.java index e0e6e6a9..9e6999c0 100644 --- a/src/main/java/org/jboss/jandex/WildcardType.java +++ b/src/main/java/org/jboss/jandex/WildcardType.java @@ -23,22 +23,22 @@ public class WildcardType extends Type { private static Type OBJECT = new ClassType(DotName.OBJECT_NAME); - private Type extendsBound; - private Type superBound; + private Type bound; + private boolean isExtends; - WildcardType(Type extendsBound, Type superBound) { - super(extendsBound != null ? extendsBound.name() : DotName.OBJECT_NAME); - this.extendsBound = extendsBound != null ? extendsBound : OBJECT; - this.superBound = superBound; + WildcardType(Type bound, boolean isExtends) { + super(isExtends && bound != null ? bound.name() : DotName.OBJECT_NAME); + this.bound = isExtends && bound == null ? OBJECT : bound; + this.isExtends = isExtends; } public Type extendsBound() { - return extendsBound; + return isExtends ? bound : OBJECT; } public Type superBound() { - return superBound; + return isExtends ? null : bound; } @Override @@ -50,12 +50,12 @@ public String toString() { StringBuilder builder = new StringBuilder(); builder.append('?'); - if (extendsBound != OBJECT) { - builder.append(" extends ").append(extendsBound); + if (isExtends && bound != OBJECT) { + builder.append(" extends ").append(bound); } - if (superBound != null) { - builder.append(" super ").append(superBound); + if (!isExtends && bound != null) { + builder.append(" super ").append(bound); } return builder.toString();