0N/A/*
4375N/A * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.reflect.generics.parser;
0N/A
0N/Aimport java.lang.reflect.GenericSignatureFormatError;
0N/Aimport java.util.*;
0N/Aimport sun.reflect.generics.tree.*;
0N/A
0N/A/**
0N/A * Parser for type signatures, as defined in the Java Virtual
4375N/A * Machine Specification (JVMS) chapter 4.
0N/A * Converts the signatures into an abstract syntax tree (AST) representation.
4375N/A * See the package sun.reflect.generics.tree for details of the AST.
0N/A */
0N/Apublic class SignatureParser {
0N/A // The input is conceptually a character stream (though currently it's
0N/A // a string). This is slightly different than traditional parsers,
0N/A // because there is no lexical scanner performing tokenization.
0N/A // Having a separate tokenizer does not fit with the nature of the
0N/A // input format.
0N/A // Other than the absence of a tokenizer, this parser is a classic
0N/A // recursive descent parser. Its structure corresponds as closely
0N/A // as possible to the grammar in the JVMS.
0N/A //
0N/A // A note on asserts vs. errors: The code contains assertions
0N/A // in situations that should never occur. An assertion failure
0N/A // indicates a failure of the parser logic. A common pattern
0N/A // is an assertion that the current input is a particular
0N/A // character. This is often paired with a separate check
0N/A // that this is the case, which seems redundant. For example:
0N/A //
0N/A // assert(current() != x);
0N/A // if (current != x {error("expected an x");
0N/A //
0N/A // where x is some character constant.
4375N/A // The assertion indicates, that, as currently written,
4375N/A // the code should never reach this point unless the input is an
0N/A // x. On the other hand, the test is there to check the legality
0N/A // of the input wrt to a given production. It may be that at a later
0N/A // time the code might be called directly, and if the input is
0N/A // invalid, the parser should flag an error in accordance
0N/A // with its logic.
0N/A
0N/A private char[] input; // the input signature
0N/A private int index = 0; // index into the input
4375N/A // used to mark end of input
0N/A private static final char EOI = ':';
0N/A private static final boolean DEBUG = false;
0N/A
0N/A // private constructor - enforces use of static factory
0N/A private SignatureParser(){}
0N/A
0N/A // Utility methods.
0N/A
0N/A // Most parsing routines use the following routines to access the
0N/A // input stream, and advance it as necessary.
0N/A // This makes it easy to adapt the parser to operate on streams
0N/A // of various kinds as well as strings.
0N/A
0N/A // returns current element of the input and advances the input
0N/A private char getNext(){
0N/A assert(index <= input.length);
0N/A try {
0N/A return input[index++];
0N/A } catch (ArrayIndexOutOfBoundsException e) { return EOI;}
0N/A }
0N/A
0N/A // returns current element of the input
0N/A private char current(){
0N/A assert(index <= input.length);
0N/A try {
0N/A return input[index];
0N/A } catch (ArrayIndexOutOfBoundsException e) { return EOI;}
0N/A }
0N/A
0N/A // advance the input
0N/A private void advance(){
0N/A assert(index <= input.length);
0N/A index++;
0N/A }
0N/A
4375N/A // For debugging, prints current character to the end of the input.
4375N/A private String remainder() {
4375N/A return new String(input, index, input.length-index);
4375N/A }
4375N/A
0N/A // Match c against a "set" of characters
0N/A private boolean matches(char c, char... set) {
0N/A for (char e : set) {
0N/A if (c == e) return true;
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A // Error handling routine. Encapsulates error handling.
0N/A // Takes a string error message as argument.
0N/A // Currently throws a GenericSignatureFormatError.
0N/A
0N/A private Error error(String errorMsg) {
4375N/A if (DEBUG)
4375N/A System.out.println("Signature Parse error: " + errorMsg +
4375N/A "\n\tRemaining input: " + remainder());
0N/A return new GenericSignatureFormatError();
0N/A }
0N/A
0N/A /**
4375N/A * Verify the parse has made forward progress; throw an exception
4375N/A * if no progress.
4375N/A */
4375N/A private void progress(int startingPosition) {
4375N/A if (index <= startingPosition)
4375N/A throw error("Failure to make progress!");
4375N/A }
4375N/A
4375N/A /**
0N/A * Static factory method. Produces a parser instance.
0N/A * @return an instance of <tt>SignatureParser</tt>
0N/A */
0N/A public static SignatureParser make() {
0N/A return new SignatureParser();
0N/A }
0N/A
0N/A /**
0N/A * Parses a class signature (as defined in the JVMS, chapter 4)
0N/A * and produces an abstract syntax tree representing it.
0N/A * @param s a string representing the input class signature
0N/A * @return An abstract syntax tree for a class signature
0N/A * corresponding to the input string
0N/A * @throws GenericSignatureFormatError if the input is not a valid
0N/A * class signature
0N/A */
0N/A public ClassSignature parseClassSig(String s) {
0N/A if (DEBUG) System.out.println("Parsing class sig:" + s);
0N/A input = s.toCharArray();
0N/A return parseClassSignature();
0N/A }
0N/A
0N/A /**
0N/A * Parses a method signature (as defined in the JVMS, chapter 4)
0N/A * and produces an abstract syntax tree representing it.
0N/A * @param s a string representing the input method signature
0N/A * @return An abstract syntax tree for a method signature
0N/A * corresponding to the input string
0N/A * @throws GenericSignatureFormatError if the input is not a valid
0N/A * method signature
0N/A */
0N/A public MethodTypeSignature parseMethodSig(String s) {
0N/A if (DEBUG) System.out.println("Parsing method sig:" + s);
0N/A input = s.toCharArray();
0N/A return parseMethodTypeSignature();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Parses a type signature
0N/A * and produces an abstract syntax tree representing it.
4375N/A *
0N/A * @param s a string representing the input type signature
0N/A * @return An abstract syntax tree for a type signature
0N/A * corresponding to the input string
0N/A * @throws GenericSignatureFormatError if the input is not a valid
0N/A * type signature
0N/A */
0N/A public TypeSignature parseTypeSig(String s) {
0N/A if (DEBUG) System.out.println("Parsing type sig:" + s);
0N/A input = s.toCharArray();
0N/A return parseTypeSignature();
0N/A }
0N/A
0N/A // Parsing routines.
0N/A // As a rule, the parsing routines access the input using the
0N/A // utilities current(), getNext() and/or advance().
0N/A // The convention is that when a parsing routine is invoked
0N/A // it expects the current input to be the first character it should parse
0N/A // and when it completes parsing, it leaves the input at the first
0N/A // character after the input parses.
0N/A
4375N/A /*
4375N/A * Note on grammar conventions: a trailing "*" matches zero or
4375N/A * more occurrences, a trailing "+" matches one or more occurrences,
4375N/A * "_opt" indicates an optional component.
4375N/A */
4375N/A
4375N/A /**
4375N/A * ClassSignature:
4375N/A * FormalTypeParameters_opt SuperclassSignature SuperinterfaceSignature*
4375N/A */
0N/A private ClassSignature parseClassSignature() {
4375N/A // parse a class signature based on the implicit input.
0N/A assert(index == 0);
0N/A return ClassSignature.make(parseZeroOrMoreFormalTypeParameters(),
4375N/A parseClassTypeSignature(), // Only rule for SuperclassSignature
0N/A parseSuperInterfaces());
0N/A }
0N/A
0N/A private FormalTypeParameter[] parseZeroOrMoreFormalTypeParameters(){
4375N/A if (current() == '<') {
4375N/A return parseFormalTypeParameters();
4375N/A } else {
4375N/A return new FormalTypeParameter[0];
4375N/A }
0N/A }
0N/A
4375N/A /**
4375N/A * FormalTypeParameters:
4375N/A * "<" FormalTypeParameter+ ">"
4375N/A */
0N/A private FormalTypeParameter[] parseFormalTypeParameters(){
4375N/A List<FormalTypeParameter> ftps = new ArrayList<>(3);
0N/A assert(current() == '<'); // should not have been called at all
4375N/A if (current() != '<') { throw error("expected '<'");}
0N/A advance();
0N/A ftps.add(parseFormalTypeParameter());
0N/A while (current() != '>') {
4375N/A int startingPosition = index;
0N/A ftps.add(parseFormalTypeParameter());
4375N/A progress(startingPosition);
0N/A }
0N/A advance();
4375N/A return ftps.toArray(new FormalTypeParameter[ftps.size()]);
0N/A }
0N/A
4375N/A /**
4375N/A * FormalTypeParameter:
4375N/A * Identifier ClassBound InterfaceBound*
4375N/A */
0N/A private FormalTypeParameter parseFormalTypeParameter(){
0N/A String id = parseIdentifier();
4375N/A FieldTypeSignature[] bs = parseBounds();
0N/A return FormalTypeParameter.make(id, bs);
0N/A }
0N/A
0N/A private String parseIdentifier(){
0N/A StringBuilder result = new StringBuilder();
0N/A while (!Character.isWhitespace(current())) {
0N/A char c = current();
0N/A switch(c) {
0N/A case ';':
0N/A case '.':
0N/A case '/':
0N/A case '[':
0N/A case ':':
0N/A case '>':
4375N/A case '<':
4375N/A return result.toString();
0N/A default:{
0N/A result.append(c);
0N/A advance();
0N/A }
0N/A
0N/A }
0N/A }
0N/A return result.toString();
0N/A }
4375N/A /**
4375N/A * FieldTypeSignature:
4375N/A * ClassTypeSignature
4375N/A * ArrayTypeSignature
4375N/A * TypeVariableSignature
4375N/A */
4375N/A private FieldTypeSignature parseFieldTypeSignature() {
4375N/A return parseFieldTypeSignature(true);
4375N/A }
0N/A
4375N/A private FieldTypeSignature parseFieldTypeSignature(boolean allowArrays) {
0N/A switch(current()) {
0N/A case 'L':
0N/A return parseClassTypeSignature();
0N/A case 'T':
0N/A return parseTypeVariableSignature();
0N/A case '[':
4375N/A if (allowArrays)
4375N/A return parseArrayTypeSignature();
4375N/A else
4375N/A throw error("Array signature not allowed here.");
0N/A default: throw error("Expected Field Type Signature");
0N/A }
0N/A }
0N/A
4375N/A /**
4375N/A * ClassTypeSignature:
4375N/A * "L" PackageSpecifier_opt SimpleClassTypeSignature ClassTypeSignatureSuffix* ";"
4375N/A */
0N/A private ClassTypeSignature parseClassTypeSignature(){
0N/A assert(current() == 'L');
0N/A if (current() != 'L') { throw error("expected a class type");}
0N/A advance();
4375N/A List<SimpleClassTypeSignature> scts = new ArrayList<>(5);
4375N/A scts.add(parsePackageNameAndSimpleClassTypeSignature());
4375N/A
0N/A parseClassTypeSignatureSuffix(scts);
0N/A if (current() != ';')
0N/A throw error("expected ';' got '" + current() + "'");
0N/A
0N/A advance();
0N/A return ClassTypeSignature.make(scts);
0N/A }
0N/A
4375N/A /**
4375N/A * PackageSpecifier:
4375N/A * Identifier "/" PackageSpecifier*
4375N/A */
4375N/A private SimpleClassTypeSignature parsePackageNameAndSimpleClassTypeSignature() {
4375N/A // Parse both any optional leading PackageSpecifier as well as
4375N/A // the following SimpleClassTypeSignature.
4375N/A
4375N/A String id = parseIdentifier();
4375N/A
4375N/A if (current() == '/') { // package name
4375N/A StringBuilder idBuild = new StringBuilder(id);
4375N/A
4375N/A while(current() == '/') {
4375N/A advance();
4375N/A idBuild.append(".");
4375N/A idBuild.append(parseIdentifier());
0N/A }
4375N/A id = idBuild.toString();
4375N/A }
4375N/A
4375N/A switch (current()) {
4375N/A case ';':
4375N/A return SimpleClassTypeSignature.make(id, false, new TypeArgument[0]); // all done!
4375N/A case '<':
4375N/A if (DEBUG) System.out.println("\t remainder: " + remainder());
4375N/A return SimpleClassTypeSignature.make(id, false, parseTypeArguments());
4375N/A default:
4375N/A throw error("expected '<' or ';' but got " + current());
4375N/A }
0N/A }
0N/A
4375N/A /**
4375N/A * SimpleClassTypeSignature:
4375N/A * Identifier TypeArguments_opt
4375N/A */
4375N/A private SimpleClassTypeSignature parseSimpleClassTypeSignature(boolean dollar){
4375N/A String id = parseIdentifier();
4375N/A char c = current();
4375N/A
4375N/A switch (c) {
4375N/A case ';':
4375N/A case '.':
4375N/A return SimpleClassTypeSignature.make(id, dollar, new TypeArgument[0]) ;
4375N/A case '<':
4375N/A return SimpleClassTypeSignature.make(id, dollar, parseTypeArguments());
4375N/A default:
4375N/A throw error("expected '<' or ';' or '.', got '" + c + "'.");
4375N/A }
4375N/A }
4375N/A
4375N/A /**
4375N/A * ClassTypeSignatureSuffix:
4375N/A * "." SimpleClassTypeSignature
4375N/A */
0N/A private void parseClassTypeSignatureSuffix(List<SimpleClassTypeSignature> scts) {
4375N/A while (current() == '.') {
0N/A advance();
4375N/A scts.add(parseSimpleClassTypeSignature(true));
0N/A }
0N/A }
0N/A
0N/A private TypeArgument[] parseTypeArgumentsOpt() {
0N/A if (current() == '<') {return parseTypeArguments();}
0N/A else {return new TypeArgument[0];}
0N/A }
0N/A
4375N/A /**
4375N/A * TypeArguments:
4375N/A * "<" TypeArgument+ ">"
4375N/A */
0N/A private TypeArgument[] parseTypeArguments() {
4375N/A List<TypeArgument> tas = new ArrayList<>(3);
0N/A assert(current() == '<');
4375N/A if (current() != '<') { throw error("expected '<'");}
0N/A advance();
0N/A tas.add(parseTypeArgument());
0N/A while (current() != '>') {
0N/A //(matches(current(), '+', '-', 'L', '[', 'T', '*')) {
0N/A tas.add(parseTypeArgument());
0N/A }
0N/A advance();
4375N/A return tas.toArray(new TypeArgument[tas.size()]);
0N/A }
0N/A
4375N/A /**
4375N/A * TypeArgument:
4375N/A * WildcardIndicator_opt FieldTypeSignature
4375N/A * "*"
4375N/A */
0N/A private TypeArgument parseTypeArgument() {
0N/A FieldTypeSignature[] ub, lb;
0N/A ub = new FieldTypeSignature[1];
0N/A lb = new FieldTypeSignature[1];
0N/A TypeArgument[] ta = new TypeArgument[0];
0N/A char c = current();
0N/A switch (c) {
0N/A case '+': {
0N/A advance();
0N/A ub[0] = parseFieldTypeSignature();
0N/A lb[0] = BottomSignature.make(); // bottom
0N/A return Wildcard.make(ub, lb);
0N/A }
0N/A case '*':{
0N/A advance();
0N/A ub[0] = SimpleClassTypeSignature.make("java.lang.Object", false, ta);
0N/A lb[0] = BottomSignature.make(); // bottom
0N/A return Wildcard.make(ub, lb);
0N/A }
0N/A case '-': {
0N/A advance();
0N/A lb[0] = parseFieldTypeSignature();
0N/A ub[0] = SimpleClassTypeSignature.make("java.lang.Object", false, ta);
0N/A return Wildcard.make(ub, lb);
0N/A }
4375N/A default:
4375N/A return parseFieldTypeSignature();
0N/A }
0N/A }
0N/A
4375N/A /**
4375N/A * TypeVariableSignature:
4375N/A * "T" Identifier ";"
4375N/A */
4375N/A private TypeVariableSignature parseTypeVariableSignature() {
0N/A assert(current() == 'T');
0N/A if (current() != 'T') { throw error("expected a type variable usage");}
0N/A advance();
4375N/A TypeVariableSignature ts = TypeVariableSignature.make(parseIdentifier());
0N/A if (current() != ';') {
0N/A throw error("; expected in signature of type variable named" +
0N/A ts.getIdentifier());
0N/A }
0N/A advance();
0N/A return ts;
0N/A }
0N/A
4375N/A /**
4375N/A * ArrayTypeSignature:
4375N/A * "[" TypeSignature
4375N/A */
0N/A private ArrayTypeSignature parseArrayTypeSignature() {
0N/A if (current() != '[') {throw error("expected array type signature");}
0N/A advance();
0N/A return ArrayTypeSignature.make(parseTypeSignature());
0N/A }
0N/A
4375N/A /**
4375N/A * TypeSignature:
4375N/A * FieldTypeSignature
4375N/A * BaseType
4375N/A */
0N/A private TypeSignature parseTypeSignature() {
0N/A switch (current()) {
0N/A case 'B':
0N/A case 'C':
0N/A case 'D':
0N/A case 'F':
0N/A case 'I':
0N/A case 'J':
0N/A case 'S':
4375N/A case 'Z':
4375N/A return parseBaseType();
4375N/A
4375N/A default:
4375N/A return parseFieldTypeSignature();
0N/A }
0N/A }
0N/A
0N/A private BaseType parseBaseType() {
0N/A switch(current()) {
0N/A case 'B':
0N/A advance();
0N/A return ByteSignature.make();
0N/A case 'C':
0N/A advance();
0N/A return CharSignature.make();
0N/A case 'D':
0N/A advance();
0N/A return DoubleSignature.make();
0N/A case 'F':
0N/A advance();
0N/A return FloatSignature.make();
0N/A case 'I':
0N/A advance();
0N/A return IntSignature.make();
0N/A case 'J':
0N/A advance();
0N/A return LongSignature.make();
0N/A case 'S':
0N/A advance();
0N/A return ShortSignature.make();
0N/A case 'Z':
0N/A advance();
0N/A return BooleanSignature.make();
0N/A default: {
0N/A assert(false);
0N/A throw error("expected primitive type");
0N/A }
4375N/A }
0N/A }
0N/A
4375N/A /**
4375N/A * ClassBound:
4375N/A * ":" FieldTypeSignature_opt
4375N/A *
4375N/A * InterfaceBound:
4375N/A * ":" FieldTypeSignature
4375N/A */
4375N/A private FieldTypeSignature[] parseBounds() {
4375N/A List<FieldTypeSignature> fts = new ArrayList<>(3);
0N/A
0N/A if (current() == ':') {
0N/A advance();
0N/A switch(current()) {
0N/A case ':': // empty class bound
0N/A break;
0N/A
0N/A default: // parse class bound
0N/A fts.add(parseFieldTypeSignature());
0N/A }
0N/A
0N/A // zero or more interface bounds
0N/A while (current() == ':') {
0N/A advance();
0N/A fts.add(parseFieldTypeSignature());
0N/A }
4375N/A } else
4375N/A error("Bound expected");
0N/A
4375N/A return fts.toArray(new FieldTypeSignature[fts.size()]);
0N/A }
0N/A
4375N/A /**
4375N/A * SuperclassSignature:
4375N/A * ClassTypeSignature
4375N/A */
0N/A private ClassTypeSignature[] parseSuperInterfaces() {
4375N/A List<ClassTypeSignature> cts = new ArrayList<>(5);
0N/A while(current() == 'L') {
0N/A cts.add(parseClassTypeSignature());
0N/A }
4375N/A return cts.toArray(new ClassTypeSignature[cts.size()]);
0N/A }
0N/A
4375N/A
4375N/A /**
4375N/A * MethodTypeSignature:
4375N/A * FormalTypeParameters_opt "(" TypeSignature* ")" ReturnType ThrowsSignature*
4375N/A */
0N/A private MethodTypeSignature parseMethodTypeSignature() {
4375N/A // Parse a method signature based on the implicit input.
0N/A FieldTypeSignature[] ets;
0N/A
0N/A assert(index == 0);
0N/A return MethodTypeSignature.make(parseZeroOrMoreFormalTypeParameters(),
0N/A parseFormalParameters(),
0N/A parseReturnType(),
0N/A parseZeroOrMoreThrowsSignatures());
0N/A }
0N/A
4375N/A // "(" TypeSignature* ")"
0N/A private TypeSignature[] parseFormalParameters() {
4375N/A if (current() != '(') {throw error("expected '('");}
0N/A advance();
0N/A TypeSignature[] pts = parseZeroOrMoreTypeSignatures();
4375N/A if (current() != ')') {throw error("expected ')'");}
0N/A advance();
0N/A return pts;
0N/A }
0N/A
4375N/A // TypeSignature*
0N/A private TypeSignature[] parseZeroOrMoreTypeSignatures() {
4375N/A List<TypeSignature> ts = new ArrayList<>();
0N/A boolean stop = false;
0N/A while (!stop) {
0N/A switch(current()) {
0N/A case 'B':
0N/A case 'C':
0N/A case 'D':
0N/A case 'F':
0N/A case 'I':
0N/A case 'J':
0N/A case 'S':
0N/A case 'Z':
0N/A case 'L':
0N/A case 'T':
0N/A case '[': {
4375N/A ts.add(parseTypeSignature());
4375N/A break;
4375N/A }
0N/A default: stop = true;
0N/A }
0N/A }
4375N/A return ts.toArray(new TypeSignature[ts.size()]);
0N/A }
0N/A
4375N/A /**
4375N/A * ReturnType:
4375N/A * TypeSignature
4375N/A * VoidDescriptor
4375N/A */
0N/A private ReturnType parseReturnType(){
4375N/A if (current() == 'V') {
0N/A advance();
0N/A return VoidDescriptor.make();
4375N/A } else
4375N/A return parseTypeSignature();
0N/A }
0N/A
0N/A // ThrowSignature*
0N/A private FieldTypeSignature[] parseZeroOrMoreThrowsSignatures(){
4375N/A List<FieldTypeSignature> ets = new ArrayList<>(3);
0N/A while( current() == '^') {
0N/A ets.add(parseThrowsSignature());
0N/A }
4375N/A return ets.toArray(new FieldTypeSignature[ets.size()]);
0N/A }
0N/A
4375N/A /**
4375N/A * ThrowsSignature:
4375N/A * "^" ClassTypeSignature
4375N/A * "^" TypeVariableSignature
4375N/A */
0N/A private FieldTypeSignature parseThrowsSignature() {
0N/A assert(current() == '^');
0N/A if (current() != '^') { throw error("expected throws signature");}
0N/A advance();
4375N/A return parseFieldTypeSignature(false);
0N/A }
0N/A }