8035424: (reflect) Performance problem in sun.reflect.generics.parser.SignatureParser

Reviewed-by: redestad
This commit is contained in:
Peter Levart 2016-11-30 19:52:20 +01:00
parent b3f52c54f9
commit 789f9198c7

View File

@ -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 @@ public class SignatureParser {
// 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 @@ public class SignatureParser {
// 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 class SignatureParser {
*/
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 class SignatureParser {
*/
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 class SignatureParser {
*/
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 @@ public class SignatureParser {
}
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 @@ public class SignatureParser {
// 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 @@ public class SignatureParser {
}
}
private TypeArgument[] parseTypeArgumentsOpt() {
if (current() == '<') {return parseTypeArguments();}
else {return new TypeArgument[0];}
}
/**
* TypeArguments:
* "<" TypeArgument+ ">"