8160000: Runtime.version() cause startup regressions in 9+119

Reviewed-by: mchung, psandoz, erikj, forax, iris
This commit is contained in:
Claes Redestad 2016-06-28 00:39:26 +02:00
parent 6223e843f9
commit a2ed889002
3 changed files with 151 additions and 89 deletions

View File

@ -34,7 +34,11 @@ $(eval $(call SetupTextFileProcessing, BUILD_VERSION_JAVA, \
@@LAUNCHER_NAME@@ => $(LAUNCHER_NAME) ; \
@@RUNTIME_NAME@@ => $(RUNTIME_NAME) ; \
@@VERSION_SHORT@@ => $(VERSION_SHORT) ; \
@@VERSION_STRING@@ => $(VERSION_STRING), \
@@VERSION_STRING@@ => $(VERSION_STRING) ; \
@@VERSION_NUMBER@@ => $(VERSION_NUMBER) ; \
@@VERSION_PRE@@ => $(VERSION_PRE) ; \
@@VERSION_BUILD@@ => $(VERSION_BUILD) ; \
@@VERSION_OPT@@ => $(VERSION_OPT), \
))
GENSRC_JAVA_BASE += $(BUILD_VERSION_JAVA)

View File

@ -27,8 +27,6 @@ package java.lang;
import java.io.*;
import java.math.BigInteger;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -36,11 +34,9 @@ import java.util.stream.Collectors;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.RandomAccess;
import java.util.StringTokenizer;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import sun.security.action.GetPropertyAction;
/**
* Every Java application has a single instance of class
@ -941,8 +937,9 @@ public class Runtime {
*/
public static Version version() {
if (version == null) {
version = Version.parse(
GetPropertyAction.privilegedGetProperty("java.runtime.version"));
version = new Version(VersionProps.versionNumbers(),
VersionProps.pre(), VersionProps.build(),
VersionProps.optional());
}
return version;
}
@ -1084,86 +1081,12 @@ public class Runtime {
private final Optional<Integer> build;
private final Optional<String> optional;
// $VNUM(-$PRE)?(\+($BUILD)?(\-$OPT)?)?
// RE limits the format of version strings
// ([1-9][0-9]*(?:(?:\.0)*\.[1-9][0-9]*)*)(?:-([a-zA-Z0-9]+))?(?:(\+)(0|[1-9][0-9]*)?)?(?:-([-a-zA-Z0-9.]+))?
private static final String VNUM
= "(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
private static final String VNUM_GROUP = "VNUM";
private static final String PRE = "(?:-(?<PRE>[a-zA-Z0-9]+))?";
private static final String PRE_GROUP = "PRE";
private static final String BUILD
= "(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
private static final String PLUS_GROUP = "PLUS";
private static final String BUILD_GROUP = "BUILD";
private static final String OPT = "(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
private static final String OPT_GROUP = "OPT";
private static final String VSTR_FORMAT
= "^" + VNUM + PRE + BUILD + OPT + "$";
private static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
/**
* Constructs a valid <a href="verStr">version string</a> containing
* a <a href="#verNum">version number</a> followed by pre-release and
* build information.
*
* @param s
* A string to be interpreted as a version
*
* @throws IllegalArgumentException
* If the given string cannot be interpreted as a valid
* version
*
* @throws NullPointerException
* If {@code s} is {@code null}
*
* @throws NumberFormatException
* If an element of the version number or the build number
* cannot be represented as an {@link Integer}
*/
private Version(String s) {
if (s == null)
throw new NullPointerException();
Matcher m = VSTR_PATTERN.matcher(s);
if (!m.matches())
throw new IllegalArgumentException("Invalid version string: '"
+ s + "'");
// $VNUM is a dot-separated list of integers of arbitrary length
List<Integer> list = new ArrayList<>();
for (String i : m.group(VNUM_GROUP).split("\\."))
list.add(Integer.parseInt(i));
version = Collections.unmodifiableList(list);
pre = Optional.ofNullable(m.group(PRE_GROUP));
String b = m.group(BUILD_GROUP);
// $BUILD is an integer
build = (b == null)
? Optional.<Integer>empty()
: Optional.ofNullable(Integer.parseInt(b));
optional = Optional.ofNullable(m.group(OPT_GROUP));
// empty '+'
if ((m.group(PLUS_GROUP) != null) && !build.isPresent()) {
if (optional.isPresent()) {
if (pre.isPresent())
throw new IllegalArgumentException("'+' found with"
+ " pre-release and optional components:'" + s
+ "'");
} else {
throw new IllegalArgumentException("'+' found with neither"
+ " build or optional components: '" + s + "'");
}
}
Version(List<Integer> version, Optional<String> pre,
Optional<Integer> build, Optional<String> optional) {
this.version = Collections.unmodifiableList(version);
this.pre = pre;
this.build = build;
this.optional = optional;
}
/**
@ -1189,7 +1112,7 @@ public class Runtime {
* @return The Version of the given string
*/
public static Version parse(String s) {
return new Version(s);
return VersionBuilder.parse(s);
}
/**
@ -1518,4 +1441,86 @@ public class Runtime {
}
}
private static class VersionBuilder {
// $VNUM(-$PRE)?(\+($BUILD)?(\-$OPT)?)?
// RE limits the format of version strings
// ([1-9][0-9]*(?:(?:\.0)*\.[1-9][0-9]*)*)(?:-([a-zA-Z0-9]+))?(?:(\+)(0|[1-9][0-9]*)?)?(?:-([-a-zA-Z0-9.]+))?
private static final String VNUM
= "(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
private static final String VNUM_GROUP = "VNUM";
private static final String PRE = "(?:-(?<PRE>[a-zA-Z0-9]+))?";
private static final String PRE_GROUP = "PRE";
private static final String BUILD
= "(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
private static final String PLUS_GROUP = "PLUS";
private static final String BUILD_GROUP = "BUILD";
private static final String OPT = "(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
private static final String OPT_GROUP = "OPT";
private static final String VSTR_FORMAT
= "^" + VNUM + PRE + BUILD + OPT + "$";
private static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
/**
* Constructs a valid <a href="verStr">version string</a> containing
* a <a href="#verNum">version number</a> followed by pre-release and
* build information.
*
* @param s
* A string to be interpreted as a version
*
* @throws IllegalArgumentException
* If the given string cannot be interpreted as a valid
* version
*
* @throws NullPointerException
* If {@code s} is {@code null}
*
* @throws NumberFormatException
* If an element of the version number or the build number
* cannot be represented as an {@link Integer}
*/
static Version parse(String s) {
if (s == null)
throw new NullPointerException();
Matcher m = VSTR_PATTERN.matcher(s);
if (!m.matches())
throw new IllegalArgumentException("Invalid version string: '"
+ s + "'");
// $VNUM is a dot-separated list of integers of arbitrary length
List<Integer> version = new ArrayList<>();
for (String i : m.group(VNUM_GROUP).split("\\."))
version.add(Integer.parseInt(i));
Optional<String> pre = Optional.ofNullable(m.group(PRE_GROUP));
String b = m.group(BUILD_GROUP);
// $BUILD is an integer
Optional<Integer> build = (b == null)
? Optional.empty()
: Optional.of(Integer.parseInt(b));
Optional<String> optional = Optional.ofNullable(m.group(OPT_GROUP));
// empty '+'
if ((m.group(PLUS_GROUP) != null) && !build.isPresent()) {
if (optional.isPresent()) {
if (pre.isPresent())
throw new IllegalArgumentException("'+' found with"
+ " pre-release and optional components:'" + s
+ "'");
} else {
throw new IllegalArgumentException("'+' found with neither"
+ " build or optional components: '" + s + "'");
}
}
return new Version(version, pre, build, optional);
}
}
}

