All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.jandex.GenericSignatureParser Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.jandex;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * A simple recursive decent generic signature parser.
 *
 * @author Jason T. Greene
 */
class GenericSignatureParser {
    /*
     * Complete Grammar  (VM Spec v8)
     *
     * JavaTypeSignature:
     *   ReferenceTypeSignature
     *   BaseType
     *
     * BaseType:
     *   B C D F I J S Z
     *
     * VoidDescriptor:
     *   V
     *
     * ReferenceTypeSignature:
     *   ClassTypeSignature
     *   TypeVariableSignature
     *   ArrayTypeSignature
     *
     * ClassTypeSignature:
     *   L [PackageSpecifier] SimpleClassTypeSignature {ClassTypeSignatureSuffix} ;
     *
     * PackageSpecifier:
     *   Identifier / {PackageSpecifier}
     *
     * SimpleClassTypeSignature:
     *   Identifier [TypeArguments]
     *
     * TypeArguments:
     *   < TypeArgument {TypeArgument} >
     *
     * TypeArgument:
     *   [WildcardIndicator] ReferenceTypeSignature
     *   *
     *
     * WildcardIndicator:
     *   +
     *   -
     *
     * ClassTypeSignatureSuffix:
     *   . SimpleClassTypeSignature
     *
     * TypeVariableSignature:
     *   T Identifier ;
     *
     * ArrayTypeSignature:
     *   [ JavaTypeSignature
     *
     * ClassSignature:
     *   [TypeParameters] SuperclassSignature {SuperinterfaceSignature}
     *
     * TypeParameters:
     *   < TypeParameter {TypeParameter} >
     *
     * TypeParameter:
     *   Identifier ClassBound {InterfaceBound}
     *
     * ClassBound:
     *   : [ReferenceTypeSignature]
     *
     * InterfaceBound:
     *   : ReferenceTypeSignature
     *
     * SuperclassSignature:
     *   ClassTypeSignature
     *
     * SuperinterfaceSignature:
     *   ClassTypeSignature
     *
     * MethodSignature:
     *   [TypeParameters] ( {JavaTypeSignature} ) Result {ThrowsSignature}
     *
     * Result:
     *   JavaTypeSignature
     *   VoidDescriptor
     *
     * ThrowsSignature:
     *   ^ ClassTypeSignature
     *   ^ TypeVariableSignature
     *
     * FieldSignature:
     *   ReferenceTypeSignature
     *
     */
    private static WildcardType UNBOUNDED_WILDCARD = new WildcardType(null, true);
    private String signature;
    private int pos;
    private NameTable names;
    private Map typeParameters;
    private Map elementTypeParameters = new HashMap();
    private Map classTypeParameters = new HashMap();

    GenericSignatureParser(NameTable names) {
        names.intern(DotName.OBJECT_NAME, '/');
        this.names = names;
    }

    static class ClassSignature {
        private final Type[] parameters;
        private final Type superClass;
        private final Type[] interfaces;

        private ClassSignature(Type[] parameters, Type superClass, Type[] interfaces) {
            this.parameters = parameters;
            this.superClass = superClass;
            this.interfaces = interfaces;
        }

        Type[] parameters() {
            return parameters;
        }

        Type superClass() {
            return superClass;
        }

        Type[] interfaces() {
            return interfaces;
        }

        public String toString() {
            StringBuilder builder = new StringBuilder();
            if (parameters.length > 0) {
                builder.append('<');
                builder.append(parameters[0]);
                for (int i = 1; i < parameters.length; i++) {
                    builder.append(", ").append(parameters[i]);
                }
                builder.append('>');
            }

            if (superClass.name() != DotName.OBJECT_NAME) {
                builder.append(" extends ").append(superClass);
            }

            if (interfaces.length > 0) {
                builder.append(" implements ").append(interfaces[0]);

                for (int i = 1; i < interfaces.length; i++) {
                    builder.append(", ").append(interfaces[i]);
                }
            }

            return builder.toString();
        }
    }

    static class MethodSignature {
        private final Type[] typeParameters;
        private final Type[] methodParameters;
        private final Type returnType;
        private final Type[] throwables;

        private MethodSignature(Type[] typeParameters, Type[] methodParameters, Type returnType, Type[] throwables) {
            this.typeParameters = typeParameters;
            this.methodParameters = methodParameters;
            this.returnType = returnType;
            this.throwables = throwables;
        }

        public Type[] typeParameters() {
            return typeParameters;
        }

        public Type returnType() {
            return returnType;
        }

        public Type[] methodParameters() {
            return methodParameters;
        }

