# HG changeset patch # User duke # Date 1503585280 -7200 # Node ID e256651a360aeb7fcea6f7dd214d12eaec7e6d9b # Parent 198011f2d98b5e66e31db5c4799dd085ec49190a# Parent 45a4132e9886cae5890c8073502025e361706c44 Merge diff -r 198011f2d98b -r e256651a360a .hgtags-top-repo --- a/.hgtags-top-repo Fri Aug 04 23:29:09 2017 +0000 +++ b/.hgtags-top-repo Thu Aug 24 16:34:40 2017 +0200 @@ -440,3 +440,4 @@ ec4159ebe7050fcc5dcee8a2d150cf948ecc97db jdk-9+178 252475ccfd84cc249f8d6faf4b7806b5e2c384ce jdk-9+179 a133a7d1007b1456bc62824382fd8ac93b45d329 jdk-10+17 +536b81db8075486ca0fe3225d8e59313df5b936c jdk-10+18 diff -r 198011f2d98b -r e256651a360a corba/.hgtags --- a/corba/.hgtags Fri Aug 04 23:29:09 2017 +0000 +++ b/corba/.hgtags Thu Aug 24 16:34:40 2017 +0200 @@ -440,3 +440,4 @@ 9c1e9712648921ae389d623042d22561fad82d75 jdk-9+178 24390da83c5ee9e23ceafbcaff4460a01e37bb3a jdk-9+179 50ff1fd66362f212a8db6de76089d9d0ffa4df0f jdk-10+17 +a923b3f30e7bddb4f960059ddfc7978fc63e2e6e jdk-10+18 diff -r 198011f2d98b -r e256651a360a hotspot/.hgtags --- a/hotspot/.hgtags Fri Aug 04 23:29:09 2017 +0000 +++ b/hotspot/.hgtags Thu Aug 24 16:34:40 2017 +0200 @@ -600,3 +600,4 @@ 9d032191f82fca5ba0aac98682f69c4ff0f1283d jdk-9+178 d2661aa42bff322badbe6c1337fc638d2e0f5730 jdk-9+179 73e2cb8700bfa51304bd4b02f224620859a3f600 jdk-10+17 +c9d3317623d48da3327232c81e3f8cfc0d29d888 jdk-10+18 diff -r 198011f2d98b -r e256651a360a jaxp/.hgtags --- a/jaxp/.hgtags Fri Aug 04 23:29:09 2017 +0000 +++ b/jaxp/.hgtags Thu Aug 24 16:34:40 2017 +0200 @@ -440,3 +440,4 @@ 0983b2dbe17ba4fed3af34e0512ca77a9845fe8a jdk-9+178 87243a3131f79e8b3903eaca6b629abc48f08ace jdk-9+179 97d6f14334cfd766f57c296a5a707c8a709aeff0 jdk-10+17 +7ba7ebbc304a4817e05b72efa6b45ed635839b98 jdk-10+18 diff -r 198011f2d98b -r e256651a360a jaxws/.hgtags --- a/jaxws/.hgtags Fri Aug 04 23:29:09 2017 +0000 +++ b/jaxws/.hgtags Thu Aug 24 16:34:40 2017 +0200 @@ -443,3 +443,4 @@ d0190aaf1816081d9b2e0577b65b793804896d1e jdk-9+178 56ac1831ac5924b5092a53a85d6fc68749501fb8 jdk-9+179 4c07d366c2e177edba7aa54c4b015e4dbf12bc83 jdk-10+17 +6859ffbe2c510c930f88983743578d8186cf6dbd jdk-10+18 diff -r 198011f2d98b -r e256651a360a jdk/.hgtags --- a/jdk/.hgtags Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/.hgtags Thu Aug 24 16:34:40 2017 +0200 @@ -440,3 +440,4 @@ 443025bee731eb2225371b92c1c74b519b7baf33 jdk-9+178 06df1ce4b9b887d05ce6a13f4def3547e434dd1a jdk-9+179 d93f2fd542b7d7855c2cd49ae15ebcc3d441a83b jdk-10+17 +c4b709bad6c5d29294124de5e74e1e2ac84fcf1f jdk-10+18 diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java Thu Aug 24 16:34:40 2017 +0200 @@ -31,6 +31,8 @@ import javax.crypto.SecretKey; import javax.crypto.spec.DESKeySpec; +import jdk.internal.ref.CleanerFactory; + /** * This class represents a DES key. * @@ -74,6 +76,11 @@ this.key = new byte[DESKeySpec.DES_KEY_LEN]; System.arraycopy(key, offset, this.key, 0, DESKeySpec.DES_KEY_LEN); DESKeyGenerator.setParityBit(this.key, 0); + + // Use the cleaner to zero the key when no longer referenced + final byte[] k = this.key; + CleanerFactory.cleaner().register(this, + () -> java.util.Arrays.fill(k, (byte)0x00)); } public byte[] getEncoded() { @@ -144,20 +151,4 @@ getFormat(), getEncoded()); } - - /** - * Ensures that the bytes of this key are - * set to zero when there are no more references to it. - */ - @SuppressWarnings("deprecation") - protected void finalize() throws Throwable { - try { - if (this.key != null) { - java.util.Arrays.fill(this.key, (byte)0x00); - this.key = null; - } - } finally { - super.finalize(); - } - } } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java Thu Aug 24 16:34:40 2017 +0200 @@ -31,6 +31,8 @@ import javax.crypto.SecretKey; import javax.crypto.spec.DESedeKeySpec; +import jdk.internal.ref.CleanerFactory; + /** * This class represents a DES-EDE key. * @@ -76,6 +78,11 @@ DESKeyGenerator.setParityBit(this.key, 0); DESKeyGenerator.setParityBit(this.key, 8); DESKeyGenerator.setParityBit(this.key, 16); + + // Use the cleaner to zero the key when no longer referenced + final byte[] k = this.key; + CleanerFactory.cleaner().register(this, + () -> java.util.Arrays.fill(k, (byte)0x00)); } public byte[] getEncoded() { @@ -145,20 +152,4 @@ getFormat(), getEncoded()); } - - /** - * Ensures that the bytes of this key are - * set to zero when there are no more references to it. - */ - @SuppressWarnings("deprecation") - protected void finalize() throws Throwable { - try { - if (this.key != null) { - java.util.Arrays.fill(this.key, (byte)0x00); - this.key = null; - } - } finally { - super.finalize(); - } - } } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java Thu Aug 24 16:34:40 2017 +0200 @@ -32,6 +32,8 @@ import javax.crypto.SecretKey; import javax.crypto.spec.PBEKeySpec; +import jdk.internal.ref.CleanerFactory; + /** * This class represents a PBE key. * @@ -49,7 +51,7 @@ /** * Creates a PBE key from a given PBE key specification. * - * @param key the given PBE key specification + * @param keytype the given PBE key specification */ PBEKey(PBEKeySpec keySpec, String keytype) throws InvalidKeySpecException { char[] passwd = keySpec.getPassword(); @@ -70,6 +72,11 @@ this.key[i] = (byte) (passwd[i] & 0x7f); java.util.Arrays.fill(passwd, ' '); type = keytype; + + // Use the cleaner to zero the key when no longer referenced + final byte[] k = this.key; + CleanerFactory.cleaner().register(this, + () -> java.util.Arrays.fill(k, (byte)0x00)); } public byte[] getEncoded() { @@ -140,20 +147,4 @@ getFormat(), getEncoded()); } - - /** - * Ensures that the password bytes of this key are - * set to zero when there are no more references to it. - */ - @SuppressWarnings("deprecation") - protected void finalize() throws Throwable { - try { - if (this.key != null) { - java.util.Arrays.fill(this.key, (byte)0x00); - this.key = null; - } - } finally { - super.finalize(); - } - } } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Thu Aug 24 16:34:40 2017 +0200 @@ -40,6 +40,8 @@ import javax.crypto.SecretKey; import javax.crypto.spec.PBEKeySpec; +import jdk.internal.ref.CleanerFactory; + /** * This class represents a PBE key derived using PBKDF2 defined * in PKCS#5 v2.0. meaning that @@ -76,7 +78,8 @@ /** * Creates a PBE key from a given PBE key specification. * - * @param key the given PBE key specification + * @param keySpec the given PBE key specification + * @param prfAlgo the given PBE key algorithm */ PBKDF2KeyImpl(PBEKeySpec keySpec, String prfAlgo) throws InvalidKeySpecException { @@ -120,6 +123,15 @@ throw ike; } this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength); + + // Use the cleaner to zero the key when no longer referenced + final byte[] k = this.key; + final char[] p = this.passwd; + CleanerFactory.cleaner().register(this, + () -> { + java.util.Arrays.fill(k, (byte)0x00); + java.util.Arrays.fill(p, '0'); + }); } private static byte[] deriveKey(final Mac prf, final byte[] password, @@ -262,24 +274,4 @@ return new KeyRep(KeyRep.Type.SECRET, getAlgorithm(), getFormat(), getEncoded()); } - - /** - * Ensures that the password bytes of this key are - * erased when there are no more references to it. - */ - @SuppressWarnings("deprecation") - protected void finalize() throws Throwable { - try { - if (this.passwd != null) { - java.util.Arrays.fill(this.passwd, '0'); - this.passwd = null; - } - if (this.key != null) { - java.util.Arrays.fill(this.key, (byte)0x00); - this.key = null; - } - } finally { - super.finalize(); - } - } } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/java/lang/Class.java --- a/jdk/src/java.base/share/classes/java/lang/Class.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/Class.java Thu Aug 24 16:34:40 2017 +0200 @@ -432,18 +432,21 @@ Objects.requireNonNull(module); Objects.requireNonNull(name); - Class caller = Reflection.getCallerClass(); - if (caller != null && caller.getModule() != module) { - // if caller is null, Class.forName is the last java frame on the stack. - // java.base has all permissions - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { + ClassLoader cl; + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + Class caller = Reflection.getCallerClass(); + if (caller != null && caller.getModule() != module) { + // if caller is null, Class.forName is the last java frame on the stack. + // java.base has all permissions sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); } + PrivilegedAction pa = module::getClassLoader; + cl = AccessController.doPrivileged(pa); + } else { + cl = module.getClassLoader(); } - PrivilegedAction pa = module::getClassLoader; - ClassLoader cl = AccessController.doPrivileged(pa); if (cl != null) { return cl.loadClass(module, name); } else { diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/java/lang/Module.java --- a/jdk/src/java.base/share/classes/java/lang/Module.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/Module.java Thu Aug 24 16:34:40 2017 +0200 @@ -246,7 +246,6 @@ return null; } - // -- // special Module to mean "all unnamed modules" @@ -257,17 +256,38 @@ private static final Module EVERYONE_MODULE = new Module(null); private static final Set EVERYONE_SET = Set.of(EVERYONE_MODULE); + /** + * The holder of data structures to support readability, exports, and + * service use added at runtime with the reflective APIs. + */ + private static class ReflectionData { + /** + * A module (1st key) reads another module (2nd key) + */ + static final WeakPairMap reads = + new WeakPairMap<>(); + + /** + * A module (1st key) exports or opens a package to another module + * (2nd key). The map value is a map of package name to a boolean + * that indicates if the package is opened. + */ + static final WeakPairMap> exports = + new WeakPairMap<>(); + + /** + * A module (1st key) uses a service (2nd key) + */ + static final WeakPairMap, Boolean> uses = + new WeakPairMap<>(); + } + // -- readability -- // the modules that this module reads private volatile Set reads; - // additional module (2nd key) that some module (1st key) reflectively reads - private static final WeakPairMap reflectivelyReads - = new WeakPairMap<>(); - - /** * Indicates if this module reads the given module. This method returns * {@code true} if invoked to test if this module reads itself. It also @@ -300,13 +320,13 @@ } // check if this module reads the other module reflectively - if (reflectivelyReads.containsKeyPair(this, other)) + if (ReflectionData.reads.containsKeyPair(this, other)) return true; // if other is an unnamed module then check if this module reads // all unnamed modules if (!other.isNamed() - && reflectivelyReads.containsKeyPair(this, ALL_UNNAMED_MODULE)) + && ReflectionData.reads.containsKeyPair(this, ALL_UNNAMED_MODULE)) return true; return false; @@ -393,7 +413,7 @@ } // add reflective read - reflectivelyReads.putIfAbsent(this, other, Boolean.TRUE); + ReflectionData.reads.putIfAbsent(this, other, Boolean.TRUE); } } @@ -408,13 +428,6 @@ // if the value contains EVERYONE_MODULE then the package is exported to all private volatile Map> exportedPackages; - // additional exports or opens added at run-time - // this module (1st key), other module (2nd key) - // (package name, open?) (value) - private static final WeakPairMap> - reflectivelyExports = new WeakPairMap<>(); - - /** * Returns {@code true} if this module exports the given package to at * least the given module. @@ -600,7 +613,7 @@ */ private boolean isReflectivelyExportedOrOpen(String pn, Module other, boolean open) { // exported or open to all modules - Map exports = reflectivelyExports.get(this, EVERYONE_MODULE); + Map exports = ReflectionData.exports.get(this, EVERYONE_MODULE); if (exports != null) { Boolean b = exports.get(pn); if (b != null) { @@ -612,7 +625,7 @@ if (other != EVERYONE_MODULE) { // exported or open to other - exports = reflectivelyExports.get(this, other); + exports = ReflectionData.exports.get(this, other); if (exports != null) { Boolean b = exports.get(pn); if (b != null) { @@ -623,7 +636,7 @@ // other is an unnamed module && exported or open to all unnamed if (!other.isNamed()) { - exports = reflectivelyExports.get(this, ALL_UNNAMED_MODULE); + exports = ReflectionData.exports.get(this, ALL_UNNAMED_MODULE); if (exports != null) { Boolean b = exports.get(pn); if (b != null) { @@ -886,8 +899,8 @@ } } - // add package name to reflectivelyExports if absent - Map map = reflectivelyExports + // add package name to exports if absent + Map map = ReflectionData.exports .computeIfAbsent(this, other, (m1, m2) -> new ConcurrentHashMap<>()); if (open) { @@ -932,10 +945,6 @@ // -- services -- - // additional service type (2nd key) that some module (1st key) uses - private static final WeakPairMap, Boolean> reflectivelyUses - = new WeakPairMap<>(); - /** * If the caller's module is this module then update this module to add a * service dependence on the given service type. This method is intended @@ -980,7 +989,7 @@ */ void implAddUses(Class service) { if (!canUse(service)) { - reflectivelyUses.putIfAbsent(this, service, Boolean.TRUE); + ReflectionData.uses.putIfAbsent(this, service, Boolean.TRUE); } } @@ -1011,7 +1020,7 @@ return true; // uses added via addUses - return reflectivelyUses.containsKeyPair(this, service); + return ReflectionData.uses.containsKeyPair(this, service); } @@ -1060,8 +1069,11 @@ Function clf, ModuleLayer layer) { - Map nameToModule = new HashMap<>(); - Map moduleToLoader = new HashMap<>(); + boolean isBootLayer = (ModuleLayer.boot() == null); + + int cap = (int)(cf.modules().size() / 0.75f + 1.0f); + Map nameToModule = new HashMap<>(cap); + Map nameToLoader = new HashMap<>(cap); Set loaders = new HashSet<>(); boolean hasPlatformModules = false; @@ -1070,7 +1082,7 @@ for (ResolvedModule resolvedModule : cf.modules()) { String name = resolvedModule.name(); ClassLoader loader = clf.apply(name); - moduleToLoader.put(name, loader); + nameToLoader.put(name, loader); if (loader == null || loader == ClassLoaders.platformClassLoader()) { if (!(clf instanceof ModuleLoaderMap.Mapper)) { throw new IllegalArgumentException("loader can't be 'null'" @@ -1087,20 +1099,19 @@ ModuleReference mref = resolvedModule.reference(); ModuleDescriptor descriptor = mref.descriptor(); String name = descriptor.name(); - URI uri = mref.location().orElse(null); - ClassLoader loader = moduleToLoader.get(resolvedModule.name()); + ClassLoader loader = nameToLoader.get(name); Module m; if (loader == null && name.equals("java.base")) { // java.base is already defined to the VM m = Object.class.getModule(); } else { + URI uri = mref.location().orElse(null); m = new Module(layer, loader, descriptor, uri); } nameToModule.put(name, m); - moduleToLoader.put(name, loader); } - // setup readability and exports + // setup readability and exports/opens for (ResolvedModule resolvedModule : cf.modules()) { ModuleReference mref = resolvedModule.reference(); ModuleDescriptor descriptor = mref.descriptor(); @@ -1146,7 +1157,18 @@ } // exports and opens - initExportsAndOpens(m, nameToSource, nameToModule, layer.parents()); + if (descriptor.isOpen() || descriptor.isAutomatic()) { + // The VM doesn't special case open or automatic modules yet + // so need to export all packages + for (String source : descriptor.packages()) { + addExportsToAll0(m, source); + } + } else if (isBootLayer && descriptor.opens().isEmpty()) { + // no open packages, no qualified exports to modules in parent layers + initExports(m, nameToModule); + } else { + initExportsAndOpens(m, nameToSource, nameToModule, layer.parents()); + } } // if there are modules defined to the boot or platform class loaders @@ -1161,7 +1183,7 @@ if (!descriptor.provides().isEmpty()) { String name = descriptor.name(); Module m = nameToModule.get(name); - ClassLoader loader = moduleToLoader.get(name); + ClassLoader loader = nameToLoader.get(name); if (loader == null) { bootCatalog.register(m); } else if (loader == pcl) { @@ -1179,7 +1201,6 @@ return nameToModule; } - /** * Find the runtime Module corresponding to the given ResolvedModule * in the given parent layer (or its parents). @@ -1201,25 +1222,55 @@ .orElse(null); } + /** + * Initialize/setup a module's exports. + * + * @param m the module + * @param nameToModule map of module name to Module (for qualified exports) + */ + private static void initExports(Module m, Map nameToModule) { + Map> exportedPackages = new HashMap<>(); + + for (Exports exports : m.getDescriptor().exports()) { + String source = exports.source(); + if (exports.isQualified()) { + // qualified exports + Set targets = new HashSet<>(); + for (String target : exports.targets()) { + Module m2 = nameToModule.get(target); + if (m2 != null) { + addExports0(m, source, m2); + targets.add(m2); + } + } + if (!targets.isEmpty()) { + exportedPackages.put(source, targets); + } + } else { + // unqualified exports + addExportsToAll0(m, source); + exportedPackages.put(source, EVERYONE_SET); + } + } + + if (!exportedPackages.isEmpty()) + m.exportedPackages = exportedPackages; + } /** - * Initialize the maps of exported and open packages for module m. + * Initialize/setup a module's exports. + * + * @param m the module + * @param nameToSource map of module name to Module for modules that m reads + * @param nameToModule map of module name to Module for modules in the layer + * under construction + * @param parents the parent layers */ private static void initExportsAndOpens(Module m, Map nameToSource, Map nameToModule, List parents) { - // The VM doesn't special case open or automatic modules so need to - // export all packages ModuleDescriptor descriptor = m.getDescriptor(); - if (descriptor.isOpen() || descriptor.isAutomatic()) { - assert descriptor.opens().isEmpty(); - for (String source : descriptor.packages()) { - addExportsToAll0(m, source); - } - return; - } - Map> openPackages = new HashMap<>(); Map> exportedPackages = new HashMap<>(); @@ -1272,7 +1323,6 @@ if (!targets.isEmpty()) { exportedPackages.put(source, targets); } - } else { // unqualified exports addExportsToAll0(m, source); diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/java/lang/System.java --- a/jdk/src/java.base/share/classes/java/lang/System.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/System.java Thu Aug 24 16:34:40 2017 +0200 @@ -313,6 +313,10 @@ * @see java.lang.RuntimePermission */ public static void setSecurityManager(final SecurityManager s) { + if (security == null) { + // ensure image reader is initialized + Object.class.getResource("java/lang/ANY"); + } if (s != null) { try { s.checkPackageAccess("java.lang"); diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/java/lang/module/Configuration.java --- a/jdk/src/java.base/share/classes/java/lang/module/Configuration.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/module/Configuration.java Thu Aug 24 16:34:40 2017 +0200 @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Deque; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -41,6 +42,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.internal.module.ModuleReferenceImpl; +import jdk.internal.module.ModuleTarget; + /** * A configuration that is the result of * resolution or resolution with service binding. @@ -121,11 +125,8 @@ this.targetPlatform = null; } - private Configuration(List parents, - Resolver resolver, - boolean check) - { - Map> g = resolver.finish(this, check); + private Configuration(List parents, Resolver resolver) { + Map> g = resolver.finish(this); @SuppressWarnings(value = {"rawtypes", "unchecked"}) Entry[] nameEntries @@ -147,6 +148,62 @@ } /** + * Creates the Configuration for the boot layer from a pre-generated + * readability graph. + * + * @apiNote This method is coded for startup performance. + */ + Configuration(ModuleFinder finder, Map> map) { + int moduleCount = map.size(); + + // create map of name -> ResolvedModule + @SuppressWarnings(value = {"rawtypes", "unchecked"}) + Entry[] nameEntries + = (Entry[])new Entry[moduleCount]; + ResolvedModule[] moduleArray = new ResolvedModule[moduleCount]; + String targetPlatform = null; + int i = 0; + for (String name : map.keySet()) { + ModuleReference mref = finder.find(name).orElse(null); + assert mref != null; + + if (targetPlatform == null && mref instanceof ModuleReferenceImpl) { + ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget(); + if (target != null) { + targetPlatform = target.targetPlatform(); + } + } + + ResolvedModule resolvedModule = new ResolvedModule(this, mref); + moduleArray[i] = resolvedModule; + nameEntries[i] = Map.entry(name, resolvedModule); + i++; + } + Map nameToModule = Map.ofEntries(nameEntries); + + // create entries for readability graph + @SuppressWarnings(value = {"rawtypes", "unchecked"}) + Entry>[] moduleEntries + = (Entry>[])new Entry[moduleCount]; + i = 0; + for (ResolvedModule resolvedModule : moduleArray) { + Set names = map.get(resolvedModule.name()); + ResolvedModule[] readsArray = new ResolvedModule[names.size()]; + int j = 0; + for (String name : names) { + readsArray[j++] = nameToModule.get(name); + } + moduleEntries[i++] = Map.entry(resolvedModule, Set.of(readsArray)); + } + + this.parents = List.of(empty()); + this.graph = Map.ofEntries(moduleEntries); + this.modules = Set.of(moduleArray); + this.nameToModule = nameToModule; + this.targetPlatform = targetPlatform; + } + + /** * Resolves a collection of root modules, with this configuration as its * parent, to create a new configuration. This method works exactly as * specified by the static {@link @@ -233,24 +290,20 @@ /** * Resolves a collection of root modules, with service binding, and with - * the empty configuration as its parent. The consistency checks - * are optionally run. + * the empty configuration as its parent. * * This method is used to create the configuration for the boot layer. */ static Configuration resolveAndBind(ModuleFinder finder, Collection roots, - boolean check, PrintStream traceOutput) { List parents = List.of(empty()); Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput); resolver.resolve(roots).bind(); - - return new Configuration(parents, resolver, check); + return new Configuration(parents, resolver); } - /** * Resolves a collection of root modules to create a configuration. * @@ -356,7 +409,7 @@ Resolver resolver = new Resolver(before, parentList, after, null); resolver.resolve(roots); - return new Configuration(parentList, resolver, true); + return new Configuration(parentList, resolver); } /** @@ -427,7 +480,7 @@ Resolver resolver = new Resolver(before, parentList, after, null); resolver.resolve(roots).bind(); - return new Configuration(parentList, resolver, true); + return new Configuration(parentList, resolver); } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java Thu Aug 24 16:34:40 2017 +0200 @@ -2728,10 +2728,15 @@ @Override public Configuration resolveAndBind(ModuleFinder finder, Collection roots, - boolean check, PrintStream traceOutput) { - return Configuration.resolveAndBind(finder, roots, check, traceOutput); + return Configuration.resolveAndBind(finder, roots, traceOutput); + } + + @Override + public Configuration newConfiguration(ModuleFinder finder, + Map> graph) { + return new Configuration(finder, graph); } }); } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java Thu Aug 24 16:34:40 2017 +0200 @@ -25,9 +25,7 @@ package java.lang.module; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.security.AccessController; import java.security.Permission; import java.security.PrivilegedAction; @@ -40,10 +38,8 @@ import java.util.Optional; import java.util.Set; -import jdk.internal.module.ModuleBootstrap; -import jdk.internal.module.ModulePatcher; import jdk.internal.module.ModulePath; -import jdk.internal.module.SystemModuleFinder; +import jdk.internal.module.SystemModuleFinders; /** * A finder of modules. A {@code ModuleFinder} is used to find modules during @@ -157,53 +153,14 @@ SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("accessSystemModules")); - PrivilegedAction pa = ModuleFinder::privilegedOfSystem; + PrivilegedAction pa = SystemModuleFinders::ofSystem; return AccessController.doPrivileged(pa); } else { - return privilegedOfSystem(); - } - } - - /** - * Returns a module finder that locates the system modules. This method - * assumes it has permissions to access the runtime image. - */ - private static ModuleFinder privilegedOfSystem() { - String home = System.getProperty("java.home"); - Path modules = Paths.get(home, "lib", "modules"); - if (Files.isRegularFile(modules)) { - return SystemModuleFinder.getInstance(); - } else { - Path dir = Paths.get(home, "modules"); - if (Files.isDirectory(dir)) { - return privilegedOf(ModuleBootstrap.patcher(), dir); - } else { - throw new InternalError("Unable to detect the run-time image"); - } + return SystemModuleFinders.ofSystem(); } } /** - * Returns a module finder that locates the system modules in an exploded - * image. The image may be patched. - */ - private static ModuleFinder privilegedOf(ModulePatcher patcher, Path dir) { - ModuleFinder finder = ModulePath.of(patcher, dir); - return new ModuleFinder() { - @Override - public Optional find(String name) { - PrivilegedAction> pa = () -> finder.find(name); - return AccessController.doPrivileged(pa); - } - @Override - public Set findAll() { - PrivilegedAction> pa = finder::findAll; - return AccessController.doPrivileged(pa); - } - }; - } - - /** * Returns a module finder that locates modules on the file system by * searching a sequence of directories and/or packaged modules. * diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/java/lang/module/Resolver.java --- a/jdk/src/java.base/share/classes/java/lang/module/Resolver.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java Thu Aug 24 16:34:40 2017 +0200 @@ -353,25 +353,13 @@ /** * Execute post-resolution checks and returns the module graph of resolved - * modules as {@code Map}. The resolved modules will be in the given - * configuration. - * - * @param check {@true} to execute the post resolution checks + * modules as a map. */ - Map> finish(Configuration cf, - boolean check) - { - if (check) { - detectCycles(); - checkHashes(); - } - + Map> finish(Configuration cf) { + detectCycles(); + checkHashes(); Map> graph = makeGraph(cf); - - if (check) { - checkExportSuppliers(graph); - } - + checkExportSuppliers(graph); return graph; } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/java/net/URL.java --- a/jdk/src/java.base/share/classes/java/net/URL.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/net/URL.java Thu Aug 24 16:34:40 2017 +0200 @@ -409,7 +409,7 @@ } } - protocol = protocol.toLowerCase(Locale.ROOT); + protocol = toLowerCase(protocol); this.protocol = protocol; if (host != null) { @@ -585,7 +585,7 @@ for (i = start ; !aRef && (i < limit) && ((c = spec.charAt(i)) != '/') ; i++) { if (c == ':') { - String s = spec.substring(start, i).toLowerCase(Locale.ROOT); + String s = toLowerCase(spec.substring(start, i)); if (isValidProtocol(s)) { newProtocol = s; start = i + 1; @@ -1318,6 +1318,17 @@ } } + /** + * Returns the protocol in lower case. Special cases known protocols + * to avoid loading locale classes during startup. + */ + static String toLowerCase(String protocol) { + if (protocol.equals("jrt") || protocol.equals("file") || protocol.equals("jar")) { + return protocol; + } else { + return protocol.toLowerCase(Locale.ROOT); + } + } /** * Non-overrideable protocols: "jrt" and "file" diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java --- a/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java Thu Aug 24 16:34:40 2017 +0200 @@ -55,13 +55,13 @@ import java.util.NoSuchElementException; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.stream.Stream; import jdk.internal.misc.VM; import jdk.internal.module.ModulePatcher.PatchedModuleReader; -import jdk.internal.module.SystemModules; import jdk.internal.module.Resources; @@ -139,7 +139,7 @@ // maps package name to loaded module for modules in the boot layer private static final Map packageToModule - = new ConcurrentHashMap<>(SystemModules.PACKAGES_IN_BOOT_LAYER); + = new ConcurrentHashMap<>(1024); // maps a module name to a module reference private final Map nameToModule; @@ -946,9 +946,16 @@ URL url = cs.getLocation(); if (url == null) return perms; - Permission p = null; + + // avoid opening connection when URL is to resource in run-time image + if (url.getProtocol().equals("jrt")) { + perms.add(new RuntimePermission("accessSystemModules")); + return perms; + } + + // open connection to determine the permission needed try { - p = url.openConnection().getPermission(); + Permission p = url.openConnection().getPermission(); if (p != null) { // for directories then need recursive access if (p instanceof FilePermission) { @@ -969,23 +976,26 @@ // -- miscellaneous supporting methods /** - * Returns the ModuleReader for the given module. + * Returns the ModuleReader for the given module, creating it if needed */ private ModuleReader moduleReaderFor(ModuleReference mref) { - return moduleToReader.computeIfAbsent(mref, BuiltinClassLoader::createModuleReader); - } - - /** - * Creates a ModuleReader for the given module. - */ - private static ModuleReader createModuleReader(ModuleReference mref) { - try { - return mref.open(); - } catch (IOException e) { - // Return a null module reader to avoid a future class load - // attempting to open the module again. - return new NullModuleReader(); + ModuleReader reader = moduleToReader.get(mref); + if (reader == null) { + // avoid method reference during startup + Function create = new Function<>() { + public ModuleReader apply(ModuleReference moduleReference) { + try { + return mref.open(); + } catch (IOException e) { + // Return a null module reader to avoid a future class + // load attempting to open the module again. + return new NullModuleReader(); + } + } + }; + reader = moduleToReader.computeIfAbsent(mref, create); } + return reader; } /** diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java --- a/jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java Thu Aug 24 16:34:40 2017 +0200 @@ -25,7 +25,6 @@ package jdk.internal.loader; -import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.InvalidPathException; @@ -38,7 +37,6 @@ import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.VM; - /** * Creates and provides access to the built-in platform and application class * loaders. It also creates the class loader that is used to locate resources @@ -61,23 +59,30 @@ */ static { - // -Xbootclasspth/a or -javaagent Boot-Class-Path + // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute URLClassPath bcp = null; String s = VM.getSavedProperty("jdk.boot.class.path.append"); if (s != null && s.length() > 0) - bcp = toURLClassPath(s); + bcp = new URLClassPath(s, true); // we have a class path if -cp is specified or -m is not specified. // If neither is specified then default to -cp // If -cp is not specified and -m is specified, the value of // java.class.path is an empty string, then no class path. - URLClassPath ucp = new URLClassPath(new URL[0]); String mainMid = System.getProperty("jdk.module.main"); String cp = System.getProperty("java.class.path"); - if (cp == null) - cp = ""; - if (mainMid == null || cp.length() > 0) - addClassPathToUCP(cp, ucp); + if (mainMid == null) { + // no main module specified so class path required + if (cp == null) { + cp = ""; + } + } else { + // main module specified, ignore empty class path + if (cp != null && cp.length() == 0) { + cp = null; + } + } + URLClassPath ucp = new URLClassPath(cp, false); // create the class loaders BOOT_LOADER = new BootClassLoader(bcp); @@ -198,7 +203,7 @@ * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch */ void appendToClassPathForInstrumentation(String path) { - addClassPathToUCP(path, ucp); + ucp.addFile(path); } /** @@ -220,40 +225,11 @@ } /** - * Returns a {@code URLClassPath} of file URLs to each of the elements in - * the given class path. - */ - private static URLClassPath toURLClassPath(String cp) { - URLClassPath ucp = new URLClassPath(new URL[0]); - addClassPathToUCP(cp, ucp); - return ucp; - } - - /** - * Converts the elements in the given class path to file URLs and adds - * them to the given URLClassPath. - */ - private static void addClassPathToUCP(String cp, URLClassPath ucp) { - int off = 0; - int next; - while ((next = cp.indexOf(File.pathSeparator, off)) != -1) { - URL url = toFileURL(cp.substring(off, next)); - if (url != null) - ucp.addURL(url); - off = next + 1; - } - - // remaining - URL url = toFileURL(cp.substring(off)); - if (url != null) - ucp.addURL(url); - } - - /** * Attempts to convert the given string to a file URL. * * @apiNote This is called by the VM */ + @Deprecated private static URL toFileURL(String s) { try { // Use an intermediate File object to construct a URI/URL without @@ -265,5 +241,4 @@ return null; } } - } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java --- a/jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java Thu Aug 24 16:34:40 2017 +0200 @@ -46,6 +46,7 @@ import java.security.PrivilegedExceptionAction; import java.security.cert.Certificate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -66,7 +67,6 @@ import java.util.zip.ZipFile; import jdk.internal.misc.JavaNetURLAccess; -import jdk.internal.misc.JavaNetURLClassLoaderAccess; import jdk.internal.misc.JavaUtilZipFileAccess; import jdk.internal.misc.SharedSecrets; import jdk.internal.util.jar.InvalidJarIndexError; @@ -100,19 +100,19 @@ } /* The original search path of URLs. */ - private ArrayList path = new ArrayList<>(); + private final List path; /* The stack of unopened URLs */ - Stack urls = new Stack<>(); + private final Stack urls = new Stack<>(); /* The resulting search path of Loaders */ - ArrayList loaders = new ArrayList<>(); + private final ArrayList loaders = new ArrayList<>(); /* Map of each URL opened to its corresponding Loader */ - HashMap lmap = new HashMap<>(); + private final HashMap lmap = new HashMap<>(); /* The jar protocol handler to use when creating new URLs */ - private URLStreamHandler jarHandler; + private final URLStreamHandler jarHandler; /* Whether this URLClassLoader has been closed yet */ private boolean closed = false; @@ -137,12 +137,16 @@ public URLClassPath(URL[] urls, URLStreamHandlerFactory factory, AccessControlContext acc) { - for (int i = 0; i < urls.length; i++) { - path.add(urls[i]); + List path = new ArrayList<>(urls.length); + for (URL url : urls) { + path.add(url); } + this.path = path; push(urls); if (factory != null) { jarHandler = factory.createURLStreamHandler("jar"); + } else { + jarHandler = null; } if (DISABLE_ACC_CHECKING) this.acc = null; @@ -150,16 +154,50 @@ this.acc = acc; } - /** - * Constructs a URLClassPath with no additional security restrictions. - * Used by code that implements the class path. - */ - public URLClassPath(URL[] urls) { - this(urls, null, null); + public URLClassPath(URL[] urls, AccessControlContext acc) { + this(urls, null, acc); } - public URLClassPath(URL[] urls, AccessControlContext acc) { - this(urls, null, acc); + /** + * Constructs a URLClassPath from a class path string. + * + * @param cp the class path string + * @param skipEmptyElements indicates if empty elements are ignored or + * treated as the current working directory + * + * @apiNote Used to create the application class path. + */ + URLClassPath(String cp, boolean skipEmptyElements) { + List path = new ArrayList<>(); + if (cp != null) { + // map each element of class path to a file URL + int off = 0; + int next; + while ((next = cp.indexOf(File.pathSeparator, off)) != -1) { + String element = cp.substring(off, next); + if (element.length() > 0 || !skipEmptyElements) { + URL url = toFileURL(element); + if (url != null) path.add(url); + } + off = next + 1; + } + + // remaining element + String element = cp.substring(off); + if (element.length() > 0 || !skipEmptyElements) { + URL url = toFileURL(element); + if (url != null) path.add(url); + } + + // push the URLs + for (int i = path.size() - 1; i >= 0; --i) { + urls.push(path.get(i)); + } + } + + this.path = path; + this.jarHandler = null; + this.acc = null; } public synchronized List closeLoaders() { @@ -198,6 +236,28 @@ } /** + * Appends the specified file path as a file URL to the search path. + */ + public void addFile(String s) { + URL url = toFileURL(s); + if (url != null) { + addURL(url); + } + } + + /** + * Returns a file URL for the given file path. + */ + private static URL toFileURL(String s) { + try { + File f = new File(s).getCanonicalFile(); + return ParseUtil.fileToEncodedURL(f); + } catch (IOException e) { + return null; + } + } + + /** * Returns the original search path of URLs. */ public URL[] getURLs() { diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java Thu Aug 24 16:34:40 2017 +0200 @@ -34,16 +34,10 @@ import java.lang.module.ModuleDescriptor.Provides; import java.lang.module.ModuleDescriptor.Version; import java.lang.module.ModuleFinder; -import java.lang.module.ModuleReader; -import java.lang.module.ModuleReference; -import java.net.URI; -import java.nio.file.Path; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Set; -import java.util.function.Supplier; - -import jdk.internal.module.ModuleHashes; /** * Provides access to non-public methods in java.lang.module. @@ -131,12 +125,16 @@ /** * Resolves a collection of root modules, with service binding - * and the empty configuration as the parent. The post resolution - * checks are optionally run. + * and the empty configuration as the parent. */ Configuration resolveAndBind(ModuleFinder finder, Collection roots, - boolean check, PrintStream traceOutput); + /** + * Creates a configuration from a pre-generated readability graph. + */ + Configuration newConfiguration(ModuleFinder finder, + Map> graph); + } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/DefaultRoots.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/DefaultRoots.java Thu Aug 24 16:34:40 2017 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.module; + +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.util.HashSet; +import java.util.Set; + +/** + * Defines methods to compute the default set of root modules for the unnamed + * module. + */ + +public final class DefaultRoots { + private static final String JAVA_SE = "java.se"; + + private DefaultRoots() { } + + /** + * Returns the default set of root modules for the unnamed module computed from + * the system modules observable with the given module finder. + */ + static Set compute(ModuleFinder systemModuleFinder, ModuleFinder finder) { + Set roots = new HashSet<>(); + + boolean hasJava = false; + if (systemModuleFinder.find(JAVA_SE).isPresent()) { + if (finder == systemModuleFinder || finder.find(JAVA_SE).isPresent()) { + // java.se is a system module + hasJava = true; + roots.add(JAVA_SE); + } + } + + for (ModuleReference mref : systemModuleFinder.findAll()) { + String mn = mref.descriptor().name(); + if (hasJava && mn.startsWith("java.")) { + // not a root + continue; + } + + if (ModuleResolution.doNotResolveByDefault(mref)) { + // not a root + continue; + } + + if ((finder == systemModuleFinder || finder.find(mn).isPresent())) { + // add as root if exports at least one package to all modules + ModuleDescriptor descriptor = mref.descriptor(); + for (ModuleDescriptor.Exports e : descriptor.exports()) { + if (!e.isQualified()) { + roots.add(mn); + break; + } + } + } + } + + return roots; + } + + /** + * Returns the default set of root modules for the unnamed module from the + * modules observable with the given module finder. + */ + public static Set compute(ModuleFinder finder) { + return compute(finder, finder); + } +} diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/ExplodedSystemModules.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ExplodedSystemModules.java Thu Aug 24 16:34:40 2017 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.module; + +import java.lang.module.ModuleDescriptor; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * A dummy SystemModules for use with exploded builds or testing. + */ + +class ExplodedSystemModules implements SystemModules { + @Override + public boolean hasSplitPackages() { + return true; // not known + } + + @Override + public boolean hasIncubatorModules() { + return true; // not known + } + + @Override + public ModuleDescriptor[] moduleDescriptors() { + throw new InternalError(); + } + + @Override + public ModuleTarget[] moduleTargets() { + throw new InternalError(); + } + + @Override + public ModuleHashes[] moduleHashes() { + throw new InternalError(); + } + + @Override + public ModuleResolution[] moduleResolutions() { + throw new InternalError(); + } + + @Override + public Map> moduleReads() { + throw new InternalError(); + } + + @Override + public Map> concealedPackagesToOpen() { + return Collections.emptyMap(); + } + + @Override + public Map> exportedPackagesToOpen() { + return Collections.emptyMap(); + } +} diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Thu Aug 24 16:34:40 2017 +0200 @@ -40,16 +40,20 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; import jdk.internal.loader.BootLoader; import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.misc.JavaLangAccess; +import jdk.internal.misc.JavaLangModuleAccess; import jdk.internal.misc.SharedSecrets; import jdk.internal.perf.PerfCounter; @@ -70,8 +74,6 @@ private static final String JAVA_BASE = "java.base"; - private static final String JAVA_SE = "java.se"; - // the token for "all default modules" private static final String ALL_DEFAULT = "ALL-DEFAULT"; @@ -84,13 +86,13 @@ // the token for "all modules on the module path" private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; + // access to java.lang/module + private static final JavaLangModuleAccess JLMA + = SharedSecrets.getJavaLangModuleAccess(); + // The ModulePatcher for the initial configuration private static final ModulePatcher patcher = initModulePatcher(); - // ModuleFinders for the initial configuration - private static ModuleFinder unlimitedFinder; - private static ModuleFinder limitedFinder; - /** * Returns the ModulePatcher for the initial configuration. */ @@ -98,21 +100,38 @@ return patcher; } + // ModuleFinders for the initial configuration + private static volatile ModuleFinder unlimitedFinder; + private static volatile ModuleFinder limitedFinder; + /** - * Returns the ModuleFinder for the initial configuration before observability - * is limited by the --limit-modules command line option. + * Returns the ModuleFinder for the initial configuration before + * observability is limited by the --limit-modules command line option. + * + * @apiNote Used to support locating modules {@code java.instrument} and + * {@code jdk.management.agent} modules when they are loaded dynamically. */ public static ModuleFinder unlimitedFinder() { - assert unlimitedFinder != null; - return unlimitedFinder; + ModuleFinder finder = unlimitedFinder; + if (finder == null) { + return ModuleFinder.ofSystem(); + } else { + return finder; + } } /** * Returns the ModuleFinder for the initial configuration. + * + * @apiNote Used to support "{@code java --list-modules}". */ public static ModuleFinder limitedFinder() { - assert limitedFinder != null; - return limitedFinder; + ModuleFinder finder = limitedFinder; + if (finder == null) { + return unlimitedFinder(); + } else { + return finder; + } } /** @@ -120,13 +139,60 @@ * * @see java.lang.System#initPhase2() */ - public static ModuleLayer boot() { + public static ModuleLayer boot() throws Exception { + + // Step 0: Command line options + + long t0 = System.nanoTime(); + + ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path"); + ModuleFinder appModulePath = finderFor("jdk.module.path"); + boolean isPatched = patcher.hasPatches(); - // Step 1: Locate system modules (may be patched) + String mainModule = System.getProperty("jdk.module.main"); + Set addModules = addModules(); + Set limitModules = limitModules(); + + PrintStream traceOutput = null; + String trace = getAndRemoveProperty("jdk.module.showModuleResolution"); + if (trace != null && Boolean.parseBoolean(trace)) + traceOutput = System.out; + + + // Step 1: The observable system modules, either all system modules + // or the system modules pre-generated for the initial module (the + // initial module may be the unnamed module). If the system modules + // are pre-generated for the initial module then resolution can be + // skipped. long t1 = System.nanoTime(); - ModuleFinder systemModules = ModuleFinder.ofSystem(); - PerfCounters.systemModulesTime.addElapsedTimeFrom(t1); + + SystemModules systemModules = null; + ModuleFinder systemModuleFinder; + + boolean haveModulePath = (appModulePath != null || upgradeModulePath != null); + boolean needResolution = true; + + if (!haveModulePath && addModules.isEmpty() && limitModules.isEmpty()) { + systemModules = SystemModuleFinders.systemModules(mainModule); + if (systemModules != null && !isPatched && (traceOutput == null)) { + needResolution = false; + } + } + if (systemModules == null) { + // all system modules are observable + systemModules = SystemModuleFinders.allSystemModules(); + } + if (systemModules != null) { + // images build + systemModuleFinder = SystemModuleFinders.of(systemModules); + } else { + // exploded build or testing + systemModules = new ExplodedSystemModules(); + systemModuleFinder = SystemModuleFinders.ofSystem(); + } + + Counters.add("jdk.module.boot.1.systemModulesTime", t1); // Step 2: Define and load java.base. This patches all classes loaded @@ -136,7 +202,7 @@ long t2 = System.nanoTime(); - ModuleReference base = systemModules.find(JAVA_BASE).orElse(null); + ModuleReference base = systemModuleFinder.find(JAVA_BASE).orElse(null); if (base == null) throw new InternalError(JAVA_BASE + " not found"); URI baseUri = base.location().orElse(null); @@ -145,171 +211,138 @@ BootLoader.loadModule(base); Modules.defineModule(null, base.descriptor(), baseUri); - PerfCounters.defineBaseTime.addElapsedTimeFrom(t2); + Counters.add("jdk.module.boot.2.defineBaseTime", t2); // Step 2a: If --validate-modules is specified then the VM needs to // start with only java.base, all other options are ignored. - String propValue = getAndRemoveProperty("jdk.module.minimumBoot"); - if (propValue != null) { + if (getAndRemoveProperty("jdk.module.minimumBoot") != null) { return createMinimalBootLayer(); } - // Step 3: Construct the module path and the set of root modules to - // resolve. If --limit-modules is specified then it limits the set - // modules that are observable. + // Step 3: If resolution is needed then create the module finder and + // the set of root modules to resolve. long t3 = System.nanoTime(); - // --upgrade-module-path option specified to launcher - ModuleFinder upgradeModulePath - = createModulePathFinder("jdk.module.upgrade.path"); - if (upgradeModulePath != null) - systemModules = ModuleFinder.compose(upgradeModulePath, systemModules); + ModuleFinder savedModuleFinder = null; + ModuleFinder finder; + Set roots; + if (needResolution) { - // --module-path option specified to the launcher - ModuleFinder appModulePath = createModulePathFinder("jdk.module.path"); + // upgraded modules override the modules in the run-time image + if (upgradeModulePath != null) + systemModuleFinder = ModuleFinder.compose(upgradeModulePath, + systemModuleFinder); - // The module finder: [--upgrade-module-path] system [--module-path] - ModuleFinder finder = systemModules; - if (appModulePath != null) - finder = ModuleFinder.compose(finder, appModulePath); + // The module finder: [--upgrade-module-path] system [--module-path] + if (appModulePath != null) { + finder = ModuleFinder.compose(systemModuleFinder, appModulePath); + } else { + finder = systemModuleFinder; + } - // The root modules to resolve - Set roots = new HashSet<>(); - - // launcher -m option to specify the main/initial module - String mainModule = System.getProperty("jdk.module.main"); - if (mainModule != null) - roots.add(mainModule); + // The root modules to resolve + roots = new HashSet<>(); - // additional module(s) specified by --add-modules - boolean addAllDefaultModules = false; - boolean addAllSystemModules = false; - boolean addAllApplicationModules = false; - for (String mod: getExtraAddModules()) { - switch (mod) { - case ALL_DEFAULT: - addAllDefaultModules = true; - break; - case ALL_SYSTEM: - addAllSystemModules = true; - break; - case ALL_MODULE_PATH: - addAllApplicationModules = true; - break; - default : - roots.add(mod); - } - } + // launcher -m option to specify the main/initial module + if (mainModule != null) + roots.add(mainModule); - // --limit-modules - unlimitedFinder = finder; - propValue = getAndRemoveProperty("jdk.module.limitmods"); - if (propValue != null) { - Set mods = new HashSet<>(); - for (String mod: propValue.split(",")) { - mods.add(mod); - } - finder = limitFinder(finder, mods, roots); - } - limitedFinder = finder; - - // If there is no initial module specified then assume that the initial - // module is the unnamed module of the application class loader. This - // is implemented by resolving "java.se" and all (non-java.*) modules - // that export an API. If "java.se" is not observable then all java.* - // modules are resolved. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT - // bit set in their ModuleResolution attribute flags are excluded from - // the default set of roots. - if (mainModule == null || addAllDefaultModules) { - boolean hasJava = false; - if (systemModules.find(JAVA_SE).isPresent()) { - // java.se is a system module - if (finder == systemModules || finder.find(JAVA_SE).isPresent()) { - // java.se is observable - hasJava = true; - roots.add(JAVA_SE); + // additional module(s) specified by --add-modules + boolean addAllDefaultModules = false; + boolean addAllSystemModules = false; + boolean addAllApplicationModules = false; + for (String mod : addModules) { + switch (mod) { + case ALL_DEFAULT: + addAllDefaultModules = true; + break; + case ALL_SYSTEM: + addAllSystemModules = true; + break; + case ALL_MODULE_PATH: + addAllApplicationModules = true; + break; + default: + roots.add(mod); } } - for (ModuleReference mref : systemModules.findAll()) { - String mn = mref.descriptor().name(); - if (hasJava && mn.startsWith("java.")) - continue; + // --limit-modules + savedModuleFinder = finder; + if (!limitModules.isEmpty()) { + finder = limitFinder(finder, limitModules, roots); + } - if (ModuleResolution.doNotResolveByDefault(mref)) - continue; + // If there is no initial module specified then assume that the initial + // module is the unnamed module of the application class loader. This + // is implemented by resolving "java.se" and all (non-java.*) modules + // that export an API. If "java.se" is not observable then all java.* + // modules are resolved. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT + // bit set in their ModuleResolution attribute flags are excluded from + // the default set of roots. + if (mainModule == null || addAllDefaultModules) { + roots.addAll(DefaultRoots.compute(systemModuleFinder, finder)); + } - // add as root if observable and exports at least one package - if ((finder == systemModules || finder.find(mn).isPresent())) { - ModuleDescriptor descriptor = mref.descriptor(); - for (ModuleDescriptor.Exports e : descriptor.exports()) { - if (!e.isQualified()) { - roots.add(mn); - break; - } - } - } + // If `--add-modules ALL-SYSTEM` is specified then all observable system + // modules will be resolved. + if (addAllSystemModules) { + ModuleFinder f = finder; // observable modules + systemModuleFinder.findAll() + .stream() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::name) + .filter(mn -> f.find(mn).isPresent()) // observable + .forEach(mn -> roots.add(mn)); } + + // If `--add-modules ALL-MODULE-PATH` is specified then all observable + // modules on the application module path will be resolved. + if (appModulePath != null && addAllApplicationModules) { + ModuleFinder f = finder; // observable modules + appModulePath.findAll() + .stream() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::name) + .filter(mn -> f.find(mn).isPresent()) // observable + .forEach(mn -> roots.add(mn)); + } + } else { + // no resolution case + finder = systemModuleFinder; + roots = null; } - // If `--add-modules ALL-SYSTEM` is specified then all observable system - // modules will be resolved. - if (addAllSystemModules) { - ModuleFinder f = finder; // observable modules - systemModules.findAll() - .stream() - .map(ModuleReference::descriptor) - .map(ModuleDescriptor::name) - .filter(mn -> f.find(mn).isPresent()) // observable - .forEach(mn -> roots.add(mn)); - } - - // If `--add-modules ALL-MODULE-PATH` is specified then all observable - // modules on the application module path will be resolved. - if (appModulePath != null && addAllApplicationModules) { - ModuleFinder f = finder; // observable modules - appModulePath.findAll() - .stream() - .map(ModuleReference::descriptor) - .map(ModuleDescriptor::name) - .filter(mn -> f.find(mn).isPresent()) // observable - .forEach(mn -> roots.add(mn)); - } - - PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t3); - + Counters.add("jdk.module.boot.3.optionsAndRootsTime", t3); // Step 4: Resolve the root modules, with service binding, to create - // the configuration for the boot layer. + // the configuration for the boot layer. If resolution is not needed + // then create the configuration for the boot layer from the + // readability graph created at link time. long t4 = System.nanoTime(); - // determine if post resolution checks are needed - boolean needPostResolutionChecks = true; - if (baseUri.getScheme().equals("jrt") // toLowerCase not needed here - && (upgradeModulePath == null) - && (appModulePath == null) - && (patcher.isEmpty())) { - needPostResolutionChecks = false; + Configuration cf; + if (needResolution) { + cf = JLMA.resolveAndBind(finder, roots, traceOutput); + } else { + Map> map = systemModules.moduleReads(); + cf = JLMA.newConfiguration(systemModuleFinder, map); } - PrintStream traceOutput = null; - propValue = getAndRemoveProperty("jdk.module.showModuleResolution"); - if (propValue != null && Boolean.parseBoolean(propValue)) - traceOutput = System.out; + // check that modules specified to --patch-module are resolved + if (isPatched) { + patcher.patchedModules() + .stream() + .filter(mn -> !cf.findModule(mn).isPresent()) + .forEach(mn -> warnUnknownModule(PATCH_MODULE, mn)); + } - // run the resolver to create the configuration - Configuration cf = SharedSecrets.getJavaLangModuleAccess() - .resolveAndBind(finder, - roots, - needPostResolutionChecks, - traceOutput); - - PerfCounters.resolveTime.addElapsedTimeFrom(t4); + Counters.add("jdk.module.boot.4.resolveTime", t4); // Step 5: Map the modules in the configuration to class loaders. @@ -326,7 +359,7 @@ // check that all modules to be mapped to the boot loader will be // loaded from the runtime image - if (needPostResolutionChecks) { + if (haveModulePath) { for (ResolvedModule resolvedModule : cf.modules()) { ModuleReference mref = resolvedModule.reference(); String name = mref.descriptor().name(); @@ -335,51 +368,54 @@ if (upgradeModulePath != null && upgradeModulePath.find(name).isPresent()) fail(name + ": cannot be loaded from upgrade module path"); - if (!systemModules.find(name).isPresent()) + if (!systemModuleFinder.find(name).isPresent()) fail(name + ": cannot be loaded from application module path"); } } - - // check if module specified in --patch-module is present - for (String mn: patcher.patchedModules()) { - if (!cf.findModule(mn).isPresent()) { - warnUnknownModule(PATCH_MODULE, mn); - } - } } // check for split packages in the modules mapped to the built-in loaders - if (SystemModules.hasSplitPackages() || needPostResolutionChecks) { + if (systemModules.hasSplitPackages() || isPatched || haveModulePath) { checkSplitPackages(cf, clf); } // load/register the modules with the built-in class loaders loadModules(cf, clf); - PerfCounters.loadModulesTime.addElapsedTimeFrom(t5); + Counters.add("jdk.module.boot.5.loadModulesTime", t5); // Step 6: Define all modules to the VM long t6 = System.nanoTime(); ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf); - PerfCounters.layerCreateTime.addElapsedTimeFrom(t6); + Counters.add("jdk.module.boot.6.layerCreateTime", t6); // Step 7: Miscellaneous // check incubating status - checkIncubatingStatus(cf); + if (systemModules.hasIncubatorModules() || haveModulePath) { + checkIncubatingStatus(cf); + } - // --add-reads, --add-exports/--add-opens, and -illegal-access + // --add-reads, --add-exports/--add-opens, and --illegal-access long t7 = System.nanoTime(); addExtraReads(bootLayer); boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer); - addIllegalAccess(bootLayer, upgradeModulePath, extraExportsOrOpens); - PerfCounters.adjustModulesTime.addElapsedTimeFrom(t7); + addIllegalAccess(upgradeModulePath, systemModules, bootLayer, extraExportsOrOpens); + Counters.add("jdk.module.boot.7.adjustModulesTime", t7); + + // save module finders for later use + if (savedModuleFinder != null) { + unlimitedFinder = new SafeModuleFinder(savedModuleFinder); + if (savedModuleFinder != finder) + limitedFinder = new SafeModuleFinder(finder); + } // total time to initialize - PerfCounters.bootstrapTime.addElapsedTimeFrom(t1); + Counters.add("jdk.module.boot.totalTime", t0); + Counters.publish(); return bootLayer; } @@ -391,7 +427,6 @@ Configuration cf = SharedSecrets.getJavaLangModuleAccess() .resolveAndBind(ModuleFinder.ofSystem(), Set.of(JAVA_BASE), - false, null); Function clf = ModuleLoaderMap.mappingFunction(cf); @@ -439,7 +474,6 @@ } } } - } } @@ -489,7 +523,7 @@ * Creates a finder from the module path that is the value of the given * system property and optionally patched by --patch-module */ - private static ModuleFinder createModulePathFinder(String prop) { + private static ModuleFinder finderFor(String prop) { String s = System.getProperty(prop); if (s == null) { return null; @@ -510,35 +544,48 @@ */ private static ModulePatcher initModulePatcher() { Map> map = decode("jdk.module.patch.", - File.pathSeparator, - false); + File.pathSeparator, + false); return new ModulePatcher(map); } /** - * Returns the set of module names specified via --add-modules options - * on the command line + * Returns the set of module names specified by --add-module options. */ - private static Set getExtraAddModules() { + private static Set addModules() { String prefix = "jdk.module.addmods."; int index = 0; - // the system property is removed after decoding String value = getAndRemoveProperty(prefix + index); if (value == null) { return Collections.emptySet(); + } else { + Set modules = new HashSet<>(); + while (value != null) { + for (String s : value.split(",")) { + if (s.length() > 0) modules.add(s); + } + index++; + value = getAndRemoveProperty(prefix + index); + } + return modules; } + } - Set modules = new HashSet<>(); - while (value != null) { - for (String s : value.split(",")) { - if (s.length() > 0) modules.add(s); + /** + * Returns the set of module names specified by --limit-modules. + */ + private static Set limitModules() { + String value = getAndRemoveProperty("jdk.module.limitmods"); + if (value == null) { + return Collections.emptySet(); + } else { + Set names = new HashSet<>(); + for (String name : value.split(",")) { + if (name.length() > 0) names.add(name); } - index++; - value = getAndRemoveProperty(prefix + index); + return names; } - - return modules; } /** @@ -676,8 +723,9 @@ * Process the --illegal-access option (and its default) to open packages * of system modules in the boot layer to code in unnamed modules. */ - private static void addIllegalAccess(ModuleLayer bootLayer, - ModuleFinder upgradeModulePath, + private static void addIllegalAccess(ModuleFinder upgradeModulePath, + SystemModules systemModules, + ModuleLayer bootLayer, boolean extraExportsOrOpens) { String value = getAndRemoveProperty("jdk.module.illegalAccess"); IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT; @@ -702,10 +750,10 @@ IllegalAccessLogger.Builder builder = new IllegalAccessLogger.Builder(mode, System.err); - Map> map1 = SystemModules.concealedPackagesToOpen(); - Map> map2 = SystemModules.exportedPackagesToOpen(); + Map> map1 = systemModules.concealedPackagesToOpen(); + Map> map2 = systemModules.exportedPackagesToOpen(); if (map1.isEmpty() && map2.isEmpty()) { - // need to generate maps when on exploded build + // need to generate (exploded build) IllegalAccessMaps maps = IllegalAccessMaps.generate(limitedFinder()); map1 = maps.concealedPackagesToOpen(); map2 = maps.exportedPackagesToOpen(); @@ -906,6 +954,10 @@ } } + /** + * Returns an iterator that yields all elements of the first iterator + * followed by all the elements of the second iterator. + */ static Iterator concat(Iterator iterator1, Iterator iterator2) { return new Iterator() { @Override @@ -921,23 +973,76 @@ }; } - static class PerfCounters { + /** + * Wraps a (potentially not thread safe) ModuleFinder created during startup + * for use after startup. + */ + static class SafeModuleFinder implements ModuleFinder { + private final Set mrefs; + private volatile Map nameToModule; + + SafeModuleFinder(ModuleFinder finder) { + this.mrefs = Collections.unmodifiableSet(finder.findAll()); + } + @Override + public Optional find(String name) { + Objects.requireNonNull(name); + Map nameToModule = this.nameToModule; + if (nameToModule == null) { + this.nameToModule = nameToModule = mrefs.stream() + .collect(Collectors.toMap(m -> m.descriptor().name(), + Function.identity())); + } + return Optional.ofNullable(nameToModule.get(name)); + } + @Override + public Set findAll() { + return mrefs; + } + } - static PerfCounter systemModulesTime - = PerfCounter.newPerfCounter("jdk.module.bootstrap.systemModulesTime"); - static PerfCounter defineBaseTime - = PerfCounter.newPerfCounter("jdk.module.bootstrap.defineBaseTime"); - static PerfCounter optionsAndRootsTime - = PerfCounter.newPerfCounter("jdk.module.bootstrap.optionsAndRootsTime"); - static PerfCounter resolveTime - = PerfCounter.newPerfCounter("jdk.module.bootstrap.resolveTime"); - static PerfCounter layerCreateTime - = PerfCounter.newPerfCounter("jdk.module.bootstrap.layerCreateTime"); - static PerfCounter loadModulesTime - = PerfCounter.newPerfCounter("jdk.module.bootstrap.loadModulesTime"); - static PerfCounter adjustModulesTime - = PerfCounter.newPerfCounter("jdk.module.bootstrap.adjustModulesTime"); - static PerfCounter bootstrapTime - = PerfCounter.newPerfCounter("jdk.module.bootstrap.totalTime"); + /** + * Counters for startup performance analysis. + */ + static class Counters { + private static final boolean PUBLISH_COUNTERS; + private static final boolean PRINT_COUNTERS; + private static Map counters; + static { + String s = System.getProperty("jdk.module.boot.usePerfData"); + if (s == null) { + PUBLISH_COUNTERS = false; + PRINT_COUNTERS = false; + } else { + PUBLISH_COUNTERS = true; + PRINT_COUNTERS = s.equals("debug"); + counters = new LinkedHashMap<>(); // preserve insert order + } + } + + /** + * Add a counter + */ + static void add(String name, long start) { + if (PUBLISH_COUNTERS || PRINT_COUNTERS) { + counters.put(name, (System.nanoTime() - start)); + } + } + + /** + * Publish the counters to the instrumentation buffer or stdout. + */ + static void publish() { + if (PUBLISH_COUNTERS || PRINT_COUNTERS) { + for (Map.Entry e : counters.entrySet()) { + String name = e.getKey(); + long value = e.getValue(); + if (PUBLISH_COUNTERS) + PerfCounter.newPerfCounter(name).set(value); + if (PRINT_COUNTERS) + System.out.println(name + " = " + value); + } + } + } } } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java Thu Aug 24 16:34:40 2017 +0200 @@ -200,10 +200,10 @@ } /** - * Returns true is this module patcher has no patches. + * Returns true is this module patcher has patches. */ - public boolean isEmpty() { - return map.isEmpty(); + public boolean hasPatches() { + return !map.isEmpty(); } /* diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java Thu Aug 24 16:34:40 2017 +0200 @@ -68,14 +68,14 @@ /** * Constructs a new instance of this class. */ - ModuleReferenceImpl(ModuleDescriptor descriptor, - URI location, - Supplier readerSupplier, - ModulePatcher patcher, - ModuleTarget target, - ModuleHashes recordedHashes, - ModuleHashes.HashSupplier hasher, - ModuleResolution moduleResolution) + public ModuleReferenceImpl(ModuleDescriptor descriptor, + URI location, + Supplier readerSupplier, + ModulePatcher patcher, + ModuleTarget target, + ModuleHashes recordedHashes, + ModuleHashes.HashSupplier hasher, + ModuleResolution moduleResolution) { super(descriptor, Objects.requireNonNull(location)); this.location = location; diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/SystemModuleFinder.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/SystemModuleFinder.java Fri Aug 04 23:29:09 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,469 +0,0 @@ -/* - * Copyright (c) 2015, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.internal.module; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleFinder; -import java.lang.module.ModuleReader; -import java.lang.module.ModuleReference; -import java.net.URI; -import java.net.URLConnection; -import java.nio.ByteBuffer; -import java.util.ArrayDeque; -import java.util.Collections; -import java.util.Deque; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.Spliterator; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -import jdk.internal.jimage.ImageLocation; -import jdk.internal.jimage.ImageReader; -import jdk.internal.jimage.ImageReaderFactory; -import jdk.internal.misc.JavaNetUriAccess; -import jdk.internal.misc.SharedSecrets; -import jdk.internal.module.ModuleHashes.HashSupplier; -import jdk.internal.perf.PerfCounter; - -/** - * A {@code ModuleFinder} that finds modules that are linked into the - * run-time image. - * - * The modules linked into the run-time image are assumed to have the - * Packages attribute. - */ - -public class SystemModuleFinder implements ModuleFinder { - - private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess(); - - private static final PerfCounter initTime - = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime"); - private static final PerfCounter moduleCount - = PerfCounter.newPerfCounter("jdk.module.finder.jimage.modules"); - private static final PerfCounter packageCount - = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages"); - private static final PerfCounter exportsCount - = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports"); - - // singleton finder to find modules in the run-time images - private static final SystemModuleFinder INSTANCE; - - public static SystemModuleFinder getInstance() { - return INSTANCE; - } - - /** - * For now, the module references are created eagerly on the assumption - * that service binding will require all modules to be located. - */ - static { - long t0 = System.nanoTime(); - - INSTANCE = new SystemModuleFinder(); - - initTime.addElapsedTimeFrom(t0); - } - - /** - * Holder class for the ImageReader - */ - private static class SystemImage { - static final ImageReader READER; - static { - long t0 = System.nanoTime(); - READER = ImageReaderFactory.getImageReader(); - initTime.addElapsedTimeFrom(t0); - } - - static ImageReader reader() { - return READER; - } - } - - private static boolean isFastPathSupported() { - return SystemModules.MODULE_NAMES.length > 0; - } - - private static String[] moduleNames() { - if (isFastPathSupported()) - // module names recorded at link time - return SystemModules.MODULE_NAMES; - - // this happens when java.base is patched with java.base - // from an exploded image - return SystemImage.reader().getModuleNames(); - } - - // the set of modules in the run-time image - private final Set modules; - - // maps module name to module reference - private final Map nameToModule; - - // module name to hashes - private final Map hashes; - - private SystemModuleFinder() { - String[] names = moduleNames(); - int n = names.length; - moduleCount.add(n); - - // fastpath is enabled by default. - // It can be disabled for troubleshooting purpose. - boolean disabled = - System.getProperty("jdk.system.module.finder.disabledFastPath") != null; - - ModuleDescriptor[] descriptors; - ModuleTarget[] targets; - ModuleHashes[] recordedHashes; - ModuleResolution[] moduleResolutions; - - // fast loading of ModuleDescriptor of system modules - if (isFastPathSupported() && !disabled) { - descriptors = SystemModules.descriptors(); - targets = SystemModules.targets(); - recordedHashes = SystemModules.hashes(); - moduleResolutions = SystemModules.moduleResolutions(); - } else { - // if fast loading of ModuleDescriptors is disabled - // fallback to read module-info.class - descriptors = new ModuleDescriptor[n]; - targets = new ModuleTarget[n]; - recordedHashes = new ModuleHashes[n]; - moduleResolutions = new ModuleResolution[n]; - ImageReader imageReader = SystemImage.reader(); - for (int i = 0; i < names.length; i++) { - String mn = names[i]; - ImageLocation loc = imageReader.findLocation(mn, "module-info.class"); - ModuleInfo.Attributes attrs = - ModuleInfo.read(imageReader.getResourceBuffer(loc), null); - descriptors[i] = attrs.descriptor(); - targets[i] = attrs.target(); - recordedHashes[i] = attrs.recordedHashes(); - moduleResolutions[i] = attrs.moduleResolution(); - } - } - - Map hashes = null; - boolean secondSeen = false; - // record the hashes to build HashSupplier - for (ModuleHashes mh : recordedHashes) { - if (mh != null) { - // if only one module contain ModuleHashes, use it - if (hashes == null) { - hashes = mh.hashes(); - } else { - if (!secondSeen) { - hashes = new HashMap<>(hashes); - secondSeen = true; - } - hashes.putAll(mh.hashes()); - } - } - } - this.hashes = (hashes == null) ? Map.of() : hashes; - - ModuleReference[] mods = new ModuleReference[n]; - - @SuppressWarnings(value = {"rawtypes", "unchecked"}) - Entry[] map - = (Entry[])new Entry[n]; - - for (int i = 0; i < n; i++) { - ModuleDescriptor md = descriptors[i]; - - // create the ModuleReference - ModuleReference mref = toModuleReference(md, - targets[i], - recordedHashes[i], - hashSupplier(names[i]), - moduleResolutions[i]); - mods[i] = mref; - map[i] = Map.entry(names[i], mref); - - // counters - packageCount.add(md.packages().size()); - exportsCount.add(md.exports().size()); - } - - modules = Set.of(mods); - nameToModule = Map.ofEntries(map); - } - - @Override - public Optional find(String name) { - Objects.requireNonNull(name); - return Optional.ofNullable(nameToModule.get(name)); - } - - @Override - public Set findAll() { - return modules; - } - - private ModuleReference toModuleReference(ModuleDescriptor md, - ModuleTarget target, - ModuleHashes recordedHashes, - HashSupplier hasher, - ModuleResolution mres) { - String mn = md.name(); - URI uri = JNUA.create("jrt", "/".concat(mn)); - - Supplier readerSupplier = new Supplier<>() { - @Override - public ModuleReader get() { - return new ImageModuleReader(mn, uri); - } - }; - - ModuleReference mref = new ModuleReferenceImpl(md, - uri, - readerSupplier, - null, - target, - recordedHashes, - hasher, - mres); - - // may need a reference to a patched module if --patch-module specified - mref = ModuleBootstrap.patcher().patchIfNeeded(mref); - - return mref; - } - - private HashSupplier hashSupplier(String name) { - if (!hashes.containsKey(name)) - return null; - - return new HashSupplier() { - @Override - public byte[] generate(String algorithm) { - return hashes.get(name); - } - }; - } - - /** - * A ModuleReader for reading resources from a module linked into the - * run-time image. - */ - static class ImageModuleReader implements ModuleReader { - private final String module; - private volatile boolean closed; - - /** - * If there is a security manager set then check permission to - * connect to the run-time image. - */ - private static void checkPermissionToConnect(URI uri) { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - try { - URLConnection uc = uri.toURL().openConnection(); - sm.checkPermission(uc.getPermission()); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - } - } - - ImageModuleReader(String module, URI uri) { - checkPermissionToConnect(uri); - this.module = module; - } - - /** - * Returns the ImageLocation for the given resource, {@code null} - * if not found. - */ - private ImageLocation findImageLocation(String name) throws IOException { - Objects.requireNonNull(name); - if (closed) - throw new IOException("ModuleReader is closed"); - ImageReader imageReader = SystemImage.reader(); - if (imageReader != null) { - return imageReader.findLocation(module, name); - } else { - // not an images build - return null; - } - } - - @Override - public Optional find(String name) throws IOException { - ImageLocation location = findImageLocation(name); - if (location != null) { - URI u = URI.create("jrt:/" + module + "/" + name); - return Optional.of(u); - } else { - return Optional.empty(); - } - } - - @Override - public Optional open(String name) throws IOException { - return read(name).map(this::toInputStream); - } - - private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer? - try { - int rem = bb.remaining(); - byte[] bytes = new byte[rem]; - bb.get(bytes); - return new ByteArrayInputStream(bytes); - } finally { - release(bb); - } - } - - @Override - public Optional read(String name) throws IOException { - ImageLocation location = findImageLocation(name); - if (location != null) { - return Optional.of(SystemImage.reader().getResourceBuffer(location)); - } else { - return Optional.empty(); - } - } - - @Override - public void release(ByteBuffer bb) { - Objects.requireNonNull(bb); - ImageReader.releaseByteBuffer(bb); - } - - @Override - public Stream list() throws IOException { - if (closed) - throw new IOException("ModuleReader is closed"); - - Spliterator s = new ModuleContentSpliterator(module); - return StreamSupport.stream(s, false); - } - - @Override - public void close() { - // nothing else to do - closed = true; - } - } - - /** - * A Spliterator for traversing the resources of a module linked into the - * run-time image. - */ - static class ModuleContentSpliterator implements Spliterator { - final String moduleRoot; - final Deque stack; - Iterator iterator; - - ModuleContentSpliterator(String module) throws IOException { - moduleRoot = "/modules/" + module; - stack = new ArrayDeque<>(); - - // push the root node to the stack to get started - ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot); - if (dir == null || !dir.isDirectory()) - throw new IOException(moduleRoot + " not a directory"); - stack.push(dir); - iterator = Collections.emptyIterator(); - } - - /** - * Returns the name of the next non-directory node or {@code null} if - * there are no remaining nodes to visit. - */ - private String next() throws IOException { - for (;;) { - while (iterator.hasNext()) { - ImageReader.Node node = iterator.next(); - String name = node.getName(); - if (node.isDirectory()) { - // build node - ImageReader.Node dir = SystemImage.reader().findNode(name); - assert dir.isDirectory(); - stack.push(dir); - } else { - // strip /modules/$MODULE/ prefix - return name.substring(moduleRoot.length() + 1); - } - } - - if (stack.isEmpty()) { - return null; - } else { - ImageReader.Node dir = stack.poll(); - assert dir.isDirectory(); - iterator = dir.getChildren().iterator(); - } - } - } - - @Override - public boolean tryAdvance(Consumer action) { - String next; - try { - next = next(); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - if (next != null) { - action.accept(next); - return true; - } else { - return false; - } - } - - @Override - public Spliterator trySplit() { - return null; - } - - @Override - public int characteristics() { - return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE; - } - - @Override - public long estimateSize() { - return Long.MAX_VALUE; - } - } -} diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java Thu Aug 24 16:34:40 2017 +0200 @@ -0,0 +1,576 @@ +/* + * Copyright (c) 2015, 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.module; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.net.URLConnection; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import jdk.internal.jimage.ImageLocation; +import jdk.internal.jimage.ImageReader; +import jdk.internal.jimage.ImageReaderFactory; +import jdk.internal.misc.JavaNetUriAccess; +import jdk.internal.misc.SharedSecrets; +import jdk.internal.module.ModuleHashes.HashSupplier; + +/** + * The factory for SystemModules objects and for creating ModuleFinder objects + * that find modules in the runtime image. + * + * This class supports initializing the module system when the runtime is an + * images build, an exploded build, or an images build with java.base patched + * by an exploded java.base. It also supports a testing mode that re-parses + * the module-info.class resources in the run-time image. + */ + +public final class SystemModuleFinders { + private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess(); + + private static final boolean USE_FAST_PATH; + static { + String value = System.getProperty("jdk.system.module.finder.disableFastPath"); + if (value == null) { + USE_FAST_PATH = true; + } else { + USE_FAST_PATH = (value.length() > 0) && !Boolean.parseBoolean(value); + } + } + + // cached ModuleFinder returned from ofSystem + private static volatile ModuleFinder cachedSystemModuleFinder; + + private SystemModuleFinders() { } + + /** + * Returns the SystemModules object to reconstitute all modules. Returns + * null if this is an exploded build or java.base is patched by an exploded + * build. + */ + static SystemModules allSystemModules() { + if (USE_FAST_PATH) { + return SystemModulesMap.allSystemModules(); + } else { + return null; + } + } + + /** + * Returns a SystemModules object to reconstitute the modules for the + * given initial module. If the initial module is null then return the + * SystemModules object to reconstitute the default modules. + * + * Return null if there is no SystemModules class for the initial module, + * this is an exploded build, or java.base is patched by an exploded build. + */ + static SystemModules systemModules(String initialModule) { + if (USE_FAST_PATH) { + if (initialModule == null) { + return SystemModulesMap.defaultSystemModules(); + } + + String[] initialModules = SystemModulesMap.moduleNames(); + for (int i = 0; i < initialModules.length; i++) { + String moduleName = initialModules[i]; + if (initialModule.equals(moduleName)) { + String cn = SystemModulesMap.classNames()[i]; + try { + // one-arg Class.forName as java.base may not be defined + Constructor ctor = Class.forName(cn).getConstructor(); + return (SystemModules) ctor.newInstance(); + } catch (Exception e) { + throw new InternalError(e); + } + } + } + } + return null; + } + + /** + * Returns a ModuleFinder that is backed by the given SystemModules object. + * + * @apiNote The returned ModuleFinder is thread safe. + */ + static ModuleFinder of(SystemModules systemModules) { + ModuleDescriptor[] descriptors = systemModules.moduleDescriptors(); + ModuleTarget[] targets = systemModules.moduleTargets(); + ModuleHashes[] recordedHashes = systemModules.moduleHashes(); + ModuleResolution[] moduleResolutions = systemModules.moduleResolutions(); + + int moduleCount = descriptors.length; + ModuleReference[] mrefs = new ModuleReference[moduleCount]; + @SuppressWarnings(value = {"rawtypes", "unchecked"}) + Map.Entry[] map + = (Map.Entry[])new Map.Entry[moduleCount]; + + Map nameToHash = generateNameToHash(recordedHashes); + + for (int i = 0; i < moduleCount; i++) { + String name = descriptors[i].name(); + HashSupplier hashSupplier = hashSupplier(nameToHash, name); + ModuleReference mref = toModuleReference(descriptors[i], + targets[i], + recordedHashes[i], + hashSupplier, + moduleResolutions[i]); + mrefs[i] = mref; + map[i] = Map.entry(name, mref); + } + + return new SystemModuleFinder(mrefs, map); + } + + /** + * Returns the ModuleFinder to find all system modules. Supports both + * images and exploded builds. + * + * @apiNote Used by ModuleFinder.ofSystem() + */ + public static ModuleFinder ofSystem() { + ModuleFinder finder = cachedSystemModuleFinder; + if (finder != null) { + return finder; + } + + // probe to see if this is an images build + String home = System.getProperty("java.home"); + Path modules = Paths.get(home, "lib", "modules"); + if (Files.isRegularFile(modules)) { + if (USE_FAST_PATH) { + SystemModules systemModules = allSystemModules(); + if (systemModules != null) { + finder = of(systemModules); + } + } + + // fall back to parsing the module-info.class files in image + if (finder == null) { + finder = ofModuleInfos(); + } + + cachedSystemModuleFinder = finder; + return finder; + + } + + // exploded build (do not cache module finder) + Path dir = Paths.get(home, "modules"); + if (!Files.isDirectory(dir)) + throw new InternalError("Unable to detect the run-time image"); + ModuleFinder f = ModulePath.of(ModuleBootstrap.patcher(), dir); + return new ModuleFinder() { + @Override + public Optional find(String name) { + PrivilegedAction> pa = () -> f.find(name); + return AccessController.doPrivileged(pa); + } + @Override + public Set findAll() { + PrivilegedAction> pa = f::findAll; + return AccessController.doPrivileged(pa); + } + }; + } + + /** + * Parses the module-info.class of all module in the runtime image and + * returns a ModuleFinder to find the modules. + * + * @apiNote The returned ModuleFinder is thread safe. + */ + private static ModuleFinder ofModuleInfos() { + // parse the module-info.class in every module + Map nameToAttributes = new HashMap<>(); + Map nameToHash = new HashMap<>(); + ImageReader reader = SystemImage.reader(); + for (String mn : reader.getModuleNames()) { + ImageLocation loc = reader.findLocation(mn, "module-info.class"); + ModuleInfo.Attributes attrs + = ModuleInfo.read(reader.getResourceBuffer(loc), null); + + nameToAttributes.put(mn, attrs); + ModuleHashes hashes = attrs.recordedHashes(); + if (hashes != null) { + for (String name : hashes.names()) { + nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name)); + } + } + } + + // create a ModuleReference for each module + Set mrefs = new HashSet<>(); + Map nameToModule = new HashMap<>(); + for (Map.Entry e : nameToAttributes.entrySet()) { + String mn = e.getKey(); + ModuleInfo.Attributes attrs = e.getValue(); + HashSupplier hashSupplier = hashSupplier(nameToHash, mn); + ModuleReference mref = toModuleReference(attrs.descriptor(), + attrs.target(), + attrs.recordedHashes(), + hashSupplier, + attrs.moduleResolution()); + mrefs.add(mref); + nameToModule.put(mn, mref); + } + + return new SystemModuleFinder(mrefs, nameToModule); + } + + /** + * A ModuleFinder that finds module in an array or set of modules. + */ + private static class SystemModuleFinder implements ModuleFinder { + final Set mrefs; + final Map nameToModule; + + SystemModuleFinder(ModuleReference[] array, + Map.Entry[] map) { + this.mrefs = Set.of(array); + this.nameToModule = Map.ofEntries(map); + } + + SystemModuleFinder(Set mrefs, + Map nameToModule) { + this.mrefs = Collections.unmodifiableSet(mrefs); + this.nameToModule = Collections.unmodifiableMap(nameToModule); + } + + @Override + public Optional find(String name) { + Objects.requireNonNull(name); + return Optional.ofNullable(nameToModule.get(name)); + } + + @Override + public Set findAll() { + return mrefs; + } + } + + /** + * Creates a ModuleReference to the system module. + */ + static ModuleReference toModuleReference(ModuleDescriptor descriptor, + ModuleTarget target, + ModuleHashes recordedHashes, + HashSupplier hasher, + ModuleResolution mres) { + String mn = descriptor.name(); + URI uri = JNUA.create("jrt", "/".concat(mn)); + + Supplier readerSupplier = new Supplier<>() { + @Override + public ModuleReader get() { + return new SystemModuleReader(mn, uri); + } + }; + + ModuleReference mref = new ModuleReferenceImpl(descriptor, + uri, + readerSupplier, + null, + target, + recordedHashes, + hasher, + mres); + + // may need a reference to a patched module if --patch-module specified + mref = ModuleBootstrap.patcher().patchIfNeeded(mref); + + return mref; + } + + /** + * Generates a map of module name to hash value. + */ + static Map generateNameToHash(ModuleHashes[] recordedHashes) { + Map nameToHash = null; + + boolean secondSeen = false; + // record the hashes to build HashSupplier + for (ModuleHashes mh : recordedHashes) { + if (mh != null) { + // if only one module contain ModuleHashes, use it + if (nameToHash == null) { + nameToHash = mh.hashes(); + } else { + if (!secondSeen) { + nameToHash = new HashMap<>(nameToHash); + secondSeen = true; + } + nameToHash.putAll(mh.hashes()); + } + } + } + return (nameToHash != null) ? nameToHash : Collections.emptyMap(); + } + + /** + * Returns a HashSupplier that returns the hash of the given module. + */ + static HashSupplier hashSupplier(Map nameToHash, String name) { + byte[] hash = nameToHash.get(name); + if (hash != null) { + // avoid lambda here + return new HashSupplier() { + @Override + public byte[] generate(String algorithm) { + return hash; + } + }; + } else { + return null; + } + } + + /** + * Holder class for the ImageReader + * + * @apiNote This class must be loaded before a security manager is set. + */ + private static class SystemImage { + static final ImageReader READER = ImageReaderFactory.getImageReader(); + static ImageReader reader() { + return READER; + } + } + + /** + * A ModuleReader for reading resources from a module linked into the + * run-time image. + */ + private static class SystemModuleReader implements ModuleReader { + private final String module; + private volatile boolean closed; + + /** + * If there is a security manager set then check permission to + * connect to the run-time image. + */ + private static void checkPermissionToConnect(URI uri) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + URLConnection uc = uri.toURL().openConnection(); + sm.checkPermission(uc.getPermission()); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + } + + SystemModuleReader(String module, URI uri) { + checkPermissionToConnect(uri); + this.module = module; + } + + /** + * Returns the ImageLocation for the given resource, {@code null} + * if not found. + */ + private ImageLocation findImageLocation(String name) throws IOException { + Objects.requireNonNull(name); + if (closed) + throw new IOException("ModuleReader is closed"); + ImageReader imageReader = SystemImage.reader(); + if (imageReader != null) { + return imageReader.findLocation(module, name); + } else { + // not an images build + return null; + } + } + + @Override + public Optional find(String name) throws IOException { + ImageLocation location = findImageLocation(name); + if (location != null) { + URI u = URI.create("jrt:/" + module + "/" + name); + return Optional.of(u); + } else { + return Optional.empty(); + } + } + + @Override + public Optional open(String name) throws IOException { + return read(name).map(this::toInputStream); + } + + private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer? + try { + int rem = bb.remaining(); + byte[] bytes = new byte[rem]; + bb.get(bytes); + return new ByteArrayInputStream(bytes); + } finally { + release(bb); + } + } + + @Override + public Optional read(String name) throws IOException { + ImageLocation location = findImageLocation(name); + if (location != null) { + return Optional.of(SystemImage.reader().getResourceBuffer(location)); + } else { + return Optional.empty(); + } + } + + @Override + public void release(ByteBuffer bb) { + Objects.requireNonNull(bb); + ImageReader.releaseByteBuffer(bb); + } + + @Override + public Stream list() throws IOException { + if (closed) + throw new IOException("ModuleReader is closed"); + + Spliterator s = new ModuleContentSpliterator(module); + return StreamSupport.stream(s, false); + } + + @Override + public void close() { + // nothing else to do + closed = true; + } + } + + /** + * A Spliterator for traversing the resources of a module linked into the + * run-time image. + */ + private static class ModuleContentSpliterator implements Spliterator { + final String moduleRoot; + final Deque stack; + Iterator iterator; + + ModuleContentSpliterator(String module) throws IOException { + moduleRoot = "/modules/" + module; + stack = new ArrayDeque<>(); + + // push the root node to the stack to get started + ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot); + if (dir == null || !dir.isDirectory()) + throw new IOException(moduleRoot + " not a directory"); + stack.push(dir); + iterator = Collections.emptyIterator(); + } + + /** + * Returns the name of the next non-directory node or {@code null} if + * there are no remaining nodes to visit. + */ + private String next() throws IOException { + for (;;) { + while (iterator.hasNext()) { + ImageReader.Node node = iterator.next(); + String name = node.getName(); + if (node.isDirectory()) { + // build node + ImageReader.Node dir = SystemImage.reader().findNode(name); + assert dir.isDirectory(); + stack.push(dir); + } else { + // strip /modules/$MODULE/ prefix + return name.substring(moduleRoot.length() + 1); + } + } + + if (stack.isEmpty()) { + return null; + } else { + ImageReader.Node dir = stack.poll(); + assert dir.isDirectory(); + iterator = dir.getChildren().iterator(); + } + } + } + + @Override + public boolean tryAdvance(Consumer action) { + String next; + try { + next = next(); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + if (next != null) { + action.accept(next); + return true; + } else { + return false; + } + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public int characteristics() { + return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE; + } + + @Override + public long estimateSize() { + return Long.MAX_VALUE; + } + } +} diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java Thu Aug 24 16:34:40 2017 +0200 @@ -26,94 +26,73 @@ package jdk.internal.module; import java.lang.module.ModuleDescriptor; -import java.util.Collections; import java.util.Map; import java.util.Set; /** - * SystemModules class will be generated at link time to create - * ModuleDescriptor for the system modules directly to improve - * the module descriptor reconstitution time. + * A SystemModules object reconstitutes module descriptors and other modules + * attributes in an efficient way to avoid parsing module-info.class files at + * startup. Implementations of this class are generated by the "system modules" + * jlink plugin. * - * This will skip parsing of module-info.class file and validating - * names such as module name, package name, service and provider type names. - * It also avoids taking a defensive copy of any collection. - * + * @see SystemModuleFinders * @see jdk.tools.jlink.internal.plugins.SystemModulesPlugin */ -public final class SystemModules { - /** - * Name of the system modules. - * - * This array provides a way for SystemModuleFinder to fallback - * and read module-info.class from the run-time image instead of - * the fastpath. - */ - public static final String[] MODULE_NAMES = new String[0]; + +interface SystemModules { /** - * Number of packages in the boot layer from the installed modules. - * - * Don't make it final to avoid inlining during compile time as - * the value will be changed at jlink time. + * Returns false if the module reconstituted by this SystemModules object + * have no overlapping packages. Returns true if there are overlapping + * packages or unknown. */ - public static int PACKAGES_IN_BOOT_LAYER = 1024; + boolean hasSplitPackages(); + + /** + * Return false if the modules reconstituted by this SystemModules object + * do not include any incubator modules. Returns true if there are + * incubating modules or unknown. + */ + boolean hasIncubatorModules(); /** - * Return true if there are no split packages in the run-time image. + * Returns the non-empty array of ModuleDescriptor objects. */ - public static boolean hasSplitPackages() { - return true; - } + ModuleDescriptor[] moduleDescriptors(); /** - * Returns a non-empty array of ModuleDescriptor objects in the run-time image. - * - * When running an exploded image it returns an empty array. + * Returns the array of ModuleTarget objects. The array elements correspond + * to the array of ModuleDescriptor objects. */ - public static ModuleDescriptor[] descriptors() { - throw new InternalError("expected to be overridden at link time"); - } + ModuleTarget[] moduleTargets(); /** - * Returns a non-empty array of ModuleTarget objects in the run-time image. - * - * When running an exploded image it returns an empty array. + * Returns the array of ModuleHashes objects. The array elements correspond + * to the array of ModuleDescriptor objects. */ - public static ModuleTarget[] targets() { - throw new InternalError("expected to be overridden at link time"); - } + ModuleHashes[] moduleHashes(); /** - * Returns a non-empty array of ModuleHashes recorded in each module - * in the run-time image. - * - * When running an exploded image it returns an empty array. + * Returns the array of ModuleResolution objects. The array elements correspond + * to the array of ModuleDescriptor objects. */ - public static ModuleHashes[] hashes() { - throw new InternalError("expected to be overridden at link time"); - } + ModuleResolution[] moduleResolutions(); /** - * Returns a non-empty array of ModuleResolutions in the run-time image. + * Returns the map representing readability graph for the modules reconstituted + * by this SystemModules object. */ - public static ModuleResolution[] moduleResolutions() { - throw new InternalError("expected to be overridden at link time"); - } + Map> moduleReads(); /** * Returns the map of module concealed packages to open. The map key is the * module name, the value is the set of concealed packages to open. */ - public static Map> concealedPackagesToOpen() { - return Collections.emptyMap(); - } + Map> concealedPackagesToOpen(); /** * Returns the map of module exported packages to open. The map key is the * module name, the value is the set of exported packages to open. */ - public static Map> exportedPackagesToOpen() { - return Collections.emptyMap(); - } + Map> exportedPackagesToOpen(); } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/jdk/internal/module/SystemModulesMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModulesMap.java Thu Aug 24 16:34:40 2017 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.module; + +/** + * This class is generated/overridden at link time to return the names of the + * SystemModules classes generated at link time. + * + * @see SystemModuleFinders + * @see jdk.tools.jlink.internal.plugins.SystemModulesPlugin + */ + +class SystemModulesMap { + + /** + * Returns the SystemModules object to reconstitute all modules or null + * if this is an exploded build. + */ + static SystemModules allSystemModules() { + return null; + } + + /** + * Returns the SystemModules object to reconstitute default modules or null + * if this is an exploded build. + */ + static SystemModules defaultSystemModules() { + return null; + } + + /** + * Returns the array of initial module names identified at link time. + */ + static String[] moduleNames() { + return new String[0]; + } + + /** + * Returns the array of of SystemModules class names. The elements + * correspond to the elements in the array returned by moduleNames(). + */ + static String[] classNames() { + return new String[0]; + } +} \ No newline at end of file diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java --- a/jdk/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java Thu Aug 24 16:34:40 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -26,10 +26,18 @@ package sun.nio.ch; import java.io.IOException; -import java.nio.channels.*; -import java.nio.channels.spi.*; import java.net.SocketException; -import java.util.*; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.IllegalSelectorException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; /** @@ -54,23 +62,18 @@ super(sp); keys = new HashSet<>(); selectedKeys = new HashSet<>(); - if (Util.atBugLevel("1.4")) { - publicKeys = keys; - publicSelectedKeys = selectedKeys; - } else { - publicKeys = Collections.unmodifiableSet(keys); - publicSelectedKeys = Util.ungrowableSet(selectedKeys); - } + publicKeys = Collections.unmodifiableSet(keys); + publicSelectedKeys = Util.ungrowableSet(selectedKeys); } public Set keys() { - if (!isOpen() && !Util.atBugLevel("1.4")) + if (!isOpen()) throw new ClosedSelectorException(); return publicKeys; } public Set selectedKeys() { - if (!isOpen() && !Util.atBugLevel("1.4")) + if (!isOpen()) throw new ClosedSelectorException(); return publicSelectedKeys; } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/sun/nio/ch/Util.java --- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java Thu Aug 24 16:34:40 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -25,13 +25,16 @@ package sun.nio.ch; -import java.lang.reflect.*; import java.io.FileDescriptor; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.*; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; import jdk.internal.misc.Unsafe; import sun.security.action.GetPropertyAction; @@ -456,21 +459,4 @@ } return dbb; } - - - // -- Bug compatibility -- - - private static volatile String bugLevel; - - static boolean atBugLevel(String bl) { // package-private - if (bugLevel == null) { - if (!jdk.internal.misc.VM.isBooted()) - return false; - String value = GetPropertyAction - .privilegedGetProperty("sun.nio.ch.bugLevel"); - bugLevel = (value != null) ? value : ""; - } - return bugLevel.equals(bl); - } - } diff -r 198011f2d98b -r e256651a360a jdk/src/java.base/share/classes/sun/security/provider/KeyProtector.java --- a/jdk/src/java.base/share/classes/sun/security/provider/KeyProtector.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/KeyProtector.java Thu Aug 24 16:34:40 2017 +0200 @@ -35,6 +35,7 @@ import java.security.UnrecoverableKeyException; import java.util.*; +import jdk.internal.ref.CleanerFactory; import sun.security.pkcs.PKCS8Key; import sun.security.pkcs.EncryptedPrivateKeyInfo; import sun.security.x509.AlgorithmId; @@ -141,18 +142,10 @@ passwdBytes[j++] = (byte)(password[i] >> 8); passwdBytes[j++] = (byte)password[i]; } - } - - /** - * Ensures that the password bytes of this key protector are - * set to zero when there are no more references to it. - */ - @SuppressWarnings("deprecation") - protected void finalize() { - if (passwdBytes != null) { - Arrays.fill(passwdBytes, (byte)0x00); - passwdBytes = null; - } + // Use the cleaner to zero the password when no longer referenced + final byte[] k = this.passwdBytes; + CleanerFactory.cleaner().register(this, + () -> java.util.Arrays.fill(k, (byte)0x00)); } /* diff -r 198011f2d98b -r e256651a360a jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java --- a/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java Thu Aug 24 16:34:40 2017 +0200 @@ -275,6 +275,12 @@ sa = new InetSocketAddress(localaddress, port); } ServerSocket ss = new ServerSocket(); + if (port == 0) { + // Only need SO_REUSEADDR if we're using a fixed port. If we + // start seeing EADDRINUSE due to collisions in free ports + // then we should retry the bind() a few times. + ss.setReuseAddress(false); + } ss.bind(sa); return new SocketListenKey(ss); } diff -r 198011f2d98b -r e256651a360a jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c --- a/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c Thu Aug 24 16:34:40 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2017, 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 @@ -119,8 +119,26 @@ return (char *)dbgsysTlsGet(tlsIndex); } +/* Set options common to client and server sides */ static jdwpTransportError -setOptions(int fd) +setOptionsCommon(int fd) +{ + jvalue dontcare; + int err; + + dontcare.i = 0; /* keep compiler happy */ + + err = dbgsysSetSocketOption(fd, TCP_NODELAY, JNI_TRUE, dontcare); + if (err < 0) { + RETURN_IO_ERROR("setsockopt TCPNODELAY failed"); + } + + return JDWPTRANSPORT_ERROR_NONE; +} + +/* Set the SO_REUSEADDR option */ +static jdwpTransportError +setReuseAddrOption(int fd) { jvalue dontcare; int err; @@ -132,11 +150,6 @@ RETURN_IO_ERROR("setsockopt SO_REUSEADDR failed"); } - err = dbgsysSetSocketOption(fd, TCP_NODELAY, JNI_TRUE, dontcare); - if (err < 0) { - RETURN_IO_ERROR("setsockopt TCPNODELAY failed"); - } - return JDWPTRANSPORT_ERROR_NONE; } @@ -350,10 +363,21 @@ RETURN_IO_ERROR("socket creation failed"); } - err = setOptions(serverSocketFD); + err = setOptionsCommon(serverSocketFD); if (err) { return err; } + if (sa.sin_port != 0) { + /* + * Only need SO_REUSEADDR if we're using a fixed port. If we + * start seeing EADDRINUSE due to collisions in free ports + * then we should retry the dbgsysBind() a few times. + */ + err = setReuseAddrOption(serverSocketFD); + if (err) { + return err; + } + } err = dbgsysBind(serverSocketFD, (struct sockaddr *)&sa, sizeof(sa)); if (err < 0) { @@ -510,12 +534,18 @@ RETURN_IO_ERROR("unable to create socket"); } - err = setOptions(socketFD); + err = setOptionsCommon(socketFD); if (err) { return err; } /* + * We don't call setReuseAddrOption() for the non-server socket + * case. If we start seeing EADDRINUSE due to collisions in free + * ports then we should retry the dbgsysConnect() a few times. + */ + + /* * To do a timed connect we make the socket non-blocking * and poll with a timeout; */ diff -r 198011f2d98b -r e256651a360a jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java Thu Aug 24 16:34:40 2017 +0200 @@ -28,6 +28,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Exports; import java.lang.module.ModuleDescriptor.Opens; @@ -37,12 +38,15 @@ import java.lang.module.ModuleFinder; import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; +import java.lang.module.ResolvedModule; import java.net.URI; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -50,18 +54,20 @@ import java.util.Set; import java.util.TreeSet; import java.util.function.IntSupplier; +import java.util.function.Supplier; import java.util.stream.Collectors; import jdk.internal.module.Checks; import jdk.internal.module.ClassFileAttributes; import jdk.internal.module.ClassFileConstants; +import jdk.internal.module.DefaultRoots; import jdk.internal.module.IllegalAccessMaps; import jdk.internal.module.ModuleHashes; import jdk.internal.module.ModuleInfo.Attributes; import jdk.internal.module.ModuleInfoExtender; +import jdk.internal.module.ModuleReferenceImpl; import jdk.internal.module.ModuleResolution; import jdk.internal.module.ModuleTarget; -import jdk.internal.module.SystemModules; import jdk.internal.org.objectweb.asm.Attribute; import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.ClassVisitor; @@ -72,33 +78,42 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; import jdk.tools.jlink.internal.ModuleSorter; +import jdk.tools.jlink.plugin.Plugin; import jdk.tools.jlink.plugin.PluginException; import jdk.tools.jlink.plugin.ResourcePool; -import jdk.tools.jlink.plugin.Plugin; import jdk.tools.jlink.plugin.ResourcePoolBuilder; import jdk.tools.jlink.plugin.ResourcePoolEntry; /** - * Jlink plugin to reconstitute module descriptors for system modules. - * It will extend module-info.class with ModulePackages attribute, - * if not present. It also determines the number of packages of - * the boot layer at link time. + * Jlink plugin to reconstitute module descriptors and other attributes for system + * modules. The plugin generates implementations of SystemModules to avoid parsing + * module-info.class files at startup. It also generates SystemModulesMap to return + * the SystemModules implementation for a specific initial module. * - * This plugin will override jdk.internal.module.SystemModules class + * As a side effect, the plugin adds the ModulePackages class file attribute to the + * module-info.class files that don't have the attribute. * - * @see jdk.internal.module.SystemModuleFinder - * @see SystemModules + * @see jdk.internal.module.SystemModuleFinders + * @see jdk.internal.module.SystemModules */ + public final class SystemModulesPlugin implements Plugin { private static final String NAME = "system-modules"; private static final String DESCRIPTION = - PluginsResourceBundle.getDescription(NAME); + PluginsResourceBundle.getDescription(NAME); + private static final String SYSTEM_MODULES_MAP_CLASS = + "jdk/internal/module/SystemModulesMap"; + private static final String SYSTEM_MODULES_CLASS_PREFIX = + "jdk/internal/module/SystemModules$"; + private static final String ALL_SYSTEM_MODULES_CLASS = + SYSTEM_MODULES_CLASS_PREFIX + "all"; + private static final String DEFAULT_SYSTEM_MODULES_CLASS = + SYSTEM_MODULES_CLASS_PREFIX + "default"; private boolean enabled; - private boolean retainModuleTarget; + public SystemModulesPlugin() { this.enabled = true; - this.retainModuleTarget = false; } @Override @@ -131,11 +146,7 @@ public void configure(Map config) { String arg = config.get(NAME); if (arg != null) { - if (arg.equals("retainModuleTarget")) { - retainModuleTarget = true; - } else { - throw new IllegalArgumentException(NAME + ": " + arg); - } + throw new IllegalArgumentException(NAME + ": " + arg); } } @@ -145,25 +156,56 @@ throw new PluginException(NAME + " was set"); } - SystemModulesClassGenerator generator = - new SystemModulesClassGenerator(retainModuleTarget); + // validate, transform (if needed), and add the module-info.class files + List moduleInfos = transformModuleInfos(in, out); + + // generate and add the SystemModuleMap and SystemModules classes + Set generated = genSystemModulesClasses(moduleInfos, out); + + // pass through all other resources + in.entries() + .filter(data -> !data.path().endsWith("/module-info.class") + && !generated.contains(data.path())) + .forEach(data -> out.add(data)); - // generate the byte code to create ModuleDescriptors - // such that the modules linked in the image would skip parsing - // of module-info.class and also skip name check + return out.build(); + } + + /** + * Validates and transforms the module-info.class files in the modules, adding + * the ModulePackages class file attribute if needed. + * + * @return the list of ModuleInfo objects, the first element is java.base + */ + List transformModuleInfos(ResourcePool in, ResourcePoolBuilder out) { + List moduleInfos = new ArrayList<>(); // Sort modules in the topological order so that java.base is always first. new ModuleSorter(in.moduleView()).sorted().forEach(module -> { ResourcePoolEntry data = module.findEntry("module-info.class").orElseThrow( - // automatic module not supported yet + // automatic modules not supported () -> new PluginException("module-info.class not found for " + - module.name() + " module") + module.name() + " module") ); assert module.name().equals(data.moduleName()); + try { - // validate the module and add to system modules - data = generator.buildModuleInfo(data, module.packages()); + byte[] content = data.contentBytes(); + Set packages = module.packages(); + ModuleInfo moduleInfo = new ModuleInfo(content, packages); + + // link-time validation + moduleInfo.validateNames(); + + // check if any exported or open package is not present + moduleInfo.validatePackages(); + + // module-info.class may be overridden to add ModulePackages + if (moduleInfo.shouldRewrite()) { + data = data.copyWithContent(moduleInfo.getBytes()); + } + moduleInfos.add(moduleInfo); // add resource pool entry out.add(data); @@ -172,37 +214,134 @@ } }); - // Generate the new class - ClassWriter cwriter = generator.getClassWriter(); - in.entries().forEach(data -> { - if (data.path().endsWith("module-info.class")) - return; - if (generator.isOverriddenClass(data.path())) { - byte[] bytes = cwriter.toByteArray(); - ResourcePoolEntry ndata = data.copyWithContent(bytes); - out.add(ndata); - } else { - out.add(data); + return moduleInfos; + } + + /** + * Generates the SystemModules classes (at least one) and the SystemModulesMap + * class to map initial modules to a SystemModules class. + * + * @return the resource names of the resources added to the pool + */ + private Set genSystemModulesClasses(List moduleInfos, + ResourcePoolBuilder out) { + int moduleCount = moduleInfos.size(); + ModuleFinder finder = finderOf(moduleInfos); + assert finder.findAll().size() == moduleCount; + + // map of initial module name to SystemModules class name + Map map = new LinkedHashMap<>(); + + // the names of resources written to the pool + Set generated = new HashSet<>(); + + // generate the SystemModules implementation to reconstitute all modules + Set allModuleNames = moduleInfos.stream() + .map(ModuleInfo::moduleName) + .collect(Collectors.toSet()); + String rn = genSystemModulesClass(moduleInfos, + resolve(finder, allModuleNames), + ALL_SYSTEM_MODULES_CLASS, + out); + generated.add(rn); + + // generate, if needed, a SystemModules class to reconstitute the modules + // needed for the case that the initial module is the unnamed module. + String defaultSystemModulesClassName; + Configuration cf = resolve(finder, DefaultRoots.compute(finder)); + if (cf.modules().size() == moduleCount) { + // all modules are resolved so no need to generate a class + defaultSystemModulesClassName = ALL_SYSTEM_MODULES_CLASS; + } else { + defaultSystemModulesClassName = DEFAULT_SYSTEM_MODULES_CLASS; + rn = genSystemModulesClass(sublist(moduleInfos, cf), + cf, + defaultSystemModulesClassName, + out); + generated.add(rn); + } + + // Generate a SystemModules class for each module with a main class + int suffix = 0; + for (ModuleInfo mi : moduleInfos) { + if (mi.descriptor().mainClass().isPresent()) { + String moduleName = mi.moduleName(); + cf = resolve(finder, Set.of(moduleName)); + if (cf.modules().size() == moduleCount) { + // resolves all modules so no need to generate a class + map.put(moduleName, ALL_SYSTEM_MODULES_CLASS); + } else { + String cn = SYSTEM_MODULES_CLASS_PREFIX + (suffix++); + rn = genSystemModulesClass(sublist(moduleInfos, cf), cf, cn, out); + map.put(moduleName, cn); + generated.add(rn); + } } - }); + } + + // generate SystemModulesMap + rn = genSystemModulesMapClass(ALL_SYSTEM_MODULES_CLASS, + defaultSystemModulesClassName, + map, + out); + generated.add(rn); + + // return the resource names of the generated classes + return generated; + } + + /** + * Resolves a collection of root modules, with service binding, to create + * configuration. + */ + private Configuration resolve(ModuleFinder finder, Set roots) { + return Configuration.empty().resolveAndBind(finder, ModuleFinder.of(), roots); + } - return out.build(); + /** + * Returns the list of ModuleInfo objects that correspond to the modules in + * the given configuration. + */ + private List sublist(List moduleInfos, Configuration cf) { + Set names = cf.modules() + .stream() + .map(ResolvedModule::name) + .collect(Collectors.toSet()); + return moduleInfos.stream() + .filter(mi -> names.contains(mi.moduleName())) + .collect(Collectors.toList()); + } + + /** + * Generate a SystemModules implementation class and add it as a resource. + * + * @return the name of the class resource added to the pool + */ + private String genSystemModulesClass(List moduleInfos, + Configuration cf, + String className, + ResourcePoolBuilder out) { + SystemModulesClassGenerator generator + = new SystemModulesClassGenerator(className, moduleInfos); + byte[] bytes = generator.getClassWriter(cf).toByteArray(); + String rn = "/java.base/" + className + ".class"; + ResourcePoolEntry e = ResourcePoolEntry.create(rn, bytes); + out.add(e); + return rn; } static class ModuleInfo { - private final ByteArrayInputStream bain; + private final ByteArrayInputStream bais; private final Attributes attrs; private final Set packages; - private final boolean dropModuleTarget; private final boolean addModulePackages; private ModuleDescriptor descriptor; // may be different that the original one - ModuleInfo(byte[] bytes, Set packages, boolean dropModuleTarget) - throws IOException - { - this.bain = new ByteArrayInputStream(bytes); + ModuleInfo(byte[] bytes, Set packages) throws IOException { + this.bais = new ByteArrayInputStream(bytes); this.packages = packages; - this.attrs = jdk.internal.module.ModuleInfo.read(bain, null); + this.attrs = jdk.internal.module.ModuleInfo.read(bais, null); + // If ModulePackages attribute is present, the packages from this // module descriptor returns the packages in that attribute. // If it's not present, ModuleDescriptor::packages only contains @@ -215,14 +354,6 @@ // add ModulePackages attribute if this module contains some packages // and ModulePackages is not present this.addModulePackages = packages.size() > 0 && !hasModulePackages(); - - // drop target attribute only if any OS property is present - ModuleTarget target = attrs.target(); - if (dropModuleTarget && target != null) { - this.dropModuleTarget = (target.targetPlatform() != null); - } else { - this.dropModuleTarget = false; - } } String moduleName() { @@ -233,7 +364,6 @@ return descriptor; } - Set packages() { return packages; } @@ -283,7 +413,6 @@ } } - /** * Validates if exported and open packages are present */ @@ -328,17 +457,15 @@ } /** - * Returns true if module-info.class should be written - * 1. add ModulePackages attribute if not present; or - * 2. drop ModuleTarget attribute except java.base + * Returns true if module-info.class should be rewritten to add the + * ModulePackages attribute. */ boolean shouldRewrite() { - return addModulePackages || dropModuleTarget; + return addModulePackages; } /** - * Returns the bytes for the module-info.class with ModulePackages - * attribute added and/or with ModuleTarget attribute dropped. + * Returns the bytes for the (possibly updated) module-info.class. */ byte[] getBytes() throws IOException { try (InputStream in = getInputStream()) { @@ -347,13 +474,10 @@ if (addModulePackages) { rewriter.addModulePackages(packages); } - if (dropModuleTarget) { - rewriter.dropModuleTarget(); - } // rewritten module descriptor byte[] bytes = rewriter.getBytes(); - try (ByteArrayInputStream bain = new ByteArrayInputStream(bytes)) { - this.descriptor = ModuleDescriptor.read(bain); + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { + this.descriptor = ModuleDescriptor.read(bais); } return bytes; } else { @@ -366,8 +490,8 @@ * Returns the input stream of the module-info.class */ InputStream getInputStream() { - bain.reset(); - return bain; + bais.reset(); + return bais; } class ModuleInfoRewriter extends ByteArrayOutputStream { @@ -383,10 +507,6 @@ } } - void dropModuleTarget() { - extender.targetPlatform(""); - } - byte[] getBytes() throws IOException { extender.write(this); return buf; @@ -395,12 +515,10 @@ } /** - * ClassWriter of a new jdk.internal.module.SystemModules class - * to reconstitute ModuleDescriptor of the system modules. + * Generates a SystemModules class to reconstitute the ModuleDescriptor + * and other attributes of system modules. */ static class SystemModulesClassGenerator { - private static final String CLASSNAME = - "jdk/internal/module/SystemModules"; private static final String MODULE_DESCRIPTOR_BUILDER = "jdk/internal/module/Builder"; private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE = @@ -422,10 +540,6 @@ private static final String MODULE_RESOLUTIONS_ARRAY_SIGNATURE = "[Ljdk/internal/module/ModuleResolution;"; - // static variables in SystemModules class - private static final String MODULE_NAMES = "MODULE_NAMES"; - private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER"; - private static final int MAX_LOCAL_VARS = 256; private final int BUILDER_VAR = 0; @@ -434,14 +548,14 @@ private final int MH_VAR = 1; // variable for ModuleHashes private int nextLocalVar = 2; // index to next local variable - private final ClassWriter cw; - private boolean dropModuleTarget; - // Method visitor for generating the SystemModules::modules() method private MethodVisitor mv; + // name of class to generate + private final String className; + // list of all ModuleDescriptorBuilders, invoked in turn when building. - private final List moduleInfos = new ArrayList<>(); + private final List moduleInfos; // A builder to create one single Set instance for a given set of // names or modifiers to reduce the footprint @@ -449,10 +563,11 @@ private final DedupSetBuilder dedupSetBuilder = new DedupSetBuilder(this::getNextLocalVar); - public SystemModulesClassGenerator(boolean retainModuleTarget) { - this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + - ClassWriter.COMPUTE_FRAMES); - this.dropModuleTarget = !retainModuleTarget; + public SystemModulesClassGenerator(String className, + List moduleInfos) { + this.className = className; + this.moduleInfos = moduleInfos; + moduleInfos.forEach(mi -> dedups(mi.descriptor())); } private int getNextLocalVar() { @@ -460,105 +575,10 @@ } /* - * static initializer initializing the static fields - * - * static Map map = new HashMap<>(); - */ - private void clinit(int numModules, int numPackages, - boolean hasSplitPackages) { - cw.visit(Opcodes.V1_8, ACC_PUBLIC+ACC_FINAL+ACC_SUPER, CLASSNAME, - null, "java/lang/Object", null); - - // public static String[] MODULE_NAMES = new String[] {....}; - cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULE_NAMES, - "[Ljava/lang/String;", null, null) - .visitEnd(); - - // public static int PACKAGES_IN_BOOT_LAYER; - cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT, - "I", null, numPackages) - .visitEnd(); - - MethodVisitor clinit = - cw.visitMethod(ACC_STATIC, "", "()V", - null, null); - clinit.visitCode(); - - // create the MODULE_NAMES array - pushInt(clinit, numModules); - clinit.visitTypeInsn(ANEWARRAY, "java/lang/String"); - - int index = 0; - for (ModuleInfo minfo : moduleInfos) { - clinit.visitInsn(DUP); // arrayref - pushInt(clinit, index++); - clinit.visitLdcInsn(minfo.moduleName()); // value - clinit.visitInsn(AASTORE); - } - - clinit.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES, - "[Ljava/lang/String;"); - - clinit.visitInsn(RETURN); - clinit.visitMaxs(0, 0); - clinit.visitEnd(); - - // public static boolean hasSplitPackages(); - MethodVisitor split = - cw.visitMethod(ACC_PUBLIC+ACC_STATIC, "hasSplitPackages", - "()Z", null, null); - split.visitCode(); - split.visitInsn(hasSplitPackages ? ICONST_1 : ICONST_0); - split.visitInsn(IRETURN); - split.visitMaxs(0, 0); - split.visitEnd(); - - } - - /* * Adds the given ModuleDescriptor to the system module list. * It performs link-time validation and prepares mapping from various * Sets to SetBuilders to emit an optimized number of sets during build. */ - public ResourcePoolEntry buildModuleInfo(ResourcePoolEntry entry, - Set packages) - throws IOException - { - if (moduleInfos.isEmpty() && !entry.moduleName().equals("java.base")) { - throw new InternalError("java.base must be the first module to process"); - } - - ModuleInfo moduleInfo; - if (entry.moduleName().equals("java.base")) { - moduleInfo = new ModuleInfo(entry.contentBytes(), packages, false); - ModuleDescriptor md = moduleInfo.descriptor; - // drop ModuleTarget attribute if java.base has all OS properties - ModuleTarget target = moduleInfo.target(); - if (dropModuleTarget && target.targetPlatform() != null) { - dropModuleTarget = true; - } else { - dropModuleTarget = false; - } - } else { - moduleInfo = new ModuleInfo(entry.contentBytes(), packages, dropModuleTarget); - } - - // link-time validation - moduleInfo.validateNames(); - // check if any exported or open package is not present - moduleInfo.validatePackages(); - - // module-info.class may be overridden for optimization - // 1. update ModuleTarget attribute to drop targetPlartform - // 2. add/update ModulePackages attribute - if (moduleInfo.shouldRewrite()) { - entry = entry.copyWithContent(moduleInfo.getBytes()); - } - moduleInfos.add(moduleInfo); - dedups(moduleInfo.descriptor()); - return entry; - } - private void dedups(ModuleDescriptor md) { // exports for (Exports e : md.exports()) { @@ -581,47 +601,123 @@ dedupSetBuilder.stringSet(md.uses()); } - /* - * Generate bytecode for SystemModules + /** + * Generate SystemModules class */ - public ClassWriter getClassWriter() { - int numModules = moduleInfos.size(); - Set allPackages = new HashSet<>(); - int packageCount = 0; - for (ModuleInfo minfo : moduleInfos) { - allPackages.addAll(minfo.packages); - packageCount += minfo.packages.size(); - } + public ClassWriter getClassWriter(Configuration cf) { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + + ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_8, + ACC_FINAL+ACC_SUPER, + className, + null, + "java/lang/Object", + new String[] { "jdk/internal/module/SystemModules" }); - int numPackages = allPackages.size(); - boolean hasSplitPackages = (numPackages < packageCount); - clinit(numModules, numPackages, hasSplitPackages); + // generate + genConstructor(cw); + + // generate hasSplitPackages + genHasSplitPackages(cw); - // generate SystemModules::descriptors - genDescriptorsMethod(); + // generate hasIncubatorModules + genIncubatorModules(cw); - // generate SystemModules::targets - genTargetsMethod(); + // generate moduleDescriptors + genModuleDescriptorsMethod(cw); + + // generate moduleTargets + genModuleTargetsMethod(cw); - // generate SystemModules::hashes - genHashesMethod(); + // generate moduleHashes + genModuleHashesMethod(cw); + + // generate moduleResolutions + genModuleResolutionsMethod(cw); - // generate SystemModules::moduleResolutions - genModuleResolutionsMethod(); + // generate moduleReads + genModuleReads(cw, cf); - // generate SystemModules::concealedPackagesToOpen and - // SystemModules::exportedPackagesToOpen - genXXXPackagesToOpenMethods(); + // generate concealedPackagesToOpen and exportedPackagesToOpen + genXXXPackagesToOpenMethods(cw); return cw; } /** - * Generate bytecode for SystemModules::descriptors method + * Generate byteccode for no-arg constructor + */ + private void genConstructor(ClassWriter cw) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, + "java/lang/Object", + "", + "()V", + false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + /** + * Generate bytecode for hasSplitPackages method */ - private void genDescriptorsMethod() { - this.mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, - "descriptors", + private void genHasSplitPackages(ClassWriter cw) { + boolean distinct = moduleInfos.stream() + .map(ModuleInfo::packages) + .flatMap(Set::stream) + .allMatch(new HashSet<>()::add); + boolean hasSplitPackages = !distinct; + + mv = cw.visitMethod(ACC_PUBLIC, + "hasSplitPackages", + "()Z", + "()Z", + null); + mv.visitCode(); + if (hasSplitPackages) { + mv.visitInsn(ICONST_1); + } else { + mv.visitInsn(ICONST_0); + } + mv.visitInsn(IRETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + /** + * Generate bytecode for hasIncubatorModules method + */ + private void genIncubatorModules(ClassWriter cw) { + boolean hasIncubatorModules = moduleInfos.stream() + .map(ModuleInfo::moduleResolution) + .filter(mres -> (mres != null && mres.hasIncubatingWarning())) + .findFirst() + .isPresent(); + + mv = cw.visitMethod(ACC_PUBLIC, + "hasIncubatorModules", + "()Z", + "()Z", + null); + mv.visitCode(); + if (hasIncubatorModules) { + mv.visitInsn(ICONST_1); + } else { + mv.visitInsn(ICONST_0); + } + mv.visitInsn(IRETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + /** + * Generate bytecode for moduleDescriptors method + */ + private void genModuleDescriptorsMethod(ClassWriter cw) { + this.mv = cw.visitMethod(ACC_PUBLIC, + "moduleDescriptors", "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, null); @@ -643,11 +739,11 @@ } /** - * Generate bytecode for SystemModules::targets method + * Generate bytecode for moduleTargets method */ - private void genTargetsMethod() { - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC, - "targets", + private void genModuleTargetsMethod(ClassWriter cw) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, + "moduleTargets", "()" + MODULE_TARGET_ARRAY_SIGNATURE, "()" + MODULE_TARGET_ARRAY_SIGNATURE, null); @@ -656,18 +752,34 @@ mv.visitTypeInsn(ANEWARRAY, MODULE_TARGET_CLASSNAME); mv.visitVarInsn(ASTORE, MT_VAR); - for (int index=0; index < moduleInfos.size(); index++) { + + // if java.base has a ModuleTarget attribute then generate the array + // with one element, all other elements will be null. + + ModuleInfo base = moduleInfos.get(0); + if (!base.moduleName().equals("java.base")) + throw new InternalError("java.base should be first module in list"); + ModuleTarget target = base.target(); + + int count; + if (target != null && target.targetPlatform() != null) { + count = 1; + } else { + count = moduleInfos.size(); + } + + for (int index = 0; index < count; index++) { ModuleInfo minfo = moduleInfos.get(index); - if (minfo.target() != null && !minfo.dropModuleTarget) { + if (minfo.target() != null) { mv.visitVarInsn(ALOAD, MT_VAR); pushInt(mv, index); - // new ModuleTarget(String, String) + // new ModuleTarget(String) mv.visitTypeInsn(NEW, MODULE_TARGET_CLASSNAME); mv.visitInsn(DUP); mv.visitLdcInsn(minfo.target().targetPlatform()); mv.visitMethodInsn(INVOKESPECIAL, MODULE_TARGET_CLASSNAME, - "", "(Ljava/lang/String;)V", false); + "", "(Ljava/lang/String;)V", false); mv.visitInsn(AASTORE); } @@ -680,12 +792,12 @@ } /** - * Generate bytecode for SystemModules::hashes method + * Generate bytecode for moduleHashes method */ - private void genHashesMethod() { + private void genModuleHashesMethod(ClassWriter cw) { MethodVisitor hmv = - cw.visitMethod(ACC_PUBLIC + ACC_STATIC, - "hashes", + cw.visitMethod(ACC_PUBLIC, + "moduleHashes", "()" + MODULE_HASHES_ARRAY_SIGNATURE, "()" + MODULE_HASHES_ARRAY_SIGNATURE, null); @@ -710,11 +822,11 @@ } /** - * Generate bytecode for SystemModules::methodResoultions method + * Generate bytecode for moduleResolutions method */ - private void genModuleResolutionsMethod() { + private void genModuleResolutionsMethod(ClassWriter cw) { MethodVisitor mresmv = - cw.visitMethod(ACC_PUBLIC+ACC_STATIC, + cw.visitMethod(ACC_PUBLIC, "moduleResolutions", "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE, "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE, @@ -746,75 +858,88 @@ } /** - * Generate SystemModules::concealedPackagesToOpen and - * SystemModules::exportedPackagesToOpen methods. + * Generate bytecode for moduleReads method */ - private void genXXXPackagesToOpenMethods() { - List descriptors = moduleInfos.stream() - .map(ModuleInfo::descriptor) - .collect(Collectors.toList()); - ModuleFinder finder = finderOf(descriptors); - IllegalAccessMaps maps = IllegalAccessMaps.generate(finder); - generate("concealedPackagesToOpen", maps.concealedPackagesToOpen()); - generate("exportedPackagesToOpen", maps.exportedPackagesToOpen()); + private void genModuleReads(ClassWriter cw, Configuration cf) { + // module name -> names of modules that it reads + Map> map = cf.modules().stream() + .collect(Collectors.toMap( + ResolvedModule::name, + m -> m.reads().stream() + .map(ResolvedModule::name) + .collect(Collectors.toSet()))); + generate(cw, "moduleReads", map, true); } /** - * Generate SystemModules:XXXPackagesToOpen + * Generate concealedPackagesToOpen and exportedPackagesToOpen methods. */ - private void generate(String methodName, Map> map) { - // Map> XXXPackagesToOpen() - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC, + private void genXXXPackagesToOpenMethods(ClassWriter cw) { + ModuleFinder finder = finderOf(moduleInfos); + IllegalAccessMaps maps = IllegalAccessMaps.generate(finder); + generate(cw, "concealedPackagesToOpen", maps.concealedPackagesToOpen(), false); + generate(cw, "exportedPackagesToOpen", maps.exportedPackagesToOpen(), false); + } + + /** + * Generate method to return {@code Map>}. + * + * If {@code dedup} is true then the values are de-duplicated. + */ + private void generate(ClassWriter cw, + String methodName, + Map> map, + boolean dedup) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, "()Ljava/util/Map;", "()Ljava/util/Map;", null); mv.visitCode(); - // new Map$Entry[moduleCount] + // map of Set -> local + Map, Integer> locals; + + // generate code to create the sets that are duplicated + if (dedup) { + Collection> values = map.values(); + Set> duplicateSets = values.stream() + .distinct() + .filter(s -> Collections.frequency(values, s) > 1) + .collect(Collectors.toSet()); + locals = new HashMap<>(); + int index = 1; + for (Set s : duplicateSets) { + genImmutableSet(mv, s); + mv.visitVarInsn(ASTORE, index); + locals.put(s, index); + if (++index >= MAX_LOCAL_VARS) { + break; + } + } + } else { + locals = Map.of(); + } + + // new Map$Entry[size] pushInt(mv, map.size()); mv.visitTypeInsn(ANEWARRAY, "java/util/Map$Entry"); int index = 0; for (Map.Entry> e : map.entrySet()) { - String moduleName = e.getKey(); - Set packages = e.getValue(); - int packageCount = packages.size(); + String name = e.getKey(); + Set s = e.getValue(); mv.visitInsn(DUP); pushInt(mv, index); - mv.visitLdcInsn(moduleName); + mv.visitLdcInsn(name); - // use Set.of(Object[]) when there are more than 2 packages - // use Set.of(Object) or Set.of(Object, Object) when fewer packages - if (packageCount > 2) { - pushInt(mv, packageCount); - mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); - int i = 0; - for (String pn : packages) { - mv.visitInsn(DUP); - pushInt(mv, i); - mv.visitLdcInsn(pn); - mv.visitInsn(AASTORE); - i++; - } - mv.visitMethodInsn(INVOKESTATIC, - "java/util/Set", - "of", - "([Ljava/lang/Object;)Ljava/util/Set;", - true); + // if de-duplicated then load the local, otherwise generate code + Integer varIndex = locals.get(s); + if (varIndex == null) { + genImmutableSet(mv, s); } else { - StringBuilder sb = new StringBuilder("("); - for (String pn : packages) { - mv.visitLdcInsn(pn); - sb.append("Ljava/lang/Object;"); - } - sb.append(")Ljava/util/Set;"); - mv.visitMethodInsn(INVOKESTATIC, - "java/util/Set", - "of", - sb.toString(), - true); + mv.visitVarInsn(ALOAD, varIndex); } String desc = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map$Entry;"; @@ -835,19 +960,42 @@ mv.visitEnd(); } - public boolean isOverriddenClass(String path) { - return path.equals("/java.base/" + CLASSNAME + ".class"); - } + /** + * Generate code to generate an immutable set. + */ + private void genImmutableSet(MethodVisitor mv, Set set) { + int size = set.size(); - void pushInt(MethodVisitor mv, int num) { - if (num <= 5) { - mv.visitInsn(ICONST_0 + num); - } else if (num < Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, num); - } else if (num < Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, num); + // use Set.of(Object[]) when there are more than 2 elements + // use Set.of(Object) or Set.of(Object, Object) when fewer + if (size > 2) { + pushInt(mv, size); + mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); + int i = 0; + for (String element : set) { + mv.visitInsn(DUP); + pushInt(mv, i); + mv.visitLdcInsn(element); + mv.visitInsn(AASTORE); + i++; + } + mv.visitMethodInsn(INVOKESTATIC, + "java/util/Set", + "of", + "([Ljava/lang/Object;)Ljava/util/Set;", + true); } else { - throw new IllegalArgumentException("exceed limit: " + num); + StringBuilder sb = new StringBuilder("("); + for (String element : set) { + mv.visitLdcInsn(element); + sb.append("Ljava/lang/Object;"); + } + sb.append(")Ljava/util/Set;"); + mv.visitMethodInsn(INVOKESTATIC, + "java/util/Set", + "of", + sb.toString(), + true); } } @@ -1564,18 +1712,159 @@ } } - static ModuleFinder finderOf(Iterable descriptors) { + /** + * Generate SystemModulesMap and add it as a resource. + * + * @return the name of the class resource added to the pool + */ + private String genSystemModulesMapClass(String allSystemModulesClassName, + String defaultSystemModulesClassName, + Map map, + ResourcePoolBuilder out) { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + + ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_8, + ACC_FINAL+ACC_SUPER, + SYSTEM_MODULES_MAP_CLASS, + null, + "java/lang/Object", + null); + + // + MethodVisitor mv = cw.visitMethod(0, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, + "java/lang/Object", + "", + "()V", + false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // allSystemModules() + mv = cw.visitMethod(ACC_STATIC, + "allSystemModules", + "()Ljdk/internal/module/SystemModules;", + "()Ljdk/internal/module/SystemModules;", + null); + mv.visitCode(); + mv.visitTypeInsn(NEW, allSystemModulesClassName); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, + allSystemModulesClassName, + "", + "()V", + false); + mv.visitInsn(ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // defaultSystemModules() + mv = cw.visitMethod(ACC_STATIC, + "defaultSystemModules", + "()Ljdk/internal/module/SystemModules;", + "()Ljdk/internal/module/SystemModules;", + null); + mv.visitCode(); + mv.visitTypeInsn(NEW, defaultSystemModulesClassName); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, + defaultSystemModulesClassName, + "", + "()V", + false); + mv.visitInsn(ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // moduleNames() + mv = cw.visitMethod(ACC_STATIC, + "moduleNames", + "()[Ljava/lang/String;", + "()[Ljava/lang/String;", + null); + mv.visitCode(); + pushInt(mv, map.size()); + mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); + + int index = 0; + for (String moduleName : map.keySet()) { + mv.visitInsn(DUP); // arrayref + pushInt(mv, index); + mv.visitLdcInsn(moduleName); + mv.visitInsn(AASTORE); + index++; + } + + mv.visitInsn(ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // classNames() + mv = cw.visitMethod(ACC_STATIC, + "classNames", + "()[Ljava/lang/String;", + "()[Ljava/lang/String;", + null); + mv.visitCode(); + pushInt(mv, map.size()); + mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); + + index = 0; + for (String className : map.values()) { + mv.visitInsn(DUP); // arrayref + pushInt(mv, index); + mv.visitLdcInsn(className.replace('/', '.')); + mv.visitInsn(AASTORE); + index++; + } + + mv.visitInsn(ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // write the class file to the pool as a resource + String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASS + ".class"; + ResourcePoolEntry e = ResourcePoolEntry.create(rn, cw.toByteArray()); + out.add(e); + + return rn; + } + + /** + * Pushes an int constant + */ + private static void pushInt(MethodVisitor mv, int value) { + if (value <= 5) { + mv.visitInsn(ICONST_0 + value); + } else if (value < Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, value); + } else if (value < Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, value); + } else { + throw new IllegalArgumentException("exceed limit: " + value); + } + } + + /** + * Returns a module finder that finds all modules in the given list + */ + private static ModuleFinder finderOf(Collection moduleInfos) { + Supplier readerSupplier = () -> null; Map namesToReference = new HashMap<>(); - for (ModuleDescriptor descriptor : descriptors) { - String name = descriptor.name(); - URI uri = URI.create("module:/" + name); - ModuleReference mref = new ModuleReference(descriptor, uri) { - @Override - public ModuleReader open() { - throw new UnsupportedOperationException(); - } - }; - namesToReference.putIfAbsent(name, mref); + for (ModuleInfo mi : moduleInfos) { + String name = mi.moduleName(); + ModuleReference mref + = new ModuleReferenceImpl(mi.descriptor(), + URI.create("jrt:/" + name), + readerSupplier, + null, + mi.target(), + null, + null, + mi.moduleResolution()); + namesToReference.put(name, mref); } return new ModuleFinder() { diff -r 198011f2d98b -r e256651a360a jdk/test/com/sun/crypto/provider/Cipher/DES/DESKeyCleanupTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/com/sun/crypto/provider/Cipher/DES/DESKeyCleanupTest.java Thu Aug 24 16:34:40 2017 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @modules java.base/com.sun.crypto.provider:+open + * @run main/othervm DESKeyCleanupTest + * @summary Verify that key storage is cleared + */ + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.reflect.Field; +import java.util.Arrays; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +/** + * Test that the array holding the key bytes is cleared when it is + * no longer referenced by the key. + */ + +public class DESKeyCleanupTest { + + private final static String SunJCEProvider = "SunJCE"; + + public static void main(String[] args) throws Exception { + testCleanupSecret("DES"); + testCleanupSecret("DESede"); + } + + static void testCleanupSecret(String algorithm) throws Exception { + KeyGenerator desGen = KeyGenerator.getInstance(algorithm, SunJCEProvider); + SecretKey key = desGen.generateKey(); + + // Break into the implementation to observe the key byte array. + Class keyClass = key.getClass(); + Field keyField = keyClass.getDeclaredField("key"); + keyField.setAccessible(true); + byte[] array = (byte[])keyField.get(key); + + byte[] zeros = new byte[array.length]; + do { + // Wait for array to be cleared; if not cleared test will timeout + System.out.printf("%s array: %s%n", algorithm, Arrays.toString(array)); + key = null; + System.gc(); // attempt to reclaim the key + } while (Arrays.compare(zeros, array) != 0); + System.out.printf("%s array: %s%n", algorithm, Arrays.toString(array)); + + Reference.reachabilityFence(key); // Keep key alive + Reference.reachabilityFence(array); // Keep array alive + } +} + + diff -r 198011f2d98b -r e256651a360a jdk/test/com/sun/crypto/provider/Cipher/PBE/PBEKeyCleanupTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/com/sun/crypto/provider/Cipher/PBE/PBEKeyCleanupTest.java Thu Aug 24 16:34:40 2017 +0200 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @modules java.base/com.sun.crypto.provider:+open + * @run main/othervm PBEKeyCleanupTest + * @summary Verify that key storage is cleared + */ + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Random; + +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +/** + * Test that the array holding the key bytes is cleared when it is + * no longer referenced by the key. + */ +public class PBEKeyCleanupTest { + + private final static String SunJCEProvider = "SunJCE"; + + private static final String PASS_PHRASE = "some hidden string"; + private static final int ITERATION_COUNT = 1000; + private static final int KEY_SIZE = 128; + + public static void main(String[] args) throws Exception { + testPBESecret("PBEWithMD5AndDES"); + testPBKSecret("PBKDF2WithHmacSHA1"); + } + + private static void testPBESecret(String algorithm) throws Exception { + char[] password = new char[] {'f', 'o', 'o'}; + PBEKeySpec pbeKeySpec = new PBEKeySpec(password); + SecretKeyFactory keyFac = + SecretKeyFactory.getInstance(algorithm, SunJCEProvider); + + testCleanupSecret(algorithm, keyFac.generateSecret(pbeKeySpec)); + } + + private static void testPBKSecret(String algorithm) throws Exception { + byte[] salt = new byte[8]; + new Random().nextBytes(salt); + char[] password = new char[] {'f', 'o', 'o'}; + PBEKeySpec pbeKeySpec = new PBEKeySpec(PASS_PHRASE.toCharArray(), salt, + ITERATION_COUNT, KEY_SIZE); + SecretKeyFactory keyFac = + SecretKeyFactory.getInstance(algorithm, SunJCEProvider); + + testCleanupSecret(algorithm, keyFac.generateSecret(pbeKeySpec)); + } + + static void testCleanupSecret(String algorithm, SecretKey key) throws Exception { + + // Break into the implementation to observe the key byte array. + Class keyClass = key.getClass(); + Field keyField = keyClass.getDeclaredField("key"); + keyField.setAccessible(true); + byte[] array = (byte[])keyField.get(key); + + byte[] zeros = new byte[array.length]; + do { + // Wait for array to be cleared; if not cleared test will timeout + System.out.printf("%s array: %s%n", algorithm, Arrays.toString(array)); + key = null; + System.gc(); // attempt to reclaim the key + } while (Arrays.compare(zeros, array) != 0); + System.out.printf("%s array: %s%n", algorithm, Arrays.toString(array)); + + Reference.reachabilityFence(key); // Keep key alive + Reference.reachabilityFence(array); // Keep array alive + } +} + + + diff -r 198011f2d98b -r e256651a360a jdk/test/java/lang/ClassLoader/getResource/GetResource.java --- a/jdk/test/java/lang/ClassLoader/getResource/GetResource.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/lang/ClassLoader/getResource/GetResource.java Thu Aug 24 16:34:40 2017 +0200 @@ -125,10 +125,9 @@ return new Object[][] { new Object[] { List.of("-Xbootclasspath/a:."), "a"}, - // "b" is the expected result when JDK-8185540 is resolved - new Object[] { List.of("-Xbootclasspath/a:" + dirB), "a"}, + new Object[] { List.of("-Xbootclasspath/a:" + dirB), "b"}, // empty path in first element - new Object[] { List.of("-Xbootclasspath/a:" + File.pathSeparator + dirB), "a"}, + new Object[] { List.of("-Xbootclasspath/a:" + File.pathSeparator + dirB), "b"}, new Object[] { List.of("-cp", File.pathSeparator), "a"}, new Object[] { List.of("-cp", dirB), "b"}, diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/0.policy --- a/jdk/test/java/net/httpclient/security/0.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/0.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/1.policy --- a/jdk/test/java/net/httpclient/security/1.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/1.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/10.policy --- a/jdk/test/java/net/httpclient/security/10.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/10.policy Thu Aug 24 16:34:40 2017 +0200 @@ -22,6 +22,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/11.policy --- a/jdk/test/java/net/httpclient/security/11.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/11.policy Thu Aug 24 16:34:40 2017 +0200 @@ -24,6 +24,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/12.policy --- a/jdk/test/java/net/httpclient/security/12.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/12.policy Thu Aug 24 16:34:40 2017 +0200 @@ -24,6 +24,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/14.policy --- a/jdk/test/java/net/httpclient/security/14.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/14.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/15.policy --- a/jdk/test/java/net/httpclient/security/15.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/15.policy Thu Aug 24 16:34:40 2017 +0200 @@ -26,6 +26,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/2.policy --- a/jdk/test/java/net/httpclient/security/2.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/2.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/3.policy --- a/jdk/test/java/net/httpclient/security/3.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/3.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/4.policy --- a/jdk/test/java/net/httpclient/security/4.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/4.policy Thu Aug 24 16:34:40 2017 +0200 @@ -24,6 +24,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/5.policy --- a/jdk/test/java/net/httpclient/security/5.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/5.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/6.policy --- a/jdk/test/java/net/httpclient/security/6.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/6.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/7.policy --- a/jdk/test/java/net/httpclient/security/7.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/7.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/8.policy --- a/jdk/test/java/net/httpclient/security/8.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/8.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/9.policy --- a/jdk/test/java/net/httpclient/security/9.policy Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/9.policy Thu Aug 24 16:34:40 2017 +0200 @@ -23,6 +23,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; diff -r 198011f2d98b -r e256651a360a jdk/test/java/net/httpclient/security/Driver.java --- a/jdk/test/java/net/httpclient/security/Driver.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/net/httpclient/security/Driver.java Thu Aug 24 16:34:40 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -34,7 +34,7 @@ * @compile ../ProxyServer.java * @build Security * - * @run driver/timeout=60 Driver + * @run driver/timeout=90 Driver */ /** diff -r 198011f2d98b -r e256651a360a jdk/test/java/nio/channels/Selector/KeySets.java --- a/jdk/test/java/nio/channels/Selector/KeySets.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/java/nio/channels/Selector/KeySets.java Thu Aug 24 16:34:40 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -26,34 +26,25 @@ * @summary Check various properties of key and selected-key sets * * @run main KeySets - * @run main/othervm -Dsun.nio.ch.bugLevel=1.4 KeySets */ import java.io.*; import java.nio.channels.*; import java.util.*; - public class KeySets { - static boolean compat; - static abstract class Catch { abstract void go() throws Exception; Catch(Class xc) throws Exception { try { go(); } catch (Exception x) { - if (compat) - throw new Exception("Exception thrown", x); if (xc.isInstance(x)) return; throw new Exception("Wrong exception", x); } - if (compat) - return; - throw new Exception("Not thrown as expected: " - + xc.getName()); + throw new Exception("Not thrown as expected: " + xc.getName()); } } @@ -74,7 +65,6 @@ void go() throws Exception { sel.selectedKeys(); }}; - } static void testNoAddition(final Set s) throws Exception { @@ -174,14 +164,10 @@ sel.selectedKeys().clear(); if (!sel.selectedKeys().isEmpty()) throw new Exception("clear failed"); - } public static void main(String[] args) throws Exception { - String bl = System.getProperty("sun.nio.ch.bugLevel"); - compat = (bl != null) && bl.equals("1.4"); testClose(); testMutability(); } - } diff -r 198011f2d98b -r e256651a360a jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java --- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java Thu Aug 24 16:34:40 2017 +0200 @@ -111,16 +111,10 @@ private void checkAttributes(ModuleReference modRef) { try { - if (modRef.descriptor().name().equals("java.base")) { - ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef); - String[] values = mt.targetPlatform().split("-"); - assertTrue(checkOSName(values[0])); - assertTrue(checkOSArch(values[1])); - } else { - // target platform attribute is dropped by jlink plugin for other modules - ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef); - assertTrue(mt == null || mt.targetPlatform() == null); - } + ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef); + String[] values = mt.targetPlatform().split("-"); + assertTrue(checkOSName(values[0])); + assertTrue(checkOSArch(values[1])); } catch (IOException exp) { throw new UncheckedIOException(exp); } diff -r 198011f2d98b -r e256651a360a jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java --- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java Thu Aug 24 16:34:40 2017 +0200 @@ -284,7 +284,6 @@ Set modules = Set.of("m1", "m4"); assertTrue(JLINK_TOOL.run(System.out, System.out, "--output", dir.toString(), - "--system-modules", "retainModuleTarget", "--exclude-resources", "m4/p4/dummy/*", "--add-modules", modules.stream().collect(Collectors.joining(",")), "--module-path", mp) == 0); diff -r 198011f2d98b -r e256651a360a jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java --- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java Fri Aug 04 23:29:09 2017 +0000 +++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java Thu Aug 24 16:34:40 2017 +0200 @@ -32,7 +32,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; +import java.util.Map; import java.util.Set; import jdk.internal.module.ClassFileAttributes; @@ -67,8 +67,7 @@ } private static boolean hasModuleTarget(String modName) throws IOException { - FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), - Collections.emptyMap()); + FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Map.of()); Path path = fs.getPath("/", "modules", modName, "module-info.class"); try (InputStream in = Files.newInputStream(path)) { return hasModuleTarget(in); @@ -86,8 +85,8 @@ expectModuleTarget = true; } - // java.base is packaged with osName/osArch/osVersion - if (! hasModuleTarget("java.base")) { + // java.base is packaged with ModuleTarget + if (!hasModuleTarget("java.base")) { throw new RuntimeException("ModuleTarget absent for java.base"); } @@ -109,8 +108,7 @@ } // verify ModuleDescriptor from module-info.class read from jimage - FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), - Collections.emptyMap()); + FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Map.of()); Path path = fs.getPath("/", "modules", mn, "module-info.class"); checkModuleDescriptor(ModuleDescriptor.read(Files.newInputStream(path)), packages); } @@ -121,16 +119,9 @@ throw new RuntimeException(md.mainClass().toString()); } - if (expectModuleTarget) { - // ModuleTarget attribute is retained - if (! hasModuleTarget(md.name())) { - throw new RuntimeException("ModuleTarget missing for " + md.name()); - } - } else { - // by default ModuleTarget attribute is dropped - if (hasModuleTarget(md.name())) { - throw new RuntimeException("ModuleTarget present for " + md.name()); - } + // ModuleTarget attribute should be present + if (!hasModuleTarget(md.name())) { + throw new RuntimeException("ModuleTarget missing for " + md.name()); } Set pkgs = md.packages(); diff -r 198011f2d98b -r e256651a360a jdk/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModules.java --- a/jdk/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModules.java Fri Aug 04 23:29:09 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.internal.module; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -/* - * Test --patch-module java.base=jdk/modules/java.base to override - * java.base with an exploded image - */ -public final class SystemModules { - public static final String[] MODULE_NAMES = new String[0]; - - public static int PACKAGES_IN_BOOT_LAYER = 1024; - - public static boolean hasSplitPackages() { - return true; - } - - public static Map> concealedPackagesToOpen() { - return Collections.emptyMap(); - } - - public static Map> exportedPackagesToOpen() { - return Collections.emptyMap(); - } -} diff -r 198011f2d98b -r e256651a360a jdk/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModulesMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModulesMap.java Thu Aug 24 16:34:40 2017 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.module; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/* + * Test --patch-module java.base=jdk/modules/java.base to override + * java.base with an exploded image + */ +class SystemModulesMap { + static SystemModules allSystemModules() { + return null; + } + static SystemModules defaultSystemModules() { + return null; + } + static String[] moduleNames() { + return new String[0]; + } + static String[] classNames() { + return new String[0]; + } +} diff -r 198011f2d98b -r e256651a360a make/Docs.gmk --- a/make/Docs.gmk Fri Aug 04 23:29:09 2017 +0000 +++ b/make/Docs.gmk Thu Aug 24 16:34:40 2017 +0200 @@ -101,9 +101,9 @@ JAVADOC_DISABLED_DOCLINT := accessibility html missing syntax reference # The initial set of options for javadoc -JAVADOC_OPTIONS := -XDignore.symbol.file=true -use -keywords -notimestamp \ - -serialwarn -encoding ISO-8859-1 -breakiterator -splitIndex --system none \ - -html5 -javafx --expand-requires transitive +JAVADOC_OPTIONS := -use -keywords -notimestamp \ + -serialwarn -encoding ISO-8859-1 -docencoding UTF-8 -breakiterator \ + -splitIndex --system none -html5 -javafx --expand-requires transitive # Should we add DRAFT stamps to the generated javadoc? ifeq ($(VERSION_IS_GA), true) diff -r 198011f2d98b -r e256651a360a nashorn/.hgtags --- a/nashorn/.hgtags Fri Aug 04 23:29:09 2017 +0000 +++ b/nashorn/.hgtags Thu Aug 24 16:34:40 2017 +0200 @@ -431,3 +431,4 @@ 7497ad85759ff010f44344b553223d1647fb6eba jdk-9+178 3adfb547e3e49e304ffc82d8c6489cb830b74d62 jdk-9+179 6ac0ca441ccb9ccc49c5007248dc1f3af8076a71 jdk-10+17 +e7fbd9c8637ff362e5a1801e3c89c579f23279f7 jdk-10+18