/* * Copyright (c) 2003, 2005, 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 inidcates, that, as currently written, // the code should nver 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++; } // 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("Parse error:" + errorMsg); return new GenericSignatureFormatError(); } /** * Static factory method. Produces a parser instance. * @return an instance of <tt>SignatureParser</tt> */ 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. // parse a class signature based on the implicit input. private ClassSignature parseClassSignature() { assert(index == 0); return ClassSignature.make(parseZeroOrMoreFormalTypeParameters(), parseClassTypeSignature(), parseSuperInterfaces()); } private FormalTypeParameter[] parseZeroOrMoreFormalTypeParameters(){ if (current() == '<') { return parseFormalTypeParameters();} else {return new FormalTypeParameter[0];} } private FormalTypeParameter[] parseFormalTypeParameters(){ Collection<FormalTypeParameter> ftps = new ArrayList<FormalTypeParameter>(3); assert(current() == '<'); // should not have been called at all if (current() != '<') { throw error("expected <");} advance(); ftps.add(parseFormalTypeParameter()); while (current() != '>') { ftps.add(parseFormalTypeParameter()); } advance(); FormalTypeParameter[] ftpa = new FormalTypeParameter[ftps.size()]; return ftps.toArray(ftpa); } private FormalTypeParameter parseFormalTypeParameter(){ String id = parseIdentifier(); FieldTypeSignature[] bs = parseZeroOrMoreBounds(); 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(); } private FieldTypeSignature parseFieldTypeSignature() { switch(current()) { case 'L': return parseClassTypeSignature(); case 'T': return parseTypeVariableSignature(); case '[': return parseArrayTypeSignature(); default: throw error("Expected Field Type Signature"); } } private ClassTypeSignature parseClassTypeSignature(){ assert(current() == 'L'); if (current() != 'L') { throw error("expected a class type");} advance(); List<SimpleClassTypeSignature> scts = new ArrayList<SimpleClassTypeSignature>(5); scts.add(parseSimpleClassTypeSignature(false)); parseClassTypeSignatureSuffix(scts); if (current() != ';') throw error("expected ';' got '" + current() + "'"); advance(); return ClassTypeSignature.make(scts); } 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 /");} } } private void parseClassTypeSignatureSuffix(List<SimpleClassTypeSignature> scts) { while (current() == '/' || current() == '.') { boolean dollar = (current() == '.'); advance(); scts.add(parseSimpleClassTypeSignature(dollar)); } } private TypeArgument[] parseTypeArgumentsOpt() { if (current() == '<') {return parseTypeArguments();} else {return new TypeArgument[0];} } private TypeArgument[] parseTypeArguments() { Collection<TypeArgument> tas = new ArrayList<TypeArgument>(3); assert(current() == '<'); if (current() != '<') { throw error("expected <");} advance(); tas.add(parseTypeArgument()); while (current() != '>') { //(matches(current(), '+', '-', 'L', '[', 'T', '*')) { tas.add(parseTypeArgument()); } advance(); TypeArgument[] taa = new TypeArgument[tas.size()]; return tas.toArray(taa); } 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 -> BaseType | FieldTypeSignature 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"); } } } private FieldTypeSignature[] parseZeroOrMoreBounds() { Collection<FieldTypeSignature> fts = new ArrayList<FieldTypeSignature>(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()); } } FieldTypeSignature[] fta = new FieldTypeSignature[fts.size()]; return fts.toArray(fta); } private ClassTypeSignature[] parseSuperInterfaces() { Collection<ClassTypeSignature> cts = new ArrayList<ClassTypeSignature>(5); while(current() == 'L') { cts.add(parseClassTypeSignature()); } ClassTypeSignature[] cta = new ClassTypeSignature[cts.size()]; return cts.toArray(cta); } // parse a method signature based on the implicit input. private MethodTypeSignature parseMethodTypeSignature() { 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() { Collection<TypeSignature> ts = new ArrayList<TypeSignature>(); 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; } } /* while( matches(current(), 'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 'L', 'T', '[') ) { ts.add(parseTypeSignature()); }*/ TypeSignature[] ta = new TypeSignature[ts.size()]; return ts.toArray(ta); } // ReturnType -> V | TypeSignature private ReturnType parseReturnType(){ if (current() == 'V') { advance(); return VoidDescriptor.make(); } else return parseTypeSignature(); } // ThrowSignature* private FieldTypeSignature[] parseZeroOrMoreThrowsSignatures(){ Collection<FieldTypeSignature> ets = new ArrayList<FieldTypeSignature>(3); while( current() == '^') { ets.add(parseThrowsSignature()); } FieldTypeSignature[] eta = new FieldTypeSignature[ets.size()]; return ets.toArray(eta); } // ThrowSignature -> ^ FieldTypeSignature private FieldTypeSignature parseThrowsSignature() { assert(current() == '^'); if (current() != '^') { throw error("expected throws signature");} advance(); return parseFieldTypeSignature(); } }