        public Type[] throwables() {
            return throwables;
        }


        public String toString() {
            StringBuilder builder = new StringBuilder();
            if (typeParameters.length > 0) {
                builder.append("<");
                builder.append(typeParameters[0]);
                for (int i = 1; i < typeParameters.length; i++) {
                    builder.append(", ").append(typeParameters[i]);
                }
                builder.append("> ");
            }

            builder.append(returnType).append(" (");
            if (methodParameters.length > 0) {
                builder.append(methodParameters[0]);

                for (int i = 1; i < methodParameters.length; i++) {
                    builder.append(", ").append(methodParameters[i]);
                }
            }
            builder.append(')');

            if (throwables.length > 0) {
                builder.append(" throws ").append(throwables[0]);

                for (int i = 1; i < throwables.length; i++) {
                    builder.append(", ").append(throwables[i]);
                }
            }

            return builder.toString();
        }

    }

    ClassSignature parseClassSignature(String signature) {
        this.signature = signature;
        this.typeParameters = this.classTypeParameters;
        this.typeParameters.clear();
        this.pos = 0;
        Type[] parameters = parseTypeParameters();
        Type superClass = names.intern(parseClassTypeSignature());
        int end = signature.length();
        List interfaces = new ArrayList();
        while (pos < end) {
            interfaces.add(names.intern(parseClassTypeSignature()));
        }

        Type[] intfArray = names.intern(interfaces.toArray(new Type[interfaces.size()]));
        return new ClassSignature(parameters, superClass, intfArray);
    }

    private void expect(char c) {
         if (signature.charAt(pos++) != c) {
             throw new IllegalArgumentException("Expected character '" + c + "' at position " + (pos - 1));
         }
     }

    Type parseFieldSignature(String signature) {
        this.signature = signature;
        this.typeParameters = this.elementTypeParameters;
        this.typeParameters.clear();
        this.pos = 0;

        return parseReferenceType();
    }


    MethodSignature parseMethodSignature(String signature) {
        this.signature = signature;
        this.typeParameters = this.elementTypeParameters;
        this.typeParameters.clear();
        this.pos = 0;

        Type[] typeParameters = parseTypeParameters();

        expect('(');
        List parameters = new ArrayList();
        while (signature.charAt(pos) != ')') {
            Type type = parseJavaType();
            if (type == null) {
                throw new IllegalArgumentException("Corrupted argument, or unclosed brace at: " + pos);
            }
            parameters.add(type);
        }
        pos++;

        Type returnType = parseReturnType();
        List exceptions = new ArrayList();
        while (pos < signature.length()) {
            expect('^');
            exceptions.add(parseReferenceType());
        }

        Type[] exceptionsArray = names.intern(exceptions.toArray(new Type[exceptions.size()]));
        Type[] types = names.intern(parameters.toArray(new Type[parameters.size()]));
        return new MethodSignature(typeParameters, types, returnType, exceptionsArray);

    }

    private Type parseClassTypeSignature() {
        String signature = this.signature;
        DotName name = parseName();
        Type[] types = parseTypeArguments();
        Type type = null;

        if (types.length > 0) {
            type = new ParameterizedType(name, types, null);
        }

        // Suffix
        while (signature.charAt(pos) == '.') {
            int mark = ++pos;
            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
            // The first parameterized type needs a standard class type for the owner
            if (type == null && types.length > 0) {
                type = names.intern(new ClassType(name.prefix()));
            }

            if (type != null) {
                type = names.intern(new ParameterizedType(name, types, type));
            }
        }
        this.pos++; // ;
        return type != null ? type : names.intern(new ClassType(name));
    }

    private Type[] parseTypeArguments() {
        return parseTypeList(true);
    }

    private Type[] parseTypeParameters() {
        return parseTypeList(false);
    }

    private Type[] parseTypeList(boolean argument) {
        String signature = this.signature;
        if (signature.charAt(pos) != '<') {
            return Type.EMPTY_ARRAY;
        }
        pos++;

        ArrayList types = new ArrayList();
        for (;;) {
            Type t = argument ? parseTypeArgument() : parseTypeParameter();
            if (t == null) {
                break;
            }
            types.add(t);
        }
        if (!argument) {
            resolveTypeList(types);
        }
        return names.intern(types.toArray(new Type[types.size()]));
    }

    private Type parseTypeArgument() {
        char c = signature.charAt(pos++);
        switch (c) {
            case '>':
                return null;
            case '*':
                return UNBOUNDED_WILDCARD;
            case '-': {
                return parseWildCard(false);
            }
            case '+': {
                return parseWildCard(true);
            }
            default:
                pos--;
                return parseReferenceType();
        }
    }