View File

@ -26,6 +26,9 @@
package java.lang;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
class VersionProps {
@ -42,6 +45,18 @@ class VersionProps {
private static final String java_runtime_version =
"@@VERSION_STRING@@";
private static final String VERSION_NUMBER =
"@@VERSION_NUMBER@@";
private static final String VERSION_BUILD =
"@@VERSION_BUILD@@";
private static final String VERSION_PRE =
"@@VERSION_PRE@@";
private static final String VERSION_OPT =
"@@VERSION_OPT@@";
static {
init();
}
@ -52,6 +67,44 @@ class VersionProps {
System.setProperty("java.runtime.name", java_runtime_name);
}
static List<Integer> versionNumbers() {
List<Integer> versionNumbers = new ArrayList<>(4);
int prevIndex = 0;
int index = VERSION_NUMBER.indexOf('.');
while (index > 0) {
versionNumbers.add(
Integer.parseInt(VERSION_NUMBER, prevIndex, index, 10));
prevIndex = index;
index = VERSION_NUMBER.indexOf('.', prevIndex);
}
versionNumbers.add(Integer.parseInt(VERSION_NUMBER,
prevIndex, VERSION_NUMBER.length(), 10));
return versionNumbers;
}
static Optional<String> pre() {
return optionalOf(VERSION_PRE);
}
static Optional<Integer> build() {
return VERSION_BUILD.isEmpty() ?
Optional.empty() :
Optional.of(Integer.parseInt(VERSION_BUILD));
}
static Optional<String> optional() {
return optionalOf(VERSION_OPT);
}
// Treat empty strings as value not being present
private static Optional<String> optionalOf(String value) {
if (!value.isEmpty()) {
return Optional.of(value);
} else {
return Optional.empty();
}
}
/**
* In case you were wondering this method is called by java -version.
* Sad that it prints to stderr; would be nicer if default printed on
@ -111,4 +164,4 @@ class VersionProps {
java_vm_info + ")");
}
}
}