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