    private Type parseWildCard(boolean isExtends) {
        Type bound = parseReferenceType();
        return new WildcardType(bound, isExtends);
    }

    private Type parseTypeParameter() {
        int start = pos;
        String signature = this.signature;

        if (signature.charAt(start) == '>') {
            pos++;
            return null;
        }

        int bound = advancePast(':');
        String name = names.intern(signature.substring(start, bound));

        ArrayList bounds = new ArrayList();

        // Class bound has an optional reference type
        if (signature.charAt(pos) != ':') {
            bounds.add(parseReferenceType());
        }

        // Interface bounds are none to many, with a required reference type
        while (signature.charAt(pos) == ':') {
            pos++;
            bounds.add(parseReferenceType());
        }

        TypeVariable type = new TypeVariable(name, bounds.toArray(new Type[bounds.size()]));
        typeParameters.put(name, type);
        return type;
    }

    private Type parseReturnType() {
        if (signature.charAt(pos) == 'V') {
            pos++;
            return VoidType.VOID;
        }

        return parseJavaType();
    }

    private Type parseReferenceType() {
        int mark = pos;
        char c = signature.charAt(mark);
        Type type;
        switch (c) {
            case 'T':
                type = parseTypeVariable();
                break;
            case 'L':
                type = parseClassTypeSignature();
                break;
            case '[':
                type = parseArrayType();
                break;
            default:
                return null;
        }

        return names.intern(type);
    }

    private Type parseArrayType() {
        int mark = this.pos;
        int last = advanceNot('[');
        return new ArrayType(parseJavaType(), last - mark);
    }

    private Type parseTypeVariable() {
        String name = names.intern(signature.substring(pos + 1, advancePast(';')));
        Type type = resolveType(name);
        return type == null ? new UnresolvedTypeVariable(name) : type;
    }

    private void resolveTypeList(ArrayList list) {
        int size = list.size();
        for (int i = 0; i < size; i++) {
            Type type = resolveType(list.get(i));
            if (type != null) {
                list.set(i, type);
                typeParameters.put(type.asTypeVariable().identifier(), type.asTypeVariable());
            }
        }
    }

    private Type resolveType(Type type) {
        if (type instanceof TypeVariable) {
            TypeVariable typeVariable = resolveBounds(type);

            return typeVariable != type ? typeVariable : null;
        }

        if (! (type instanceof UnresolvedTypeVariable)) {
            return null;
        }

        return resolveType(((UnresolvedTypeVariable) type).identifier());
    }

    private TypeVariable resolveBounds(Type type) {
        TypeVariable typeVariable = type.asTypeVariable();
        Type[] bounds = typeVariable.boundArray();
        for (int i = 0; i < bounds.length; i++) {
            Type newType = resolveType(bounds[i]);
            if (newType != null && newType != bounds[i]) {
                typeVariable = typeVariable.copyType(i, newType);
            }
        }
        return typeVariable;
    }

    private TypeVariable resolveType(String identifier) {
        TypeVariable ret = elementTypeParameters.get(identifier);
        return ret == null ? classTypeParameters.get(identifier) : ret;
    }

    private Type parseJavaType() {
        Type type = PrimitiveType.decode(signature.charAt(pos));
        if (type != null) {
            pos++;
            return type;
        }
        return parseReferenceType();
    }

    private int advancePast(char c) {
        int pos = signature.indexOf(c, this.pos);
        if (pos == -1) {
            throw new IllegalStateException("Corruption");
        }

        this.pos = pos + 1;
        return pos;
    }

    private int advanceNot(char c) {
        while (signature.charAt(pos) == c) {
            pos++;
        }

        return pos;
    }

    private DotName parseName() {
        int start = pos;
        int end = advanceNameEnd();

        if (signature.charAt(start++) != 'L') {
            throw new IllegalArgumentException("Invalid signature, invalid class designator");
        }

        return names.convertToName(signature.substring(start, end), '/');
    }

    private int advanceNameEnd() {
        int end = pos;
        String signature = this.signature;

        for (; end < signature.length(); end++) {
            char c = signature.charAt(end);
            if (c == '.' || c == '<' || c ==';') {
                return pos = end;
            }
        }

        throw new IllegalStateException("Corrupted name");
    }

    public static void main(String[] args) throws IOException {
        GenericSignatureParser parser = new GenericSignatureParser(new NameTable());
        MethodSignature sig1 = parser.parseMethodSignature("(Ljava/lang/Class;TU;)Ljava/lang/Class<+TU;>;");
//        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);
//            }
//        }

    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy