8035424: (reflect) Performance problem in sun.reflect.generics.parser.SignatureParser
Reviewed-by: redestad
--- a/jdk/src/java.base/share/classes/sun/reflect/generics/parser/SignatureParser.java Wed Nov 30 15:52:50 2016 +0000
+++ b/jdk/src/java.base/share/classes/sun/reflect/generics/parser/SignatureParser.java Wed Nov 30 19:52:20 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -64,20 +64,22 @@
// 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
+ private String input; // the input signature
+ private int index; // index into the input
+ private int mark; // index of mark
// used to mark end of input
private static final char EOI = ':';
private static final boolean DEBUG = false;
- // StringBuilder does a lot of array copies if we don't pre-size
- // it when parsing a full class name. This value was determined
- // empirically by measurements of real-world code.
- private static final int CLASS_NAME_SB_SIZE = 48;
-
// private constructor - enforces use of static factory
private SignatureParser(){}
+ // prepares parser for new parsing session
+ private void init(String s) {
+ input = s;
+ mark = index = 0;
+ }
+
// Utility methods.
// Most parsing routines use the following routines to access the
@@ -85,39 +87,32 @@
// 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;}
+ assert(index <= input.length());
+ return index < input.length() ? input.charAt(index) : EOI;
}
// advance the input
private void advance(){
- assert(index <= input.length);
- index++;
+ assert(index <= input.length());
+ if (index < input.length()) index++;
+ }
+
+ // mark current position
+ private void mark() {
+ mark = index;
}
// For debugging, prints current character to the end of the input.
private String remainder() {
- return new String(input, index, input.length-index);
+ return input.substring(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;
+ // returns a substring of input from mark (inclusive)
+ // to current position (exclusive)
+ private String markToCurrent() {
+ return input.substring(mark, index);
}
// Error handling routine. Encapsulates error handling.
@@ -157,7 +152,7 @@
*/
public ClassSignature parseClassSig(String s) {
if (DEBUG) System.out.println("Parsing class sig:" + s);
- input = s.toCharArray();
+ init(s);
return parseClassSignature();
}
@@ -172,7 +167,7 @@
*/
public MethodTypeSignature parseMethodSig(String s) {
if (DEBUG) System.out.println("Parsing method sig:" + s);
- input = s.toCharArray();
+ init(s);
return parseMethodTypeSignature();
}
@@ -189,13 +184,13 @@
*/
public TypeSignature parseTypeSig(String s) {
if (DEBUG) System.out.println("Parsing type sig:" + s);
- input = s.toCharArray();
+ init(s);
return parseTypeSignature();
}
// Parsing routines.
// As a rule, the parsing routines access the input using the
- // utilities current(), getNext() and/or advance().
+ // utilities current() and 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
@@ -257,33 +252,19 @@
}
private String parseIdentifier() {
- StringBuilder result = new StringBuilder();
- parseIdentifierInto(result);
- return result.toString();
+ mark();
+ skipIdentifier();
+ return markToCurrent();
}
- // This is separate from parseIdentifier for performance reasons.
- // For a caller who already has a StringBuilder, it's much faster to
- // re-use the existing builder, because it results in far fewer internal
- // array copies inside of StringBuilder.
- private void parseIdentifierInto(StringBuilder result) {
- int startIndex = index;
- parseLoop: while (!Character.isWhitespace(current())) {
- char c = current();
- switch(c) {
- case ';':
- case '.':
- case '/':
- case '[':
- case ':':
- case '>':
- case '<':
- break parseLoop;
- default:
- advance();
- }
+ private void skipIdentifier() {
+ char c = current();
+ while (c != ';' && c != '.' && c != '/' &&
+ c != '[' && c != ':' && c != '>' &&
+ c != '<' && !Character.isWhitespace(c)) {
+ advance();
+ c = current();
}
- result.append(input, startIndex, index - startIndex);
}
/**
@@ -338,16 +319,13 @@
// Parse both any optional leading PackageSpecifier as well as
// the following SimpleClassTypeSignature.
- StringBuilder idBuild = new StringBuilder(CLASS_NAME_SB_SIZE);
- parseIdentifierInto(idBuild);
-
- while (current() == '/') { // package name
+ mark();
+ skipIdentifier();
+ while (current() == '/') {
advance();
- idBuild.append('.');
- parseIdentifierInto(idBuild);
+ skipIdentifier();
}
-
- String id = idBuild.toString();
+ String id = markToCurrent().replace('/', '.');
switch (current()) {
case ';':
@@ -390,11 +368,6 @@
}
}
- private TypeArgument[] parseTypeArgumentsOpt() {
- if (current() == '<') {return parseTypeArguments();}
- else {return new TypeArgument[0];}
- }
-
/**
* TypeArguments:
* "<" TypeArgument+ ">"