# HG changeset patch # User redestad # Date 1504116773 -7200 # Node ID 94c45ad89b9c28b8d88e8aa220b235d22f4ecaac # Parent e78bddc74bf579dcd050cef11242e83bb9cc76e3 8186517: sun.nio.cs.StandardCharsets$Aliases and Classes can be lazily loaded Reviewed-by: sherman, martin, plevart diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/make/src/classes/build/tools/charsetmapping/DBCS.java --- a/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java Wed Aug 30 20:12:53 2017 +0200 @@ -179,7 +179,7 @@ .replace("$NAME_CLZ$", clzName) .replace("$NAME_ALIASES$", "sun.nio.cs".equals(pkgName) ? - "StandardCharsets.aliases_" + clzName : + "StandardCharsets.aliases_" + clzName + "()" : "ExtendedCharsets.aliasesFor(\"" + csName + "\")") .replace("$NAME_CS$" , csName) .replace("$CONTAINS$", diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/make/src/classes/build/tools/charsetmapping/SBCS.java --- a/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java Wed Aug 30 20:12:53 2017 +0200 @@ -161,7 +161,7 @@ if (line.indexOf("$NAME_ALIASES$", i) != -1) { if ("sun.nio.cs".equals(pkgName)) line = line.replace("$NAME_ALIASES$", - "StandardCharsets.aliases_" + clzName); + "StandardCharsets.aliases_" + clzName + "()"); else line = line.replace("$NAME_ALIASES$", "ExtendedCharsets.aliasesFor(\"" + csName + "\")"); diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/make/src/classes/build/tools/charsetmapping/SPI.java --- a/jdk/make/src/classes/build/tools/charsetmapping/SPI.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/make/src/classes/build/tools/charsetmapping/SPI.java Wed Aug 30 20:12:53 2017 +0200 @@ -93,21 +93,28 @@ .filter(cs -> cs.pkgName.equals("sun.nio.cs")) .forEach( cs -> { if (cs.aliases == null || cs.aliases.length == 0) { - out.printf(" static final String[] aliases_%s = null;%n%n", + out.printf(" static String[] aliases_%s() { return null; }%n%n", cs.clzName); } else { + boolean methodEnd = true; // non-final for SJIS and MS932 to support sun.nio.cs.map if (cs.clzName.equals("SJIS") || cs.clzName.equals("MS932")) { + out.printf(" static String[] aliases_%s() { return aliases_%s; }%n%n", + cs.clzName, cs.clzName); out.printf(" static String[] aliases_%s = new String[] {%n", cs.clzName); + methodEnd = false; } else { - out.printf(" static final String[] aliases_%s = new String[] {%n", + out.printf(" static String[] aliases_%s() { return new String[] {%n", cs.clzName); } for (String alias : cs.aliases) { - out.printf(" \"%s\",%n", alias); + out.printf(" \"%s\",%n", alias); } - out.printf(" };%n%n"); + out.printf(" };%n%n"); + if (methodEnd) { + out.printf(" }%n%n"); + } } }); Charset cs = charsets.get("SJIS"); diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/make/src/classes/build/tools/charsetmapping/SRC.java --- a/jdk/make/src/classes/build/tools/charsetmapping/SRC.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/make/src/classes/build/tools/charsetmapping/SRC.java Wed Aug 30 20:12:53 2017 +0200 @@ -51,7 +51,7 @@ } else if (line.indexOf("$ALIASES$") != -1) { if ("sun.nio.cs".equals(pkgName)) out.println(line.replace("$ALIASES$", - "StandardCharsets.aliases_" + clzName)); + "StandardCharsets.aliases_" + clzName + "()")); else out.println(line.replace("$ALIASES$", "ExtendedCharsets.aliasesFor(\"" + csName + "\")")); diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/java/lang/StringCoding.java --- a/jdk/src/java.base/share/classes/java/lang/StringCoding.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/StringCoding.java Wed Aug 30 20:12:53 2017 +0200 @@ -42,6 +42,7 @@ import sun.nio.cs.HistoricallyNamedCharset; import sun.nio.cs.ArrayDecoder; import sun.nio.cs.ArrayEncoder; +import sun.nio.cs.StandardCharsets; import static java.lang.String.LATIN1; import static java.lang.String.UTF16; @@ -61,9 +62,9 @@ private static final ThreadLocal> encoder = new ThreadLocal<>(); - private static final Charset ISO_8859_1 = Charset.forName("iso-8859-1"); - private static final Charset US_ASCII = Charset.forName("us-ascii"); - private static final Charset UTF_8 = Charset.forName("utf-8"); + private static final Charset ISO_8859_1 = sun.nio.cs.ISO_8859_1.INSTANCE; + private static final Charset US_ASCII = sun.nio.cs.US_ASCII.INSTANCE; + private static final Charset UTF_8 = sun.nio.cs.UTF_8.INSTANCE; private static boolean warnUnsupportedCharset = true; diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/java/nio/charset/Charset.java --- a/jdk/src/java.base/share/classes/java/nio/charset/Charset.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/java/nio/charset/Charset.java Wed Aug 30 20:12:53 2017 +0200 @@ -609,7 +609,7 @@ if (cs != null) defaultCharset = cs; else - defaultCharset = forName("UTF-8"); + defaultCharset = sun.nio.cs.UTF_8.INSTANCE; } } return defaultCharset; @@ -639,11 +639,10 @@ String[] as = Objects.requireNonNullElse(aliases, zeroAliases); // Skip checks for the standard, built-in Charsets we always load - // during initialization. Use of identity is intentional to be - // consistent with sun.nio.cs.StandardCharsets - if (canonicalName != StandardCharsets.ISO_8859_1 - && canonicalName != StandardCharsets.US_ASCII - && canonicalName != StandardCharsets.UTF_8) { + // during initialization. + if (canonicalName != "ISO-8859-1" + && canonicalName != "US-ASCII" + && canonicalName != "UTF-8") { checkName(canonicalName); for (int i = 0; i < as.length; i++) { checkName(as[i]); diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/java/nio/charset/StandardCharsets.java --- a/jdk/src/java.base/share/classes/java/nio/charset/StandardCharsets.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/java/nio/charset/StandardCharsets.java Wed Aug 30 20:12:53 2017 +0200 @@ -41,15 +41,15 @@ * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the * Unicode character set */ - public static final Charset US_ASCII = Charset.forName("US-ASCII"); + public static final Charset US_ASCII = sun.nio.cs.US_ASCII.INSTANCE; /** * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1 */ - public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + public static final Charset ISO_8859_1 = sun.nio.cs.ISO_8859_1.INSTANCE; /** * Eight-bit UCS Transformation Format */ - public static final Charset UTF_8 = Charset.forName("UTF-8"); + public static final Charset UTF_8 = sun.nio.cs.UTF_8.INSTANCE; /** * Sixteen-bit UCS Transformation Format, big-endian byte order */ diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/CESU_8.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/CESU_8.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/CESU_8.java Wed Aug 30 20:12:53 2017 +0200 @@ -52,7 +52,7 @@ class CESU_8 extends Unicode { public CESU_8() { - super("CESU-8", StandardCharsets.aliases_CESU_8); + super("CESU-8", StandardCharsets.aliases_CESU_8()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java Wed Aug 30 20:12:53 2017 +0200 @@ -31,18 +31,19 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; -import java.util.Arrays; import java.util.Objects; import jdk.internal.HotSpotIntrinsicCandidate; -class ISO_8859_1 +public class ISO_8859_1 extends Charset implements HistoricallyNamedCharset { + public static final ISO_8859_1 INSTANCE = new ISO_8859_1(); + public ISO_8859_1() { - super(StandardCharsets.ISO_8859_1, StandardCharsets.aliases_ISO_8859_1); + super("ISO-8859-1", StandardCharsets.aliases_ISO_8859_1()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template --- a/jdk/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template Wed Aug 30 20:12:53 2017 +0200 @@ -33,6 +33,8 @@ import java.nio.charset.spi.CharsetProvider; import java.util.Iterator; import java.util.Map; +import java.util.Set; +import jdk.internal.vm.annotation.Stable; import sun.security.action.GetPropertyAction; public class StandardCharsets extends CharsetProvider { @@ -43,31 +45,52 @@ _INCLUDE_CACHE_MAP_ // Maps canonical names to class names - private final Map classMap; + private @Stable Map classMap; + // Maps alias names to canonical names - private final Map aliasMap; - // Maps canonical names to cached instances - private final Map cache; + private @Stable Map aliasMap; - private static final String packagePrefix = "sun.nio.cs"; - - public static final String US_ASCII = "US-ASCII"; + // Maps canonical names to cached instances + private @Stable Map cache; - public static final String ISO_8859_1 = "ISO-8859-1"; - - public static final String UTF_8 = "UTF-8"; + private static final String packagePrefix = "sun.nio.cs."; public StandardCharsets() { - this.aliasMap = new Aliases(); - this.classMap = new Classes(); - this.cache = new Cache(); } private String canonicalize(String csn) { - String acn = aliasMap.get(csn); + String acn = aliasMap().get(csn); return (acn != null) ? acn : csn; } + private Map aliasMap() { + Map map = aliasMap; + if (map == null) { + aliasMap = map = new Aliases(); + } + return map; + } + + private Map classMap() { + Map map = classMap; + if (map == null) { + classMap = map = new Classes(); + } + return map; + } + + private Map cache() { + Map map = cache; + if (map == null) { + map = new Cache(); + map.put("utf-8", UTF_8.INSTANCE); + map.put("iso-8859-1", ISO_8859_1.INSTANCE); + map.put("us-ascii", US_ASCII.INSTANCE); + cache = map; + } + return map; + } + // Private ASCII-only version, optimized for interpretation during startup // private static String toLower(String s) { @@ -82,47 +105,47 @@ } if (allLower) return s; - char[] ca = new char[n]; + StringBuilder sb = new StringBuilder(n); for (int i = 0; i < n; i++) { int c = s.charAt(i); if (((c - 'A') | ('Z' - c)) >= 0) - ca[i] = (char)(c + 0x20); + sb.append((char)(c + 0x20)); else - ca[i] = (char)c; + sb.append((char)c); } - return new String(ca); + return sb.toString(); } private Charset lookup(String charsetName) { init(); - String csn = canonicalize(toLower(charsetName)); + + // By checking these built-ins we can avoid initializing Aliases and + // Classes eagerly during bootstrap + String csn; + if (charsetName.equals("UTF-8")) { + return UTF_8.INSTANCE; + } else if (charsetName.equals("US-ASCII")) { + return US_ASCII.INSTANCE; + } else if (charsetName.equals("ISO-8859-1")) { + return ISO_8859_1.INSTANCE; + } else { + csn = canonicalize(toLower(charsetName)); + } // Check cache first - Charset cs = cache.get(csn); + Charset cs = cache().get(csn); if (cs != null) return cs; // Do we even support this charset? - String cln = classMap.get(csn); + String cln = classMap().get(csn); if (cln == null) return null; - // As all charset class names added to classMap are string literals we - // can check identity here as an optimization - if (cln == US_ASCII) { - return cache(csn, new US_ASCII()); - } - if (cln == ISO_8859_1) { - return cache(csn, new ISO_8859_1()); - } - if (cln == UTF_8) { - return cache(csn, new UTF_8()); - } - // Instantiate the charset and cache it try { @SuppressWarnings("deprecation") - Object o = Class.forName(packagePrefix + "." + cln, + Object o = Class.forName(packagePrefix + cln, true, this.getClass().getClassLoader()).newInstance(); return cache(csn, (Charset)o); @@ -134,23 +157,28 @@ } private Charset cache(String csn, Charset cs) { - cache.put(csn, cs); + cache().put(csn, cs); return cs; } public final Charset charsetForName(String charsetName) { synchronized (this) { - return lookup(canonicalize(charsetName)); + return lookup(charsetName); } } public final Iterator charsets() { + Set charsetNames; synchronized (this) { init(); + // Ensure initialized in synchronized block + charsetNames = classMap().keySet(); + aliasMap(); + cache(); } return new Iterator() { - Iterator i = classMap.keySet().iterator(); + Iterator i = charsetNames.iterator(); public boolean hasNext() { return i.hasNext(); @@ -181,6 +209,8 @@ String map = GetPropertyAction.privilegedGetProperty("sun.nio.cs.map"); if (map != null) { + Map aliasMap = aliasMap(); + Map classMap = classMap(); String[] maps = map.split(","); for (int i = 0; i < maps.length; i++) { if (maps[i].equalsIgnoreCase("Windows-31J/Shift_JIS")) { @@ -207,7 +237,7 @@ for (String alias : aliases_MS932) { aliasMap.put(toLower(alias), "windows-31j"); } - cache.put("shift_jis", null); + cache().put("shift_jis", null); break; } } diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java Wed Aug 30 20:12:53 2017 +0200 @@ -36,9 +36,10 @@ extends Charset implements HistoricallyNamedCharset { + public static final US_ASCII INSTANCE = new US_ASCII(); public US_ASCII() { - super(StandardCharsets.US_ASCII, StandardCharsets.aliases_US_ASCII); + super("US-ASCII", StandardCharsets.aliases_US_ASCII()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_16.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_16.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_16.java Wed Aug 30 20:12:53 2017 +0200 @@ -33,7 +33,7 @@ { public UTF_16() { - super("UTF-16", StandardCharsets.aliases_UTF_16); + super("UTF-16", StandardCharsets.aliases_UTF_16()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_16BE.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_16BE.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_16BE.java Wed Aug 30 20:12:53 2017 +0200 @@ -33,7 +33,7 @@ { public UTF_16BE() { - super("UTF-16BE", StandardCharsets.aliases_UTF_16BE); + super("UTF-16BE", StandardCharsets.aliases_UTF_16BE()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_16LE.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_16LE.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_16LE.java Wed Aug 30 20:12:53 2017 +0200 @@ -33,7 +33,7 @@ { public UTF_16LE() { - super("UTF-16LE", StandardCharsets.aliases_UTF_16LE); + super("UTF-16LE", StandardCharsets.aliases_UTF_16LE()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_16LE_BOM.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_16LE_BOM.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_16LE_BOM.java Wed Aug 30 20:12:53 2017 +0200 @@ -33,7 +33,7 @@ { public UTF_16LE_BOM() { - super("x-UTF-16LE-BOM", StandardCharsets.aliases_UTF_16LE_BOM); + super("x-UTF-16LE-BOM", StandardCharsets.aliases_UTF_16LE_BOM()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_32.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32.java Wed Aug 30 20:12:53 2017 +0200 @@ -31,7 +31,7 @@ public class UTF_32 extends Unicode { public UTF_32() { - super("UTF-32", StandardCharsets.aliases_UTF_32); + super("UTF-32", StandardCharsets.aliases_UTF_32()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_32BE.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32BE.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32BE.java Wed Aug 30 20:12:53 2017 +0200 @@ -31,7 +31,7 @@ public class UTF_32BE extends Unicode { public UTF_32BE() { - super("UTF-32BE", StandardCharsets.aliases_UTF_32BE); + super("UTF-32BE", StandardCharsets.aliases_UTF_32BE()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_32BE_BOM.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32BE_BOM.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32BE_BOM.java Wed Aug 30 20:12:53 2017 +0200 @@ -31,7 +31,7 @@ public class UTF_32BE_BOM extends Unicode { public UTF_32BE_BOM() { - super("X-UTF-32BE-BOM", StandardCharsets.aliases_UTF_32BE_BOM); + super("X-UTF-32BE-BOM", StandardCharsets.aliases_UTF_32BE_BOM()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_32LE.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32LE.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32LE.java Wed Aug 30 20:12:53 2017 +0200 @@ -32,7 +32,7 @@ public class UTF_32LE extends Unicode { public UTF_32LE() { - super("UTF-32LE", StandardCharsets.aliases_UTF_32LE); + super("UTF-32LE", StandardCharsets.aliases_UTF_32LE()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_32LE_BOM.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32LE_BOM.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_32LE_BOM.java Wed Aug 30 20:12:53 2017 +0200 @@ -31,7 +31,7 @@ public class UTF_32LE_BOM extends Unicode { public UTF_32LE_BOM() { - super("X-UTF-32LE-BOM", StandardCharsets.aliases_UTF_32LE_BOM); + super("X-UTF-32LE-BOM", StandardCharsets.aliases_UTF_32LE_BOM()); } public String historicalName() { diff -r e78bddc74bf5 -r 94c45ad89b9c jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java Wed Aug 30 10:09:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java Wed Aug 30 20:12:53 2017 +0200 @@ -54,10 +54,12 @@ * */ -class UTF_8 extends Unicode -{ +public final class UTF_8 extends Unicode { + + public static final UTF_8 INSTANCE = new UTF_8(); + public UTF_8() { - super(StandardCharsets.UTF_8, StandardCharsets.aliases_UTF_8); + super("UTF-8", StandardCharsets.aliases_UTF_8()); } public String historicalName() { @@ -72,7 +74,7 @@ return new Encoder(this); } - private static final void updatePositions(Buffer src, int sp, + static final void updatePositions(Buffer src, int sp, Buffer dst, int dp) { src.position(sp - src.arrayOffset()); dst.position(dp - dst.arrayOffset());