8198548: Initialization race in com.sun.org.apache.xerces.internal.impl.xpath.regex.Token.getRange() on Token.categories

Reviewed-by: lancea
This commit is contained in:
Joe Wang 2018-05-18 18:31:28 -07:00
parent 00b1a87625
commit 1d4a122367

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
*/ */
/* /*
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
@ -37,7 +37,7 @@ import java.util.Vector;
* This class represents a node in parse tree. * This class represents a node in parse tree.
* *
* @xerces.internal * @xerces.internal
* * @LastModified: May 2018
*/ */
class Token implements java.io.Serializable { class Token implements java.io.Serializable {
@ -592,8 +592,9 @@ class Token implements java.io.Serializable {
} }
// ------------------------------------------------------ // ------------------------------------------------------
private final static Map<String, Token> categories = new HashMap<>(); private static volatile Map<String, Token> categories = null;
private final static Map<String, Token> categories2 = new HashMap<>(); private static volatile Map<String, Token> categories2 = null;
private static final Object lock = new Object();
private static final String[] categoryNames = { private static final String[] categoryNames = {
"Cn", "Lu", "Ll", "Lt", "Lm", "Lo", "Mn", "Me", "Mc", "Nd", "Cn", "Lu", "Ll", "Lt", "Lm", "Lo", "Mn", "Me", "Mc", "Nd",
"Nl", "No", "Zs", "Zl", "Zp", "Cc", "Cf", null, "Co", "Cs", "Nl", "No", "Zs", "Zl", "Zp", "Cc", "Cf", null, "Co", "Cs",
@ -742,8 +743,15 @@ class Token implements java.io.Serializable {
private static final int NONBMP_BLOCK_START = 84; private static final int NONBMP_BLOCK_START = 84;
static protected RangeToken getRange(String name, boolean positive) { static protected RangeToken getRange(String name, boolean positive) {
if (Token.categories.size() == 0) { // use local variable for better performance
synchronized (Token.categories) { Map<String, Token> localCat = Token.categories;
if (localCat == null) {
synchronized (lock) {
localCat = Token.categories;
if (localCat == null) {
Map<String, Token> tmpCat = new HashMap<>();
Map<String, Token> tmpCat2 = new HashMap<>();
Token[] ranges = new Token[Token.categoryNames.length]; Token[] ranges = new Token[Token.categoryNames.length];
for (int i = 0; i < ranges.length; i ++) { for (int i = 0; i < ranges.length; i ++) {
ranges[i] = Token.createRange(); ranges[i] = Token.createRange();
@ -821,8 +829,8 @@ class Token implements java.io.Serializable {
if (i == Character.UNASSIGNED) { // Unassigned if (i == Character.UNASSIGNED) { // Unassigned
ranges[i].addRange(0x10000, Token.UTF16_MAX); ranges[i].addRange(0x10000, Token.UTF16_MAX);
} }
Token.categories.put(Token.categoryNames[i], ranges[i]); tmpCat.put(Token.categoryNames[i], ranges[i]);
Token.categories2.put(Token.categoryNames[i], tmpCat2.put(Token.categoryNames[i],
Token.complementRanges(ranges[i])); Token.complementRanges(ranges[i]));
} }
} }
@ -853,8 +861,8 @@ class Token implements java.io.Serializable {
r1.addRange(0xF0000,0xFFFFD); r1.addRange(0xF0000,0xFFFFD);
r1.addRange(0x100000,0x10FFFD); r1.addRange(0x100000,0x10FFFD);
} }
Token.categories.put(n, r1); tmpCat.put(n, r1);
Token.categories2.put(n, Token.complementRanges(r1)); tmpCat2.put(n, Token.complementRanges(r1));
buffer.setLength(0); buffer.setLength(0);
buffer.append("Is"); buffer.append("Is");
if (n.indexOf(' ') >= 0) { if (n.indexOf(' ') >= 0) {
@ -864,16 +872,16 @@ class Token implements java.io.Serializable {
else { else {
buffer.append(n); buffer.append(n);
} }
Token.setAlias(buffer.toString(), n, true); Token.setAlias(tmpCat, tmpCat2, buffer.toString(), n, true);
} }
// TR#18 1.2 // TR#18 1.2
Token.setAlias("ASSIGNED", "Cn", false); Token.setAlias(tmpCat, tmpCat2, "ASSIGNED", "Cn", false);
Token.setAlias("UNASSIGNED", "Cn", true); Token.setAlias(tmpCat, tmpCat2, "UNASSIGNED", "Cn", true);
Token all = Token.createRange(); Token all = Token.createRange();
all.addRange(0, Token.UTF16_MAX); all.addRange(0, Token.UTF16_MAX);
Token.categories.put("ALL", all); tmpCat.put("ALL", all);
Token.categories2.put("ALL", Token.complementRanges(all)); tmpCat2.put("ALL", Token.complementRanges(all));
Token.registerNonXS("ASSIGNED"); Token.registerNonXS("ASSIGNED");
Token.registerNonXS("UNASSIGNED"); Token.registerNonXS("UNASSIGNED");
Token.registerNonXS("ALL"); Token.registerNonXS("ALL");
@ -882,58 +890,58 @@ class Token implements java.io.Serializable {
isalpha.mergeRanges(ranges[Character.UPPERCASE_LETTER]); // Lu isalpha.mergeRanges(ranges[Character.UPPERCASE_LETTER]); // Lu
isalpha.mergeRanges(ranges[Character.LOWERCASE_LETTER]); // Ll isalpha.mergeRanges(ranges[Character.LOWERCASE_LETTER]); // Ll
isalpha.mergeRanges(ranges[Character.OTHER_LETTER]); // Lo isalpha.mergeRanges(ranges[Character.OTHER_LETTER]); // Lo
Token.categories.put("IsAlpha", isalpha); tmpCat.put("IsAlpha", isalpha);
Token.categories2.put("IsAlpha", Token.complementRanges(isalpha)); tmpCat2.put("IsAlpha", Token.complementRanges(isalpha));
Token.registerNonXS("IsAlpha"); Token.registerNonXS("IsAlpha");
Token isalnum = Token.createRange(); Token isalnum = Token.createRange();
isalnum.mergeRanges(isalpha); // Lu Ll Lo isalnum.mergeRanges(isalpha); // Lu Ll Lo
isalnum.mergeRanges(ranges[Character.DECIMAL_DIGIT_NUMBER]); // Nd isalnum.mergeRanges(ranges[Character.DECIMAL_DIGIT_NUMBER]); // Nd
Token.categories.put("IsAlnum", isalnum); tmpCat.put("IsAlnum", isalnum);
Token.categories2.put("IsAlnum", Token.complementRanges(isalnum)); tmpCat2.put("IsAlnum", Token.complementRanges(isalnum));
Token.registerNonXS("IsAlnum"); Token.registerNonXS("IsAlnum");
Token isspace = Token.createRange(); Token isspace = Token.createRange();
isspace.mergeRanges(Token.token_spaces); isspace.mergeRanges(Token.token_spaces);
isspace.mergeRanges(ranges[CHAR_SEPARATOR]); // Z isspace.mergeRanges(ranges[CHAR_SEPARATOR]); // Z
Token.categories.put("IsSpace", isspace); tmpCat.put("IsSpace", isspace);
Token.categories2.put("IsSpace", Token.complementRanges(isspace)); tmpCat2.put("IsSpace", Token.complementRanges(isspace));
Token.registerNonXS("IsSpace"); Token.registerNonXS("IsSpace");
Token isword = Token.createRange(); Token isword = Token.createRange();
isword.mergeRanges(isalnum); // Lu Ll Lo Nd isword.mergeRanges(isalnum); // Lu Ll Lo Nd
isword.addRange('_', '_'); isword.addRange('_', '_');
Token.categories.put("IsWord", isword); tmpCat.put("IsWord", isword);
Token.categories2.put("IsWord", Token.complementRanges(isword)); tmpCat2.put("IsWord", Token.complementRanges(isword));
Token.registerNonXS("IsWord"); Token.registerNonXS("IsWord");
Token isascii = Token.createRange(); Token isascii = Token.createRange();
isascii.addRange(0, 127); isascii.addRange(0, 127);
Token.categories.put("IsASCII", isascii); tmpCat.put("IsASCII", isascii);
Token.categories2.put("IsASCII", Token.complementRanges(isascii)); tmpCat2.put("IsASCII", Token.complementRanges(isascii));
Token.registerNonXS("IsASCII"); Token.registerNonXS("IsASCII");
Token isnotgraph = Token.createRange(); Token isnotgraph = Token.createRange();
isnotgraph.mergeRanges(ranges[CHAR_OTHER]); isnotgraph.mergeRanges(ranges[CHAR_OTHER]);
isnotgraph.addRange(' ', ' '); isnotgraph.addRange(' ', ' ');
Token.categories.put("IsGraph", Token.complementRanges(isnotgraph)); tmpCat.put("IsGraph", Token.complementRanges(isnotgraph));
Token.categories2.put("IsGraph", isnotgraph); tmpCat2.put("IsGraph", isnotgraph);
Token.registerNonXS("IsGraph"); Token.registerNonXS("IsGraph");
Token isxdigit = Token.createRange(); Token isxdigit = Token.createRange();
isxdigit.addRange('0', '9'); isxdigit.addRange('0', '9');
isxdigit.addRange('A', 'F'); isxdigit.addRange('A', 'F');
isxdigit.addRange('a', 'f'); isxdigit.addRange('a', 'f');
Token.categories.put("IsXDigit", Token.complementRanges(isxdigit)); tmpCat.put("IsXDigit", Token.complementRanges(isxdigit));
Token.categories2.put("IsXDigit", isxdigit); tmpCat2.put("IsXDigit", isxdigit);
Token.registerNonXS("IsXDigit"); Token.registerNonXS("IsXDigit");
Token.setAlias("IsDigit", "Nd", true); Token.setAlias(tmpCat, tmpCat2, "IsDigit", "Nd", true);
Token.setAlias("IsUpper", "Lu", true); Token.setAlias(tmpCat, tmpCat2, "IsUpper", "Lu", true);
Token.setAlias("IsLower", "Ll", true); Token.setAlias(tmpCat, tmpCat2, "IsLower", "Ll", true);
Token.setAlias("IsCntrl", "C", true); Token.setAlias(tmpCat, tmpCat2, "IsCntrl", "C", true);
Token.setAlias("IsPrint", "C", false); Token.setAlias(tmpCat, tmpCat2, "IsPrint", "C", false);
Token.setAlias("IsPunct", "P", true); Token.setAlias(tmpCat, tmpCat2, "IsPunct", "P", true);
Token.registerNonXS("IsDigit"); Token.registerNonXS("IsDigit");
Token.registerNonXS("IsUpper"); Token.registerNonXS("IsUpper");
Token.registerNonXS("IsLower"); Token.registerNonXS("IsLower");
@ -941,19 +949,19 @@ class Token implements java.io.Serializable {
Token.registerNonXS("IsPrint"); Token.registerNonXS("IsPrint");
Token.registerNonXS("IsPunct"); Token.registerNonXS("IsPunct");
Token.setAlias("alpha", "IsAlpha", true); Token.setAlias(tmpCat, tmpCat2, "alpha", "IsAlpha", true);
Token.setAlias("alnum", "IsAlnum", true); Token.setAlias(tmpCat, tmpCat2, "alnum", "IsAlnum", true);
Token.setAlias("ascii", "IsASCII", true); Token.setAlias(tmpCat, tmpCat2, "ascii", "IsASCII", true);
Token.setAlias("cntrl", "IsCntrl", true); Token.setAlias(tmpCat, tmpCat2, "cntrl", "IsCntrl", true);
Token.setAlias("digit", "IsDigit", true); Token.setAlias(tmpCat, tmpCat2, "digit", "IsDigit", true);
Token.setAlias("graph", "IsGraph", true); Token.setAlias(tmpCat, tmpCat2, "graph", "IsGraph", true);
Token.setAlias("lower", "IsLower", true); Token.setAlias(tmpCat, tmpCat2, "lower", "IsLower", true);
Token.setAlias("print", "IsPrint", true); Token.setAlias(tmpCat, tmpCat2, "print", "IsPrint", true);
Token.setAlias("punct", "IsPunct", true); Token.setAlias(tmpCat, tmpCat2, "punct", "IsPunct", true);
Token.setAlias("space", "IsSpace", true); Token.setAlias(tmpCat, tmpCat2, "space", "IsSpace", true);
Token.setAlias("upper", "IsUpper", true); Token.setAlias(tmpCat, tmpCat2, "upper", "IsUpper", true);
Token.setAlias("word", "IsWord", true); // Perl extension Token.setAlias(tmpCat, tmpCat2, "word", "IsWord", true); // Perl extension
Token.setAlias("xdigit", "IsXDigit", true); Token.setAlias(tmpCat, tmpCat2, "xdigit", "IsXDigit", true);
Token.registerNonXS("alpha"); Token.registerNonXS("alpha");
Token.registerNonXS("alnum"); Token.registerNonXS("alnum");
Token.registerNonXS("ascii"); Token.registerNonXS("ascii");
@ -967,12 +975,13 @@ class Token implements java.io.Serializable {
Token.registerNonXS("upper"); Token.registerNonXS("upper");
Token.registerNonXS("word"); Token.registerNonXS("word");
Token.registerNonXS("xdigit"); Token.registerNonXS("xdigit");
Token.categories = localCat = Collections.unmodifiableMap(tmpCat);
Token.categories2 = Collections.unmodifiableMap(tmpCat2);
} // localCat == null
} // synchronized } // synchronized
} // if null } // if null
RangeToken tok = positive ? (RangeToken)Token.categories.get(name) return positive ? (RangeToken)localCat.get(name)
: (RangeToken)Token.categories2.get(name); : (RangeToken)Token.categories2.get(name);
//if (tok == null) System.out.println(name);
return tok;
} }
static protected RangeToken getRange(String name, boolean positive, boolean xs) { static protected RangeToken getRange(String name, boolean positive, boolean xs) {
RangeToken range = Token.getRange(name, positive); RangeToken range = Token.getRange(name, positive);
@ -994,15 +1003,16 @@ class Token implements java.io.Serializable {
return Token.nonxs.contains(name); return Token.nonxs.contains(name);
} }
private static void setAlias(String newName, String name, boolean positive) { private static void setAlias(Map<String, Token> tmpCat, Map<String, Token> tmpCat2,
Token t1 = Token.categories.get(name); String newName, String name, boolean positive) {
Token t2 = Token.categories2.get(name); Token t1 = tmpCat.get(name);
Token t2 = tmpCat2.get(name);
if (positive) { if (positive) {
Token.categories.put(newName, t1); tmpCat.put(newName, t1);
Token.categories2.put(newName, t2); tmpCat2.put(newName, t2);
} else { } else {
Token.categories2.put(newName, t1); tmpCat2.put(newName, t1);
Token.categories.put(newName, t2); tmpCat.put(newName, t2);
} }
} }