changeset 42332:8bf488890cb9

8035424: (reflect) Performance problem in sun.reflect.generics.parser.SignatureParser Reviewed-by: redestad
author plevart
date Wed, 30 Nov 2016 19:52:20 +0100
parents 85c6abf55d70
children 83f37c05391b
files jdk/src/java.base/share/classes/sun/reflect/generics/parser/SignatureParser.java
diffstat 1 files changed, 43 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- 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+ ">"