This commit is contained in:
Alan Bateman 2016-12-16 08:17:45 +00:00
commit 8c79e61d46
72 changed files with 3989 additions and 1356 deletions

View File

@ -317,7 +317,7 @@ public class PackerImpl extends TLGlobals implements Pack200.Packer {
this(null, je);
}
boolean isClassFile() {
if (!name.endsWith(".class")) {
if (!name.endsWith(".class") || name.endsWith("module-info.class")) {
return false;
}
for (String prefix = name;;) {

View File

@ -14,15 +14,6 @@ pack.code.attribute.CharacterRangeTable = NH[PHPOHIIH]
pack.class.attribute.SourceID = RUH
pack.class.attribute.CompilationID = RUH
# Module attributes, supported by the tool and not JSR-200
pack.class.attribute.Module = RUHFHNH[RUHFH]NH[RUHFHNH[RUH]]NH[RUHFHNH[RUH]]NH[RCH]NH[RCHNH[RCH]]
pack.class.attribute.ModulePackages = NH[RUH]
pack.class.attribute.ModuleVersion = RUH
pack.class.attribute.ModuleMainClass = RCH
pack.class.attribute.ModuleTarget = RUHRUHRUH
pack.class.attribute.ModuleHashes = RUHNH[RUHNH[B]]
# Note: Zero-length ("marker") attributes do not need to be specified here.
# They are automatically defined to have an empty layout.
#pack.class.attribute.Deprecated =

View File

@ -26,10 +26,13 @@
package java.lang;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleReferenceImpl;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.lang.reflect.Layer;
import java.lang.reflect.Module;
import java.util.HashSet;
@ -477,13 +480,16 @@ public final class StackTraceElement implements java.io.Serializable {
static Set<String> HASHED_MODULES = hashedModules();
static Set<String> hashedModules() {
Module javaBase = Layer.boot().findModule("java.base").get();
Optional<ModuleHashes> ohashes =
SharedSecrets.getJavaLangModuleAccess()
.hashes(javaBase.getDescriptor());
if (ohashes.isPresent()) {
Set<String> names = new HashSet<>(ohashes.get().names());
Optional<ResolvedModule> resolvedModule = Layer.boot()
.configuration()
.findModule("java.base");
assert resolvedModule.isPresent();
ModuleReference mref = resolvedModule.get().reference();
assert mref instanceof ModuleReferenceImpl;
ModuleHashes hashes = ((ModuleReferenceImpl)mref).recordedHashes();
if (hashes != null) {
Set<String> names = new HashSet<>(hashes.names());
names.add("java.base");
return names;
}

View File

@ -172,6 +172,7 @@ public class MethodHandles {
* @throws IllegalAccessException if the access check specified above fails
* @throws SecurityException if denied by the security manager
* @since 9
* @see Lookup#dropLookupMode
*/
public static Lookup privateLookupIn(Class<?> targetClass, Lookup lookup) throws IllegalAccessException {
SecurityManager sm = System.getSecurityManager();
@ -691,10 +692,15 @@ public class MethodHandles {
* A lookup object on a new lookup class
* {@linkplain java.lang.invoke.MethodHandles.Lookup#in created from a previous lookup object}
* may have some mode bits set to zero.
* Mode bits can also be
* {@linkplain java.lang.invoke.MethodHandles.Lookup#dropLookupMode directly cleared}.
* Once cleared, mode bits cannot be restored from the downgraded lookup object.
* The purpose of this is to restrict access via the new lookup object,
* so that it can access only names which can be reached by the original
* lookup object, and also by the new lookup class.
* @return the lookup modes, which limit the kinds of access performed by this lookup object
* @see #in
* @see #dropLookupMode
*/
public int lookupModes() {
return allowedModes & ALL_MODES;
@ -748,7 +754,8 @@ public class MethodHandles {
* which may change due to this operation.
*
* @param requestedLookupClass the desired lookup class for the new lookup object
* @return a lookup object which reports the desired lookup class
* @return a lookup object which reports the desired lookup class, or the same object
* if there is no change
* @throws NullPointerException if the argument is null
*/
public Lookup in(Class<?> requestedLookupClass) {
@ -788,6 +795,40 @@ public class MethodHandles {
return new Lookup(requestedLookupClass, newModes);
}
/**
* Creates a lookup on the same lookup class which this lookup object
* finds members, but with a lookup mode that has lost the given lookup mode.
* The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE
* MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED} or {@link #PRIVATE PRIVATE}.
* {@link #PROTECTED PROTECTED} is always dropped and so the resulting lookup
* mode will never have this access capability. When dropping {@code PACKAGE}
* then the resulting lookup will not have {@code PACKAGE} or {@code PRIVATE}
* access. When dropping {@code MODULE} then the resulting lookup will not
* have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code
* PUBLIC} is dropped then the resulting lookup has no access.
* @param modeToDrop the lookup mode to drop
* @return a lookup object which lacks the indicated mode, or the same object if there is no change
* @throws IllegalArgumentException if {@code modeToDrop} is not one of {@code PUBLIC},
* {@code MODULE}, {@code PACKAGE}, {@code PROTECTED} or {@code PRIVATE}
* @since 9
* @see MethodHandles#privateLookupIn
*/
public Lookup dropLookupMode(int modeToDrop) {
int oldModes = lookupModes();
int newModes = oldModes & ~(modeToDrop | PROTECTED);
switch (modeToDrop) {
case PUBLIC: newModes &= ~(ALL_MODES); break;
case MODULE: newModes &= ~(PACKAGE | PRIVATE); break;
case PACKAGE: newModes &= ~(PRIVATE); break;
case PROTECTED:
case PRIVATE: break;
default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
}
if (newModes == oldModes) return this; // return self if no change
return new Lookup(lookupClass(), newModes);
}
// Make sure outer class is initialized first.
static { IMPL_NAMES.getClass(); }

View File

@ -29,7 +29,6 @@ import java.io.InputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.ArrayList;
@ -38,7 +37,6 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -52,7 +50,7 @@ import static jdk.internal.module.Checks.*;
import static java.util.Objects.*;
import jdk.internal.module.Checks;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleInfo;
/**
@ -123,8 +121,9 @@ public class ModuleDescriptor
private final Set<Modifier> mods;
private final String name;
private final Version compiledVersion;
private Requires(Set<Modifier> ms, String mn) {
private Requires(Set<Modifier> ms, String mn, Version v) {
if (ms.isEmpty()) {
ms = Collections.emptySet();
} else {
@ -132,11 +131,13 @@ public class ModuleDescriptor
}
this.mods = ms;
this.name = mn;
this.compiledVersion = v;
}
private Requires(Set<Modifier> ms, String mn, boolean unused) {
private Requires(Set<Modifier> ms, String mn, Version v, boolean unused) {
this.mods = ms;
this.name = mn;
this.compiledVersion = v;
}
/**
@ -157,13 +158,27 @@ public class ModuleDescriptor
return name;
}
/**
* Returns the version of the module if recorded at compile-time.
*
* @return The version of the module if recorded at compile-time
*/
public Optional<Version> compiledVersion() {
return Optional.ofNullable(compiledVersion);
}
/**
* Compares this module dependence to another.
*
* <p> Two {@code Requires} objects are compared by comparing their
* module name lexicographically. Where the module names are equal then
* the sets of modifiers are compared based on a value computed from the
* ordinal of each modifier. </p>
* ordinal of each modifier. Where the module names are equal and the
* set of modifiers are equal then the version of the modules recorded
* at compile-time are compared. When comparing the versions recorded
* at compile-time then a dependence that has a recorded version is
* considered to succeed a dependence that does not have a recorded
* version. </p>
*
* @return A negative integer, zero, or a positive integer if this module
* dependence is less than, equal to, or greater than the given
@ -174,8 +189,24 @@ public class ModuleDescriptor
int c = this.name().compareTo(that.name());
if (c != 0)
return c;
// same name, compare by modifiers
return Long.compare(this.modsValue(), that.modsValue());
// modifiers
c = Long.compare(this.modsValue(), that.modsValue());
if (c != 0)
return c;
// compiledVersion
if (this.compiledVersion != null) {
if (that.compiledVersion != null)
c = this.compiledVersion.compareTo(that.compiledVersion);
else
c = 1;
} else {
if (that.compiledVersion != null)
c = -1;
}
return c;
}
/**
@ -195,7 +226,9 @@ public class ModuleDescriptor
*
* <p> If the given object is not a {@code Requires} then this method
* returns {@code false}. Two module dependence objects are equal if
* the module names are equal and set of modifiers are equal. </p>
* the module names are equal, set of modifiers are equal, and the
* compiled version of both modules is equal or not recorded for
* both modules. </p>
*
* <p> This method satisfies the general contract of the {@link
* java.lang.Object#equals(Object) Object.equals} method. </p>
@ -211,21 +244,25 @@ public class ModuleDescriptor
if (!(ob instanceof Requires))
return false;
Requires that = (Requires)ob;
return (name.equals(that.name) && mods.equals(that.mods));
return name.equals(that.name) && mods.equals(that.mods)
&& Objects.equals(compiledVersion, that.compiledVersion);
}
/**
* Computes a hash code for this module dependence.
*
* <p> The hash code is based upon the module name and modifiers. It
* satisfies the general contract of the {@link Object#hashCode
* Object.hashCode} method. </p>
* <p> The hash code is based upon the module name, modifiers, and the
* module version if recorded at compile time. It satisfies the general
* contract of the {@link Object#hashCode Object.hashCode} method. </p>
*
* @return The hash-code value for this module dependence
*/
@Override
public int hashCode() {
return name.hashCode() * 43 + mods.hashCode();
int hash = name.hashCode() * 43 + mods.hashCode();
if (compiledVersion != null)
hash = hash * 43 + compiledVersion.hashCode();
return hash;
}
/**
@ -235,7 +272,13 @@ public class ModuleDescriptor
*/
@Override
public String toString() {
return ModuleDescriptor.toString(mods, name);
String what;
if (compiledVersion != null) {
what = name() + " (@" + compiledVersion + ")";
} else {
what = name();
}
return ModuleDescriptor.toString(mods, what);
}
}
@ -967,9 +1010,8 @@ public class ModuleDescriptor
}
// From module declarations
private final String name;
private final Version version;
private final boolean open;
// Indicates if synthesised for a JAR file found on the module path
@ -984,17 +1026,16 @@ public class ModuleDescriptor
private final Set<String> uses;
private final Set<Provides> provides;
// "Extended" information, added post-compilation by tools
private final Version version;
// Added post-compilation by tools
private final Set<String> packages;
private final String mainClass;
private final String osName;
private final String osArch;
private final String osVersion;
private final Set<String> packages;
private final ModuleHashes hashes;
private ModuleDescriptor(String name,
Version version,
boolean open,
boolean automatic,
boolean synthetic,
@ -1003,16 +1044,14 @@ public class ModuleDescriptor
Set<Opens> opens,
Set<String> uses,
Set<Provides> provides,
Version version,
Set<String> packages,
String mainClass,
String osName,
String osArch,
String osVersion,
Set<String> packages,
ModuleHashes hashes)
String osVersion)
{
this.name = name;
this.version = version;
this.open = open;
this.automatic = automatic;
this.synthetic = synthetic;
@ -1020,18 +1059,16 @@ public class ModuleDescriptor
assert (requires.stream().map(Requires::name).distinct().count()
== requires.size());
this.requires = emptyOrUnmodifiableSet(requires);
this.exports = emptyOrUnmodifiableSet(exports);
this.opens = emptyOrUnmodifiableSet(opens);
this.uses = emptyOrUnmodifiableSet(uses);
this.provides = emptyOrUnmodifiableSet(provides);
this.version = version;
this.packages = emptyOrUnmodifiableSet(packages);
this.mainClass = mainClass;
this.osName = osName;
this.osArch = osArch;
this.osVersion = osVersion;
this.hashes = hashes;
this.packages = emptyOrUnmodifiableSet(packages);
}
/**
@ -1039,6 +1076,7 @@ public class ModuleDescriptor
*/
ModuleDescriptor(ModuleDescriptor md, Set<String> pkgs) {
this.name = md.name;
this.version = md.version;
this.open = md.open;
this.automatic = md.automatic;
this.synthetic = md.synthetic;
@ -1049,16 +1087,14 @@ public class ModuleDescriptor
this.uses = md.uses;
this.provides = md.provides;
this.version = md.version;
Set<String> packages = new HashSet<>(md.packages);
packages.addAll(pkgs);
this.packages = emptyOrUnmodifiableSet(packages);
this.mainClass = md.mainClass;
this.osName = md.osName;
this.osArch = md.osArch;
this.osVersion = md.osVersion;
this.hashes = null; // need to ignore
Set<String> packages = new HashSet<>(md.packages);
packages.addAll(pkgs);
this.packages = emptyOrUnmodifiableSet(packages);
}
/**
@ -1066,6 +1102,7 @@ public class ModuleDescriptor
* The arguments are pre-validated and sets are unmodifiable sets.
*/
ModuleDescriptor(String name,
Version version,
boolean open,
boolean automatic,
boolean synthetic,
@ -1074,16 +1111,15 @@ public class ModuleDescriptor
Set<Opens> opens,
Set<String> uses,
Set<Provides> provides,
Version version,
Set<String> packages,
String mainClass,
String osName,
String osArch,
String osVersion,
Set<String> packages,
ModuleHashes hashes,
int hashCode,
boolean unused) {
this.name = name;
this.version = version;
this.open = open;
this.automatic = automatic;
this.synthetic = synthetic;
@ -1093,12 +1129,10 @@ public class ModuleDescriptor
this.uses = uses;
this.provides = provides;
this.packages = packages;
this.version = version;
this.mainClass = mainClass;
this.osName = osName;
this.osArch = osArch;
this.osVersion = osVersion;
this.hashes = hashes;
this.hash = hashCode;
}
@ -1284,13 +1318,6 @@ public class ModuleDescriptor
return packages;
}
/**
* Returns the object with the hashes of other modules
*/
Optional<ModuleHashes> hashes() {
return Optional.ofNullable(hashes);
}
/**
* A builder used for building {@link ModuleDescriptor} objects.
@ -1317,15 +1344,13 @@ public class ModuleDescriptor
public static final class Builder {
final String name;
final boolean strict; // true if module names are checked
boolean open;
final boolean open;
final boolean synthetic;
boolean automatic;
boolean synthetic;
final Map<String, Requires> requires = new HashMap<>();
final Map<String, Exports> exports = new HashMap<>();
final Map<String, Opens> opens = new HashMap<>();
final Set<String> concealedPackages = new HashSet<>();
final Set<String> uses = new HashSet<>();
final Map<String, Provides> provides = new HashMap<>();
Version version;
@ -1333,7 +1358,6 @@ public class ModuleDescriptor
String osArch;
String osVersion;
String mainClass;
ModuleHashes hashes;
/**
* Initializes a new builder with the given module name.
@ -1341,14 +1365,11 @@ public class ModuleDescriptor
* @param strict
* Indicates whether module names are checked or not
*/
Builder(String name, boolean strict) {
this.strict = strict;
Builder(String name, boolean strict, boolean open, boolean synthetic) {
this.name = (strict) ? requireModuleName(name) : name;
}
/* package */ Builder open(boolean open) {
this.strict = strict;
this.open = open;
return this;
this.synthetic = synthetic;
}
/* package */ Builder automatic(boolean automatic) {
@ -1356,10 +1377,20 @@ public class ModuleDescriptor
return this;
}
/* package */ boolean isOpen() { return open; }
/**
* Returns the set of packages that are exported (unconditionally or
* unconditionally).
*/
/* package */ Set<String> exportedPackages() {
return exports.keySet();
}
/* package */ boolean isAutomatic() {
return automatic;
/**
* Returns the set of packages that are opened (unconditionally or
* unconditionally).
*/
/* package */Set<String> openPackages() {
return opens.keySet();
}
/**
@ -1387,6 +1418,36 @@ public class ModuleDescriptor
return this;
}
/**
* Adds a dependence on a module with the given (and possibly empty)
* set of modifiers. The dependence includes the version of the
* module that that was recorded at compile-time.
*
* @param ms
* The set of modifiers
* @param mn
* The module name
* @param compiledVersion
* The version of the module recorded at compile-time
*
* @return This builder
*
* @throws IllegalArgumentException
* If the module name is {@code null}, is not a legal Java
* identifier, or is equal to the module name that this builder
* was initialized to build
* @throws IllegalStateException
* If the dependence on the module has already been declared
*/
public Builder requires(Set<Requires.Modifier> ms,
String mn,
Version compiledVersion) {
Objects.requireNonNull(compiledVersion);
if (strict)
mn = requireModuleName(mn);
return requires(new Requires(ms, mn, compiledVersion));
}
/**
* Adds a dependence on a module with the given (and possibly empty)
* set of modifiers.
@ -1408,7 +1469,7 @@ public class ModuleDescriptor
public Builder requires(Set<Requires.Modifier> ms, String mn) {
if (strict)
mn = requireModuleName(mn);
return requires(new Requires(ms, mn));
return requires(new Requires(ms, mn, null));
}
/**
@ -1705,17 +1766,6 @@ public class ModuleDescriptor
return opens(Collections.emptySet(), pn);
}
// Used by ModuleInfo, after a packageFinder is invoked
/* package */ Set<String> exportedAndOpenPackages() {
if (opens.isEmpty())
return exports.keySet();
Set<String> result = new HashSet<>();
result.addAll(exports.keySet());
result.addAll(opens.keySet());
return result;
}
/**
* Adds a service dependence.
*
@ -1789,7 +1839,6 @@ public class ModuleDescriptor
if (providerNames.isEmpty())
throw new IllegalArgumentException("Empty providers set");
providerNames.forEach(Checks::requireServiceProviderName);
provides.put(service, p);
return this;
}
@ -1914,7 +1963,7 @@ public class ModuleDescriptor
* If {@code mainClass} is null or is not a legal Java identifier
*/
public Builder mainClass(String mc) {
mainClass = requireJavaIdentifier("main class name", mc);
mainClass = requireBinaryName("main class name", mc);
return this;
}
@ -1972,16 +2021,6 @@ public class ModuleDescriptor
return this;
}
/* package */ Builder hashes(ModuleHashes hashes) {
this.hashes = hashes;
return this;
}
/* package */ Builder synthetic(boolean v) {
this.synthetic = v;
return this;
}
/**
* Builds and returns a {@code ModuleDescriptor} from its components.
*
@ -1990,7 +2029,9 @@ public class ModuleDescriptor
public ModuleDescriptor build() {
Set<Requires> requires = new HashSet<>(this.requires.values());
Set<String> packages = new HashSet<>(exportedAndOpenPackages());
Set<String> packages = new HashSet<>();
packages.addAll(exports.keySet());
packages.addAll(opens.keySet());
packages.addAll(concealedPackages);
Set<Exports> exports = new HashSet<>(this.exports.values());
@ -1999,6 +2040,7 @@ public class ModuleDescriptor
Set<Provides> provides = new HashSet<>(this.provides.values());
return new ModuleDescriptor(name,
version,
open,
automatic,
synthetic,
@ -2007,13 +2049,11 @@ public class ModuleDescriptor
opens,
uses,
provides,
version,
packages,
mainClass,
osName,
osArch,
osVersion,
packages,
hashes);
osVersion);
}
}
@ -2088,8 +2128,7 @@ public class ModuleDescriptor
&& Objects.equals(osName, that.osName)
&& Objects.equals(osArch, that.osArch)
&& Objects.equals(osVersion, that.osVersion)
&& Objects.equals(packages, that.packages)
&& Objects.equals(hashes, that.hashes));
&& Objects.equals(packages, that.packages));
}
private transient int hash; // cached hash code
@ -2122,7 +2161,6 @@ public class ModuleDescriptor
hc = hc * 43 + Objects.hashCode(osArch);
hc = hc * 43 + Objects.hashCode(osVersion);
hc = hc * 43 + Objects.hashCode(packages);
hc = hc * 43 + Objects.hashCode(hashes);
if (hc == 0)
hc = -1;
hash = hc;
@ -2145,7 +2183,7 @@ public class ModuleDescriptor
if (!requires.isEmpty())
sb.append(", ").append(requires);
if (!uses.isEmpty())
sb.append(", ").append(uses);
sb.append(", uses: ").append(uses);
if (!exports.isEmpty())
sb.append(", exports: ").append(exports);
if (!opens.isEmpty())
@ -2171,7 +2209,7 @@ public class ModuleDescriptor
* identifier
*/
public static Builder module(String name) {
return new Builder(name, true);
return new Builder(name, true, false, false);
}
/**
@ -2199,7 +2237,7 @@ public class ModuleDescriptor
* identifier
*/
public static Builder openModule(String name) {
return new Builder(name, true).open(true);
return new Builder(name, true, true, false);
}
/**
@ -2221,7 +2259,7 @@ public class ModuleDescriptor
* @see ModuleFinder#of(Path[])
*/
public static Builder automaticModule(String name) {
return new Builder(name, true).automatic(true);
return new Builder(name, true, false, false).automatic(true);
}
@ -2263,7 +2301,7 @@ public class ModuleDescriptor
Supplier<Set<String>> packageFinder)
throws IOException
{
return ModuleInfo.read(in, requireNonNull(packageFinder));
return ModuleInfo.read(in, requireNonNull(packageFinder)).descriptor();
}
/**
@ -2281,7 +2319,7 @@ public class ModuleDescriptor
* If an I/O error occurs reading from the input stream
*/
public static ModuleDescriptor read(InputStream in) throws IOException {
return ModuleInfo.read(in, null);
return ModuleInfo.read(in, null).descriptor();
}
/**
@ -2320,7 +2358,7 @@ public class ModuleDescriptor
public static ModuleDescriptor read(ByteBuffer bb,
Supplier<Set<String>> packageFinder)
{
return ModuleInfo.read(bb, requireNonNull(packageFinder));
return ModuleInfo.read(bb, requireNonNull(packageFinder)).descriptor();
}
/**
@ -2336,7 +2374,7 @@ public class ModuleDescriptor
* If an invalid module descriptor is detected
*/
public static ModuleDescriptor read(ByteBuffer bb) {
return ModuleInfo.read(bb, null);
return ModuleInfo.read(bb, null).descriptor();
}
private static <K,V> Map<K,V> emptyOrUnmodifiableMap(Map<K,V> map) {
@ -2377,18 +2415,26 @@ public class ModuleDescriptor
jdk.internal.misc.SharedSecrets
.setJavaLangModuleAccess(new jdk.internal.misc.JavaLangModuleAccess() {
@Override
public Builder newModuleBuilder(String mn, boolean strict) {
return new Builder(mn, strict);
public Builder newModuleBuilder(String mn,
boolean strict,
boolean open,
boolean synthetic) {
return new Builder(mn, strict, open, synthetic);
}
@Override
public Builder newOpenModuleBuilder(String mn, boolean strict) {
return new Builder(mn, strict).open(true);
public Set<String> exportedPackages(ModuleDescriptor.Builder builder) {
return builder.exportedPackages();
}
@Override
public Requires newRequires(Set<Requires.Modifier> ms, String mn) {
return new Requires(ms, mn, true);
public Set<String> openPackages(ModuleDescriptor.Builder builder) {
return builder.openPackages();
}
@Override
public Requires newRequires(Set<Requires.Modifier> ms, String mn, Version v) {
return new Requires(ms, mn, v, true);
}
@Override
@ -2433,6 +2479,7 @@ public class ModuleDescriptor
@Override
public ModuleDescriptor newModuleDescriptor(String name,
Version version,
boolean open,
boolean automatic,
boolean synthetic,
@ -2441,15 +2488,14 @@ public class ModuleDescriptor
Set<Opens> opens,
Set<String> uses,
Set<Provides> provides,
Version version,
Set<String> packages,
String mainClass,
String osName,
String osArch,
String osVersion,
Set<String> packages,
ModuleHashes hashes,
int hashCode) {
return new ModuleDescriptor(name,
version,
open,
automatic,
synthetic,
@ -2458,22 +2504,15 @@ public class ModuleDescriptor
opens,
uses,
provides,
version,
packages,
mainClass,
osName,
osArch,
osVersion,
packages,
hashes,
hashCode,
false);
}
@Override
public Optional<ModuleHashes> hashes(ModuleDescriptor descriptor) {
return descriptor.hashes();
}
@Override
public Configuration resolveRequiresAndUses(ModuleFinder finder,
Collection<String> roots,
@ -2482,20 +2521,6 @@ public class ModuleDescriptor
{
return Configuration.resolveRequiresAndUses(finder, roots, check, traceOutput);
}
@Override
public ModuleReference newPatchedModule(ModuleDescriptor descriptor,
URI location,
Supplier<ModuleReader> s) {
return new ModuleReference(descriptor, location, s, true, null);
}
@Override
public ModuleFinder newModulePath(Runtime.Version version,
boolean isLinkPhase,
Path... entries) {
return new ModulePath(version, isLinkPhase, entries);
}
});
}

View File

@ -42,6 +42,8 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import jdk.internal.module.ModulePath;
import jdk.internal.module.SystemModuleFinder;
import sun.security.action.GetPropertyAction;
/**
@ -137,7 +139,7 @@ public interface ModuleFinder {
/**
* Returns a module finder that locates the <em>system modules</em>. The
* system modules are typically linked into the Java run-time image.
* system modules are the modules in the Java run-time image.
* The module finder will always find {@code java.base}.
*
* <p> If there is a security manager set then its {@link
@ -166,7 +168,7 @@ public interface ModuleFinder {
Path modules = Paths.get(home, "lib", "modules");
if (Files.isRegularFile(modules)) {
return new SystemModuleFinder();
return SystemModuleFinder.getInstance();
} else {
Path mlib = Paths.get(home, "modules");
if (Files.isDirectory(mlib)) {

View File

@ -26,96 +26,42 @@
package java.lang.module;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import jdk.internal.module.ModuleHashes.HashSupplier;
/**
* A reference to a module's content.
*
* <p> A module reference contains the module's descriptor and its location, if
* known. It also has the ability to create a {@link ModuleReader} in order to
* access the module's content, which may be inside the Java run-time system
* itself or in an artifact such as a modular JAR file.
* <p> A module reference is a concrete implementation of this class that
* implements the abstract methods defined by this class. It contains the
* module's descriptor and its location, if known. It also has the ability to
* create a {@link ModuleReader} in order to access the module's content, which
* may be inside the Java run-time system itself or in an artifact such as a
* modular JAR file.
*
* @see ModuleFinder
* @see ModuleReader
* @since 9
*/
public final class ModuleReference {
public abstract class ModuleReference {
private final ModuleDescriptor descriptor;
private final URI location;
private final Supplier<ModuleReader> readerSupplier;
// true if this is a reference to a patched module
private boolean patched;
// the function that computes the hash of this module reference
private final HashSupplier hasher;
// cached hash to avoid needing to compute it many times
private byte[] cachedHash;
/**
* Constructs a new instance of this class.
*/
ModuleReference(ModuleDescriptor descriptor,
URI location,
Supplier<ModuleReader> readerSupplier,
boolean patched,
HashSupplier hasher)
{
this.descriptor = Objects.requireNonNull(descriptor);
this.location = location;
this.readerSupplier = Objects.requireNonNull(readerSupplier);
this.patched = patched;
this.hasher = hasher;
}
/**
* Constructs a new instance of this class.
*/
ModuleReference(ModuleDescriptor descriptor,
URI location,
Supplier<ModuleReader> readerSupplier,
HashSupplier hasher)
{
this(descriptor, location, readerSupplier, false, hasher);
}
/**
* Constructs a new instance of this class.
*
* <p> The {@code readSupplier} parameter is the supplier of the {@link
* ModuleReader} that may be used to read the module content. Its {@link
* Supplier#get() get()} method throws {@link UncheckedIOException} if an
* I/O error occurs opening the module content. The {@code get()} method
* throws {@link SecurityException} if opening the module is denied by the
* security manager.
*
* @param descriptor
* The module descriptor
* @param location
* The module location or {@code null} if not known
* @param readerSupplier
* The {@code Supplier} of the {@code ModuleReader}
*/
public ModuleReference(ModuleDescriptor descriptor,
URI location,
Supplier<ModuleReader> readerSupplier)
{
this(descriptor, location, readerSupplier, false, null);
protected ModuleReference(ModuleDescriptor descriptor, URI location) {
this.descriptor = Objects.requireNonNull(descriptor);
this.location = location;
}
/**
@ -123,11 +69,10 @@ public final class ModuleReference {
*
* @return The module descriptor
*/
public ModuleDescriptor descriptor() {
public final ModuleDescriptor descriptor() {
return descriptor;
}
/**
* Returns the location of this module's content, if known.
*
@ -139,18 +84,13 @@ public final class ModuleReference {
*
* @return The location or an empty {@code Optional} if not known
*/
public Optional<URI> location() {
public final Optional<URI> location() {
return Optional.ofNullable(location);
}
/**
* Opens the module content for reading.
*
* <p> This method opens the module content by invoking the {@link
* Supplier#get() get()} method of the {@code readSupplier} specified at
* construction time. </p>
*
* @return A {@code ModuleReader} to read the module
*
* @throws IOException
@ -158,113 +98,5 @@ public final class ModuleReference {
* @throws SecurityException
* If denied by the security manager
*/
public ModuleReader open() throws IOException {
try {
return readerSupplier.get();
} catch (UncheckedIOException e) {
throw e.getCause();
}
}
/**
* Returns {@code true} if this module has been patched via --patch-module.
*/
boolean isPatched() {
return patched;
}
/**
* Returns the hash supplier for this module.
*/
HashSupplier hasher() {
return hasher;
}
/**
* Computes the hash of this module. Returns {@code null} if the hash
* cannot be computed.
*
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
byte[] computeHash(String algorithm) {
byte[] result = cachedHash;
if (result != null)
return result;
if (hasher == null)
return null;
cachedHash = result = hasher.generate(algorithm);
return result;
}
/**
* Computes a hash code for this module reference.
*
* <p> The hash code is based upon the components of the reference, and
* satisfies the general contract of the {@link Object#hashCode
* Object.hashCode} method. </p>
*
* @return The hash-code value for this module reference
*/
@Override
public int hashCode() {
int hc = hash;
if (hc == 0) {
hc = descriptor.hashCode();
hc = 43 * hc + readerSupplier.hashCode();
hc = 43 * hc + Objects.hashCode(location);
hc = 43 * hc + Objects.hashCode(hasher);
hc = 43 * hc + Boolean.hashCode(patched);
if (hc == 0)
hc = -1;
hash = hc;
}
return hc;
}
private int hash;
/**
* Tests this module reference for equality with the given object.
*
* <p> If the given object is not a {@code ModuleReference} then this
* method returns {@code false}. Two module references are equal if their
* module descriptors are equal, their locations are equal or both unknown,
* and were created with equal supplier objects to access the module
* content. </p>
*
* <p> This method satisfies the general contract of the {@link
* java.lang.Object#equals(Object) Object.equals} method. </p>
*
* @param ob
* the object to which this object is to be compared
*
* @return {@code true} if, and only if, the given object is a module
* reference that is equal to this module reference
*/
@Override
public boolean equals(Object ob) {
if (!(ob instanceof ModuleReference))
return false;
ModuleReference that = (ModuleReference)ob;
return Objects.equals(this.descriptor, that.descriptor)
&& Objects.equals(this.location, that.location)
&& Objects.equals(this.readerSupplier, that.readerSupplier)
&& Objects.equals(this.hasher, that.hasher)
&& this.patched == that.patched;
}
/**
* Returns a string describing this module reference.
*
* @return A string describing this module reference
*/
@Override
public String toString() {
return ("[module " + descriptor().name()
+ ", location=" + location + "]");
}
public abstract ModuleReader open() throws IOException;
}

View File

@ -46,6 +46,7 @@ import java.util.StringJoiner;
import java.util.stream.Collectors;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleReferenceImpl;
/**
* The resolver used by {@link Configuration#resolveRequires} and
@ -438,24 +439,32 @@ final class Resolver {
*/
private void checkHashes() {
for (ModuleReference mref : nameToReference.values()) {
ModuleDescriptor descriptor = mref.descriptor();
// get map of module hashes
Optional<ModuleHashes> ohashes = descriptor.hashes();
if (!ohashes.isPresent())
// get the recorded hashes, if any
if (!(mref instanceof ModuleReferenceImpl))
continue;
ModuleHashes hashes = ((ModuleReferenceImpl)mref).recordedHashes();
if (hashes == null)
continue;
ModuleHashes hashes = ohashes.get();
ModuleDescriptor descriptor = mref.descriptor();
String algorithm = hashes.algorithm();
for (String dn : hashes.names()) {
ModuleReference other = nameToReference.get(dn);
if (other == null) {
ModuleReference mref2 = nameToReference.get(dn);
if (mref2 == null) {
ResolvedModule resolvedModule = findInParent(dn);
if (resolvedModule != null)
other = resolvedModule.reference();
mref2 = resolvedModule.reference();
}
if (mref2 == null)
continue;
if (!(mref2 instanceof ModuleReferenceImpl)) {
fail("Unable to compute the hash of module %s", dn);
}
// skip checking the hash if the module has been patched
ModuleReferenceImpl other = (ModuleReferenceImpl)mref2;
if (other != null && !other.isPatched()) {
byte[] recordedHash = hashes.hashFor(dn);
byte[] actualHash = other.computeHash(algorithm);

View File

@ -28,9 +28,11 @@ package java.lang.reflect;
import java.lang.annotation.Annotation;
import java.security.AccessController;
import jdk.internal.misc.VM;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
import sun.security.action.GetPropertyAction;
/**
* The AccessibleObject class is the base class for Field, Method and
@ -172,8 +174,10 @@ public class AccessibleObject implements AnnotatedElement {
// package is open to caller
String pn = packageName(declaringClass);
if (declaringModule.isOpen(pn, callerModule))
if (declaringModule.isOpen(pn, callerModule)) {
printStackTraceIfOpenedReflectively(declaringModule, pn, callerModule);
return;
}
// package is exported to caller and class/member is public
boolean isExported = declaringModule.isExported(pn, callerModule);
@ -185,8 +189,10 @@ public class AccessibleObject implements AnnotatedElement {
modifiers = ((Field) this).getModifiers();
}
boolean isMemberPublic = Modifier.isPublic(modifiers);
if (isExported && isClassPublic && isMemberPublic)
if (isExported && isClassPublic && isMemberPublic) {
printStackTraceIfExportedReflectively(declaringModule, pn, callerModule);
return;
}
// not accessible
String msg = "Unable to make ";
@ -198,7 +204,44 @@ public class AccessibleObject implements AnnotatedElement {
else
msg += "opens";
msg += " " + pn + "\" to " + callerModule;
Reflection.throwInaccessibleObjectException(msg);
InaccessibleObjectException e = new InaccessibleObjectException(msg);
if (Reflection.printStackTraceWhenAccessFails()) {
e.printStackTrace(System.err);
}
throw e;
}
private void printStackTraceIfOpenedReflectively(Module module,
String pn,
Module other) {
printStackTraceIfExposedReflectively(module, pn, other, true);
}
private void printStackTraceIfExportedReflectively(Module module,
String pn,
Module other) {
printStackTraceIfExposedReflectively(module, pn, other, false);
}
private void printStackTraceIfExposedReflectively(Module module,
String pn,
Module other,
boolean open)
{
if (Reflection.printStackTraceWhenAccessSucceeds()
&& !module.isStaticallyExportedOrOpen(pn, other, open))
{
String msg = other + " allowed to invoke setAccessible on ";
if (this instanceof Field)
msg += "field ";
msg += this;
new Exception(msg) {
private static final long serialVersionUID = 42L;
public String toString() {
return "WARNING: " + getMessage();
}
}.printStackTrace(System.err);
}
}
/**

View File

@ -245,7 +245,6 @@ public final class Layer {
* @see Module#addOpens
*/
public Controller addOpens(Module source, String pn, Module target) {
Objects.requireNonNull(source);
Objects.requireNonNull(source);
Objects.requireNonNull(target);
ensureInLayer(source);
@ -541,7 +540,7 @@ public final class Layer {
* {@link ClassLoader#registerAsParallelCapable parallel-capable} so as to
* avoid deadlocks during class loading. In addition, the entity creating
* a new layer with this method should arrange that the class loaders are
* ready to load from these module before there are any attempts to load
* ready to load from these modules before there are any attempts to load
* classes or resources.
*
* <p> Creating a {@code Layer} can fail for the following reasons: </p>

View File

@ -559,7 +559,7 @@ public final class Module implements AnnotatedElement {
* Returns {@code true} if this module exports or opens a package to
* the given module via its module declaration.
*/
private boolean isStaticallyExportedOrOpen(String pn, Module other, boolean open) {
boolean isStaticallyExportedOrOpen(String pn, Module other, boolean open) {
// package is open to everyone or <other>
Map<String, Set<Module>> openPackages = this.openPackages;
if (openPackages != null) {
@ -643,6 +643,12 @@ public final class Module implements AnnotatedElement {
* <em>open</em>) to the given module. It also has no effect if
* invoked on an {@link ModuleDescriptor#isOpen open} module. </p>
*
* @apiNote As specified in section 5.4.3 of the <cite>The Java&trade;
* Virtual Machine Specification </cite>, if an attempt to resolve a
* symbolic reference fails because of a linkage error, then subsequent
* attempts to resolve the reference always fail with the same error that
* was thrown as a result of the initial resolution attempt.
*
* @param pn
* The package name
* @param other
@ -656,6 +662,7 @@ public final class Module implements AnnotatedElement {
* @throws IllegalStateException
* If this is a named module and the caller is not this module
*
* @jvms 5.4.3 Resolution
* @see #isExported(String,Module)
*/
@CallerSensitive
@ -676,8 +683,8 @@ public final class Module implements AnnotatedElement {
}
/**
* If the caller's module is this module then update this module to
* <em>open</em> the given package to the given module.
* If this module has <em>opened</em> a package to at least the caller
* module then update this module to open the package to the given module.
* Opening a package with this method allows all types in the package,
* and all their members, not just public types and their public members,
* to be reflected on by the given module when using APIs that support
@ -699,7 +706,8 @@ public final class Module implements AnnotatedElement {
* If {@code pn} is {@code null}, or this is a named module and the
* package {@code pn} is not a package in this module
* @throws IllegalStateException
* If this is a named module and the caller is not this module
* If this is a named module and this module has not opened the
* package to at least the caller
*
* @see #isOpen(String,Module)
* @see AccessibleObject#setAccessible(boolean)
@ -713,9 +721,8 @@ public final class Module implements AnnotatedElement {
if (isNamed() && !descriptor.isOpen()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
throw new IllegalStateException(caller + " != " + this);
}
if (caller != this && !isOpen(pn, caller))
throw new IllegalStateException(pn + " is not open to " + caller);
implAddExportsOrOpens(pn, other, /*open*/true, /*syncVM*/true);
}
@ -1568,6 +1575,10 @@ public final class Module implements AnnotatedElement {
public Stream<Layer> layers(ClassLoader loader) {
return Layer.layers(loader);
}
@Override
public boolean isStaticallyExported(Module module, String pn, Module other) {
return module.isStaticallyExportedOrOpen(pn, other, false);
}
});
}
}

View File

@ -64,8 +64,10 @@ public class StringSharingDecompressor implements ResourceDecompressor {
private static final int CONSTANT_MethodHandle = 15;
private static final int CONSTANT_MethodType = 16;
private static final int CONSTANT_InvokeDynamic = 18;
private static final int CONSTANT_Module = 19;
private static final int CONSTANT_Package = 20;
private static final int[] SIZES = new int[20];
private static final int[] SIZES = new int[21];
static {
@ -83,6 +85,8 @@ public class StringSharingDecompressor implements ResourceDecompressor {
SIZES[CONSTANT_MethodHandle] = 3;
SIZES[CONSTANT_MethodType] = 2;
SIZES[CONSTANT_InvokeDynamic] = 4;
SIZES[CONSTANT_Module] = 2;
SIZES[CONSTANT_Package] = 2;
}
public static int[] getSizes() {

View File

@ -38,10 +38,8 @@ import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.net.URI;
import java.nio.file.Path;
import java.util.Map;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
@ -59,21 +57,28 @@ public interface JavaLangModuleAccess {
* @param strict
* Indicates whether module names are checked or not
*/
ModuleDescriptor.Builder newModuleBuilder(String mn, boolean strict);
ModuleDescriptor.Builder newModuleBuilder(String mn,
boolean strict,
boolean open,
boolean synthetic);
/**
* Creates a builder for building an open module with the given module name.
*
* @param strict
* Indicates whether module names are checked or not
* Returns the set of packages that are exported (unconditionally or
* unconditionally).
*/
ModuleDescriptor.Builder newOpenModuleBuilder(String mn, boolean strict);
Set<String> exportedPackages(ModuleDescriptor.Builder builder);
/**
* Returns the set of packages that are opened (unconditionally or
* unconditionally).
*/
Set<String> openPackages(ModuleDescriptor.Builder builder);
/**
* Returns a {@code ModuleDescriptor.Requires} of the given modifiers
* and module name.
*/
Requires newRequires(Set<Requires.Modifier> ms, String mn);
Requires newRequires(Set<Requires.Modifier> ms, String mn, Version v);
/**
* Returns an unqualified {@code ModuleDescriptor.Exports}
@ -122,6 +127,7 @@ public interface JavaLangModuleAccess {
* Returns a new {@code ModuleDescriptor} instance.
*/
ModuleDescriptor newModuleDescriptor(String name,
Version version,
boolean open,
boolean automatic,
boolean synthetic,
@ -130,20 +136,13 @@ public interface JavaLangModuleAccess {
Set<Opens> opens,
Set<String> uses,
Set<Provides> provides,
Version version,
Set<String> packages,
String mainClass,
String osName,
String osArch,
String osVersion,
Set<String> packages,
ModuleHashes hashes,
int hashCode);
/**
* Returns the object with the hashes of other modules
*/
Optional<ModuleHashes> hashes(ModuleDescriptor descriptor);
/**
* Resolves a collection of root modules, with service binding
* and the empty configuration as the parent. The post resolution
@ -154,18 +153,4 @@ public interface JavaLangModuleAccess {
boolean check,
PrintStream traceOutput);
/**
* Creates a ModuleReference to a "patched" module.
*/
ModuleReference newPatchedModule(ModuleDescriptor descriptor,
URI location,
Supplier<ModuleReader> readerSupplier);
/**
* Creates a ModuleFinder for a module path.
*/
ModuleFinder newModulePath(Runtime.Version version,
boolean isLinkPhase,
Path... entries);
}

View File

@ -123,4 +123,12 @@ public interface JavaLangReflectModuleAccess {
* given class loader.
*/
Stream<Layer> layers(ClassLoader loader);
/**
* Tests if a module exports a package at least {@code other} via its
* module declaration.
*
* @apiNote This is a temporary method for debugging features.
*/
boolean isStaticallyExported(Module module, String pn, Module other);
}

View File

@ -30,11 +30,8 @@ import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Version;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jdk.internal.misc.JavaLangModuleAccess;
@ -52,7 +49,7 @@ import jdk.internal.misc.SharedSecrets;
* SystemModules should contain modules for the boot layer.
*/
final class Builder {
private static final JavaLangModuleAccess jlma =
private static final JavaLangModuleAccess JLMA =
SharedSecrets.getJavaLangModuleAccess();
// Static cache of the most recently seen Version to cheaply deduplicate
@ -60,13 +57,36 @@ final class Builder {
static Version cachedVersion;
/**
* Returns a {@link Requires} for a dependence on a module
* with the given (and possibly empty) set of modifiers.
* Returns a {@link Requires} for a dependence on a module with the given
* (and possibly empty) set of modifiers, and optionally the version
* recorded at compile time.
*/
public static Requires newRequires(Set<Requires.Modifier> mods,
String mn,
String compiledVersion)
{
Version version = null;
if (compiledVersion != null) {
// use the cached version if the same version string
Version ver = cachedVersion;
if (ver != null && compiledVersion.equals(ver.toString())) {
version = ver;
} else {
version = Version.parse(compiledVersion);
}
}
return JLMA.newRequires(mods, mn, version);
}
/**
* Returns a {@link Requires} for a dependence on a module with the given
* (and possibly empty) set of modifiers, and optionally the version
* recorded at compile time.
*/
public static Requires newRequires(Set<Requires.Modifier> mods,
String mn)
{
return jlma.newRequires(mods, mn);
return newRequires(mods, mn, null);
}
/**
@ -77,7 +97,7 @@ final class Builder {
public static Exports newExports(Set<Exports.Modifier> ms,
String pn,
Set<String> targets) {
return jlma.newExports(ms, pn, targets);
return JLMA.newExports(ms, pn, targets);
}
/**
@ -85,7 +105,7 @@ final class Builder {
* modifiers.
*/
public static Opens newOpens(Set<Opens.Modifier> ms, String pn) {
return jlma.newOpens(ms, pn);
return JLMA.newOpens(ms, pn);
}
/**
@ -96,7 +116,7 @@ final class Builder {
public static Opens newOpens(Set<Opens.Modifier> ms,
String pn,
Set<String> targets) {
return jlma.newOpens(ms, pn, targets);
return JLMA.newOpens(ms, pn, targets);
}
/**
@ -104,7 +124,7 @@ final class Builder {
* of modifiers.
*/
public static Exports newExports(Set<Exports.Modifier> ms, String pn) {
return jlma.newExports(ms, pn);
return JLMA.newExports(ms, pn);
}
/**
@ -112,7 +132,7 @@ final class Builder {
* implementation classes.
*/
public static Provides newProvides(String st, List<String> pcs) {
return jlma.newProvides(st, pcs);
return JLMA.newProvides(st, pcs);
}
final String name;
@ -130,8 +150,6 @@ final class Builder {
String osName;
String osArch;
String osVersion;
String algorithm;
Map<String, byte[]> hashes;
Builder(String name) {
this.name = name;
@ -274,35 +292,14 @@ final class Builder {
return this;
}
/**
* Sets the algorithm of the module hashes
*/
public Builder algorithm(String algorithm) {
this.algorithm = algorithm;
return this;
}
/**
* Sets the module hash for the given module name
*/
public Builder moduleHash(String mn, byte[] hash) {
if (hashes == null)
hashes = new HashMap<>();
hashes.put(mn, hash);
return this;
}
/**
* Builds a {@code ModuleDescriptor} from the components.
*/
public ModuleDescriptor build(int hashCode) {
assert name != null;
ModuleHashes moduleHashes =
hashes != null ? new ModuleHashes(algorithm, hashes) : null;
return jlma.newModuleDescriptor(name,
return JLMA.newModuleDescriptor(name,
version,
open,
automatic,
synthetic,
@ -311,13 +308,11 @@ final class Builder {
opens,
uses,
provides,
version,
packages,
mainClass,
osName,
osArch,
osVersion,
packages,
moduleHashes,
hashCode);
}
}

View File

@ -25,79 +25,200 @@
package jdk.internal.module;
/**
* Utility class for checking module name and binary names.
*/
public final class Checks {
private Checks() { }
private static void fail(String what, String id, int i) {
throw new IllegalArgumentException(id
+ ": Invalid " + what + ": "
+ " Illegal character"
+ " at index " + i);
/**
* Checks a name to ensure that it's a legal module name.
*
* @throws IllegalArgumentException if name is null or not a legal
* module name
*/
public static String requireModuleName(String name) {
if (name == null)
throw new IllegalArgumentException("Null module name");
int next;
int off = 0;
while ((next = name.indexOf('.', off)) != -1) {
if (isJavaIdentifier(name, off, (next - off)) == -1) {
String id = name.substring(off, next);
throw new IllegalArgumentException(name + ": Invalid module name"
+ ": '" + id + "' is not a Java identifier");
}
off = next+1;
}
int last = isJavaIdentifier(name, off, name.length() - off);
if (last == -1) {
String id = name.substring(off);
throw new IllegalArgumentException(name + ": Invalid module name"
+ ": '" + id + "' is not a Java identifier");
}
//if (!Character.isJavaIdentifierStart(last))
// throw new IllegalArgumentException(name + ": Module name ends in digit");
return name;
}
/**
* Returns {@code true} if the given identifier is a legal Java identifier.
* Returns {@code true} if the given name is a legal module name.
*/
public static boolean isJavaIdentifier(String id) {
int n = id.length();
if (n == 0)
return false;
if (!Character.isJavaIdentifierStart(id.codePointAt(0)))
return false;
int cp = id.codePointAt(0);
int i = Character.charCount(cp);
for (; i < n; i += Character.charCount(cp)) {
cp = id.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && id.charAt(i) != '.')
public static boolean isModuleName(String name) {
int next;
int off = 0;
while ((next = name.indexOf('.', off)) != -1) {
if (isJavaIdentifier(name, off, (next - off)) == -1)
return false;
off = next+1;
}
if (cp == '.')
int last = isJavaIdentifier(name, off, name.length() - off);
if (last == -1)
return false;
//if (!Character.isJavaIdentifierStart(last))
// return false;
return true;
}
/**
* Checks if a given identifier is a legal Java identifier.
* Checks a name to ensure that it's a legal package name.
*
* @throws IllegalArgumentException if name is null or not a legal
* package name
*/
public static String requireJavaIdentifier(String what, String id) {
if (id == null)
throw new IllegalArgumentException("Null " + what);
int n = id.length();
if (n == 0)
throw new IllegalArgumentException("Empty " + what);
if (!Character.isJavaIdentifierStart(id.codePointAt(0)))
fail(what, id, 0);
int cp = id.codePointAt(0);
int i = Character.charCount(cp);
int last = 0;
for (; i < n; i += Character.charCount(cp)) {
cp = id.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && id.charAt(i) != '.')
fail(what, id, i);
last = i;
public static String requirePackageName(String name) {
return requireBinaryName("package name", name);
}
/**
* Checks a name to ensure that it's a legal type name.
*
* @throws IllegalArgumentException if name is null or not a legal
* type name
*/
public static String requireServiceTypeName(String name) {
return requireBinaryName("service type name", name);
}
/**
* Checks a name to ensure that it's a legal type name.
*
* @throws IllegalArgumentException if name is null or not a legal
* type name
*/
public static String requireServiceProviderName(String name) {
return requireBinaryName("service provider name", name);
}
/**
* Returns {@code true} if the given name is a legal binary name.
*/
public static boolean isJavaIdentifier(String name) {
return isBinaryName(name);
}
/**
* Returns {@code true} if the given name is a legal binary name.
*/
public static boolean isBinaryName(String name) {
int next;
int off = 0;
while ((next = name.indexOf('.', off)) != -1) {
if (isJavaIdentifier(name, off, (next - off)) == -1)
return false;
off = next+1;
}
if (cp == '.')
fail(what, id, last);
return id;
int count = name.length() - off;
return (isJavaIdentifier(name, off, count) != -1);
}
public static String requireModuleName(String id) {
return requireJavaIdentifier("module name", id);
/**
* Checks if the given name is a legal binary name.
*
* @throws IllegalArgumentException if name is null or not a legal
* binary name
*/
public static String requireBinaryName(String what, String name) {
if (name == null)
throw new IllegalArgumentException("Null " + what);
int next;
int off = 0;
while ((next = name.indexOf('.', off)) != -1) {
if (isJavaIdentifier(name, off, (next - off)) == -1) {
String id = name.substring(off, next);
throw new IllegalArgumentException(name + ": Invalid " + what
+ ": '" + id + "' is not a Java identifier");
}
off = next + 1;
}
if (isJavaIdentifier(name, off, name.length() - off) == -1) {
String id = name.substring(off, name.length());
throw new IllegalArgumentException(name + ": Invalid " + what
+ ": '" + id + "' is not a Java identifier");
}
return name;
}
public static String requirePackageName(String id) {
return requireJavaIdentifier("package name", id);
/**
* Returns {@code true} if the last character of the given name is legal
* as the last character of a module name.
*
* @throws IllegalArgumentException if name is empty
*/
public static boolean hasLegalModuleNameLastCharacter(String name) {
if (name.isEmpty())
throw new IllegalArgumentException("name is empty");
int len = name.length();
if (isASCIIString(name)) {
char c = name.charAt(len-1);
return Character.isJavaIdentifierStart(c);
} else {
int i = 0;
int cp = -1;
while (i < len) {
cp = name.codePointAt(i);
i += Character.charCount(cp);
}
return Character.isJavaIdentifierStart(cp);
}
}
public static String requireServiceTypeName(String id) {
return requireJavaIdentifier("service type name", id);
/**
* Returns true if the given string only contains ASCII characters.
*/
private static boolean isASCIIString(String s) {
int i = 0;
while (i < s.length()) {
int c = s.charAt(i);
if (c > 0x7F)
return false;
i++;
}
return true;
}
public static String requireServiceProviderName(String id) {
return requireJavaIdentifier("service provider name", id);
}
/**
* Checks if a char sequence is a legal Java identifier, returning the code
* point of the last character if legal or {@code -1} if not legal.
*/
private static int isJavaIdentifier(CharSequence cs, int offset, int count) {
if (count == 0)
return -1;
int first = Character.codePointAt(cs, offset);
if (!Character.isJavaIdentifierStart(first))
return -1;
int cp = first;
int i = Character.charCount(first);
while (i < count) {
cp = Character.codePointAt(cs, offset+i);
if (!Character.isJavaIdentifierPart(cp))
return -1;
i += Character.charCount(cp);
}
return cp;
}
}

View File

@ -68,12 +68,18 @@ public final class ClassFileAttributes {
= SharedSecrets.getJavaLangModuleAccess();
private ModuleDescriptor descriptor;
private Version replacementVersion;
public ModuleAttribute(ModuleDescriptor descriptor) {
super(MODULE);
this.descriptor = descriptor;
}
public ModuleAttribute(Version v) {
super(MODULE);
this.replacementVersion = v;
}
public ModuleAttribute() {
super(MODULE);
}
@ -86,46 +92,70 @@ public final class ClassFileAttributes {
int codeOff,
Label[] labels)
{
ModuleAttribute attr = new ModuleAttribute();
// module_name
String mn = cr.readUTF8(off, buf).replace('/', '.');
// module_name (CONSTANT_Module_info)
String mn = cr.readModule(off, buf);
off += 2;
// module_flags
int module_flags = cr.readUnsignedShort(off);
boolean open = ((module_flags & ACC_OPEN) != 0);
boolean synthetic = ((module_flags & ACC_SYNTHETIC) != 0);
off += 2;
ModuleDescriptor.Builder builder;
if (open) {
builder = JLMA.newOpenModuleBuilder(mn, false);
} else {
builder = JLMA.newModuleBuilder(mn, false);
ModuleDescriptor.Builder builder = JLMA.newModuleBuilder(mn,
false,
open,
synthetic);
// module_version
String module_version = cr.readUTF8(off, buf);
off += 2;
if (replacementVersion != null) {
builder.version(replacementVersion);
} else if (module_version != null) {
builder.version(module_version);
}
// requires_count and requires[requires_count]
int requires_count = cr.readUnsignedShort(off);
off += 2;
for (int i=0; i<requires_count; i++) {
String dn = cr.readUTF8(off, buf).replace('/', '.');
int flags = cr.readUnsignedShort(off + 2);
// CONSTANT_Module_info
String dn = cr.readModule(off, buf);
off += 2;
// requires_flags
int requires_flags = cr.readUnsignedShort(off);
off += 2;
Set<Requires.Modifier> mods;
if (flags == 0) {
if (requires_flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_TRANSITIVE) != 0)
if ((requires_flags & ACC_TRANSITIVE) != 0)
mods.add(Requires.Modifier.TRANSITIVE);
if ((flags & ACC_STATIC_PHASE) != 0)
if ((requires_flags & ACC_STATIC_PHASE) != 0)
mods.add(Requires.Modifier.STATIC);
if ((flags & ACC_SYNTHETIC) != 0)
if ((requires_flags & ACC_SYNTHETIC) != 0)
mods.add(Requires.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
if ((requires_flags & ACC_MANDATED) != 0)
mods.add(Requires.Modifier.MANDATED);
}
builder.requires(mods, dn);
off += 4;
// requires_version
Version compiledVersion = null;
String requires_version = cr.readUTF8(off, buf);
off += 2;
if (requires_version != null) {
compiledVersion = Version.parse(requires_version);
}
if (compiledVersion == null) {
builder.requires(mods, dn);
} else {
builder.requires(mods, dn, compiledVersion);
}
}
// exports_count and exports[exports_count]
@ -133,19 +163,20 @@ public final class ClassFileAttributes {
off += 2;
if (exports_count > 0) {
for (int i=0; i<exports_count; i++) {
String pkg = cr.readUTF8(off, buf).replace('/', '.');
// CONSTANT_Package_info
String pkg = cr.readPackage(off, buf).replace('/', '.');
off += 2;
int flags = cr.readUnsignedShort(off);
int exports_flags = cr.readUnsignedShort(off);
off += 2;
Set<Exports.Modifier> mods;
if (flags == 0) {
if (exports_flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_SYNTHETIC) != 0)
if ((exports_flags & ACC_SYNTHETIC) != 0)
mods.add(Exports.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
if ((exports_flags & ACC_MANDATED) != 0)
mods.add(Exports.Modifier.MANDATED);
}
@ -154,7 +185,7 @@ public final class ClassFileAttributes {
if (exports_to_count > 0) {
Set<String> targets = new HashSet<>();
for (int j=0; j<exports_to_count; j++) {
String t = cr.readUTF8(off, buf).replace('/', '.');
String t = cr.readModule(off, buf);
off += 2;
targets.add(t);
}
@ -170,19 +201,20 @@ public final class ClassFileAttributes {
off += 2;
if (open_count > 0) {
for (int i=0; i<open_count; i++) {
String pkg = cr.readUTF8(off, buf).replace('/', '.');
// CONSTANT_Package_info
String pkg = cr.readPackage(off, buf).replace('/', '.');
off += 2;
int flags = cr.readUnsignedShort(off);
int opens_flags = cr.readUnsignedShort(off);
off += 2;
Set<Opens.Modifier> mods;
if (flags == 0) {
if (opens_flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_SYNTHETIC) != 0)
if ((opens_flags & ACC_SYNTHETIC) != 0)
mods.add(Opens.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
if ((opens_flags & ACC_MANDATED) != 0)
mods.add(Opens.Modifier.MANDATED);
}
@ -191,7 +223,7 @@ public final class ClassFileAttributes {
if (opens_to_count > 0) {
Set<String> targets = new HashSet<>();
for (int j=0; j<opens_to_count; j++) {
String t = cr.readUTF8(off, buf).replace('/', '.');
String t = cr.readModule(off, buf);
off += 2;
targets.add(t);
}
@ -232,8 +264,7 @@ public final class ClassFileAttributes {
}
}
attr.descriptor = builder.build();
return attr;
return new ModuleAttribute(builder.build());
}
@Override
@ -248,7 +279,7 @@ public final class ClassFileAttributes {
// module_name
String mn = descriptor.name();
int module_name_index = cw.newUTF8(mn.replace('.', '/'));
int module_name_index = cw.newModule(mn);
attr.putShort(module_name_index);
// module_flags
@ -259,66 +290,83 @@ public final class ClassFileAttributes {
module_flags |= ACC_SYNTHETIC;
attr.putShort(module_flags);
// module_version
Version v = descriptor.version().orElse(null);
if (v == null) {
attr.putShort(0);
} else {
int module_version_index = cw.newUTF8(v.toString());
attr.putShort(module_version_index);
}
// requires_count
attr.putShort(descriptor.requires().size());
// requires[requires_count]
for (Requires md : descriptor.requires()) {
String dn = md.name();
int flags = 0;
if (md.modifiers().contains(Requires.Modifier.TRANSITIVE))
flags |= ACC_TRANSITIVE;
if (md.modifiers().contains(Requires.Modifier.STATIC))
flags |= ACC_STATIC_PHASE;
if (md.modifiers().contains(Requires.Modifier.SYNTHETIC))
flags |= ACC_SYNTHETIC;
if (md.modifiers().contains(Requires.Modifier.MANDATED))
flags |= ACC_MANDATED;
int index = cw.newUTF8(dn.replace('.', '/'));
attr.putShort(index);
attr.putShort(flags);
for (Requires r : descriptor.requires()) {
int requires_index = cw.newModule(r.name());
attr.putShort(requires_index);
int requires_flags = 0;
if (r.modifiers().contains(Requires.Modifier.TRANSITIVE))
requires_flags |= ACC_TRANSITIVE;
if (r.modifiers().contains(Requires.Modifier.STATIC))
requires_flags |= ACC_STATIC_PHASE;
if (r.modifiers().contains(Requires.Modifier.SYNTHETIC))
requires_flags |= ACC_SYNTHETIC;
if (r.modifiers().contains(Requires.Modifier.MANDATED))
requires_flags |= ACC_MANDATED;
attr.putShort(requires_flags);
int requires_version_index;
v = r.compiledVersion().orElse(null);
if (v == null) {
requires_version_index = 0;
} else {
requires_version_index = cw.newUTF8(v.toString());
}
attr.putShort(requires_version_index);
}
// exports_count and exports[exports_count];
attr.putShort(descriptor.exports().size());
for (Exports e : descriptor.exports()) {
String pkg = e.source().replace('.', '/');
attr.putShort(cw.newUTF8(pkg));
attr.putShort(cw.newPackage(pkg));
int flags = 0;
int exports_flags = 0;
if (e.modifiers().contains(Exports.Modifier.SYNTHETIC))
flags |= ACC_SYNTHETIC;
exports_flags |= ACC_SYNTHETIC;
if (e.modifiers().contains(Exports.Modifier.MANDATED))
flags |= ACC_MANDATED;
attr.putShort(flags);
exports_flags |= ACC_MANDATED;
attr.putShort(exports_flags);
if (e.isQualified()) {
Set<String> ts = e.targets();
attr.putShort(ts.size());
ts.forEach(t -> attr.putShort(cw.newUTF8(t.replace('.', '/'))));
ts.forEach(target -> attr.putShort(cw.newModule(target)));
} else {
attr.putShort(0);
}
}
// opens_counts and opens[opens_counts]
attr.putShort(descriptor.opens().size());
for (Opens obj : descriptor.opens()) {
String pkg = obj.source().replace('.', '/');
attr.putShort(cw.newUTF8(pkg));
attr.putShort(cw.newPackage(pkg));
int flags = 0;
int opens_flags = 0;
if (obj.modifiers().contains(Opens.Modifier.SYNTHETIC))
flags |= ACC_SYNTHETIC;
opens_flags |= ACC_SYNTHETIC;
if (obj.modifiers().contains(Opens.Modifier.MANDATED))
flags |= ACC_MANDATED;
attr.putShort(flags);
opens_flags |= ACC_MANDATED;
attr.putShort(opens_flags);
if (obj.isQualified()) {
Set<String> ts = obj.targets();
attr.putShort(ts.size());
ts.forEach(t -> attr.putShort(cw.newUTF8(t.replace('.', '/'))));
ts.forEach(target -> attr.putShort(cw.newModule(target)));
} else {
attr.putShort(0);
}
@ -369,7 +417,7 @@ public final class ClassFileAttributes {
*
* // the number of entries in the packages table
* u2 packages_count;
* { // index to CONSTANT_CONSTANT_utf8_info structure with the package name
* { // index to CONSTANT_Package_info structure with the package name
* u2 package_index
* } packages[package_count];
*
@ -402,7 +450,7 @@ public final class ClassFileAttributes {
// packages
Set<String> packages = new HashSet<>();
for (int i=0; i<package_count; i++) {
String pkg = cr.readUTF8(off, buf).replace('/', '.');
String pkg = cr.readPackage(off, buf).replace('/', '.');
packages.add(pkg);
off += 2;
}
@ -427,68 +475,13 @@ public final class ClassFileAttributes {
// packages
packages.stream()
.map(p -> p.replace('.', '/'))
.forEach(p -> attr.putShort(cw.newUTF8(p)));
.forEach(p -> attr.putShort(cw.newPackage(p)));
return attr;
}
}
/**
* ModuleVersion attribute.
*
* <pre> {@code
*
* ModuleVersion_attribute {
* // index to CONSTANT_utf8_info structure in constant pool representing
* // the string "ModuleVersion"
* u2 attribute_name_index;
* u4 attribute_length;
*
* // index to CONSTANT_CONSTANT_utf8_info structure with the version
* u2 version_index;
* }
*
* } </pre>
*/
public static class ModuleVersionAttribute extends Attribute {
private final Version version;
public ModuleVersionAttribute(Version version) {
super(MODULE_VERSION);
this.version = version;
}
public ModuleVersionAttribute() {
this(null);
}
@Override
protected Attribute read(ClassReader cr,
int off,
int len,
char[] buf,
int codeOff,
Label[] labels)
{
String value = cr.readUTF8(off, buf);
return new ModuleVersionAttribute(Version.parse(value));
}
@Override
protected ByteVector write(ClassWriter cw,
byte[] code,
int len,
int maxStack,
int maxLocals)
{
ByteVector attr = new ByteVector();
int index = cw.newUTF8(version.toString());
attr.putShort(index);
return attr;
}
}
/**
* ModuleMainClass attribute.
*
@ -526,7 +519,7 @@ public final class ClassFileAttributes {
int codeOff,
Label[] labels)
{
String value = cr.readClass(off, buf);
String value = cr.readClass(off, buf).replace('/', '.');
return new ModuleMainClassAttribute(value);
}
@ -538,7 +531,7 @@ public final class ClassFileAttributes {
int maxLocals)
{
ByteVector attr = new ByteVector();
int index = cw.newClass(mainClass);
int index = cw.newClass(mainClass.replace('.', '/'));
attr.putShort(index);
return attr;
}
@ -555,11 +548,11 @@ public final class ClassFileAttributes {
* u2 attribute_name_index;
* u4 attribute_length;
*
* // index to CONSTANT_CONSTANT_utf8_info structure with the OS name
* // index to CONSTANT_utf8_info structure with the OS name
* u2 os_name_index;
* // index to CONSTANT_CONSTANT_utf8_info structure with the OS arch
* // index to CONSTANT_utf8_info structure with the OS arch
* u2 os_arch_index
* // index to CONSTANT_CONSTANT_utf8_info structure with the OS version
* // index to CONSTANT_utf8_info structure with the OS version
* u2 os_version_index;
* }
*
@ -656,7 +649,7 @@ public final class ClassFileAttributes {
*
* // the number of entries in the hashes table
* u2 hashes_count;
* { u2 module_name_index
* { u2 module_name_index (index to CONSTANT_Module_info structure)
* u2 hash_length;
* u1 hash[hash_length];
* } hashes[hashes_count];
@ -691,7 +684,7 @@ public final class ClassFileAttributes {
Map<String, byte[]> map = new HashMap<>();
for (int i=0; i<hashes_count; i++) {
String mn = cr.readUTF8(off, buf).replace('/', '.');
String mn = cr.readModule(off, buf);
off += 2;
int hash_length = cr.readUnsignedShort(off);
@ -728,7 +721,7 @@ public final class ClassFileAttributes {
for (String mn : names) {
byte[] hash = hashes.hashFor(mn);
assert hash != null;
attr.putShort(cw.newUTF8(mn.replace('.', '/')));
attr.putShort(cw.newModule(mn));
attr.putShort(hash.length);
for (byte b: hash) {
@ -740,4 +733,58 @@ public final class ClassFileAttributes {
}
}
/**
* ModuleResolution_attribute {
* u2 attribute_name_index; // "ModuleResolution"
* u4 attribute_length; // 2
* u2 resolution_flags;
*
* The value of the resolution_flags item is a mask of flags used to denote
* properties of module resolution. The flags are as follows:
*
* // Optional
* 0x0001 (DO_NOT_RESOLVE_BY_DEFAULT)
*
* // At most one of:
* 0x0002 (WARN_DEPRECATED)
* 0x0004 (WARN_DEPRECATED_FOR_REMOVAL)
* 0x0008 (WARN_INCUBATING)
*/
static class ModuleResolutionAttribute extends Attribute {
private final int value;
ModuleResolutionAttribute() {
super(MODULE_RESOLUTION);
value = 0;
}
ModuleResolutionAttribute(int value) {
super(MODULE_RESOLUTION);
this.value = value;
}
@Override
protected Attribute read(ClassReader cr,
int off,
int len,
char[] buf,
int codeOff,
Label[] labels)
{
int flags = cr.readUnsignedShort(off);
return new ModuleResolutionAttribute(flags);
}
@Override
protected ByteVector write(ClassWriter cw,
byte[] code,
int len,
int maxStack,
int maxLocals)
{
ByteVector attr = new ByteVector();
attr.putShort(value);
return attr;
}
}
}

View File

@ -38,10 +38,10 @@ public class ClassFileConstants {
public static final String SDE = "SourceDebugExtension";
public static final String MODULE_PACKAGES = "ModulePackages";
public static final String MODULE_VERSION = "ModuleVersion";
public static final String MODULE_MAIN_CLASS = "ModuleMainClass";
public static final String MODULE_TARGET = "ModuleTarget";
public static final String MODULE_HASHES = "ModuleHashes";
public static final String MODULE_RESOLUTION = "ModuleResolution";
// access, requires, exports, and opens flags
public static final int ACC_MODULE = 0x8000;
@ -51,4 +51,10 @@ public class ClassFileConstants {
public static final int ACC_SYNTHETIC = 0x1000;
public static final int ACC_MANDATED = 0x8000;
// ModuleResolution_attribute resolution flags
public static final int DO_NOT_RESOLVE_BY_DEFAULT = 0x0001;
public static final int WARN_DEPRECATED = 0x0002;
public static final int WARN_DEPRECATED_FOR_REMOVAL = 0x0004;
public static final int WARN_INCUBATING = 0x0008;
}

View File

@ -195,7 +195,9 @@ public final class ModuleBootstrap {
// 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 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()) {
@ -212,6 +214,9 @@ public final class ModuleBootstrap {
if (hasJava && mn.startsWith("java."))
continue;
if (ModuleResolution.doNotResolveByDefault(mref))
continue;
// add as root if observable and exports at least one package
if ((finder == systemModules || finder.find(mn).isPresent())) {
ModuleDescriptor descriptor = mref.descriptor();
@ -231,6 +236,7 @@ public final class ModuleBootstrap {
ModuleFinder f = finder; // observable modules
systemModules.findAll()
.stream()
.filter(mref -> !ModuleResolution.doNotResolveByDefault(mref))
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.filter(mn -> f.find(mn).isPresent()) // observable
@ -277,6 +283,8 @@ public final class ModuleBootstrap {
// time to create configuration
PerfCounters.resolveTime.addElapsedTimeFrom(t3);
// check module names and incubating status
checkModuleNamesAndStatus(cf);
// mapping of modules to class loaders
Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
@ -508,12 +516,12 @@ public final class ModuleBootstrap {
String key = e.getKey();
String[] s = key.split("/");
if (s.length != 2)
fail("Unable to parse: " + key);
fail("Unable to parse as <module>/<package>: " + key);
String mn = s[0];
String pn = s[1];
if (mn.isEmpty() || pn.isEmpty())
fail("Module and package name must be specified:" + key);
fail("Module and package name must be specified: " + key);
// The exporting module is in the boot layer
Module m;
@ -585,7 +593,7 @@ public final class ModuleBootstrap {
int pos = value.indexOf('=');
if (pos == -1)
fail("Unable to parse: " + value);
fail("Unable to parse as <module>=<value>: " + value);
if (pos == 0)
fail("Missing module name in: " + value);
@ -594,7 +602,7 @@ public final class ModuleBootstrap {
String rhs = value.substring(pos+1);
if (rhs.isEmpty())
fail("Unable to parse: " + value);
fail("Unable to parse as <module>=<value>: " + value);
// value is <module>(,<module>)* or <file>(<pathsep><file>)*
if (!allowDuplicates && map.containsKey(key))
@ -626,6 +634,33 @@ public final class ModuleBootstrap {
return (String)System.getProperties().remove(key);
}
/**
* Checks the names and resolution bit of each module in the configuration,
* emitting warnings if needed.
*/
private static void checkModuleNamesAndStatus(Configuration cf) {
String incubating = null;
for (ResolvedModule rm : cf.modules()) {
ModuleReference mref = rm.reference();
String mn = mref.descriptor().name();
// emit warning if module name ends with a non-Java letter
if (!Checks.hasLegalModuleNameLastCharacter(mn))
warn("Module name \"" + mn + "\" may soon be illegal");
// emit warning if the WARN_INCUBATING module resolution bit set
if (ModuleResolution.hasIncubatingWarning(mref)) {
if (incubating == null) {
incubating = mn;
} else {
incubating += ", " + mn;
}
}
}
if (incubating != null)
warn("Using incubator modules: " + incubating);
}
/**
* Throws a RuntimeException with the given message
*/

View File

@ -35,6 +35,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
@ -50,7 +51,6 @@ public final class ModuleHashes {
byte[] generate(String algorithm);
}
private final String algorithm;
private final Map<String, byte[]> nameToHash;
@ -142,4 +142,39 @@ public final class ModuleHashes {
}
return new ModuleHashes(algorithm, nameToHash);
}
/**
* This is used by jdk.internal.module.SystemModules class
* generated at link time.
*/
public static class Builder {
final String algorithm;
Map<String, byte[]> nameToHash;
Builder(String algorithm) {
this.algorithm = Objects.requireNonNull(algorithm);
}
/**
* Sets the module hash for the given module name
*/
public Builder hashForModule(String mn, byte[] hash) {
if (nameToHash == null)
nameToHash = new HashMap<>();
nameToHash.put(mn, hash);
return this;
}
/**
* Builds a {@code ModuleHashes}.
*/
public ModuleHashes build() {
if (nameToHash != null) {
return new ModuleHashes(algorithm, nameToHash);
} else {
return null;
}
}
}
}

View File

@ -23,7 +23,7 @@
* questions.
*/
package java.lang.module;
package jdk.internal.module;
import java.io.DataInput;
import java.io.DataInputStream;
@ -31,10 +31,13 @@ import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.module.InvalidModuleDescriptorException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Builder;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Version;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
import java.util.ArrayList;
@ -46,7 +49,9 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import jdk.internal.module.ModuleHashes;
import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModuleResolution;
import static jdk.internal.module.ClassFileConstants.*;
@ -58,7 +63,10 @@ import static jdk.internal.module.ClassFileConstants.*;
* and fine control over the throwing of InvalidModuleDescriptorException.
*/
final class ModuleInfo {
public final class ModuleInfo {
private static final JavaLangModuleAccess JLMA
= SharedSecrets.getJavaLangModuleAccess();
// supplies the set of packages when ModulePackages attribute not present
private final Supplier<Set<String>> packageFinder;
@ -75,14 +83,42 @@ final class ModuleInfo {
this(pf, true);
}
/**
* A holder class for the ModuleDescriptor that is created by reading the
* Module and other standard class file attributes. It also holds the objects
* that represent the non-standard class file attributes that are read from
* the class file.
*/
public static final class Attributes {
private final ModuleDescriptor descriptor;
private final ModuleHashes recordedHashes;
private final ModuleResolution moduleResolution;
Attributes(ModuleDescriptor descriptor,
ModuleHashes recordedHashes,
ModuleResolution moduleResolution) {
this.descriptor = descriptor;
this.recordedHashes = recordedHashes;
this.moduleResolution = moduleResolution;
}
public ModuleDescriptor descriptor() {
return descriptor;
}
public ModuleHashes recordedHashes() {
return recordedHashes;
}
public ModuleResolution moduleResolution() {
return moduleResolution;
}
}
/**
* Reads a {@code module-info.class} from the given input stream.
*
* @throws InvalidModuleDescriptorException
* @throws IOException
*/
public static ModuleDescriptor read(InputStream in,
Supplier<Set<String>> pf)
public static Attributes read(InputStream in, Supplier<Set<String>> pf)
throws IOException
{
try {
@ -100,9 +136,7 @@ final class ModuleInfo {
* @throws InvalidModuleDescriptorException
* @throws UncheckedIOException
*/
public static ModuleDescriptor read(ByteBuffer bb,
Supplier<Set<String>> pf)
{
public static Attributes read(ByteBuffer bb, Supplier<Set<String>> pf) {
try {
return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
} catch (IllegalArgumentException | IllegalStateException e) {
@ -121,9 +155,7 @@ final class ModuleInfo {
* @throws InvalidModuleDescriptorException
* @throws UncheckedIOException
*/
static ModuleDescriptor readIgnoringHashes(ByteBuffer bb,
Supplier<Set<String>> pf)
{
public static Attributes readIgnoringHashes(ByteBuffer bb, Supplier<Set<String>> pf) {
try {
return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
} catch (IllegalArgumentException | IllegalStateException e) {
@ -144,7 +176,7 @@ final class ModuleInfo {
* because an identifier is not a legal Java identifier, duplicate
* exports, and many other reasons
*/
private ModuleDescriptor doRead(DataInput in) throws IOException {
private Attributes doRead(DataInput in) throws IOException {
int magic = in.readInt();
if (magic != 0xCAFEBABE)
@ -163,8 +195,9 @@ final class ModuleInfo {
throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
int this_class = in.readUnsignedShort();
if (this_class != 0)
throw invalidModuleDescriptor("this_class must be 0");
String mn = cpool.getClassName(this_class);
if (!"module-info".equals(mn))
throw invalidModuleDescriptor("this_class should be module-info");
int super_class = in.readUnsignedShort();
if (super_class > 0)
@ -189,10 +222,10 @@ final class ModuleInfo {
Builder builder = null;
Set<String> packages = null;
String version = null;
String mainClass = null;
String[] osValues = null;
ModuleHashes hashes = null;
ModuleResolution moduleResolution = null;
for (int i = 0; i < attributes_count ; i++) {
int name_index = in.readUnsignedShort();
@ -215,10 +248,6 @@ final class ModuleInfo {
packages = readModulePackagesAttribute(in, cpool);
break;
case MODULE_VERSION :
version = readModuleVersionAttribute(in, cpool);
break;
case MODULE_MAIN_CLASS :
mainClass = readModuleMainClassAttribute(in, cpool);
break;
@ -235,6 +264,10 @@ final class ModuleInfo {
}
break;
case MODULE_RESOLUTION :
moduleResolution = readModuleResolution(in, cpool);
break;
default:
if (isAttributeDisallowed(attribute_name)) {
throw invalidModuleDescriptor(attribute_name
@ -263,23 +296,33 @@ final class ModuleInfo {
usedPackageFinder = true;
}
if (packages != null) {
for (String pn : builder.exportedAndOpenPackages()) {
if (!packages.contains(pn)) {
String tail;
if (usedPackageFinder) {
tail = " not found by package finder";
} else {
tail = " missing from ModulePackages attribute";
Set<String> exportedPackages = JLMA.exportedPackages(builder);
Set<String> openPackages = JLMA.openPackages(builder);
if (packages.containsAll(exportedPackages)
&& packages.containsAll(openPackages)) {
packages.removeAll(exportedPackages);
packages.removeAll(openPackages);
} else {
// the set of packages is not complete
Set<String> exportedAndOpenPackages = new HashSet<>();
exportedAndOpenPackages.addAll(exportedPackages);
exportedAndOpenPackages.addAll(openPackages);
for (String pn : exportedAndOpenPackages) {
if (!packages.contains(pn)) {
String tail;
if (usedPackageFinder) {
tail = " not found by package finder";
} else {
tail = " missing from ModulePackages attribute";
}
throw invalidModuleDescriptor("Package " + pn + tail);
}
throw invalidModuleDescriptor("Package " + pn + tail);
}
packages.remove(pn);
assert false; // should not get here
}
builder.contains(packages);
}
if (version != null)
builder.version(version);
if (mainClass != null)
builder.mainClass(mainClass);
if (osValues != null) {
@ -287,10 +330,9 @@ final class ModuleInfo {
if (osValues[1] != null) builder.osArch(osValues[1]);
if (osValues[2] != null) builder.osVersion(osValues[2]);
}
if (hashes != null)
builder.hashes(hashes);
return builder.build();
ModuleDescriptor descriptor = builder.build();
return new Attributes(descriptor, hashes, moduleResolution);
}
/**
@ -302,38 +344,55 @@ final class ModuleInfo {
{
// module_name
int module_name_index = in.readUnsignedShort();
String mn = cpool.getUtf8AsBinaryName(module_name_index);
Builder builder = new ModuleDescriptor.Builder(mn, /*strict*/ false);
String mn = cpool.getModuleName(module_name_index);
int module_flags = in.readUnsignedShort();
boolean open = ((module_flags & ACC_OPEN) != 0);
if (open)
builder.open(true);
if ((module_flags & ACC_SYNTHETIC) != 0)
builder.synthetic(true);
boolean synthetic = ((module_flags & ACC_SYNTHETIC) != 0);
Builder builder = JLMA.newModuleBuilder(mn, false, open, synthetic);
int module_version_index = in.readUnsignedShort();
if (module_version_index != 0) {
String vs = cpool.getUtf8(module_version_index);
builder.version(vs);
}
int requires_count = in.readUnsignedShort();
boolean requiresJavaBase = false;
for (int i=0; i<requires_count; i++) {
int index = in.readUnsignedShort();
int flags = in.readUnsignedShort();
String dn = cpool.getUtf8AsBinaryName(index);
int requires_index = in.readUnsignedShort();
String dn = cpool.getModuleName(requires_index);
int requires_flags = in.readUnsignedShort();
Set<Requires.Modifier> mods;
if (flags == 0) {
if (requires_flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_TRANSITIVE) != 0)
if ((requires_flags & ACC_TRANSITIVE) != 0)
mods.add(Requires.Modifier.TRANSITIVE);
if ((flags & ACC_STATIC_PHASE) != 0)
if ((requires_flags & ACC_STATIC_PHASE) != 0)
mods.add(Requires.Modifier.STATIC);
if ((flags & ACC_SYNTHETIC) != 0)
if ((requires_flags & ACC_SYNTHETIC) != 0)
mods.add(Requires.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
if ((requires_flags & ACC_MANDATED) != 0)
mods.add(Requires.Modifier.MANDATED);
}
builder.requires(mods, dn);
int requires_version_index = in.readUnsignedShort();
Version compiledVersion = null;
if (requires_version_index != 0) {
String vs = cpool.getUtf8(requires_version_index);
compiledVersion = Version.parse(vs);
}
if (compiledVersion == null) {
builder.requires(mods, dn);
} else {
builder.requires(mods, dn, compiledVersion);
}
if (dn.equals("java.base"))
requiresJavaBase = true;
}
@ -350,18 +409,18 @@ final class ModuleInfo {
int exports_count = in.readUnsignedShort();
if (exports_count > 0) {
for (int i=0; i<exports_count; i++) {
int index = in.readUnsignedShort();
String pkg = cpool.getUtf8AsBinaryName(index);
int exports_index = in.readUnsignedShort();
String pkg = cpool.getPackageName(exports_index);
Set<Exports.Modifier> mods;
int flags = in.readUnsignedShort();
if (flags == 0) {
int exports_flags = in.readUnsignedShort();
if (exports_flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_SYNTHETIC) != 0)
if ((exports_flags & ACC_SYNTHETIC) != 0)
mods.add(Exports.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
if ((exports_flags & ACC_MANDATED) != 0)
mods.add(Exports.Modifier.MANDATED);
}
@ -370,7 +429,7 @@ final class ModuleInfo {
Set<String> targets = new HashSet<>(exports_to_count);
for (int j=0; j<exports_to_count; j++) {
int exports_to_index = in.readUnsignedShort();
targets.add(cpool.getUtf8AsBinaryName(exports_to_index));
targets.add(cpool.getModuleName(exports_to_index));
}
builder.exports(mods, pkg, targets);
} else {
@ -386,18 +445,18 @@ final class ModuleInfo {
+ " module must be 0 length");
}
for (int i=0; i<opens_count; i++) {
int index = in.readUnsignedShort();
String pkg = cpool.getUtf8AsBinaryName(index);
int opens_index = in.readUnsignedShort();
String pkg = cpool.getPackageName(opens_index);
Set<Opens.Modifier> mods;
int flags = in.readUnsignedShort();
if (flags == 0) {
int opens_flags = in.readUnsignedShort();
if (opens_flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_SYNTHETIC) != 0)
if ((opens_flags & ACC_SYNTHETIC) != 0)
mods.add(Opens.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
if ((opens_flags & ACC_MANDATED) != 0)
mods.add(Opens.Modifier.MANDATED);
}
@ -406,7 +465,7 @@ final class ModuleInfo {
Set<String> targets = new HashSet<>(open_to_count);
for (int j=0; j<open_to_count; j++) {
int opens_to_index = in.readUnsignedShort();
targets.add(cpool.getUtf8AsBinaryName(opens_to_index));
targets.add(cpool.getModuleName(opens_to_index));
}
builder.opens(mods, pkg, targets);
} else {
@ -419,7 +478,7 @@ final class ModuleInfo {
if (uses_count > 0) {
for (int i=0; i<uses_count; i++) {
int index = in.readUnsignedShort();
String sn = cpool.getClassNameAsBinaryName(index);
String sn = cpool.getClassName(index);
builder.uses(sn);
}
}
@ -428,12 +487,12 @@ final class ModuleInfo {
if (provides_count > 0) {
for (int i=0; i<provides_count; i++) {
int index = in.readUnsignedShort();
String sn = cpool.getClassNameAsBinaryName(index);
String sn = cpool.getClassName(index);
int with_count = in.readUnsignedShort();
List<String> providers = new ArrayList<>(with_count);
for (int j=0; j<with_count; j++) {
index = in.readUnsignedShort();
String pn = cpool.getClassNameAsBinaryName(index);
String pn = cpool.getClassName(index);
providers.add(pn);
}
builder.provides(sn, providers);
@ -453,22 +512,16 @@ final class ModuleInfo {
Set<String> packages = new HashSet<>(package_count);
for (int i=0; i<package_count; i++) {
int index = in.readUnsignedShort();
String pn = cpool.getUtf8AsBinaryName(index);
packages.add(pn);
String pn = cpool.getPackageName(index);
boolean added = packages.add(pn);
if (!added) {
throw invalidModuleDescriptor("Package " + pn + " in ModulePackages"
+ "attribute more than once");
}
}
return packages;
}
/**
* Reads the ModuleVersion attribute
*/
private String readModuleVersionAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
int index = in.readUnsignedShort();
return cpool.getUtf8(index);
}
/**
* Reads the ModuleMainClass attribute
*/
@ -476,7 +529,7 @@ final class ModuleInfo {
throws IOException
{
int index = in.readUnsignedShort();
return cpool.getClassNameAsBinaryName(index);
return cpool.getClassName(index);
}
/**
@ -516,7 +569,7 @@ final class ModuleInfo {
Map<String, byte[]> map = new HashMap<>(hash_count);
for (int i=0; i<hash_count; i++) {
int module_name_index = in.readUnsignedShort();
String mn = cpool.getUtf8AsBinaryName(module_name_index);
String mn = cpool.getModuleName(module_name_index);
int hash_length = in.readUnsignedShort();
if (hash_length == 0) {
throw invalidModuleDescriptor("hash_length == 0");
@ -529,6 +582,31 @@ final class ModuleInfo {
return new ModuleHashes(algorithm, map);
}
/**
* Reads the ModuleResolution attribute.
*/
private ModuleResolution readModuleResolution(DataInput in,
ConstantPool cpool)
throws IOException
{
int flags = in.readUnsignedShort();
int reason = 0;
if ((flags & WARN_DEPRECATED) != 0)
reason = WARN_DEPRECATED;
if ((flags & WARN_DEPRECATED_FOR_REMOVAL) != 0) {
if (reason != 0)
throw invalidModuleDescriptor("Bad module resolution flags:" + flags);
reason = WARN_DEPRECATED_FOR_REMOVAL;
}
if ((flags & WARN_INCUBATING) != 0) {
if (reason != 0)
throw invalidModuleDescriptor("Bad module resolution flags:" + flags);
}
return new ModuleResolution(flags);
}
/**
* Returns true if the given attribute can be present at most once
@ -540,10 +618,10 @@ final class ModuleInfo {
name.equals(SOURCE_FILE) ||
name.equals(SDE) ||
name.equals(MODULE_PACKAGES) ||
name.equals(MODULE_VERSION) ||
name.equals(MODULE_MAIN_CLASS) ||
name.equals(MODULE_TARGET) ||
name.equals(MODULE_HASHES))
name.equals(MODULE_HASHES) ||
name.equals(MODULE_RESOLUTION))
return true;
return false;
@ -604,6 +682,8 @@ final class ModuleInfo {
static final int CONSTANT_MethodHandle = 15;
static final int CONSTANT_MethodType = 16;
static final int CONSTANT_InvokeDynamic = 18;
static final int CONSTANT_Module = 19;
static final int CONSTANT_Package = 20;
private static class Entry {
protected Entry(int tag) {
@ -653,6 +733,8 @@ final class ModuleInfo {
break;
case CONSTANT_Class:
case CONSTANT_Package:
case CONSTANT_Module:
case CONSTANT_String:
int index = in.readUnsignedShort();
pool[i] = new IndexEntry(tag, index);
@ -715,14 +797,34 @@ final class ModuleInfo {
throw invalidModuleDescriptor("CONSTANT_Class expected at entry: "
+ index);
}
return getUtf8(((IndexEntry) e).index);
String value = getUtf8(((IndexEntry) e).index);
checkUnqualifiedName("CONSTANT_Class", index, value);
return value.replace('/', '.'); // internal form -> binary name
}
String getClassNameAsBinaryName(int index) {
String value = getClassName(index);
String getPackageName(int index) {
checkIndex(index);
Entry e = pool[index];
if (e.tag != CONSTANT_Package) {
throw invalidModuleDescriptor("CONSTANT_Package expected at entry: "
+ index);
}
String value = getUtf8(((IndexEntry) e).index);
checkUnqualifiedName("CONSTANT_Package", index, value);
return value.replace('/', '.'); // internal form -> binary name
}
String getModuleName(int index) {
checkIndex(index);
Entry e = pool[index];
if (e.tag != CONSTANT_Module) {
throw invalidModuleDescriptor("CONSTANT_Module expected at entry: "
+ index);
}
String value = getUtf8(((IndexEntry) e).index);
return decodeModuleName(index, value);
}
String getUtf8(int index) {
checkIndex(index);
Entry e = pool[index];
@ -733,15 +835,103 @@ final class ModuleInfo {
return (String) (((ValueEntry) e).value);
}
String getUtf8AsBinaryName(int index) {
String value = getUtf8(index);
return value.replace('/', '.'); // internal -> binary name
}
void checkIndex(int index) {
if (index < 1 || index >= pool.length)
throw invalidModuleDescriptor("Index into constant pool out of range");
}
void checkUnqualifiedName(String what, int index, String value) {
int len = value.length();
if (len == 0) {
throw invalidModuleDescriptor(what + " at entry " + index
+ " has zero length");
}
for (int i=0; i<len; i++) {
char c = value.charAt(i);
if (c == '.' || c == ';' || c == '[') {
throw invalidModuleDescriptor(what + " at entry " + index
+ " has illegal character: '"
+ c + "'");
}
}
}
/**
* "Decode" a module name that has been read from the constant pool.
*/
String decodeModuleName(int index, String value) {
int len = value.length();
if (len == 0) {
throw invalidModuleDescriptor("CONSTANT_Module at entry "
+ index + " is zero length");
}
int i = 0;
while (i < len) {
int cp = value.codePointAt(i);
if (cp == ':' || cp == '@' || cp < 0x20) {
throw invalidModuleDescriptor("CONSTANT_Module at entry "
+ index + " has illegal character: "
+ Character.getName(cp));
}
// blackslash is the escape character
if (cp == '\\')
return decodeModuleName(index, i, value);
i += Character.charCount(cp);
}
return value;
}
/**
* "Decode" a module name that has been read from the constant pool and
* partly checked for illegal characters (up to position {@code i}).
*/
String decodeModuleName(int index, int i, String value) {
StringBuilder sb = new StringBuilder();
// copy the code points that have been checked
int j = 0;
while (j < i) {
int cp = value.codePointAt(j);
sb.appendCodePoint(cp);
j += Character.charCount(cp);
}
// decode from position {@code i} to end
int len = value.length();
while (i < len) {
int cp = value.codePointAt(i);
if (cp == ':' || cp == '@' || cp < 0x20) {
throw invalidModuleDescriptor("CONSTANT_Module at entry "
+ index + " has illegal character: "
+ Character.getName(cp));
}
// blackslash is the escape character
if (cp == '\\') {
j = i + Character.charCount(cp);
if (j >= len) {
throw invalidModuleDescriptor("CONSTANT_Module at entry "
+ index + " has illegal "
+ "escape sequence");
}
int next = value.codePointAt(j);
if (next != '\\' && next != ':' && next != '@') {
throw invalidModuleDescriptor("CONSTANT_Module at entry "
+ index + " has illegal "
+ "escape sequence");
}
sb.appendCodePoint(next);
i += Character.charCount(next);
} else {
sb.appendCodePoint(cp);
}
i += Character.charCount(cp);
}
return sb.toString();
}
}
/**

View File

@ -70,6 +70,9 @@ public final class ModuleInfoExtender {
// the hashes for the Hashes attribute
private ModuleHashes hashes;
// the value of the ModuleResolution attribute
private ModuleResolution moduleResolution;
private ModuleInfoExtender(InputStream in) {
this.in = in;
}
@ -120,6 +123,14 @@ public final class ModuleInfoExtender {
return this;
}
/**
* Sets the value for the ModuleResolution attribute.
*/
public ModuleInfoExtender moduleResolution(ModuleResolution mres) {
this.moduleResolution = mres;
return this;
}
/**
* A ClassVisitor that supports adding class file attributes. If an
* attribute already exists then the first occurence of the attribute
@ -183,21 +194,20 @@ public final class ModuleInfoExtender {
if (packages != null)
cv.addAttribute(new ModulePackagesAttribute(packages));
if (version != null)
cv.addAttribute(new ModuleVersionAttribute(version));
if (mainClass != null)
cv.addAttribute(new ModuleMainClassAttribute(mainClass));
if (osName != null || osArch != null || osVersion != null)
cv.addAttribute(new ModuleTargetAttribute(osName, osArch, osVersion));
if (hashes != null)
cv.addAttribute(new ModuleHashesAttribute(hashes));
if (moduleResolution != null)
cv.addAttribute(new ModuleResolutionAttribute(moduleResolution.value()));
List<Attribute> attrs = new ArrayList<>();
// prototypes of attributes that should be parsed
attrs.add(new ModuleAttribute());
attrs.add(new ModuleAttribute(version));
attrs.add(new ModulePackagesAttribute());
attrs.add(new ModuleVersionAttribute());
attrs.add(new ModuleMainClassAttribute());
attrs.add(new ModuleTargetAttribute());
attrs.add(new ModuleHashesAttribute());

View File

@ -49,13 +49,12 @@ public final class ModuleInfoWriter {
* returning it in a byte array.
*/
private static byte[] toModuleInfo(ModuleDescriptor md) {
ClassWriter cw = new ClassWriter(0);
cw.visit(Opcodes.V1_9, ACC_MODULE, null, null, null, null);
cw.visit(Opcodes.V1_9, ACC_MODULE, "module-info", null, null, null);
cw.visitAttribute(new ModuleAttribute(md));
// for tests: write the Packages attribute when there are packages that
// aren't exported or open
// for tests: write the ModulePackages attribute when there are packages
// that aren't exported or open
Stream<String> exported = md.exports().stream()
.map(ModuleDescriptor.Exports::source);
Stream<String> open = md.opens().stream()
@ -64,10 +63,10 @@ public final class ModuleInfoWriter {
if (md.packages().size() > exportedOrOpen)
cw.visitAttribute(new ModulePackagesAttribute(md.packages()));
md.version().ifPresent(v -> cw.visitAttribute(new ModuleVersionAttribute(v)));
// write ModuleMainClass if the module has a main class
md.mainClass().ifPresent(mc -> cw.visitAttribute(new ModuleMainClassAttribute(mc)));
// write the TargetPlatform attribute if have any of OS name/arch/version
// write ModuleTarget attribute if have any of OS name/arch/version
String osName = md.osName().orElse(null);
String osArch = md.osArch().orElse(null);
String osVersion = md.osVersion().orElse(null);
@ -76,7 +75,6 @@ public final class ModuleInfoWriter {
}
cw.visitEnd();
return cw.toByteArray();
}

View File

@ -149,9 +149,22 @@ public final class ModulePatcher {
// return a module reference to the patched module
URI location = mref.location().orElse(null);
return JLMA.newPatchedModule(descriptor,
location,
() -> new PatchedModuleReader(paths, mref));
ModuleHashes recordedHashes = null;
ModuleResolution mres = null;
if (mref instanceof ModuleReferenceImpl) {
ModuleReferenceImpl impl = (ModuleReferenceImpl)mref;
recordedHashes = impl.recordedHashes();
mres = impl.moduleResolution();
}
return new ModuleReferenceImpl(descriptor,
location,
() -> new PatchedModuleReader(paths, mref),
this,
recordedHashes,
null,
mres);
}

View File

@ -23,7 +23,7 @@
* questions.
*/
package java.lang.module;
package jdk.internal.module;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
@ -32,7 +32,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.lang.module.FindException;
import java.lang.module.InvalidModuleDescriptorException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
@ -59,7 +64,6 @@ import java.util.zip.ZipFile;
import jdk.internal.jmod.JmodFile;
import jdk.internal.jmod.JmodFile.Section;
import jdk.internal.module.Checks;
import jdk.internal.perf.PerfCounter;
import jdk.internal.util.jar.VersionedStream;
@ -74,7 +78,7 @@ import jdk.internal.util.jar.VersionedStream;
* modules in JMOD files.
*/
class ModulePath implements ModuleFinder {
public class ModulePath implements ModuleFinder {
private static final String MODULE_INFO = "module-info.class";
// the version to use for multi-release modular JARs
@ -90,7 +94,7 @@ class ModulePath implements ModuleFinder {
// map of module name to module reference map for modules already located
private final Map<String, ModuleReference> cachedModules = new HashMap<>();
ModulePath(Runtime.Version version, boolean isLinkPhase, Path... entries) {
public ModulePath(Runtime.Version version, boolean isLinkPhase, Path... entries) {
this.releaseVersion = version;
this.isLinkPhase = isLinkPhase;
this.entries = entries.clone();
@ -99,7 +103,7 @@ class ModulePath implements ModuleFinder {
}
}
ModulePath(Path... entries) {
public ModulePath(Path... entries) {
this(JarFile.runtimeVersion(), false, entries);
}
@ -343,11 +347,11 @@ class ModulePath implements ModuleFinder {
*/
private ModuleReference readJMod(Path file) throws IOException {
try (JmodFile jf = new JmodFile(file)) {
ModuleDescriptor md;
ModuleInfo.Attributes attrs;
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
md = ModuleDescriptor.read(in, () -> jmodPackages(jf));
attrs = ModuleInfo.read(in, () -> jmodPackages(jf));
}
return ModuleReferences.newJModModule(md, file);
return ModuleReferences.newJModModule(attrs, file);
}
}
@ -557,13 +561,14 @@ class ModulePath implements ModuleFinder {
ZipFile.OPEN_READ,
releaseVersion))
{
ModuleDescriptor md;
ModuleInfo.Attributes attrs;
JarEntry entry = jf.getJarEntry(MODULE_INFO);
if (entry == null) {
// no module-info.class so treat it as automatic module
try {
md = deriveModuleDescriptor(jf);
ModuleDescriptor md = deriveModuleDescriptor(jf);
attrs = new ModuleInfo.Attributes(md, null, null);
} catch (IllegalArgumentException iae) {
throw new FindException(
"Unable to derive module descriptor for: "
@ -571,11 +576,11 @@ class ModulePath implements ModuleFinder {
}
} else {
md = ModuleDescriptor.read(jf.getInputStream(entry),
() -> jarPackages(jf));
attrs = ModuleInfo.read(jf.getInputStream(entry),
() -> jarPackages(jf));
}
return ModuleReferences.newJarModule(md, file);
return ModuleReferences.newJarModule(attrs, file);
}
}
@ -604,15 +609,15 @@ class ModulePath implements ModuleFinder {
*/
private ModuleReference readExplodedModule(Path dir) throws IOException {
Path mi = dir.resolve(MODULE_INFO);
ModuleDescriptor md;
ModuleInfo.Attributes attrs;
try (InputStream in = Files.newInputStream(mi)) {
md = ModuleDescriptor.read(new BufferedInputStream(in),
() -> explodedPackages(dir));
attrs = ModuleInfo.read(new BufferedInputStream(in),
() -> explodedPackages(dir));
} catch (NoSuchFileException e) {
// for now
return null;
}
return ModuleReferences.newExplodedModule(md, dir);
return ModuleReferences.newExplodedModule(attrs, dir);
}
/**

View File

@ -0,0 +1,169 @@
/*
* 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. 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.IOException;
import java.io.UncheckedIOException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.net.URI;
import java.util.Objects;
import java.util.function.Supplier;
/**
* A ModuleReference implementation that supports referencing a module that
* is patched and/or can be tied to other modules by means of hashes.
*/
public class ModuleReferenceImpl extends ModuleReference {
private final Supplier<ModuleReader> readerSupplier;
// non-null if the module is patched
private final ModulePatcher patcher;
// the hashes of other modules recorded in this module
private final ModuleHashes recordedHashes;
// the function that computes the hash of this module
private final ModuleHashes.HashSupplier hasher;
// ModuleResolution flags
private final ModuleResolution moduleResolution;
// cached hash of this module to avoid needing to compute it many times
private byte[] cachedHash;
/**
* Constructs a new instance of this class.
*/
ModuleReferenceImpl(ModuleDescriptor descriptor,
URI location,
Supplier<ModuleReader> readerSupplier,
ModulePatcher patcher,
ModuleHashes recordedHashes,
ModuleHashes.HashSupplier hasher,
ModuleResolution moduleResolution)
{
super(descriptor, Objects.requireNonNull(location));
this.readerSupplier = readerSupplier;
this.patcher = patcher;
this.recordedHashes = recordedHashes;
this.hasher = hasher;
this.moduleResolution = moduleResolution;
}
@Override
public ModuleReader open() throws IOException {
try {
return readerSupplier.get();
} catch (UncheckedIOException e) {
throw e.getCause();
}
}
/**
* Returns {@code true} if this module has been patched via --patch-module.
*/
public boolean isPatched() {
return (patcher != null);
}
/**
* Returns the hashes recorded in this module or {@code null} if there
* are no hashes recorded.
*/
public ModuleHashes recordedHashes() {
return recordedHashes;
}
/**
* Returns the supplier that computes the hash of this module.
*/
ModuleHashes.HashSupplier hasher() {
return hasher;
}
/**
* Returns the ModuleResolution flags.
*/
public ModuleResolution moduleResolution() {
return moduleResolution;
}
/**
* Computes the hash of this module. Returns {@code null} if the hash
* cannot be computed.
*
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
public byte[] computeHash(String algorithm) {
byte[] result = cachedHash;
if (result != null)
return result;
if (hasher == null)
return null;
cachedHash = result = hasher.generate(algorithm);
return result;
}
@Override
public int hashCode() {
int hc = hash;
if (hc == 0) {
hc = descriptor().hashCode();
hc = 43 * hc + Objects.hashCode(location());
hc = 43 * hc + Objects.hashCode(patcher);
if (hc == 0)
hc = -1;
hash = hc;
}
return hc;
}
private int hash;
@Override
public boolean equals(Object ob) {
if (!(ob instanceof ModuleReferenceImpl))
return false;
ModuleReferenceImpl that = (ModuleReferenceImpl)ob;
// assume module content, recorded hashes, etc. are the same
// when the modules have equal module descriptors, are at the
// same location, and are patched by the same patcher.
return Objects.equals(this.descriptor(), that.descriptor())
&& Objects.equals(this.location(), that.location())
&& Objects.equals(this.patcher, that.patcher);
}
@Override
public String toString() {
return super.toString();
}
}

View File

@ -23,13 +23,15 @@
* questions.
*/
package java.lang.module;
package jdk.internal.module;
import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Files;
@ -51,10 +53,7 @@ import java.util.zip.ZipFile;
import jdk.internal.jmod.JmodFile;
import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleHashes.HashSupplier;
import jdk.internal.module.ModulePatcher;
import jdk.internal.util.jar.VersionedStream;
import sun.net.www.ParseUtil;
@ -75,12 +74,18 @@ class ModuleReferences {
* Creates a ModuleReference to a module or to patched module when
* creating modules for the boot Layer and --patch-module is specified.
*/
private static ModuleReference newModule(ModuleDescriptor md,
private static ModuleReference newModule(ModuleInfo.Attributes attrs,
URI uri,
Supplier<ModuleReader> supplier,
HashSupplier hasher) {
ModuleReference mref = new ModuleReference(md, uri, supplier, hasher);
ModuleReference mref = new ModuleReferenceImpl(attrs.descriptor(),
uri,
supplier,
null,
attrs.recordedHashes(),
hasher,
attrs.moduleResolution());
if (JLA.getBootLayer() == null)
mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
@ -90,29 +95,29 @@ class ModuleReferences {
/**
* Creates a ModuleReference to a module packaged as a modular JAR.
*/
static ModuleReference newJarModule(ModuleDescriptor md, Path file) {
static ModuleReference newJarModule(ModuleInfo.Attributes attrs, Path file) {
URI uri = file.toUri();
Supplier<ModuleReader> supplier = () -> new JarModuleReader(file, uri);
HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
return newModule(md, uri, supplier, hasher);
return newModule(attrs, uri, supplier, hasher);
}
/**
* Creates a ModuleReference to a module packaged as a JMOD.
*/
static ModuleReference newJModModule(ModuleDescriptor md, Path file) {
static ModuleReference newJModModule(ModuleInfo.Attributes attrs, Path file) {
URI uri = file.toUri();
Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);
HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
return newModule(md, file.toUri(), supplier, hasher);
return newModule(attrs, uri, supplier, hasher);
}
/**
* Creates a ModuleReference to an exploded module.
*/
static ModuleReference newExplodedModule(ModuleDescriptor md, Path dir) {
static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs, Path dir) {
Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir);
return newModule(md, dir.toUri(), supplier, null);
return newModule(attrs, dir.toUri(), supplier, null);
}

View File

@ -0,0 +1,114 @@
/*
* 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. 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.ModuleReference;
import static jdk.internal.module.ClassFileConstants.*;
/**
* Represents the Module Resolution flags.
*/
public final class ModuleResolution {
final int value;
ModuleResolution(int value) {
this.value = value;
}
public static ModuleResolution empty() {
return new ModuleResolution(0);
}
public boolean doNotResolveByDefault() {
return (value & DO_NOT_RESOLVE_BY_DEFAULT) != 0;
}
public boolean hasDeprecatedWarning() {
return (value & WARN_DEPRECATED) != 0;
}
public boolean hasDeprecatedForRemovalWarning() {
return (value & WARN_DEPRECATED_FOR_REMOVAL) != 0;
}
public boolean hasIncubatingWarning() {
return (value & WARN_INCUBATING) != 0;
}
public ModuleResolution withDoNotResolveByDefault() {
return new ModuleResolution(value | DO_NOT_RESOLVE_BY_DEFAULT);
}
public ModuleResolution withDeprecated() {
if ((value & (WARN_DEPRECATED_FOR_REMOVAL | WARN_INCUBATING)) != 0)
throw new InternalError("cannot add deprecated to " + value);
return new ModuleResolution(value | WARN_DEPRECATED);
}
public ModuleResolution withDeprecatedForRemoval() {
if ((value & (WARN_DEPRECATED | WARN_INCUBATING)) != 0)
throw new InternalError("cannot add deprecated for removal to " + value);
return new ModuleResolution(value | WARN_DEPRECATED_FOR_REMOVAL);
}
public ModuleResolution withIncubating() {
if ((value & (WARN_DEPRECATED | WARN_DEPRECATED_FOR_REMOVAL)) != 0)
throw new InternalError("cannot add incubating to " + value);
return new ModuleResolution(value | WARN_INCUBATING);
}
public int value() {
return value;
}
public static boolean doNotResolveByDefault(ModuleReference mref) {
// get the DO_NOT_RESOLVE_BY_DEFAULT flag, if any
if (!(mref instanceof ModuleReferenceImpl))
return false;
ModuleResolution mres = ((ModuleReferenceImpl)mref).moduleResolution();
if (mres != null)
return mres.doNotResolveByDefault();
return false;
}
public static boolean hasIncubatingWarning(ModuleReference mref) {
if (!(mref instanceof ModuleReferenceImpl))
return false;
ModuleResolution mres = ((ModuleReferenceImpl)mref).moduleResolution();
if (mres != null)
return mres.hasIncubatingWarning();
return false;
}
@Override
public String toString() {
return super.toString() + "[value=" + value + "]";
}
}

View File

@ -23,12 +23,16 @@
* questions.
*/
package java.lang.module;
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;
@ -36,7 +40,6 @@ 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.Map.Entry;
@ -54,11 +57,7 @@ import jdk.internal.jimage.ImageReader;
import jdk.internal.jimage.ImageReaderFactory;
import jdk.internal.misc.JavaNetUriAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleHashes.HashSupplier;
import jdk.internal.module.SystemModules;
import jdk.internal.module.ModulePatcher;
import jdk.internal.perf.PerfCounter;
/**
@ -69,7 +68,7 @@ import jdk.internal.perf.PerfCounter;
* Packages attribute.
*/
class SystemModuleFinder implements ModuleFinder {
public class SystemModuleFinder implements ModuleFinder {
private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
@ -84,11 +83,12 @@ class SystemModuleFinder implements ModuleFinder {
// ImageReader used to access all modules in the image
private static final ImageReader imageReader;
// the set of modules in the run-time image
private static final Set<ModuleReference> modules;
// singleton finder to find modules in the run-time images
private static final SystemModuleFinder INSTANCE;
// maps module name to module reference
private static final Map<String, ModuleReference> nameToModule;
public static SystemModuleFinder getInstance() {
return INSTANCE;
}
/**
* For now, the module references are created eagerly on the assumption
@ -98,72 +98,11 @@ class SystemModuleFinder implements ModuleFinder {
long t0 = System.nanoTime();
imageReader = ImageReaderFactory.getImageReader();
String[] names = moduleNames();
ModuleDescriptor[] descriptors = descriptors(names);
int n = names.length;
moduleCount.add(n);
ModuleReference[] mods = new ModuleReference[n];
@SuppressWarnings(value = {"rawtypes", "unchecked"})
Entry<String, ModuleReference>[] map
= (Entry<String, ModuleReference>[])new Entry[n];
for (int i = 0; i < n; i++) {
ModuleDescriptor md = descriptors[i];
// create the ModuleReference
ModuleReference mref = toModuleReference(md, hashSupplier(i, names[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);
INSTANCE = new SystemModuleFinder();
initTime.addElapsedTimeFrom(t0);
}
/*
* Returns an array of ModuleDescriptor of the given module names.
*
* This obtains ModuleDescriptors from SystemModules class that is generated
* from the jlink system-modules plugin. ModuleDescriptors have already
* been validated at link time.
*
* If java.base is patched, or fastpath is disabled for troubleshooting
* purpose, it will fall back to find system modules via jrt file system.
*/
private static ModuleDescriptor[] descriptors(String[] names) {
// fastpath is enabled by default.
// It can be disabled for troubleshooting purpose.
boolean disabled =
System.getProperty("jdk.system.module.finder.disabledFastPath") != null;
// fast loading of ModuleDescriptor of system modules
if (isFastPathSupported() && !disabled)
return SystemModules.modules();
// if fast loading of ModuleDescriptors is disabled
// fallback to read module-info.class
ModuleDescriptor[] descriptors = new ModuleDescriptor[names.length];
for (int i = 0; i < names.length; i++) {
String mn = names[i];
ImageLocation loc = imageReader.findLocation(mn, "module-info.class");
descriptors[i] = ModuleDescriptor.read(imageReader.getResourceBuffer(loc));
// add the recorded hashes of tied modules
Hashes.add(descriptors[i]);
}
return descriptors;
}
private static boolean isFastPathSupported() {
return SystemModules.MODULE_NAMES.length > 0;
}
@ -178,69 +117,83 @@ class SystemModuleFinder implements ModuleFinder {
return imageReader.getModuleNames();
}
private static ModuleReference toModuleReference(ModuleDescriptor md,
HashSupplier hash)
{
String mn = md.name();
URI uri = JNUA.create("jrt", "/".concat(mn));
// the set of modules in the run-time image
private final Set<ModuleReference> modules;
Supplier<ModuleReader> readerSupplier = new Supplier<>() {
@Override
public ModuleReader get() {
return new ImageModuleReader(mn, uri);
}
};
// maps module name to module reference
private final Map<String, ModuleReference> nameToModule;
ModuleReference mref =
new ModuleReference(md, uri, readerSupplier, hash);
// module name to hashes
private final Map<String, byte[]> hashes = new HashMap<>();
// may need a reference to a patched module if --patch-module specified
mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
private SystemModuleFinder() {
String[] names = moduleNames();
int n = names.length;
moduleCount.add(n);
return mref;
}
// fastpath is enabled by default.
// It can be disabled for troubleshooting purpose.
boolean disabled =
System.getProperty("jdk.system.module.finder.disabledFastPath") != null;
private static HashSupplier hashSupplier(int index, String name) {
if (isFastPathSupported()) {
return new HashSupplier() {
@Override
public byte[] generate(String algorithm) {
return SystemModules.MODULES_TO_HASH[index];
}
};
ModuleDescriptor[] descriptors;
ModuleHashes[] recordedHashes;
ModuleResolution[] moduleResolutions;
// fast loading of ModuleDescriptor of system modules
if (isFastPathSupported() && !disabled) {
descriptors = SystemModules.descriptors();
recordedHashes = SystemModules.hashes();
moduleResolutions = SystemModules.moduleResolutions();
} else {
return Hashes.hashFor(name);
}
}
/*
* This helper class is only used when SystemModules is patched.
* It will get the recorded hashes from module-info.class.
*/
private static class Hashes {
static Map<String, byte[]> hashes = new HashMap<>();
static void add(ModuleDescriptor descriptor) {
Optional<ModuleHashes> ohashes = descriptor.hashes();
if (ohashes.isPresent()) {
hashes.putAll(ohashes.get().hashes());
// if fast loading of ModuleDescriptors is disabled
// fallback to read module-info.class
descriptors = new ModuleDescriptor[n];
recordedHashes = new ModuleHashes[n];
moduleResolutions = new ModuleResolution[n];
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();
recordedHashes[i] = attrs.recordedHashes();
moduleResolutions[i] = attrs.moduleResolution();
}
}
static HashSupplier hashFor(String name) {
if (!hashes.containsKey(name))
return null;
return new HashSupplier() {
@Override
public byte[] generate(String algorithm) {
return hashes.get(name);
}
};
// record the hashes to build HashSupplier
for (ModuleHashes mh : recordedHashes) {
if (mh != null) {
hashes.putAll(mh.hashes());
}
}
}
SystemModuleFinder() { }
ModuleReference[] mods = new ModuleReference[n];
@SuppressWarnings(value = {"rawtypes", "unchecked"})
Entry<String, ModuleReference>[] map
= (Entry<String, ModuleReference>[])new Entry[n];
for (int i = 0; i < n; i++) {
ModuleDescriptor md = descriptors[i];
// create the ModuleReference
ModuleReference mref = toModuleReference(md,
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<ModuleReference> find(String name) {
@ -253,6 +206,41 @@ class SystemModuleFinder implements ModuleFinder {
return modules;
}
private ModuleReference toModuleReference(ModuleDescriptor md,
ModuleHashes recordedHashes,
HashSupplier hasher,
ModuleResolution mres) {
String mn = md.name();
URI uri = JNUA.create("jrt", "/".concat(mn));
Supplier<ModuleReader> readerSupplier = new Supplier<>() {
@Override
public ModuleReader get() {
return new ImageModuleReader(mn, uri);
}
};
ModuleReference mref =
new ModuleReferenceImpl(md, uri, readerSupplier, null,
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

View File

@ -29,14 +29,14 @@ import java.lang.module.ModuleDescriptor;
/*
* SystemModules class will be generated at link time to create
* ModuleDescriptor for the installed modules directly to improve
* ModuleDescriptor for the system modules directly to improve
* the module descriptor reconstitution time.
*
* 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 jdk.tools.jlink.internal.plugins.SystemModuleDescriptorPlugin
* @see jdk.tools.jlink.internal.plugins.SystemModulesPlugin
*/
public final class SystemModules {
/**
@ -48,11 +48,6 @@ public final class SystemModules {
*/
public static final String[] MODULE_NAMES = new String[0];
/**
* Hash of system modules.
*/
public static byte[][] MODULES_TO_HASH = new byte[0][];
/**
* Number of packages in the boot layer from the installed modules.
*
@ -66,8 +61,24 @@ public final class SystemModules {
*
* When running an exploded image it returns an empty array.
*/
public static ModuleDescriptor[] modules() {
throw new InternalError("should not reach here");
public static ModuleDescriptor[] descriptors() {
throw new InternalError("expected to be overridden at link time");
}
/**
* 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.
*/
public static ModuleHashes[] hashes() {
throw new InternalError("expected to be overridden at link time");
}
/**
* Returns a non-empty array of ModuleResolutions in the run-time image.
*/
public static ModuleResolution[] moduleResolutions() {
throw new InternalError("expected to be overridden at link time");
}
}

View File

@ -2490,6 +2490,40 @@ public class ClassReader {
return readUTF8(items[readUnsignedShort(index)], buf);
}
/**
* Reads a CONSTANT_Module_info item in {@code b}. This method is intended
* for {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.</i>
*
* @param index
* the start index of an unsigned short value in {@link #b b},
* whose value is the index of a module constant pool item.
* @param buf
* buffer to be used to read the item. This buffer must be
* sufficiently large. It is not automatically resized.
* @return the String corresponding to the specified module item.
*/
public String readModule(int index, char[] buf) {
return readUTF8(items[readUnsignedShort(index)], buf);
}
/**
* Reads a CONSTANT_Pakcage_info item in {@code b}. This method is
* intended for {@link Attribute} sub slasses, and is normally not needed
* by class generators or adapters.</i>
*
* @param index
* the start index of an unsigned short value in {@link #b b},
* whose value is the index of a package constant pool item.
* @param buf
* buffer to be used to read the item. This buffer must be
* sufficiently large. It is not automatically resized.
* @return the String corresponding to the specified package item.
*/
public String readPackage(int index, char[] buf) {
return readUTF8(items[readUnsignedShort(index)], buf);
}
/**
* Reads a numeric or string constant pool item in {@link #b b}. <i>This
* method is intended for {@link Attribute} sub classes, and is normally not
@ -2516,6 +2550,8 @@ public class ClassReader {
case ClassWriter.DOUBLE:
return Double.longBitsToDouble(readLong(index));
case ClassWriter.CLASS:
case ClassWriter.MODULE:
case ClassWriter.PACKAGE:
return Type.getObjectType(readUTF8(index, buf));
case ClassWriter.STR:
return readUTF8(index, buf);

View File

@ -271,6 +271,16 @@ public class ClassWriter extends ClassVisitor {
*/
static final int INDY = 18;
/**
* The type of CONSTANT_Module constant pool items.
*/
static final int MODULE = 19;
/**
* The type of CONSTANT_Package constant pool items.
*/
static final int PACKAGE = 20;
/**
* The base value for all CONSTANT_MethodHandle constant pool items.
* Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9
@ -1160,6 +1170,50 @@ public class ClassWriter extends ClassVisitor {
return newClassItem(value).index;
}
/**
* Adds a module name to the constant pool.
*
* Does nothing if the constant pool already contains a similar item.
* <i>This method is intended for {@link Attribute} sub classes, and is
* normally not needed by class generators or adapters.</i>
*
* @param value
* the module name
* @return the index of a new or already existing module reference item.
*/
public int newModule(String value) {
key2.set(MODULE, value, null, null);
Item result = get(key2);
if (result == null) {
pool.put12(MODULE, newUTF8(value));
result = new Item(index++, key2);
put(result);
}
return result.index;
}
/**
* Adds a package name to the constant pool.
*
* Does nothing if the constant pool already contains a similar item.
* <i>This method is intended for {@link Attribute} sub classes, and is
* normally not needed by class generators or adapters.</i>
*
* @param value
* the internal name of the package.
* @return the index of a new or already existing package reference item.
*/
public int newPackage(String value) {
key2.set(PACKAGE, value, null, null);
Item result = get(key2);
if (result == null) {
pool.put12(PACKAGE, newUTF8(value));
result = new Item(index++, key2);
put(result);
}
return result.index;
}
/**
* Adds a method type reference to the constant pool of the class being
* build. Does nothing if the constant pool already contains a similar item.

View File

@ -239,6 +239,8 @@ final class Item {
this.strVal3 = strVal3;
switch (type) {
case ClassWriter.CLASS:
case ClassWriter.MODULE:
case ClassWriter.PACKAGE:
this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
case ClassWriter.UTF8:
case ClassWriter.STR:
@ -311,6 +313,8 @@ final class Item {
case ClassWriter.UTF8:
case ClassWriter.STR:
case ClassWriter.CLASS:
case ClassWriter.MODULE:
case ClassWriter.PACKAGE:
case ClassWriter.MTYPE:
case ClassWriter.TYPE_NORMAL:
return i.strVal1.equals(strVal1);

View File

@ -31,6 +31,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.VM;
import sun.security.action.GetPropertyAction;
@ -218,8 +219,16 @@ public class Reflection {
if (c.isPrimitive())
return true;
// check that memberModule exports the package to currentModule
return memberModule.isExported(c.getPackageName(), currentModule);
String pkg = c.getPackageName();
boolean allowed = memberModule.isExported(pkg, currentModule);
if (allowed && memberModule.isNamed() && printStackTraceWhenAccessSucceeds()) {
if (!SharedSecrets.getJavaLangReflectModuleAccess()
.isStaticallyExported(memberModule, pkg, currentModule)) {
String msg = currentModule + " allowed access to member of " + memberClass;
new Exception(msg).printStackTrace(System.err);
}
}
return allowed;
}
/**
@ -348,25 +357,43 @@ public class Reflection {
}
// true to print a stack trace when IAE is thrown
// true to print a stack trace when access fails
private static volatile boolean printStackWhenAccessFails;
// true if printStackWhenAccessFails has been initialized
private static volatile boolean printStackWhenAccessFailsSet;
// true to print a stack trace when access succeeds
private static volatile boolean printStackWhenAccessSucceeds;
private static void printStackTraceIfNeeded(Throwable e) {
if (!printStackWhenAccessFailsSet && VM.initLevel() >= 1) {
// true if printStack* values are initialized
private static volatile boolean printStackPropertiesSet;
private static void ensurePrintStackPropertiesSet() {
if (!printStackPropertiesSet && VM.initLevel() >= 1) {
String s = GetPropertyAction.privilegedGetProperty(
"sun.reflect.debugModuleAccessChecks");
printStackWhenAccessFails =
(s != null && !s.equalsIgnoreCase("false"));
printStackWhenAccessFailsSet = true;
}
if (printStackWhenAccessFails) {
e.printStackTrace();
if (s != null) {
printStackWhenAccessFails = !s.equalsIgnoreCase("false");
printStackWhenAccessSucceeds = s.equalsIgnoreCase("access");
}
printStackPropertiesSet = true;
}
}
public static void enableStackTraces() {
printStackWhenAccessFails = true;
printStackWhenAccessSucceeds = true;
printStackPropertiesSet = true;
}
public static boolean printStackTraceWhenAccessFails() {
ensurePrintStackPropertiesSet();
return printStackWhenAccessFails;
}
public static boolean printStackTraceWhenAccessSucceeds() {
ensurePrintStackPropertiesSet();
return printStackWhenAccessSucceeds;
}
/**
* Throws IllegalAccessException with the an exception message based on
* the access that is denied.
@ -416,17 +443,10 @@ public class Reflection {
throws IllegalAccessException
{
IllegalAccessException e = new IllegalAccessException(msg);
printStackTraceIfNeeded(e);
ensurePrintStackPropertiesSet();
if (printStackWhenAccessFails) {
e.printStackTrace(System.err);
}
throw e;
}
/**
* Throws InaccessibleObjectException with the given exception message.
*/
public static void throwInaccessibleObjectException(String msg) {
InaccessibleObjectException e = new InaccessibleObjectException(msg);
printStackTraceIfNeeded(e);
throw e;
}
}

View File

@ -34,6 +34,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import jdk.internal.module.ModuleResolution;
/**
* Parser for GNU Style Options.
@ -158,6 +159,31 @@ class GNUStyleOptions {
ModuleFinder.of(paths));
}
},
new Option(false, OptionType.CREATE_UPDATE, "--do-not-resolve-by-default") {
void process(Main jartool, String opt, String arg) {
ModuleResolution mres = jartool.moduleResolution;
jartool.moduleResolution = mres.withDoNotResolveByDefault();
}
boolean isExtra() { return true; }
},
new Option(true, OptionType.CREATE_UPDATE, "--warn-if-resolved") {
void process(Main jartool, String opt, String arg) throws BadArgs {
ModuleResolution mres = ModuleResolution.empty();
if (jartool.moduleResolution.doNotResolveByDefault())
mres.withDoNotResolveByDefault();
if (arg.equals("deprecated")) {
jartool.moduleResolution = mres.withDeprecated();
} else if (arg.equals("deprecated-for-removal")) {
jartool.moduleResolution = mres.withDeprecatedForRemoval();
} else if (arg.equals("incubating")) {
jartool.moduleResolution = mres.withIncubating();
} else {
throw new BadArgs("error.bad.reason", arg);
}
}
boolean isExtra() { return true; }
},
new Option(false, OptionType.CREATE_UPDATE_INDEX, "--no-compress", "-0") {
void process(Main jartool, String opt, String arg) {
jartool.flag0 = true;
@ -175,17 +201,20 @@ class GNUStyleOptions {
// Other options
new Option(true, true, OptionType.OTHER, "--help", "-h") {
void process(Main jartool, String opt, String arg) throws BadArgs {
if (jartool.info == null) {
if (arg == null) {
jartool.info = Main.Info.HELP;
return;
}
if (!arg.equals("compat"))
throw new BadArgs("error.illegal.option", arg).showUsage(true);
jartool.info = Main.Info.COMPAT_HELP;
if (arg == null) {
jartool.info = Main.Info.HELP;
return;
}
if (!arg.equals("compat"))
throw new BadArgs("error.illegal.option", arg).showUsage(true);
jartool.info = Main.Info.COMPAT_HELP;
}
},
new Option(false, OptionType.OTHER, "--help-extra") {
void process(Main jartool, String opt, String arg) throws BadArgs {
jartool.info = Main.Info.HELP_EXTRA;
}
},
new Option(false, OptionType.OTHER, "--version") {
@ -229,6 +258,8 @@ class GNUStyleOptions {
boolean isHidden() { return false; }
boolean isExtra() { return false; }
boolean matches(String opt) {
for (String a : aliases) {
if (a.equals(opt)) {
@ -292,6 +323,14 @@ class GNUStyleOptions {
}
static void printHelp(PrintWriter out) {
printHelp(out, false);
}
static void printHelpExtra(PrintWriter out) {
printHelp(out, true);
}
private static void printHelp(PrintWriter out, boolean printExtra) {
out.format("%s%n", Main.getMsg("main.help.preopt"));
for (OptionType type : OptionType.values()) {
boolean typeHeadingWritten = false;
@ -304,6 +343,9 @@ class GNUStyleOptions {
if (o.isHidden() || name.equals("h")) {
continue;
}
if (o.isExtra() && !printExtra) {
continue;
}
if (!typeHeadingWritten) {
out.format("%n%s%n", Main.getMsg("main.help.opt." + type.name));
typeHeadingWritten = true;

View File

@ -47,7 +47,6 @@ import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -57,11 +56,11 @@ import java.util.jar.Pack200.*;
import java.util.jar.Manifest;
import java.text.MessageFormat;
import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.Checks;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleInfo;
import jdk.internal.module.ModuleInfoExtender;
import jdk.internal.module.ModuleResolution;
import jdk.internal.util.jar.JarIndex;
import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
@ -211,6 +210,7 @@ class Main {
/* To support additional GNU Style informational options */
enum Info {
HELP(GNUStyleOptions::printHelp),
HELP_EXTRA(GNUStyleOptions::printHelpExtra),
COMPAT_HELP(GNUStyleOptions::printCompatHelp),
USAGE_TRYHELP(GNUStyleOptions::printUsageTryHelp),
VERSION(GNUStyleOptions::printVersion);
@ -225,6 +225,7 @@ class Main {
/* Modular jar related options */
Version moduleVersion;
Pattern modulesToHash;
ModuleResolution moduleResolution = ModuleResolution.empty();
ModuleFinder moduleFinder = ModuleFinder.of();
private static final String MODULE_INFO = "module-info.class";
@ -1991,12 +1992,13 @@ class Main {
.collect(joining(" "));
}
private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
private void printModuleDescriptor(InputStream entryInputStream)
throws IOException
{
ModuleDescriptor md = ModuleDescriptor.read(entryInputStream);
ModuleInfo.Attributes attrs = ModuleInfo.read(entryInputStream, null);
ModuleDescriptor md = attrs.descriptor();
ModuleHashes hashes = attrs.recordedHashes();
StringBuilder sb = new StringBuilder();
sb.append("\n");
if (md.isOpen())
@ -2043,15 +2045,24 @@ class Main {
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
JLMA.hashes(md).ifPresent(hashes ->
hashes.names().stream().sorted().forEach(
if (hashes != null) {
hashes.names().stream().sorted().forEach(
mod -> sb.append("\n hashes ").append(mod).append(" ")
.append(hashes.algorithm()).append(" ")
.append(hashes.hashFor(mod))));
.append(toHex(hashes.hashFor(mod))));
}
output(sb.toString());
}
private static String toHex(byte[] ba) {
StringBuilder sb = new StringBuilder(ba.length);
for (byte b: ba) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
private static String toBinaryName(String classname) {
return (classname.replace('.', '/')) + ".class";
}
@ -2212,6 +2223,10 @@ class Main {
}
}
if (moduleResolution.value() != 0) {
extender.moduleResolution(moduleResolution);
}
extender.write(baos);
return baos.toByteArray();
}
@ -2228,13 +2243,12 @@ class Main {
// Create a module finder that finds the modular JAR
// being created/updated
URI uri = Paths.get(fname).toUri();
ModuleReference mref = new ModuleReference(descriptor, uri,
new Supplier<>() {
@Override
public ModuleReader get() {
throw new UnsupportedOperationException("should not reach here");
}
});
ModuleReference mref = new ModuleReference(descriptor, uri) {
@Override
public ModuleReader open() {
throw new UnsupportedOperationException("should not reach here");
}
};
// Compose a module finder with the module path and
// the modular JAR being created or updated

View File

@ -46,6 +46,8 @@ error.bad.eflag=\
together!
error.bad.dflag=\
'-d, --print-module-descriptor' option requires no input file(s) to be specified: {0}
error.bad.reason=\
bad reason: {0}, must be one of deprecated, deprecated-for-removal, or incubating
error.nosuch.fileordir=\
{0} : no such file or directory
error.write.file=\
@ -246,6 +248,12 @@ main.help.opt.create.update.hash-modules=\
main.help.opt.create.update.module-path=\
\ -p, --module-path Location of module dependence for generating\n\
\ the hash
main.help.opt.create.update.do-not-resolve-by-default=\
\ --do-not-resolve-by-default Exclude from the default root set of modules
main.help.opt.create.update.warn-if-resolved=\
\ --warn-if-resolved Hint for a tool to issue a warning if the module\n\
\ is resolved. One of deprecated, deprecated-for-removal,\n\
\ or incubating
main.help.opt.create.update.index=\
\ Operation modifiers valid only in create, update, and generate-index mode:\n
main.help.opt.create.update.index.no-compress=\
@ -254,6 +262,8 @@ main.help.opt.other=\
\ Other options:\n
main.help.opt.other.help=\
\ -?, --help[:compat] Give this, or optionally the compatibility, help
main.help.opt.other.help-extra=\
\ --help-extra Give help on extra options
main.help.opt.other.version=\
\ --version Print program version
main.help.postopt=\

View File

@ -53,7 +53,9 @@ import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider;
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.builder.DefaultImageBuilder;
import jdk.tools.jlink.plugin.Plugin;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.Checks;
import jdk.internal.module.ModulePath;
import jdk.internal.module.ModuleResolution;
/**
* Implementation for the jlink tool.
@ -260,7 +262,8 @@ public class JlinkTask {
config.getModules(),
config.getByteOrder(),
null,
IGNORE_SIGNING_DEFAULT);
IGNORE_SIGNING_DEFAULT,
null);
// Then create the Plugin Stack
ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins);
@ -328,7 +331,8 @@ public class JlinkTask {
roots,
options.endian,
options.packagedModulesPath,
options.ignoreSigning);
options.ignoreSigning,
log);
// Then create the Plugin Stack
ImagePluginStack stack = ImagePluginConfiguration.
@ -344,9 +348,7 @@ public class JlinkTask {
*/
private ModuleFinder modulePathFinder() {
Path[] entries = options.modulePath.toArray(new Path[0]);
ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
.newModulePath(Runtime.version(), true, entries);
ModuleFinder finder = new ModulePath(Runtime.version(), true, entries);
if (!options.limitMods.isEmpty()) {
finder = limitFinder(finder, options.limitMods, Collections.emptySet());
}
@ -364,8 +366,7 @@ public class JlinkTask {
Set<String> roots)
{
Path[] entries = paths.toArray(new Path[0]);
ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
.newModulePath(Runtime.version(), true, entries);
ModuleFinder finder = new ModulePath(Runtime.version(), true, entries);
// if limitmods is specified then limit the universe
if (!limitMods.isEmpty()) {
@ -386,7 +387,8 @@ public class JlinkTask {
Set<String> roots,
ByteOrder order,
Path retainModulesPath,
boolean ignoreSigning)
boolean ignoreSigning,
PrintWriter log)
throws IOException
{
if (roots.isEmpty()) {
@ -398,6 +400,27 @@ public class JlinkTask {
ModuleFinder.of(),
roots);
// emit warning for modules that end with a digit
cf.modules().stream()
.map(ResolvedModule::name)
.filter(mn -> !Checks.hasLegalModuleNameLastCharacter(mn))
.forEach(mn -> System.err.println("WARNING: Module name \""
+ mn + "\" may soon be illegal"));
// emit a warning for any incubating modules in the configuration
if (log != null) {
String im = cf.modules()
.stream()
.map(ResolvedModule::reference)
.filter(ModuleResolution::hasIncubatingWarning)
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.collect(Collectors.joining(", "));
if (!"".equals(im))
log.println("WARNING: Using incubator modules: " + im);
}
Map<String, Path> mods = cf.modules().stream()
.collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
return new ImageHelper(cf, mods, order, retainModulesPath, ignoreSigning);

View File

@ -28,6 +28,7 @@ package jdk.tools.jlink.internal;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.io.IOException;
import java.io.UncheckedIOException;
@ -78,10 +79,12 @@ final class ResourcePoolConfiguration {
}
private static ModuleReference moduleReference(ModuleDescriptor desc) {
return new ModuleReference(desc, null, () -> {
IOException ioe = new IOException("<module reader unsupported>");
throw new UncheckedIOException(ioe);
});
return new ModuleReference(desc, null) {
@Override
public ModuleReader open() {
throw new UnsupportedOperationException();
}
};
}
private static Map<String, ModuleReference> allModRefs(ResourcePool pool) {

View File

@ -58,7 +58,7 @@ import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
import jdk.tools.jlink.internal.plugins.StripDebugPlugin;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModulePath;
/**
*
@ -765,9 +765,7 @@ public final class TaskHelper {
static Layer createPluginsLayer(List<Path> paths) {
Path[] dirs = paths.toArray(new Path[0]);
ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
.newModulePath(Runtime.version(), true, dirs);
ModuleFinder finder = new ModulePath(Runtime.version(), true, dirs);
Configuration bootConfiguration = Layer.boot().configuration();
try {
Configuration cf = bootConfiguration

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -34,17 +34,19 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.IntSupplier;
import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.Checks;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleInfo.Attributes;
import jdk.internal.module.ModuleInfoExtender;
import jdk.internal.module.ModuleResolution;
import jdk.internal.module.SystemModules;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
@ -55,7 +57,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.plugin.ResourcePool;
import jdk.tools.jlink.plugin.Plugin;
import jdk.tools.jlink.internal.plugins.SystemModuleDescriptorPlugin.SystemModulesClassGenerator.*;
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
import jdk.tools.jlink.plugin.ResourcePoolEntry;
@ -67,10 +68,10 @@ import jdk.tools.jlink.plugin.ResourcePoolEntry;
*
* This plugin will override jdk.internal.module.SystemModules class
*
* @see java.lang.module.SystemModuleFinder
* @see jdk.internal.module.SystemModuleFinder
* @see SystemModules
*/
public final class SystemModuleDescriptorPlugin implements Plugin {
public final class SystemModulesPlugin implements Plugin {
private static final JavaLangModuleAccess JLMA =
SharedSecrets.getJavaLangModuleAccess();
@ -79,7 +80,7 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
PluginsResourceBundle.getDescription(NAME);
private boolean enabled;
public SystemModuleDescriptorPlugin() {
public SystemModulesPlugin() {
this.enabled = true;
}
@ -126,20 +127,18 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
assert module.name().equals(data.moduleName());
try {
ByteArrayInputStream bain = new ByteArrayInputStream(data.contentBytes());
ModuleDescriptor md = ModuleDescriptor.read(bain);
validateNames(md);
ModuleInfo moduleInfo = new ModuleInfo(data.contentBytes(), module.packages());
generator.addModule(moduleInfo);
Set<String> packages = module.packages();
generator.addModule(md, module.packages());
// link-time validation
moduleInfo.validateNames();
// check if any exported or open package is not present
moduleInfo.validatePackages();
// add Packages attribute if not exist
if (md.packages().isEmpty() && packages.size() > 0) {
bain.reset();
ModuleInfoRewriter minfoWriter =
new ModuleInfoRewriter(bain, module.packages());
// Packages attribute needs update
if (moduleInfo.shouldAddPackagesAttribute()) {
// replace with the overridden version
data = data.copyWithContent(minfoWriter.getBytes());
data = data.copyWithContent(moduleInfo.getBytes());
}
out.add(data);
} catch (IOException e) {
@ -164,61 +163,119 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
return out.build();
}
/*
* Add Packages attribute
*/
class ModuleInfoRewriter extends ByteArrayOutputStream {
final ModuleInfoExtender extender;
ModuleInfoRewriter(InputStream in, Set<String> packages) throws IOException {
this.extender = ModuleInfoExtender.newExtender(in);
// Add Packages attribute
this.extender.packages(packages);
this.extender.write(this);
class ModuleInfo {
final ModuleDescriptor descriptor;
final ModuleHashes recordedHashes;
final ModuleResolution moduleResolution;
final Set<String> packages;
final ByteArrayInputStream bain;
ModuleInfo(byte[] bytes, Set<String> packages) throws IOException {
this.bain = new ByteArrayInputStream(bytes);
this.packages = packages;
Attributes attrs = jdk.internal.module.ModuleInfo.read(bain, null);
this.descriptor = attrs.descriptor();
this.recordedHashes = attrs.recordedHashes();
this.moduleResolution = attrs.moduleResolution();
if (descriptor.isAutomatic()) {
throw new InternalError("linking automatic module is not supported");
}
}
byte[] getBytes() {
return buf;
String moduleName() {
return descriptor.name();
}
}
void validateNames(ModuleDescriptor md) {
Checks.requireModuleName(md.name());
for (Requires req : md.requires()) {
Checks.requireModuleName(req.name());
/**
* Validates names in ModuleDescriptor
*/
void validateNames() {
Checks.requireModuleName(descriptor.name());
for (Requires req : descriptor.requires()) {
Checks.requireModuleName(req.name());
}
for (Exports e : descriptor.exports()) {
Checks.requirePackageName(e.source());
if (e.isQualified())
e.targets().forEach(Checks::requireModuleName);
}
for (Opens opens : descriptor.opens()) {
Checks.requirePackageName(opens.source());
if (opens.isQualified())
opens.targets().forEach(Checks::requireModuleName);
}
for (Provides provides : descriptor.provides()) {
Checks.requireServiceTypeName(provides.service());
provides.providers().forEach(Checks::requireServiceProviderName);
}
for (String service : descriptor.uses()) {
Checks.requireServiceTypeName(service);
}
for (String pn : descriptor.packages()) {
Checks.requirePackageName(pn);
}
}
for (Exports e : md.exports()) {
Checks.requirePackageName(e.source());
if (e.isQualified())
e.targets().forEach(Checks::requireModuleName);
}
for (Opens opens : md.opens()) {
Checks.requirePackageName(opens.source());
if (opens.isQualified())
opens.targets().forEach(Checks::requireModuleName);
}
for (Provides provides : md.provides()) {
Checks.requireServiceTypeName(provides.service());
provides.providers().forEach(Checks::requireServiceProviderName);
}
for (String service : md.uses()) {
Checks.requireServiceTypeName(service);
}
for (String pn : md.packages()) {
Checks.requirePackageName(pn);
}
}
/*
* Returns the initial capacity for a new Set or Map of the given size
* to avoid resizing.
*/
static final int initialCapacity(int size) {
if (size == 0) {
return 0;
} else {
// Adjust to try and get size/capacity as close to the
// HashSet/HashMap default load factor without going over.
return (int)(Math.ceil((double)size / 0.75));
/**
* Validates if exported and open packages are present
*/
void validatePackages() {
Set<String> nonExistPackages = new TreeSet<>();
descriptor.exports().stream()
.map(Exports::source)
.filter(pn -> !packages.contains(pn))
.forEach(nonExistPackages::add);
descriptor.opens().stream()
.map(Opens::source)
.filter(pn -> !packages.contains(pn))
.forEach(nonExistPackages::add);
if (!nonExistPackages.isEmpty()) {
throw new PluginException("Packages that are exported or open in "
+ descriptor.name() + " are not present: " + nonExistPackages);
}
}
/**
* Returns true if the PackagesAttribute should be written
*/
boolean shouldAddPackagesAttribute() {
return descriptor.packages().isEmpty() && packages.size() > 0;
}
/**
* Returns the bytes for the module-info.class with PackagesAttribute
* if it contains at least one package
*/
byte[] getBytes() throws IOException {
bain.reset();
// add Packages attribute if not exist
if (shouldAddPackagesAttribute()) {
return new ModuleInfoRewriter(bain, packages).getBytes();
} else {
return bain.readAllBytes();
}
}
class ModuleInfoRewriter extends ByteArrayOutputStream {
final ModuleInfoExtender extender;
ModuleInfoRewriter(InputStream in, Set<String> packages) throws IOException {
this.extender = ModuleInfoExtender.newExtender(in);
// Add Packages attribute
if (packages.size() > 0) {
this.extender.packages(packages);
}
this.extender.write(this);
}
byte[] getBytes() {
return buf;
}
}
}
@ -239,30 +296,31 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
"java/lang/module/ModuleDescriptor$Exports$Modifier";
private static final String OPENS_MODIFIER_CLASSNAME =
"java/lang/module/ModuleDescriptor$Opens$Modifier";
private static final String MODULE_HASHES_ARRAY_SIGNATURE =
"[Ljdk/internal/module/ModuleHashes;";
private static final String MODULE_RESOLUTION_CLASSNAME =
"jdk/internal/module/ModuleResolution";
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 MODULES_TO_HASH = "MODULES_TO_HASH";
private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER";
private static final int MAX_LOCAL_VARS = 256;
private final int BUILDER_VAR = 0;
private final int MD_VAR = 1; // variable for ModuleDescriptor
private final int MH_VAR = 1; // variable for ModuleHashes
private int nextLocalVar = 2; // index to next local variable
private final ClassWriter cw;
// Method visitor for generating the SystemModules::modules() method
private MethodVisitor mv;
private int nextModulesIndex = 0;
// list of all ModuleDescriptorBuilders, invoked in turn when building.
private final List<ModuleDescriptorBuilder> builders = new ArrayList<>();
// module name to hash
private final Map<String, byte[]> modulesToHash = new HashMap<>();
// module name to index in MODULES_TO_HASH
private final Map<String, Integer> modulesToHashIndex = new HashMap<>();
private final List<ModuleInfo> moduleInfos = new ArrayList<>();
// A builder to create one single Set instance for a given set of
// names or modifiers to reduce the footprint
@ -293,66 +351,34 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
"[Ljava/lang/String;", null, null)
.visitEnd();
// public static byte[][] MODULES_TO_HASH
cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULES_TO_HASH,
"[[B", null, null)
.visitEnd();
// public static int PACKAGES_IN_BOOT_LAYER;
cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT,
"I", null, numPackages)
.visitEnd();
this.mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V",
null, null);
mv.visitCode();
MethodVisitor clinit =
cw.visitMethod(ACC_STATIC, "<clinit>", "()V",
null, null);
clinit.visitCode();
// create the MODULE_NAMES array
pushInt(numModules);
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
pushInt(clinit, numModules);
clinit.visitTypeInsn(ANEWARRAY, "java/lang/String");
int index = 0;
for (ModuleDescriptorBuilder builder : builders) {
mv.visitInsn(DUP); // arrayref
pushInt(index++);
mv.visitLdcInsn(builder.md.name()); // value
mv.visitInsn(AASTORE);
for (ModuleInfo minfo : moduleInfos) {
clinit.visitInsn(DUP); // arrayref
pushInt(clinit, index++);
clinit.visitLdcInsn(minfo.moduleName()); // value
clinit.visitInsn(AASTORE);
}
mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES,
clinit.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES,
"[Ljava/lang/String;");
// create the MODULES_TO_HASH array
pushInt(numModules);
mv.visitTypeInsn(ANEWARRAY, "[B");
index = 0;
for (ModuleDescriptorBuilder builder : builders) {
String mn = builder.md.name();
byte[] recordedHash = modulesToHash.get(mn);
if (recordedHash != null) {
mv.visitInsn(DUP); // arrayref
pushInt(index);
pushInt(recordedHash.length);
mv.visitIntInsn(NEWARRAY, T_BYTE);
for (int i = 0; i < recordedHash.length; i++) {
mv.visitInsn(DUP); // arrayref
pushInt(i);
mv.visitIntInsn(BIPUSH, recordedHash[i]);
mv.visitInsn(BASTORE);
}
mv.visitInsn(AASTORE);
modulesToHashIndex.put(mn, index);
}
index++;
}
mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULES_TO_HASH, "[[B");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
clinit.visitInsn(RETURN);
clinit.visitMaxs(0, 0);
clinit.visitEnd();
}
/*
@ -360,9 +386,9 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
* prepares mapping from various Sets to SetBuilders to emit an
* optimized number of sets during build.
*/
public void addModule(ModuleDescriptor md, Set<String> packages) {
ModuleDescriptorBuilder builder = new ModuleDescriptorBuilder(md, packages);
builders.add(builder);
public void addModule(ModuleInfo moduleInfo) {
ModuleDescriptor md = moduleInfo.descriptor;
moduleInfos.add(moduleInfo);
// exports
for (Exports e : md.exports()) {
@ -383,45 +409,129 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
// uses
dedupSetBuilder.stringSet(md.uses());
// hashes
JLMA.hashes(md).ifPresent(mh -> modulesToHash.putAll(mh.hashes()));
}
/*
* Generate bytecode for SystemModules
*/
public ClassWriter getClassWriter() {
int numModules = builders.size();
int numModules = moduleInfos.size();
int numPackages = 0;
for (ModuleDescriptorBuilder builder : builders) {
numPackages += builder.md.packages().size();
for (ModuleInfo minfo : moduleInfos) {
numPackages += minfo.packages.size();
}
this.clinit(numModules, numPackages);
this.mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
"modules", "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
"()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, null);
clinit(numModules, numPackages);
// generate SystemModules::descriptors
genDescriptorsMethod();
// generate SystemModules::hashes
genHashesMethod();
// generate SystemModules::moduleResolutions
genModuleResolutionsMethod();
return cw;
}
/*
* Generate bytecode for SystemModules::descriptors method
*/
private void genDescriptorsMethod() {
this.mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
"descriptors",
"()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
"()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
null);
mv.visitCode();
pushInt(numModules);
pushInt(mv, moduleInfos.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor");
mv.visitVarInsn(ASTORE, MD_VAR);
for (ModuleDescriptorBuilder builder : builders) {
builder.build();
for (int index = 0; index < moduleInfos.size(); index++) {
ModuleInfo minfo = moduleInfos.get(index);
new ModuleDescriptorBuilder(minfo.descriptor,
minfo.packages,
index).build();
}
mv.visitVarInsn(ALOAD, MD_VAR);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
return cw;
}
/*
* Generate bytecode for SystemModules::hashes method
*/
private void genHashesMethod() {
MethodVisitor hmv =
cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
"hashes",
"()" + MODULE_HASHES_ARRAY_SIGNATURE,
"()" + MODULE_HASHES_ARRAY_SIGNATURE,
null);
hmv.visitCode();
pushInt(hmv, moduleInfos.size());
hmv.visitTypeInsn(ANEWARRAY, "jdk/internal/module/ModuleHashes");
hmv.visitVarInsn(ASTORE, MH_VAR);
for (int index = 0; index < moduleInfos.size(); index++) {
ModuleInfo minfo = moduleInfos.get(index);
if (minfo.recordedHashes != null) {
new ModuleHashesBuilder(minfo.recordedHashes,
index,
hmv).build();
}
}
hmv.visitVarInsn(ALOAD, MH_VAR);
hmv.visitInsn(ARETURN);
hmv.visitMaxs(0, 0);
hmv.visitEnd();
}
/*
* Generate bytecode for SystemModules::methodResoultions method
*/
private void genModuleResolutionsMethod() {
MethodVisitor mresmv =
cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
"moduleResolutions",
"()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
"()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
null);
mresmv.visitCode();
pushInt(mresmv, moduleInfos.size());
mresmv.visitTypeInsn(ANEWARRAY, MODULE_RESOLUTION_CLASSNAME);
mresmv.visitVarInsn(ASTORE, 0);
for (int index=0; index < moduleInfos.size(); index++) {
ModuleInfo minfo = moduleInfos.get(index);
if (minfo.moduleResolution != null) {
mresmv.visitVarInsn(ALOAD, 0);
pushInt(mresmv, index);
mresmv.visitTypeInsn(NEW, MODULE_RESOLUTION_CLASSNAME);
mresmv.visitInsn(DUP);
mresmv.visitLdcInsn(minfo.moduleResolution.value());
mresmv.visitMethodInsn(INVOKESPECIAL,
MODULE_RESOLUTION_CLASSNAME,
"<init>",
"(I)V", false);
mresmv.visitInsn(AASTORE);
}
}
mresmv.visitVarInsn(ALOAD, 0);
mresmv.visitInsn(ARETURN);
mresmv.visitMaxs(0, 0);
mresmv.visitEnd();
}
public boolean isOverriddenClass(String path) {
return path.equals("/java.base/" + CLASSNAME + ".class");
}
void pushInt(int num) {
void pushInt(MethodVisitor mv, int num) {
if (num <= 5) {
mv.visitInsn(ICONST_0 + num);
} else if (num < Byte.MAX_VALUE) {
@ -460,6 +570,8 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
"(Ljava/lang/String;Ljava/util/List;)" + PROVIDES_TYPE;
static final String REQUIRES_SET_STRING_SIG =
"(Ljava/util/Set;Ljava/lang/String;)" + REQUIRES_TYPE;
static final String REQUIRES_SET_STRING_STRING_SIG =
"(Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;)" + REQUIRES_TYPE;
// method signature for Builder instance methods that
// return this Builder instance
@ -473,20 +585,18 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
"([" + REQUIRES_TYPE + ")" + BUILDER_TYPE;
static final String SET_SIG = "(Ljava/util/Set;)" + BUILDER_TYPE;
static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE;
static final String STRING_BYTE_ARRAY_SIG =
"(Ljava/lang/String;[B)" + BUILDER_TYPE;
static final String BOOLEAN_SIG = "(Z)" + BUILDER_TYPE;
final ModuleDescriptor md;
final Set<String> packages;
ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages) {
final int index;
ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages, int index) {
if (md.isAutomatic()) {
throw new InternalError("linking automatic module is not supported");
}
this.md = md;
this.packages = packages;
this.index = index;
}
void build() {
@ -517,12 +627,6 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
// main class
md.mainClass().ifPresent(this::mainClass);
// hashes
JLMA.hashes(md).ifPresent(mh -> {
algorithm(mh.algorithm());
mh.names().forEach(mn -> moduleHash(mn, mh.hashFor(mn)));
});
putModuleDescriptor();
}
@ -554,7 +658,7 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
mv.visitInsn(ICONST_0);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
methodName, BOOLEAN_SIG, false);
methodName, BOOLEAN_SIG, false);
mv.visitInsn(POP);
}
@ -563,12 +667,12 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
*/
void putModuleDescriptor() {
mv.visitVarInsn(ALOAD, MD_VAR);
pushInt(nextModulesIndex++);
pushInt(mv, index);
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(md.hashCode());
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"build", "(I)Ljava/lang/module/ModuleDescriptor;",
false);
"build", "(I)Ljava/lang/module/ModuleDescriptor;",
false);
mv.visitInsn(AASTORE);
}
@ -580,31 +684,42 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
*/
void requires(Set<Requires> requires) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
pushInt(requires.size());
pushInt(mv, requires.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires");
int arrayIndex = 0;
for (Requires require : requires) {
mv.visitInsn(DUP); // arrayref
pushInt(arrayIndex++);
newRequires(require.modifiers(), require.name());
String compiledVersion = null;
if (require.compiledVersion().isPresent()) {
compiledVersion = require.compiledVersion().get().toString();
}
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
newRequires(require.modifiers(), require.name(), compiledVersion);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"requires", REQUIRES_ARRAY_SIG, false);
"requires", REQUIRES_ARRAY_SIG, false);
}
/*
* Invoke Builder.newRequires(Set<Modifier> mods, String mn)
* Invoke Builder.newRequires(Set<Modifier> mods, String mn, String compiledVersion)
*
* Set<Modifier> mods = ...
* Builder.newRequires(mods, mn);
* Builder.newRequires(mods, mn, compiledVersion);
*/
void newRequires(Set<Requires.Modifier> mods, String name) {
void newRequires(Set<Requires.Modifier> mods, String name, String compiledVersion) {
int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods);
mv.visitVarInsn(ALOAD, varIndex);
mv.visitLdcInsn(name);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newRequires", REQUIRES_SET_STRING_SIG, false);
if (compiledVersion != null) {
mv.visitLdcInsn(compiledVersion);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newRequires", REQUIRES_SET_STRING_STRING_SIG, false);
} else {
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newRequires", REQUIRES_SET_STRING_SIG, false);
}
}
/*
@ -615,17 +730,17 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
*/
void exports(Set<Exports> exports) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
pushInt(exports.size());
pushInt(mv, exports.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports");
int arrayIndex = 0;
for (Exports export : exports) {
mv.visitInsn(DUP); // arrayref
pushInt(arrayIndex++);
pushInt(mv, arrayIndex++);
newExports(export.modifiers(), export.source(), export.targets());
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"exports", EXPORTS_ARRAY_SIG, false);
"exports", EXPORTS_ARRAY_SIG, false);
}
/*
@ -664,22 +779,21 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
/**
* Call Builder::newOpens to create Opens instances and
* then pass it to the builder by calling:
* Builder.opens(Opens[])
*
* Builder.opens(Opens[])
*/
void opens(Set<Opens> opens) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
pushInt(opens.size());
pushInt(mv, opens.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens");
int arrayIndex = 0;
for (Opens open : opens) {
mv.visitInsn(DUP); // arrayref
pushInt(arrayIndex++);
pushInt(mv, arrayIndex++);
newOpens(open.modifiers(), open.source(), open.targets());
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"opens", OPENS_ARRAY_SIG, false);
"opens", OPENS_ARRAY_SIG, false);
}
/*
@ -705,12 +819,12 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
mv.visitLdcInsn(pn);
mv.visitVarInsn(ALOAD, stringSetIndex);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false);
"newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false);
} else {
mv.visitVarInsn(ALOAD, modifiersSetIndex);
mv.visitLdcInsn(pn);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newOpens", OPENS_MODIFIER_SET_STRING_SIG, false);
"newOpens", OPENS_MODIFIER_SET_STRING_SIG, false);
}
}
@ -722,7 +836,7 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitVarInsn(ALOAD, varIndex);
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"uses", SET_SIG, false);
"uses", SET_SIG, false);
mv.visitInsn(POP);
}
@ -734,17 +848,17 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
*/
void provides(Collection<Provides> provides) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
pushInt(provides.size());
pushInt(mv, provides.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides");
int arrayIndex = 0;
for (Provides provide : provides) {
mv.visitInsn(DUP); // arrayref
pushInt(arrayIndex++);
pushInt(mv, arrayIndex++);
newProvides(provide.service(), provide.providers());
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"provides", PROVIDES_ARRAY_SIG, false);
"provides", PROVIDES_ARRAY_SIG, false);
}
/*
@ -758,19 +872,19 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
*/
void newProvides(String service, List<String> providers) {
mv.visitLdcInsn(service);
pushInt(providers.size());
pushInt(mv, providers.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int arrayIndex = 0;
for (String provider : providers) {
mv.visitInsn(DUP); // arrayref
pushInt(arrayIndex++);
pushInt(mv, arrayIndex++);
mv.visitLdcInsn(provider);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKESTATIC, "java/util/List",
"of", "([Ljava/lang/Object;)Ljava/util/List;", true);
"of", "([Ljava/lang/Object;)Ljava/util/List;", true);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newProvides", PROVIDES_STRING_LIST_SIG, false);
"newProvides", PROVIDES_STRING_LIST_SIG, false);
}
/*
@ -781,7 +895,7 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitVarInsn(ALOAD, varIndex);
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"packages", SET_SIG, false);
"packages", SET_SIG, false);
mv.visitInsn(POP);
}
@ -792,7 +906,7 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(cn);
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"mainClass", STRING_SIG, false);
"mainClass", STRING_SIG, false);
mv.visitInsn(POP);
}
@ -803,48 +917,96 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(v.toString());
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"version", STRING_SIG, false);
"version", STRING_SIG, false);
mv.visitInsn(POP);
}
}
class ModuleHashesBuilder {
private static final String MODULE_HASHES_BUILDER =
"jdk/internal/module/ModuleHashes$Builder";
private static final String MODULE_HASHES_BUILDER_TYPE =
"L" + MODULE_HASHES_BUILDER + ";";
static final String STRING_BYTE_ARRAY_SIG =
"(Ljava/lang/String;[B)" + MODULE_HASHES_BUILDER_TYPE;
final ModuleHashes recordedHashes;
final MethodVisitor hmv;
final int index;
ModuleHashesBuilder(ModuleHashes hashes, int index, MethodVisitor hmv) {
this.recordedHashes = hashes;
this.hmv = hmv;
this.index = index;
}
/**
* Build ModuleHashes
*/
void build() {
if (recordedHashes == null)
return;
// new jdk.internal.module.ModuleHashes.Builder
newModuleHashesBuilder();
// Invoke ModuleHashes.Builder::hashForModule
recordedHashes
.names()
.forEach(mn -> hashForModule(mn, recordedHashes.hashFor(mn)));
// Put ModuleHashes into the hashes array
pushModuleHashes();
}
/*
* Create ModuleHashes.Builder instance
*/
void newModuleHashesBuilder() {
hmv.visitTypeInsn(NEW, MODULE_HASHES_BUILDER);
hmv.visitInsn(DUP);
hmv.visitLdcInsn(recordedHashes.algorithm());
hmv.visitMethodInsn(INVOKESPECIAL, MODULE_HASHES_BUILDER,
"<init>", "(Ljava/lang/String;)V", false);
hmv.visitVarInsn(ASTORE, BUILDER_VAR);
hmv.visitVarInsn(ALOAD, BUILDER_VAR);
}
/*
* Invoke ModuleHashes.Builder::build and put the returned
* ModuleHashes to the hashes array
*/
void pushModuleHashes() {
hmv.visitVarInsn(ALOAD, MH_VAR);
pushInt(hmv, index);
hmv.visitVarInsn(ALOAD, BUILDER_VAR);
hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
"build", "()Ljdk/internal/module/ModuleHashes;",
false);
hmv.visitInsn(AASTORE);
}
/*
* Invoke Builder.algorithm(String a);
* Invoke ModuleHashes.Builder.hashForModule(String name, byte[] hash);
*/
void algorithm(String alg) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(alg);
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"algorithm", STRING_SIG, false);
mv.visitInsn(POP);
}
void hashForModule(String name, byte[] hash) {
hmv.visitVarInsn(ALOAD, BUILDER_VAR);
hmv.visitLdcInsn(name);
/*
* Invoke Builder.moduleHash(String name, byte[] hash);
*/
void moduleHash(String name, byte[] hash) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(name);
// must exist
Integer index = modulesToHashIndex.get(name);
if (index != null) {
mv.visitFieldInsn(GETSTATIC, CLASSNAME, MODULES_TO_HASH, "[[B");
pushInt(index);
mv.visitInsn(AALOAD);
assert(Objects.equals(hash, modulesToHash.get(name)));
} else {
pushInt(hash.length);
mv.visitIntInsn(NEWARRAY, T_BYTE);
for (int i = 0; i < hash.length; i++) {
mv.visitInsn(DUP); // arrayref
pushInt(i);
mv.visitIntInsn(BIPUSH, hash[i]);
mv.visitInsn(BASTORE);
}
pushInt(hmv, hash.length);
hmv.visitIntInsn(NEWARRAY, T_BYTE);
for (int i = 0; i < hash.length; i++) {
hmv.visitInsn(DUP); // arrayref
pushInt(hmv, i);
hmv.visitIntInsn(BIPUSH, hash[i]);
hmv.visitInsn(BASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"moduleHash", STRING_BYTE_ARRAY_SIG, false);
mv.visitInsn(POP);
hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
"hashForModule", STRING_BYTE_ARRAY_SIG, false);
hmv.visitInsn(POP);
}
}
@ -1039,12 +1201,12 @@ public final class SystemModuleDescriptorPlugin implements Plugin {
"of", sb.toString(), true);
} else {
// call Set.of(E... elements)
pushInt(elements.size());
pushInt(mv, elements.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int arrayIndex = 0;
for (T t : elements) {
mv.visitInsn(DUP); // arrayref
pushInt(arrayIndex);
pushInt(mv, arrayIndex);
visitElement(t, mv); // value
mv.visitInsn(AASTORE);
arrayIndex++;

View File

@ -66,6 +66,7 @@ import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -98,10 +99,11 @@ import jdk.internal.joptsimple.OptionSet;
import jdk.internal.joptsimple.OptionSpec;
import jdk.internal.joptsimple.ValueConverter;
import jdk.internal.loader.ResourceHelper;
import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleInfo;
import jdk.internal.module.ModuleInfoExtender;
import jdk.internal.module.ModulePath;
import jdk.internal.module.ModuleResolution;
import jdk.tools.jlink.internal.Utils;
import static java.util.stream.Collectors.joining;
@ -163,6 +165,7 @@ public class JmodTask {
Mode mode;
Path jmodFile;
boolean help;
boolean helpExtra;
boolean version;
List<Path> classpath;
List<Path> cmds;
@ -178,6 +181,7 @@ public class JmodTask {
String osArch;
String osVersion;
Pattern modulesToHash;
ModuleResolution moduleResolution;
boolean dryrun;
List<PathMatcher> excludes;
Path extractDir;
@ -191,7 +195,7 @@ public class JmodTask {
showUsageSummary();
return EXIT_CMDERR;
}
if (options.help) {
if (options.help || options.helpExtra) {
showHelp();
return EXIT_OK;
}
@ -287,8 +291,8 @@ public class JmodTask {
private boolean describe() throws IOException {
try (JmodFile jf = new JmodFile(options.jmodFile)) {
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
ModuleDescriptor md = ModuleDescriptor.read(in);
printModuleDescriptor(md);
ModuleInfo.Attributes attrs = ModuleInfo.read(in, null);
printModuleDescriptor(attrs.descriptor(), attrs.recordedHashes());
return true;
} catch (IOException e) {
throw new CommandException("err.module.descriptor.not.found");
@ -302,9 +306,7 @@ public class JmodTask {
.collect(joining(" "));
}
private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
private void printModuleDescriptor(ModuleDescriptor md)
private void printModuleDescriptor(ModuleDescriptor md, ModuleHashes hashes)
throws IOException
{
StringBuilder sb = new StringBuilder();
@ -350,15 +352,24 @@ public class JmodTask {
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
JLMA.hashes(md).ifPresent(
hashes -> hashes.names().stream().sorted().forEach(
mod -> sb.append("\n hashes ").append(mod).append(" ")
.append(hashes.algorithm()).append(" ")
.append(hashes.hashFor(mod))));
if (hashes != null) {
hashes.names().stream().sorted().forEach(
mod -> sb.append("\n hashes ").append(mod).append(" ")
.append(hashes.algorithm()).append(" ")
.append(toHex(hashes.hashFor(mod))));
}
out.println(sb.toString());
}
private String toHex(byte[] ba) {
StringBuilder sb = new StringBuilder(ba.length);
for (byte b: ba) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
private boolean create() throws IOException {
JmodFileWriter jmod = new JmodFileWriter();
@ -400,6 +411,7 @@ public class JmodTask {
final String osVersion = options.osVersion;
final List<PathMatcher> excludes = options.excludes;
final Hasher hasher = hasher();
final ModuleResolution moduleResolution = options.moduleResolution;
JmodFileWriter() { }
@ -509,6 +521,10 @@ public class JmodTask {
}
}
if (moduleResolution != null && moduleResolution.value() != 0) {
extender.moduleResolution(moduleResolution);
}
// write the (possibly extended or modified) module-info.class
out.writeEntry(extender.toByteArray(), Section.CLASSES, MODULE_INFO);
}
@ -537,12 +553,12 @@ public class JmodTask {
}
URI uri = options.jmodFile.toUri();
ModuleReference mref = new ModuleReference(descriptor, uri, new Supplier<>() {
ModuleReference mref = new ModuleReference(descriptor, uri) {
@Override
public ModuleReader get() {
public ModuleReader open() {
throw new UnsupportedOperationException();
}
});
};
// compose a module finder with the module path and also
// a module finder that can find the jmod file being created
@ -1139,6 +1155,28 @@ public class JmodTask {
@Override public String valuePattern() { return "module-version"; }
}
static class WarnIfResolvedReasonConverter
implements ValueConverter<ModuleResolution>
{
@Override
public ModuleResolution convert(String value) {
if (value.equals("deprecated"))
return ModuleResolution.empty().withDeprecated();
else if (value.equals("deprecated-for-removal"))
return ModuleResolution.empty().withDeprecatedForRemoval();
else if (value.equals("incubating"))
return ModuleResolution.empty().withIncubating();
else
throw new CommandException("err.bad.WarnIfResolvedReason", value);
}
@Override public Class<ModuleResolution> valueType() {
return ModuleResolution.class;
}
@Override public String valuePattern() { return "reason"; }
}
static class PatternConverter implements ValueConverter<Pattern> {
@Override
public Pattern convert(String value) {
@ -1182,12 +1220,24 @@ public class JmodTask {
*/
private static final class JmodHelpFormatter extends BuiltinHelpFormatter {
private JmodHelpFormatter() { super(80, 2); }
private final Options opts;
private JmodHelpFormatter(Options opts) {
super(80, 2);
this.opts = opts;
}
@Override
public String format(Map<String, ? extends OptionDescriptor> options) {
Map<String, OptionDescriptor> all = new HashMap<>();
Map<String, OptionDescriptor> all = new LinkedHashMap<>();
all.putAll(options);
// extra options
if (!opts.helpExtra) {
all.remove("do-not-resolve-by-default");
all.remove("warn-if-resolved");
}
all.put(CMD_FILENAME, new OptionDescriptor() {
@Override
public Collection<String> options() {
@ -1243,7 +1293,8 @@ public class JmodTask {
private final OptionParser parser = new OptionParser("hp");
private void handleOptions(String[] args) {
parser.formatHelpWith(new JmodHelpFormatter());
options = new Options();
parser.formatHelpWith(new JmodHelpFormatter(options));
OptionSpec<Path> classPath
= parser.accepts("class-path", getMessage("main.opt.class-path"))
@ -1285,6 +1336,9 @@ public class JmodTask {
= parser.acceptsAll(Set.of("h", "help"), getMessage("main.opt.help"))
.forHelp();
OptionSpec<Void> helpExtra
= parser.accepts("help-extra", getMessage("main.opt.help-extra"));
OptionSpec<Path> headerFiles
= parser.accepts("header-files", getMessage("main.opt.header-files"))
.withRequiredArg()
@ -1342,6 +1396,15 @@ public class JmodTask {
.withRequiredArg()
.describedAs(getMessage("main.opt.os-version.arg"));
OptionSpec<Void> doNotResolveByDefault
= parser.accepts("do-not-resolve-by-default",
getMessage("main.opt.do-not-resolve-by-default"));
OptionSpec<ModuleResolution> warnIfResolved
= parser.accepts("warn-if-resolved", getMessage("main.opt.warn-if-resolved"))
.withRequiredArg()
.withValuesConvertedBy(new WarnIfResolvedReasonConverter());
OptionSpec<Void> version
= parser.accepts("version", getMessage("main.opt.version"));
@ -1351,9 +1414,9 @@ public class JmodTask {
try {
OptionSet opts = parser.parse(args);
if (opts.has(help) || opts.has(version)) {
options = new Options();
if (opts.has(help) || opts.has(helpExtra) || opts.has(version)) {
options.help = opts.has(help);
options.helpExtra = opts.has(helpExtra);
options.version = opts.has(version);
return; // informational message will be shown
}
@ -1362,7 +1425,6 @@ public class JmodTask {
if (words.isEmpty())
throw new CommandException("err.missing.mode").showUsage(true);
String verb = words.get(0);
options = new Options();
try {
options.mode = Enum.valueOf(Mode.class, verb.toUpperCase());
} catch (IllegalArgumentException e) {
@ -1391,7 +1453,7 @@ public class JmodTask {
options.legalNotices = opts.valuesOf(legalNotices);
if (opts.has(modulePath)) {
Path[] dirs = opts.valuesOf(modulePath).toArray(new Path[0]);
options.moduleFinder = JLMA.newModulePath(Runtime.version(), true, dirs);
options.moduleFinder = new ModulePath(Runtime.version(), true, dirs);
}
if (opts.has(moduleVersion))
options.moduleVersion = opts.valueOf(moduleVersion);
@ -1403,6 +1465,13 @@ public class JmodTask {
options.osArch = opts.valueOf(osArch);
if (opts.has(osVersion))
options.osVersion = opts.valueOf(osVersion);
if (opts.has(warnIfResolved))
options.moduleResolution = opts.valueOf(warnIfResolved);
if (opts.has(doNotResolveByDefault)) {
if (options.moduleResolution == null)
options.moduleResolution = ModuleResolution.empty();
options.moduleResolution = options.moduleResolution.withDoNotResolveByDefault();
}
if (opts.has(hashModules)) {
options.modulesToHash = opts.valueOf(hashModules);
// if storing hashes then the module path is required

View File

@ -47,6 +47,7 @@ main.opt.mode.hash=\
\hash - Records hashes of tied modules.
main.opt.help=Print this usage message
main.opt.help-extra=Print help on extra options
main.opt.version=Version information
main.opt.class-path=Application jar files|dir containing classes
main.opt.libs=Location of native libraries
@ -74,6 +75,9 @@ main.opt.hash-modules=Compute and record hashes to tie a packaged module\
\ with modules matching the given <regex-pattern> and depending upon it directly\
\ or indirectly. The hashes are recorded in the JMOD file being created, or\
\ a JMOD file or modular JAR on the module path specified the jmod hash command.
main.opt.do-not-resolve-by-default=Exclude from the default root set of modules
main.opt.warn-if-resolved=Hint for a tool to issue a warning if the module \
is resolved. One of deprecated, deprecated-for-removal, or incubating
main.opt.cmdfile=Read options from the specified file
@ -96,6 +100,8 @@ err.invalid.class.path.entry=invalid class path entry: {0}
err.file.already.exists=file already exists: {0}
err.jmod.not.found=no jmod file found: {0}
err.bad.pattern=bad pattern {0}
err.bad.WarnIfResolvedReason=bad reason: {0}, must be one of deprecated,\
\ deprecated-for-removal, or incubating
err.unknown.option=unknown option(s): {0}
err.missing.arg=no value given for {0}
err.internal.error=internal error: {0} {1} {2}

View File

@ -41,7 +41,7 @@ module jdk.jlink {
jdk.tools.jlink.internal.plugins.ExcludeFilesPlugin,
jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin,
jdk.tools.jlink.internal.plugins.LegalNoticeFilePlugin,
jdk.tools.jlink.internal.plugins.SystemModuleDescriptorPlugin,
jdk.tools.jlink.internal.plugins.SystemModulesPlugin,
jdk.tools.jlink.internal.plugins.StripNativeCommandsPlugin,
jdk.tools.jlink.internal.plugins.OrderResourcesPlugin,
jdk.tools.jlink.internal.plugins.DefaultCompressPlugin,

View File

@ -0,0 +1,152 @@
/*
* 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.
*/
/**
* @test
* @run testng DropLookupModeTest
* @summary Basic unit tests Lookup::dropLookupMode
*/
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import static java.lang.invoke.MethodHandles.Lookup.*;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@Test
public class DropLookupModeTest {
/**
* Basic test of dropLookupMode
*/
public void testBasic() {
final Lookup fullPowerLookup = MethodHandles.lookup();
final Class<?> lc = fullPowerLookup.lookupClass();
assertTrue(fullPowerLookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PROTECTED|PRIVATE));
Lookup lookup = fullPowerLookup.dropLookupMode(PRIVATE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE|PACKAGE));
lookup = fullPowerLookup.dropLookupMode(PROTECTED);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PRIVATE));
lookup = fullPowerLookup.dropLookupMode(PACKAGE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE));
lookup = fullPowerLookup.dropLookupMode(MODULE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == (PUBLIC));
lookup = fullPowerLookup.dropLookupMode(PUBLIC);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == 0);
}
/**
* Starting with a full power Lookup, use dropLookupMode to create new Lookups
* with reduced access.
*/
public void testReducingAccess() {
Lookup lookup = MethodHandles.lookup();
final Class<?> lc = lookup.lookupClass();
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PROTECTED|PRIVATE));
lookup = lookup.dropLookupMode(PROTECTED);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PRIVATE));
lookup = lookup.dropLookupMode(PRIVATE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE|PACKAGE));
lookup = lookup.dropLookupMode(PACKAGE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE));
lookup = lookup.dropLookupMode(MODULE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == PUBLIC);
lookup = lookup.dropLookupMode(PUBLIC);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == 0);
// repeat with lookup has no access
lookup = lookup.dropLookupMode(PUBLIC);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == 0);
}
/**
* Test dropLookupMode on the public Lookup.
*/
public void testPublicLookup() {
final Lookup publicLookup = MethodHandles.publicLookup();
final Class<?> lc = publicLookup.lookupClass();
assertTrue(publicLookup.lookupModes() == PUBLIC);
Lookup lookup = publicLookup.dropLookupMode(PRIVATE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == PUBLIC);
lookup = publicLookup.dropLookupMode(PROTECTED);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == PUBLIC);
lookup = publicLookup.dropLookupMode(PACKAGE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == PUBLIC);
lookup = publicLookup.dropLookupMode(MODULE);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == PUBLIC);
lookup = publicLookup.dropLookupMode(PUBLIC);
assertTrue(lookup.lookupClass() == lc);
assertTrue(lookup.lookupModes() == 0);
}
@DataProvider(name = "badInput")
public Object[][] badInput() {
return new Object[][] {
{ 0, null },
{ (PACKAGE|PRIVATE), null }, // two modes
{ Integer.MAX_VALUE, null },
{ Integer.MIN_VALUE, null },
};
}
/**
* Check that IllegalArgumentException is thrown for bad input
*/
@Test(dataProvider = "badInput", expectedExceptions = {IllegalArgumentException.class})
public void testBadInput(Integer modeToDrop, Object ignore) {
MethodHandles.lookup().dropLookupMode(modeToDrop);
}
}

View File

@ -84,8 +84,7 @@ public class PrivateLookupInTests {
// Invoke MethodHandles.privateLookupIn with a reduced-power caller
public void testReducedAccessCallerSameModule() throws Throwable {
// drop access
Lookup caller = MethodHandles.lookup().in(publicType);
Lookup caller = MethodHandles.lookup().dropLookupMode(PACKAGE);
assertTrue((caller.lookupModes() & PRIVATE) == 0);
assertTrue((caller.lookupModes() & PACKAGE) == 0);
assertTrue((caller.lookupModes() & MODULE) != 0);

View File

@ -23,8 +23,7 @@
/**
* @test
* @modules java.base/java.lang.module:open
* java.base/jdk.internal.module
* @modules java.base/jdk.internal.module
* @run testng ModuleDescriptorTest
* @summary Basic test for java.lang.module.ModuleDescriptor and its builder
*/
@ -41,16 +40,13 @@ import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Requires.Modifier;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.reflect.Constructor;
import java.lang.reflect.Module;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
@ -66,10 +62,20 @@ public class ModuleDescriptorTest {
public Object[][] invalidJavaIdentifiers() {
return new Object[][]{
{ null, null },
{ ".foo", null },
{ "foo.", null },
{ "[foo]", null },
{ null, null },
{ "1", null },
{ "1foo", null },
{ ".foo", null },
{ "foo.", null },
{ "[foo]", null },
{ "foo.1", null },
{ "1foo.bar", null },
{ "foo.1bar", null },
{ "foo.[bar]", null },
{ "foo..bar", null },
{ "foo.bar.1", null },
{ "foo.bar.1gus", null },
{ "foo.bar.[gus]", null },
};
}
@ -86,6 +92,15 @@ public class ModuleDescriptorTest {
.next();
}
private Requires requires(Set<Modifier> mods, String mn, Version v) {
return ModuleDescriptor.module("m")
.requires(mods, mn, v)
.build()
.requires()
.iterator()
.next();
}
private Requires requires(String mn) {
return requires(Collections.emptySet(), mn);
}
@ -103,6 +118,7 @@ public class ModuleDescriptorTest {
assertTrue(r.compareTo(r) == 0);
assertTrue(r.modifiers().isEmpty());
assertEquals(r.name(), "foo");
assertFalse(r.compiledVersion().isPresent());
}
public void testRequiresWithOneModifier() {
@ -111,6 +127,7 @@ public class ModuleDescriptorTest {
assertTrue(r.compareTo(r) == 0);
assertEquals(r.modifiers(), EnumSet.of(TRANSITIVE));
assertEquals(r.name(), "foo");
assertFalse(r.compiledVersion().isPresent());
}
public void testRequiresWithTwoModifiers() {
@ -119,6 +136,7 @@ public class ModuleDescriptorTest {
assertTrue(r.compareTo(r) == 0);
assertEquals(r.modifiers(), EnumSet.of(TRANSITIVE, SYNTHETIC));
assertEquals(r.name(), "foo");
assertFalse(r.compiledVersion().isPresent());
}
public void testRequiresWithAllModifiers() {
@ -127,6 +145,18 @@ public class ModuleDescriptorTest {
assertTrue(r.compareTo(r) == 0);
assertEquals(r.modifiers(), EnumSet.of(TRANSITIVE, STATIC, SYNTHETIC, MANDATED));
assertEquals(r.name(), "foo");
assertFalse(r.compiledVersion().isPresent());
}
public void testRequiresWithCompiledVersion() {
Version v = Version.parse("1.0");
Requires r = requires(Set.of(), "foo", v);
assertEquals(r, r);
assertTrue(r.compareTo(r) == 0);
assertEquals(r.modifiers(), Set.of());
assertEquals(r.name(), "foo");
assertTrue(r.compiledVersion().isPresent());
assertEquals(r.compiledVersion().get().toString(), "1.0");
}
@Test(expectedExceptions = IllegalStateException.class)
@ -167,6 +197,16 @@ public class ModuleDescriptorTest {
ModuleDescriptor.module("m").requires((Requires) null);
}
@Test(expectedExceptions = NullPointerException.class)
public void testRequiresWithNullModifiers() {
ModuleDescriptor.module("m").requires(null, "foo");
}
@Test(expectedExceptions = NullPointerException.class)
public void testRequiresWithNullVersion() {
ModuleDescriptor.module("m").requires(Set.of(), "foo", null);
}
public void testRequiresCompare() {
Requires r1 = requires(EnumSet.noneOf(Modifier.class), "foo");
Requires r2 = requires(EnumSet.noneOf(Modifier.class), "bar");
@ -190,6 +230,20 @@ public class ModuleDescriptorTest {
assertTrue(r2.compareTo(r1) == 0);
}
public void testRequiresCompareWithSameCompiledVersion() {
Requires r1 = requires(Set.of(), "foo", Version.parse("2.0"));
Requires r2 = requires(Set.of(), "foo", Version.parse("2.0"));
assertTrue(r1.compareTo(r2) == 0);
assertTrue(r2.compareTo(r1) == 0);
}
public void testRequiresCompareWithDifferentCompiledVersion() {
Requires r1 = requires(Set.of(), "foo", Version.parse("1.0"));
Requires r2 = requires(Set.of(), "foo", Version.parse("2.0"));
assertTrue(r1.compareTo(r2) < 0);
assertTrue(r2.compareTo(r1) > 0);
}
public void testRequiresEqualsAndHashCode() {
Requires r1 = requires("foo");
Requires r2 = requires("foo");
@ -208,6 +262,17 @@ public class ModuleDescriptorTest {
r1 = requires(EnumSet.allOf(Requires.Modifier.class), "foo");
r2 = requires(Set.of(), "foo");
assertNotEquals(r1, r2);
Version v1 = Version.parse("1.0");
r1 = requires(EnumSet.allOf(Requires.Modifier.class), "foo", v1);
r2 = requires(EnumSet.allOf(Requires.Modifier.class), "foo", v1);
assertEquals(r1, r2);
assertTrue(r1.hashCode() == r2.hashCode());
Version v2 = Version.parse("2.0");
r1 = requires(EnumSet.allOf(Requires.Modifier.class), "foo", v1);
r2 = requires(EnumSet.allOf(Requires.Modifier.class), "foo", v2);
assertNotEquals(r1, r2);
}
public void testRequiresToString() {
@ -938,7 +1003,7 @@ public class ModuleDescriptorTest {
};
// basic test reading module-info.class
public void testRead1() throws Exception {
public void testRead() throws Exception {
Module base = Object.class.getModule();
try (InputStream in = base.getResourceAsStream("module-info.class")) {
@ -954,45 +1019,6 @@ public class ModuleDescriptorTest {
assertEquals(descriptor.name(), "java.base");
}
}
/**
* Test reading a module-info.class that has a module name, requires,
* and qualified exports with module names that are not supported in the
* Java Language.
*/
public void testRead2() throws Exception {
// use non-public constructor to create a Builder that is not strict
Constructor<?> ctor = Builder.class.getDeclaredConstructor(String.class, boolean.class);
ctor.setAccessible(true);
Builder builder = (ModuleDescriptor.Builder) ctor.newInstance("m?1", false);
ModuleDescriptor descriptor = builder
.requires("java.base")
.requires("-m1")
.exports("p", Set.of("m2-"))
.build();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ModuleInfoWriter.write(descriptor, baos);
ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray());
descriptor = ModuleDescriptor.read(bb);
assertEquals(descriptor.name(), "m?1");
Set<String> requires = descriptor.requires()
.stream()
.map(Requires::name)
.collect(Collectors.toSet());
assertTrue(requires.size() == 2);
assertTrue(requires.contains("java.base"));
assertTrue(requires.contains("-m1"));
assertTrue(descriptor.exports().size() == 1);
Exports e = descriptor.exports().iterator().next();
assertTrue(e.targets().size() == 1);
assertTrue(e.targets().contains("m2-"));
}
/**
* Test ModuleDescriptor with a packager finder
*/

View File

@ -0,0 +1,261 @@
/*
* 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.
*/
/**
* @test
* @modules java.base/jdk.internal.misc
* java.base/jdk.internal.module
* @run testng ModuleNamesTest
* @summary Basic test of reading a module-info.class with module names that
* are legal in class files but not legal in the Java Language
*/
import java.io.ByteArrayOutputStream;
import java.lang.module.InvalidModuleDescriptorException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Builder;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Requires;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.Set;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModuleInfoWriter;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@Test
public class ModuleNamesTest {
@DataProvider(name = "legalModuleNames")
public Object[][] legalModuleNames() {
return new Object[][] {
{ ".", "." },
{ ".foo", ".foo" },
{ "foo.", "foo." },
{ "foo.bar", "foo.bar" },
{ "..", ".." },
{ "..foo", "..foo" },
{ "foo..", "foo.." },
{ "foo..bar", "foo..bar" },
{ "[", "[" },
{ "[foo", "[foo" },
{ "foo[", "foo[" },
{ "foo[bar", "foo[bar" },
{ ";", ";" },
{ ";foo", ";foo" },
{ "foo;", "foo;" },
{ "foo;bar", "foo;bar" },
{ "\\\\", "\\" },
{ "\\\\foo", "\\foo" },
{ "foo\\\\", "foo\\" },
{ "foo\\\\bar", "foo\\bar" },
{ "\\\\\\\\", "\\\\" },
{ "\\\\\\\\foo", "\\\\foo" },
{ "foo\\\\\\\\", "foo\\\\" },
{ "foo\\\\\\\\bar", "foo\\\\bar" },
{ "\\:", ":" },
{ "\\:foo", ":foo" },
{ "foo\\:", "foo:" },
{ "foo\\:bar", "foo:bar" },
{ "\\:\\:", "::" },
{ "\\:\\:foo", "::foo" },
{ "foo\\:\\:", "foo::" },
{ "foo\\:\\:bar", "foo::bar" },
{ "\\@", "@" },
{ "\\@foo", "@foo" },
{ "foo\\@", "foo@" },
{ "foo\\@bar", "foo@bar" },
{ "\\@\\@", "@@" },
{ "\\@\\@foo", "@@foo" },
{ "foo\\@\\@", "foo@@" },
{ "foo\\@\\@bar", "foo@@bar" },
{ makeString("", 0x20, ""), " " },
{ makeString("foo", 0x20, ""), "foo " },
{ makeString("", 0x20, "foo"), " foo" },
{ makeString("foo", 0x20, "bar"), "foo bar" },
};
}
@DataProvider(name = "illegalModuleNames")
public Object[][] illegalModuleNames() {
return new Object[][] {
{ "", null },
{ ":", null },
{ ":foo", null },
{ "foo:", null },
{ "foo:bar", null },
{ "@", null },
{ "@foo", null },
{ "foo@", null },
{ "foo@bar", null },
{ "\\", null },
{ "\\foo", null },
{ "foo\\", null },
{ "foo\\bar", null },
{ makeString("", 0x00, ""), null },
{ makeString("", 0x00, "foo"), null },
{ makeString("foo", 0x00, ""), null },
{ makeString("foo", 0x00, "bar"), null },
{ makeString("", 0x1f, ""), null },
{ makeString("", 0x1f, "foo"), null },
{ makeString("foo", 0x1f, ""), null },
{ makeString("foo", 0x1f, "bar"), null },
};
}
@Test(dataProvider = "legalModuleNames")
public void testLegalModuleName(String mn, String expected) throws Exception {
ModuleDescriptor md = newBuilder(mn).requires("java.base").build();
ByteBuffer bb = toBuffer(md);
String name = ModuleDescriptor.read(bb).name();
assertEquals(name, expected);
}
@Test(dataProvider = "illegalModuleNames",
expectedExceptions = InvalidModuleDescriptorException.class)
public void testIllegalModuleName(String mn, String ignore) throws Exception {
ModuleDescriptor md = newBuilder(mn).requires("java.base").build();
ByteBuffer bb = toBuffer(md);
ModuleDescriptor.read(bb); // throws InvalidModuleDescriptorException
}
@Test(dataProvider = "legalModuleNames")
public void testLegalRequires(String mn, String expected) throws Exception {
ModuleDescriptor md = newBuilder("m").requires("java.base").requires(mn).build();
ByteBuffer bb = toBuffer(md);
ModuleDescriptor descriptor = ModuleDescriptor.read(bb);
Optional<Requires> requires = descriptor.requires().stream()
.filter(r -> !r.name().equals("java.base"))
.findAny();
assertTrue(requires.isPresent());
assertEquals(requires.get().name(), expected);
}
@Test(dataProvider = "illegalModuleNames",
expectedExceptions = InvalidModuleDescriptorException.class)
public void testIllegalRequires(String mn, String ignore) throws Exception {
ModuleDescriptor md = newBuilder("m").requires("java.base").requires(mn).build();
ByteBuffer bb = toBuffer(md);
ModuleDescriptor.read(bb); // throws InvalidModuleDescriptorException
}
@Test(dataProvider = "legalModuleNames")
public void testLegalExports(String mn, String expected) throws Exception {
ModuleDescriptor md = newBuilder("m")
.requires("java.base")
.exports("p", Set.of(mn))
.build();
ByteBuffer bb = toBuffer(md);
ModuleDescriptor descriptor = ModuleDescriptor.read(bb);
Optional<Exports> export = descriptor.exports().stream().findAny();
assertTrue(export.isPresent());
assertTrue(export.get().targets().contains(expected));
}
@Test(dataProvider = "illegalModuleNames",
expectedExceptions = InvalidModuleDescriptorException.class)
public void testIllegalExports(String mn, String ignore) throws Exception {
ModuleDescriptor md = newBuilder("m")
.requires("java.base")
.exports("p", Set.of(mn))
.build();
ByteBuffer bb = toBuffer(md);
ModuleDescriptor.read(bb); // throws InvalidModuleDescriptorException
}
@Test(dataProvider = "legalModuleNames")
public void testLegalOpens(String mn, String expected) throws Exception {
ModuleDescriptor md = newBuilder("m")
.requires("java.base")
.opens("p", Set.of(mn))
.build();
ByteBuffer bb = toBuffer(md);
ModuleDescriptor descriptor = ModuleDescriptor.read(bb);
Optional<Opens> opens = descriptor.opens().stream().findAny();
assertTrue(opens.isPresent());
assertTrue(opens.get().targets().contains(expected));
}
@Test(dataProvider = "illegalModuleNames",
expectedExceptions = InvalidModuleDescriptorException.class)
public void testIllegalOpens(String mn, String ignore) throws Exception {
ModuleDescriptor md = newBuilder("m")
.requires("java.base")
.opens("p", Set.of(mn))
.build();
ByteBuffer bb = toBuffer(md);
ModuleDescriptor.read(bb); // throws InvalidModuleDescriptorException
}
/**
* Returns a Builder that does not validate module names.
*/
private Builder newBuilder(String mn) {
return SharedSecrets.getJavaLangModuleAccess()
.newModuleBuilder(mn, false, false, false);
}
/**
* Returns a {@code ByteBuffer} containing the given module descriptor
* in module-info.class format.
*/
private ByteBuffer toBuffer(ModuleDescriptor descriptor) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ModuleInfoWriter.write(descriptor, baos);
return ByteBuffer.wrap(baos.toByteArray());
}
/**
* Returns a string containing a given code point.
*/
private String makeString(String prefix, int codePoint, String suffix) {
StringBuilder sb = new StringBuilder();
sb.append(prefix);
sb.appendCodePoint(codePoint);
sb.append(suffix);
return sb.toString();
}
}

View File

@ -24,7 +24,7 @@
/**
* @test
* @library /lib/testlibrary
* @modules java.base/jdk.internal.misc
* @modules java.base/jdk.internal.module
* jdk.compiler
* @build ModuleReaderTest CompilerUtils JarUtils
* @run testng ModuleReaderTest
@ -53,7 +53,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.spi.ToolProvider;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModulePath;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
@ -216,9 +216,7 @@ public class ModuleReaderTest {
*/
void test(Path mp) throws IOException {
ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
.newModulePath(Runtime.version(), true, mp);
ModuleFinder finder = new ModulePath(Runtime.version(), true, mp);
ModuleReference mref = finder.find(TEST_MODULE).get();
ModuleReader reader = mref.open();

View File

@ -31,7 +31,6 @@ import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.net.URI;
import java.util.function.Supplier;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@ -39,8 +38,13 @@ import static org.testng.Assert.*;
@Test
public class ModuleReferenceTest {
private Supplier<ModuleReader> makeSupplier() {
return () -> { throw new UnsupportedOperationException(); };
private ModuleReference newModuleReference(ModuleDescriptor descriptor, URI uri) {
return new ModuleReference(descriptor, uri) {
@Override
public ModuleReader open() {
throw new UnsupportedOperationException();
}
};
}
public void testBasic() throws Exception {
@ -53,25 +57,16 @@ public class ModuleReferenceTest {
URI uri = URI.create("module:/m");
Supplier<ModuleReader> supplier = makeSupplier();
ModuleReference mref = new ModuleReference(descriptor, uri, supplier);
ModuleReference mref = newModuleReference(descriptor, uri);
assertTrue(mref.descriptor().equals(descriptor));
assertTrue(mref.location().get().equals(uri));
// check that the supplier is called
try {
mref.open();
assertTrue(false);
} catch (UnsupportedOperationException expected) { }
}
@Test(expectedExceptions = { NullPointerException.class })
public void testNullDescriptor() throws Exception {
URI location = URI.create("module:/m");
new ModuleReference(null, location, makeSupplier());
newModuleReference(null, location);
}
public void testNullLocation() {
@ -79,55 +74,8 @@ public class ModuleReferenceTest {
= ModuleDescriptor.module("m")
.exports("p")
.build();
Supplier<ModuleReader> supplier = makeSupplier();
ModuleReference mref = new ModuleReference(descriptor, null, supplier);
ModuleReference mref = newModuleReference(descriptor, null);
assertTrue(!mref.location().isPresent());
}
@Test(expectedExceptions = { NullPointerException.class })
public void testNullSupplier() throws Exception {
ModuleDescriptor descriptor = ModuleDescriptor.module("m").build();
URI location = URI.create("module:/m");
new ModuleReference(descriptor, location, null);
}
public void testEqualsAndHashCode() {
ModuleDescriptor descriptor1
= ModuleDescriptor.module("m1")
.exports("p")
.build();
ModuleDescriptor descriptor2
= ModuleDescriptor.module("m1")
.exports("p")
.build();
URI uri = URI.create("module:/m1");
Supplier<ModuleReader> supplier = makeSupplier();
ModuleReference mref1 = new ModuleReference(descriptor1, uri, supplier);
ModuleReference mref2 = new ModuleReference(descriptor2, uri, supplier);
ModuleReference mref3 = new ModuleReference(descriptor1, null, supplier);
assertTrue(mref1.equals(mref1));
assertTrue(mref1.equals(mref2));
assertTrue(mref2.equals(mref1));
assertTrue(mref1.hashCode() == mref2.hashCode());
assertTrue(mref3.equals(mref3));
assertFalse(mref3.equals(mref1));
assertFalse(mref1.equals(mref3));
}
public void testToString() {
ModuleDescriptor descriptor = ModuleDescriptor.module("m1").build();
URI uri = URI.create("module:/m1");
Supplier<ModuleReader> supplier = makeSupplier();
ModuleReference mref = new ModuleReference(descriptor, uri, supplier);
String s = mref.toString();
assertTrue(s.contains("m1"));
assertTrue(s.contains(uri.toString()));
}
}

View File

@ -119,7 +119,6 @@ public class AnnotationsTest {
List<Attribute> attrs = new ArrayList<>();
attrs.add(new ClassFileAttributes.ModuleAttribute());
attrs.add(new ClassFileAttributes.ModulePackagesAttribute());
attrs.add(new ClassFileAttributes.ModuleVersionAttribute());
attrs.add(new ClassFileAttributes.ModuleTargetAttribute());
cr.accept(cv, attrs.toArray(new Attribute[0]), 0);

View File

@ -0,0 +1,158 @@
/*
* 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.
*/
/*
* @test
* @bug 8170859
* @summary Ensure no incubator modules are resolved by default in the image
* @library /lib/testlibrary
* @modules jdk.compiler
* @build CompilerUtils
* @run testng DefaultImage
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.nio.charset.StandardCharsets.UTF_8;
import static jdk.testlibrary.ProcessTools.executeCommand;
import static org.testng.Assert.*;
public class DefaultImage {
private static final String JAVA_HOME = System.getProperty("java.home");
private static final Path TEST_SRC = Paths.get(System.getProperty("test.src"));
private static final Path CP_DIR = Paths.get("cp");
@BeforeTest
private void setup() throws Throwable {
Path src = TEST_SRC.resolve("src").resolve("cp").resolve("listmods");
assertTrue(CompilerUtils.compile(src, CP_DIR));
}
public void test() throws Throwable {
if (isExplodedBuild()) {
System.out.println("Test cannot run on exploded build");
return;
}
java("-cp", CP_DIR.toString(),
"listmods.ListModules")
.assertSuccess()
.resultChecker(r -> r.assertOutputContains("java.base"))
.resultChecker(r -> r.assertOutputDoesNotContain("jdk.incubator"));
}
@DataProvider(name = "tokens")
public Object[][] singleModuleValues() throws IOException {
return new Object[][]{ { "ALL-DEFAULT" }, { "ALL-SYSTEM"} };
}
@Test(dataProvider = "tokens")
public void testAddMods(String addModsToken) throws Throwable {
if (isExplodedBuild()) {
System.out.println("Test cannot run on exploded build");
return;
}
java("--add-modules", addModsToken,
"-cp", CP_DIR.toString(),
"listmods.ListModules")
.assertSuccess()
.resultChecker(r -> r.assertOutputContains("java.base"))
.resultChecker(r -> r.assertOutputDoesNotContain("jdk.incubator"));
}
static ToolResult java(String... opts) throws Throwable {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
String[] options = Stream.concat(Stream.of(getJava()), Stream.of(opts))
.toArray(String[]::new);
ProcessBuilder pb = new ProcessBuilder(options);
int exitValue = executeCommand(pb).outputTo(ps)
.errorTo(ps)
.getExitValue();
return new ToolResult(exitValue, new String(baos.toByteArray(), UTF_8));
}
static class ToolResult {
final int exitCode;
final String output;
ToolResult(int exitValue, String output) {
this.exitCode = exitValue;
this.output = output;
}
ToolResult assertSuccess() {
assertEquals(exitCode, 0,
"Expected exit code 0, got " + exitCode
+ ", with output[" + output + "]");
return this;
}
ToolResult resultChecker(Consumer<ToolResult> r) {
r.accept(this);
return this;
}
ToolResult assertOutputContains(String subString) {
assertTrue(output.contains(subString),
"Expected to find [" + subString + "], in output ["
+ output + "]" + "\n");
return this;
}
ToolResult assertOutputDoesNotContain(String subString) {
assertFalse(output.contains(subString),
"Expected to NOT find [" + subString + "], in output ["
+ output + "]" + "\n");
return this;
}
}
static String getJava() {
Path image = Paths.get(JAVA_HOME);
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
Path java = image.resolve("bin").resolve(isWindows ? "java.exe" : "java");
if (Files.notExists(java))
throw new RuntimeException(java + " not found");
return java.toAbsolutePath().toString();
}
static boolean isExplodedBuild() {
Path modulesPath = Paths.get(JAVA_HOME).resolve("lib").resolve("modules");
return Files.notExists(modulesPath);
}
}

View File

@ -0,0 +1,382 @@
/*
* 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.
*/
/*
* @test
* @bug 8170859
* @summary Basic test for incubator modules in jmods and images
* @library /lib/testlibrary
* @modules jdk.compiler jdk.jartool jdk.jlink
* @build CompilerUtils
* @run testng/othervm ImageModules
*/
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.spi.ToolProvider;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.testlibrary.FileUtils;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.nio.charset.StandardCharsets.UTF_8;
import static jdk.testlibrary.ProcessTools.executeCommand;
import static org.testng.Assert.*;
public class ImageModules {
private static final String JAVA_HOME = System.getProperty("java.home");
private static final Path JDK_JMODS = Paths.get(JAVA_HOME, "jmods");
private static final Path TEST_SRC = Paths.get(System.getProperty("test.src"));
private static final Path MODS_DIR = Paths.get("mods");
private static final Path CP_DIR = Paths.get("cp");
private static final Path JARS_DIR = Paths.get("jars");
private static final Path JMODS_DIR = Paths.get("jmods");
private static final Path IMAGE = Paths.get("image");
private static final String JAVA_BASE = "java.base";
private final String[] modules = new String[] { "message.writer",
"message.converter" };
@BeforeTest
private void setup() throws Throwable {
Path src = TEST_SRC.resolve("src");
for (String name : modules) {
assertTrue(CompilerUtils.compile(src.resolve(name),
MODS_DIR,
"--module-source-path", src.toString()));
}
assertTrue(CompilerUtils.compile(src.resolve("cp"),
CP_DIR,
"--module-path", MODS_DIR.toString(),
"--add-modules", "message.writer"));
}
@DataProvider(name = "singleModule")
public Object[][] singleModuleValues() throws IOException {
Object[][] values = new Object[][]{
// { Extra args to the build the message.converter jmod
// Tokens to pass to the run time --add-modules option
// SUCCESS or FAILURE expected
// Messages expected in the run time output
// Messages that must not appear in the run time output },
{ "",
List.of("ALL-DEFAULT", "ALL-SYSTEM"),
ToolResult.ASSERT_SUCCESS,
List.of("hello world", "message.converter", "java.base"),
List.of("WARNING") },
{ "--do-not-resolve-by-default",
List.of("ALL-DEFAULT", "ALL-SYSTEM"),
ToolResult.ASSERT_FAILURE,
List.of("java.base", "java.lang.ClassNotFoundException: converter.MessageConverter"),
List.of("WARNING", "message.converter") },
{ "--warn-if-resolved=incubating",
List.of("ALL-DEFAULT", "ALL-SYSTEM"),
ToolResult.ASSERT_SUCCESS,
List.of("hello world", "message.converter", "java.base",
"WARNING: Using incubator modules: message.converter"),
List.of() },
{ "--do-not-resolve-by-default --warn-if-resolved=incubating",
List.of("ALL-DEFAULT", "ALL-SYSTEM"),
ToolResult.ASSERT_FAILURE,
List.of("java.base", "java.lang.ClassNotFoundException: converter.MessageConverter"),
List.of("WARNING", "message.converter") },
{ "--do-not-resolve-by-default --warn-if-resolved=incubating",
List.of("message.converter"),
ToolResult.ASSERT_SUCCESS,
List.of("hello world", "message.converter", "java.base", "WARNING"),
List.of() }
};
return values;
}
@Test(dataProvider = "singleModule")
public void singleModule(String extraJmodArg,
List<String> addModsTokens,
Consumer<ToolResult> assertExitCode,
List<String> expectedOutput,
List<String> unexpectedOutput)
throws Throwable
{
if (Files.notExists(JDK_JMODS)) {
System.out.println("JDK jmods not found test cannot run.");
return;
}
FileUtils.deleteFileTreeUnchecked(JMODS_DIR);
FileUtils.deleteFileTreeUnchecked(IMAGE);
Files.createDirectories(JMODS_DIR);
Path converterJmod = JMODS_DIR.resolve("converter.jmod");
jmod("create",
"--class-path", MODS_DIR.resolve("message.converter").toString(),
extraJmodArg,
converterJmod.toString())
.assertSuccess();
String mpath = JDK_JMODS.toString() + File.pathSeparator + JMODS_DIR.toString();
jlink("--module-path", mpath,
"--add-modules", JAVA_BASE + ",message.converter",
"--output", IMAGE.toString())
.assertSuccess();
for (String addModsToken : addModsTokens) {
String[] props = new String[] {"", "-Djdk.system.module.finder.disabledFastPath"};
for (String systemProp : props)
java(IMAGE,
systemProp,
"--add-modules", addModsToken,
"-cp", CP_DIR.toString(),
"test.ConvertToLowerCase", "HEllo WoRlD")
.resultChecker(assertExitCode)
.resultChecker(r -> {
expectedOutput.forEach(e -> r.assertContains(e));
unexpectedOutput.forEach(e -> r.assertDoesNotContains(e));
});
}
}
@Test
public void singleModularJar() throws Throwable {
FileUtils.deleteFileTreeUnchecked(JARS_DIR);
Files.createDirectories(JARS_DIR);
Path converterJar = JARS_DIR.resolve("converter.jar");
jar("--create",
"--file", converterJar.toString(),
"--warn-if-resolved=incubating",
"-C", MODS_DIR.resolve("message.converter").toString() , ".")
.assertSuccess();
java(Paths.get(JAVA_HOME),
"--module-path", JARS_DIR.toString(),
"--add-modules", "message.converter",
"-cp", CP_DIR.toString(),
"test.ConvertToLowerCase", "HEllo WoRlD")
.assertSuccess()
.resultChecker(r -> {
r.assertContains("WARNING: Using incubator modules: message.converter");
});
}
@DataProvider(name = "twoModules")
public Object[][] twoModulesValues() throws IOException {
Object[][] values = new Object[][]{
// { Extra args to the build the message.writer jmod
// Extra args to the build the message.converter jmod
// Tokens to pass to the run time --add-modules option
// SUCCESS or FAILURE expected
// Messages expected in the run time output
// Messages that must not appear in the run time output },
{ "",
"",
List.of("ALL-DEFAULT", "ALL-SYSTEM"),
ToolResult.ASSERT_SUCCESS,
List.of("HELLO CHEGAR !!!", "message.writer", "message.converter", "java.base"),
List.of() },
{ "",
"--do-not-resolve-by-default",
List.of("ALL-DEFAULT", "ALL-SYSTEM"),
ToolResult.ASSERT_SUCCESS,
List.of("HELLO CHEGAR !!!", "message.writer", "message.converter", "java.base"),
List.of() },
{ "--do-not-resolve-by-default",
"",
List.of("ALL-DEFAULT", "ALL-SYSTEM"),
ToolResult.ASSERT_FAILURE,
List.of("java.lang.ClassNotFoundException: writer.MessageWriter", "java.base"),
List.of("message.writer") },
{ "--do-not-resolve-by-default",
"--do-not-resolve-by-default",
List.of("ALL-DEFAULT", "ALL-SYSTEM"),
ToolResult.ASSERT_FAILURE,
List.of("java.lang.ClassNotFoundException: writer.MessageWriter", "java.base"),
List.of("message.converter", "message.writer") },
// now add in warnings
{ "--do-not-resolve-by-default --warn-if-resolved=incubating",
"",
List.of("message.writer"),
ToolResult.ASSERT_SUCCESS,
List.of("HELLO CHEGAR !!!", "message.writer", "message.converter", "java.base",
"WARNING: Using incubator modules: message.writer"),
List.of() },
{ "",
"--do-not-resolve-by-default --warn-if-resolved=incubating",
List.of("message.writer"),
ToolResult.ASSERT_SUCCESS,
List.of("HELLO CHEGAR !!!", "message.writer", "message.converter", "java.base",
"WARNING: Using incubator modules: message.converter"),
List.of() } };
return values;
}
@Test(dataProvider = "twoModules")
public void doNotResolveByDefaultTwoModules(String extraFirstJmodArg,
String extraSecondJmodArg,
List<String> addModsTokens,
Consumer<ToolResult> assertExitCode,
List<String> expectedOutput,
List<String> unexpectedOutput)
throws Throwable
{
if (Files.notExists(JDK_JMODS)) {
System.out.println("JDK jmods not found test cannot run.");
return;
}
FileUtils.deleteFileTreeUnchecked(JMODS_DIR);
FileUtils.deleteFileTreeUnchecked(IMAGE);
Files.createDirectories(JMODS_DIR);
Path writerJmod = JMODS_DIR.resolve("writer.jmod");
Path converterJmod = JMODS_DIR.resolve("converter.jmod");
jmod("create",
extraFirstJmodArg,
"--class-path", MODS_DIR.resolve("message.writer").toString(),
writerJmod.toString());
jmod("create",
"--class-path", MODS_DIR.resolve("message.converter").toString(),
extraSecondJmodArg,
converterJmod.toString())
.assertSuccess();
String mpath = JDK_JMODS.toString() + File.pathSeparator + JMODS_DIR.toString();
jlink("--module-path", mpath,
"--add-modules", JAVA_BASE + ",message.writer,message.converter",
"--output", IMAGE.toString())
.assertSuccess();
for (String addModsToken : addModsTokens) {
String[] props = new String[] {"", "-Djdk.system.module.finder.disabledFastPath"};
for (String systemProp : props)
java(IMAGE,
systemProp,
"--add-modules", addModsToken,
"-cp", CP_DIR.toString(),
"test.WriteUpperCase", "hello chegar !!!")
.resultChecker(assertExitCode)
.resultChecker(r -> {
expectedOutput.forEach(e -> r.assertContains(e));
unexpectedOutput.forEach(e -> r.assertDoesNotContains(e));
});
}
}
static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
.orElseThrow(() -> new RuntimeException("jmod tool not found"));
static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
.orElseThrow(() -> new RuntimeException("jar tool not found"));
static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
.orElseThrow(() -> new RuntimeException("jlink tool not found"));
static ToolResult jmod(String... args) { return execTool(JMOD_TOOL, args); }
static ToolResult jar(String... args) { return execTool(JAR_TOOL, args); }
static ToolResult jlink(String... args) { return execTool(JLINK_TOOL, args); }
static ToolResult java(Path image, String... opts) throws Throwable {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
String[] options = Stream.concat(Stream.of(getJava(image)),
Stream.of(opts).filter(s -> !s.equals("")))
.toArray(String[]::new);
ProcessBuilder pb = new ProcessBuilder(options);
int exitValue = executeCommand(pb).outputTo(ps)
.errorTo(ps)
.getExitValue();
return new ToolResult(exitValue, new String(baos.toByteArray(), UTF_8));
}
static ToolResult execTool(ToolProvider tool, String... args) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
List<String> filteredArgs = Stream.of(args)
.map(s -> s.split(" ")).flatMap(Stream::of)
.filter(s -> !s.equals(""))
.collect(Collectors.toList());
System.out.println(tool + " " + filteredArgs);
int ec = tool.run(ps, ps, filteredArgs.toArray(new String[] {}));
return new ToolResult(ec, new String(baos.toByteArray(), UTF_8));
}
static class ToolResult {
final int exitCode;
final String output;
ToolResult(int exitValue, String output) {
this.exitCode = exitValue;
this.output = output;
}
static Consumer<ToolResult> ASSERT_SUCCESS = r ->
assertEquals(r.exitCode, 0,
"Expected exit code 0, got " + r.exitCode
+ ", with output[" + r.output + "]");
static Consumer<ToolResult> ASSERT_FAILURE = r ->
assertNotEquals(r.exitCode, 0,
"Expected exit code != 0, got " + r.exitCode
+ ", with output[" + r.output + "]");
ToolResult assertSuccess() { ASSERT_SUCCESS.accept(this); return this; }
ToolResult assertFailure() { ASSERT_FAILURE.accept(this); return this; }
ToolResult resultChecker(Consumer<ToolResult> r) { r.accept(this); return this; }
ToolResult assertContains(String subString) {
assertTrue(output.contains(subString),
"Expected to find [" + subString + "], in output ["
+ output + "]" + "\n");
return this;
}
ToolResult assertDoesNotContains(String subString) {
assertFalse(output.contains(subString),
"Expected to NOT find [" + subString + "], in output ["
+ output + "]" + "\n");
return this;
}
}
static String getJava(Path image) {
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
Path java = image.resolve("bin").resolve(isWindows ? "java.exe" : "java");
if (Files.notExists(java))
throw new RuntimeException(java + " not found");
return java.toAbsolutePath().toString();
}
}

View File

@ -0,0 +1,39 @@
/*
* 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 listmods;
import java.lang.reflect.Layer;
import java.lang.reflect.Module;
import static java.util.stream.Collectors.joining;
public class ListModules {
public static void main(String[] args) throws Exception {
System.out.println(Layer.boot()
.modules()
.stream()
.map(Module::getName)
.sorted()
.collect(joining("\n")));
}
}

View File

@ -0,0 +1,40 @@
/*
* 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 test;
import java.lang.reflect.Layer;
import java.lang.reflect.Module;
import static java.util.stream.Collectors.joining;
public class ConvertToLowerCase {
public static void main(String[] args) {
System.out.println(Layer.boot()
.modules()
.stream()
.map(Module::getName)
.sorted()
.collect(joining("\n")));
System.out.println(converter.MessageConverter.toLowerCase(args[0]));
}
}

View File

@ -0,0 +1,40 @@
/*
* 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 test;
import java.lang.reflect.Layer;
import java.lang.reflect.Module;
import static java.util.stream.Collectors.joining;
public class WriteUpperCase {
public static void main(String[] args) {
System.out.println(Layer.boot()
.modules()
.stream()
.map(Module::getName)
.sorted()
.collect(joining("\n")));
writer.MessageWriter.writeOn(args[0], System.out);
}
}

View File

@ -0,0 +1,34 @@
/*
* 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 converter;
public class MessageConverter {
public static String toUpperCase(String message) {
return message.toUpperCase(java.util.Locale.ROOT);
}
public static String toLowerCase(String message) {
return message.toLowerCase(java.util.Locale.ROOT);
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.
*/
module message.converter {
exports converter;
}

View File

@ -0,0 +1,27 @@
/*
* 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.
*/
module message.writer {
requires message.converter;
exports writer;
}

View File

@ -0,0 +1,38 @@
/*
* 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 writer;
import java.io.PrintStream;
import java.util.Locale;
public class MessageWriter {
public static void writeOn(String message, PrintStream out) {
String newMesssage = converter.MessageConverter.toUpperCase(message);
if (!newMesssage.equals(message.toUpperCase(Locale.ROOT)))
throw new RuntimeException("Expected " + message.toUpperCase(Locale.ROOT)
+ ", got " + newMesssage );
out.println(newMesssage);
}
}

View File

@ -32,7 +32,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
/**
@ -58,12 +57,13 @@ public final class ModuleUtils {
URI uri = URI.create("module:/" + name);
Supplier<ModuleReader> supplier = () -> {
throw new UnsupportedOperationException();
ModuleReference mref = new ModuleReference(descriptor, uri) {
@Override
public ModuleReader open() {
throw new UnsupportedOperationException();
}
};
ModuleReference mref = new ModuleReference(descriptor, uri, supplier);
namesToReference.put(name, mref);
}

View File

@ -43,6 +43,7 @@ import org.testng.annotations.Test;
import static java.lang.String.format;
import static java.lang.System.out;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
/*
@ -428,10 +429,12 @@ public class CLICompatibility {
jar("--help")
.assertSuccess()
.resultChecker(r ->
.resultChecker(r -> {
assertTrue(r.output.startsWith("Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files]"),
"Failed, got [" + r.output + "]")
);
"Failed, got [" + r.output + "]");
assertFalse(r.output.contains("--do-not-resolve-by-default"));
assertFalse(r.output.contains("--warn-if-resolved"));
});
jar("--help:compat")
.assertSuccess()
@ -439,6 +442,15 @@ public class CLICompatibility {
assertTrue(r.output.startsWith("Compatibility Interface:"),
"Failed, got [" + r.output + "]")
);
jar("--help-extra")
.assertSuccess()
.resultChecker(r -> {
assertTrue(r.output.startsWith("Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files]"),
"Failed, got [" + r.output + "]");
assertTrue(r.output.contains("--do-not-resolve-by-default"));
assertTrue(r.output.contains("--warn-if-resolved"));
});
}
// -- Infrastructure

View File

@ -25,16 +25,18 @@ package jdk.test.bar;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.lang.reflect.Module;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.HashSet;
import java.util.Set;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleReferenceImpl;
import jdk.test.bar.internal.Message;
public class Bar {
@ -71,9 +73,14 @@ public class Bar {
if (!sj.toString().equals(""))
System.out.println("contains:" + sj.toString());
ModuleDescriptor foo = jdk.test.foo.Foo.class.getModule().getDescriptor();
JavaLangModuleAccess jlma = SharedSecrets.getJavaLangModuleAccess();
Optional<ModuleHashes> oHashes = jlma.hashes(foo);
System.out.println("hashes:" + oHashes.get().hashFor("bar"));
Module foo = jdk.test.foo.Foo.class.getModule();
Optional<ResolvedModule> om = foo.getLayer().configuration().findModule(foo.getName());
assert om.isPresent();
ModuleReference mref = om.get().reference();
assert mref instanceof ModuleReferenceImpl;
ModuleHashes hashes = ((ModuleReferenceImpl) mref).recordedHashes();
assert hashes != null;
System.out.println("hashes:" + hashes.hashFor("bar"));
}
}

View File

@ -49,7 +49,7 @@ import jdk.internal.jimage.ImageLocation;
* @test
* @summary Verify jimage
* @modules java.base/jdk.internal.jimage
* @run main/othervm --add-modules=ALL-SYSTEM VerifyJimage
* @run main/othervm --add-modules=ALL-SYSTEM,jdk.incubator.httpclient VerifyJimage
*/
/**

View File

@ -0,0 +1,129 @@
/*
* 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.
*/
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.testlibrary.FileUtils;
import static jdk.testlibrary.ProcessTools.*;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* @test
* @library /lib/testlibrary
* @modules jdk.compiler jdk.jlink
* @build CompiledVersionTest CompilerUtils jdk.testlibrary.FileUtils jdk.testlibrary.ProcessTools
* @run testng CompiledVersionTest
*/
public class CompiledVersionTest {
private static final String JAVA_HOME = System.getProperty("java.home");
private static final String TEST_SRC = System.getProperty("test.src");
private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
private static final Path MODS_DIR = Paths.get("mods");
private static final Path IMAGE = Paths.get("image");
private static final Path JMODS = Paths.get(JAVA_HOME, "jmods");
private static final String MAIN_MID = "test/jdk.test.Main";
// the names of the modules in this test
private static String[] modules = new String[] { "m1", "m2", "test"};
private static String[] versions = new String[] { "1.0", "2-ea", "3-internal"};
private static boolean hasJmods() {
if (!Files.exists(JMODS)) {
System.err.println("Test skipped. NO jmods directory");
return false;
}
return true;
}
/*
* Compiles all modules used by the test
*/
@BeforeTest
public void compileAll() throws Throwable {
if (!hasJmods()) return;
for (int i=0; i < modules.length; i++) {
String mn = modules[i];
String version = versions[i];
Path msrc = SRC_DIR.resolve(mn);
if (version.equals("0")) {
assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
"--module-source-path", SRC_DIR.toString()));
} else {
assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
"--module-source-path", SRC_DIR.toString(),
"--module-version", version));
}
}
if (Files.exists(IMAGE)) {
FileUtils.deleteFileTreeUnchecked(IMAGE);
}
createImage(IMAGE, modules);
}
private void createImage(Path outputDir, String... modules) throws Throwable {
Path jlink = Paths.get(JAVA_HOME, "bin", "jlink");
String mp = JMODS.toString() + File.pathSeparator + MODS_DIR.toString();
assertTrue(executeProcess(jlink.toString(), "--output", outputDir.toString(),
"--add-modules", Arrays.stream(modules).collect(Collectors.joining(",")),
"--module-path", mp)
.outputTo(System.out)
.errorTo(System.out)
.getExitValue() == 0);
}
/*
* Test the image created when linking with a module with
* no Packages attribute
*/
@Test
public void testCompiledVersions() throws Throwable {
if (!hasJmods()) return;
Path java = IMAGE.resolve("bin").resolve("java");
Stream<String> options = Stream.concat(
Stream.of(java.toString(), "-m", MAIN_MID, String.valueOf(modules.length)),
Stream.concat(Arrays.stream(modules), Arrays.stream(versions))
);
assertTrue(executeProcess(options.toArray(String[]::new))
.outputTo(System.out)
.errorTo(System.out)
.getExitValue() == 0);
}
}

View File

@ -59,7 +59,7 @@ public class SystemModulesTest {
private void testModuleDescriptor(ModuleDescriptor md) {
assertUnmodifiable(md.packages(), "package");
assertUnmodifiable(md.requires(),
jlma.newRequires(Set.of(Requires.Modifier.TRANSITIVE), "require"));
jlma.newRequires(Set.of(Requires.Modifier.TRANSITIVE), "require", null));
for (Requires req : md.requires()) {
assertUnmodifiable(req.modifiers(), Requires.Modifier.TRANSITIVE);
}

View File

@ -39,7 +39,7 @@ import static org.testng.Assert.*;
/**
* @test
* @library /lib/testlibrary
* @modules jdk.compiler
* @modules jdk.compiler jdk.jlink
* @build UserModuleTest CompilerUtils jdk.testlibrary.FileUtils jdk.testlibrary.ProcessTools
* @run testng UserModuleTest
*/

View File

@ -0,0 +1,104 @@
/*
* 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.test;
import java.lang.module.ModuleDescriptor;
import java.lang.reflect.Layer;
import java.lang.reflect.Module;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/*
* Main class to verify if ModuleDescriptor carries the correct version
*/
public class Main {
static final Map<String, String> nameToVersion = new HashMap<>();
// jdk.test.Main $count $module-name... $version...
public static void main(String... args) throws Exception {
int count = args.length > 0 ? Integer.valueOf(args[0]) : 0;
if (count < 1 || args.length != count*2+1) {
throw new IllegalArgumentException(Arrays.toString(args));
}
List<String> modules = List.of(Arrays.copyOfRange(args, 1, 1+count));
List<String> versions = List.of(Arrays.copyOfRange(args, 1+count, args.length));
for (int i=0; i < modules.size(); i++) {
System.out.format("module %s expects %s version%n",
modules.get(i), versions.get(i));
nameToVersion.put(modules.get(i), versions.get(i));
}
FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"),
Collections.emptyMap());
// check the module descriptor of a system module
for (int i=0; i < modules.size(); i++) {
String mn = modules.get(i);
Module module = Layer.boot().findModule(mn).orElseThrow(
() -> new RuntimeException(mn + " not found")
);
// check ModuleDescriptor from the run-time
validate(module.getDescriptor());
// check module-info.class in the image
Path path = fs.getPath("/", "modules", modules.get(i), "module-info.class");
validate(ModuleDescriptor.read(Files.newInputStream(path)));
}
}
static void validate(ModuleDescriptor descriptor) {
checkVersion(descriptor.name(), descriptor.version());
descriptor.requires()
.stream()
.filter(r -> !r.name().equals("java.base"))
.forEach(r -> checkVersion(r.name(), r.compiledVersion()));
}
static void checkVersion(String mn, Optional<ModuleDescriptor.Version> version) {
boolean matched;
String v = nameToVersion.get(mn);
if (version.isPresent()) {
matched = version.get().toString().equals(v);
} else {
// 0 indicate no version
matched = v.equals("0");
}
if (!matched) {
throw new RuntimeException(mn + " mismatched version " + version
+ " expected: " + v);
}
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.
*/
module test {
requires m1;
requires m2;
exports jdk.test;
}

View File

@ -470,9 +470,22 @@ public class JmodTest {
public void testHelp() {
jmod("--help")
.assertSuccess()
.resultChecker(r ->
assertTrue(r.output.startsWith("Usage: jmod"), "Help not printed")
);
.resultChecker(r -> {
assertTrue(r.output.startsWith("Usage: jmod"), "Help not printed");
assertFalse(r.output.contains("--do-not-resolve-by-default"));
assertFalse(r.output.contains("--warn-if-resolved"));
});
}
@Test
public void testHelpExtra() {
jmod("--help-extra")
.assertSuccess()
.resultChecker(r -> {
assertTrue(r.output.startsWith("Usage: jmod"), "Extra help not printed");
assertContains(r.output, "--do-not-resolve-by-default");
assertContains(r.output, "--warn-if-resolved");
});
}
@Test

View File

@ -50,14 +50,13 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.spi.ToolProvider;
import java.util.stream.Collectors;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.module.ModuleInfo;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModulePath;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
@ -103,54 +102,53 @@ public class HashesTest {
@Test
public void test() throws Exception {
for (String mn : modules) {
assertFalse(hashes(mn).isPresent());
assertTrue(hashes(mn) == null);
}
// hash m1 in m2
jmod("m2", "--module-path", jmods.toString(), "--hash-modules", "m1");
checkHashes(hashes("m2").get(), "m1");
checkHashes(hashes("m2"), "m1");
// hash m1 in m2
jmod("m2", "--module-path", jmods.toString(), "--hash-modules", ".*");
checkHashes(hashes("m2").get(), "m1");
checkHashes(hashes("m2"), "m1");
// create m2.jmod with no hash
jmod("m2");
// run jmod hash command to hash m1 in m2 and m3
runJmod(Arrays.asList("hash", "--module-path", jmods.toString(),
"--hash-modules", ".*"));
checkHashes(hashes("m2").get(), "m1");
checkHashes(hashes("m3").get(), "m1");
checkHashes(hashes("m2"), "m1");
checkHashes(hashes("m3"), "m1");
jmod("org.bar");
jmod("org.foo");
jmod("org.bar", "--module-path", jmods.toString(), "--hash-modules", "org.*");
checkHashes(hashes("org.bar").get(), "org.foo");
checkHashes(hashes("org.bar"), "org.foo");
jmod("m3", "--module-path", jmods.toString(), "--hash-modules", ".*");
checkHashes(hashes("m3").get(), "org.foo", "org.bar", "m1");
checkHashes(hashes("m3"), "org.foo", "org.bar", "m1");
}
private void checkHashes(ModuleHashes hashes, String... hashModules) {
assertTrue(hashes.names().equals(Set.of(hashModules)));
}
private Optional<ModuleHashes> hashes(String name) throws Exception {
ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
.newModulePath(Runtime.version(), true, jmods.resolve(name + ".jmod"));
private ModuleHashes hashes(String name) throws Exception {
ModuleFinder finder = new ModulePath(Runtime.version(),
true,
jmods.resolve(name + ".jmod"));
ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new);
ModuleReader reader = mref.open();
try (InputStream in = reader.open("module-info.class").get()) {
ModuleDescriptor md = ModuleDescriptor.read(in);
JavaLangModuleAccess jmla = SharedSecrets.getJavaLangModuleAccess();
Optional<ModuleHashes> hashes = jmla.hashes(md);
ModuleHashes hashes = ModuleInfo.read(in, null).recordedHashes();
System.out.format("hashes in module %s %s%n", name,
hashes.isPresent() ? "present" : "absent");
if (hashes.isPresent()) {
hashes.get().names().stream()
(hashes != null) ? "present" : "absent");
if (hashes != null) {
hashes.names().stream()
.sorted()
.forEach(n -> System.out.format(" %s %s%n", n, hashes.get().hashFor(n)));
.forEach(n -> System.out.format(" %s %s%n", n, hashes.hashFor(n)));
}
return hashes;
} finally {

View File

@ -41,6 +41,8 @@ import com.sun.tools.classfile.Code_attribute;
import com.sun.tools.classfile.CompilationID_attribute;
import com.sun.tools.classfile.ConstantPool;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Module_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Package_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Double_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Fieldref_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Float_info;
@ -80,9 +82,9 @@ import com.sun.tools.classfile.Module_attribute.RequiresEntry;
import com.sun.tools.classfile.ModuleHashes_attribute;
import com.sun.tools.classfile.ModuleHashes_attribute.Entry;
import com.sun.tools.classfile.ModuleMainClass_attribute;
import com.sun.tools.classfile.ModuleResolution_attribute;
import com.sun.tools.classfile.ModuleTarget_attribute;
import com.sun.tools.classfile.ModulePackages_attribute;
import com.sun.tools.classfile.ModuleVersion_attribute;
import com.sun.tools.classfile.Opcode;
import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
@ -669,6 +671,40 @@ class ConstantPoolVisitor implements ConstantPool.Visitor<String, Integer> {
return value;
}
@Override
public String visitModule(CONSTANT_Module_info info, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(info.name_index), info.name_index);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Module",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitPackage(CONSTANT_Package_info info, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(info.name_index), info.name_index);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Package",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitDouble(CONSTANT_Double_info c, Integer p) {
String value = slist.get(p);
@ -1495,20 +1531,20 @@ class AttributeVisitor implements Attribute.Visitor<Element, Element> {
}
@Override
public Element visitModuleTarget(ModuleTarget_attribute attr, Element p) {
Element e = new Element(x.getCpString(attr.attribute_name_index));
e.add(x.getCpString(attr.os_name_index));
e.add(x.getCpString(attr.os_arch_index));
e.add(x.getCpString(attr.os_version_index));
public Element visitModuleResolution(ModuleResolution_attribute attr, Element p) {
Element e = new Element("ModuleResolution");
e.setAttr("flags", Integer.toString(attr.resolution_flags));
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitModuleVersion(ModuleVersion_attribute attr, Element p) {
public Element visitModuleTarget(ModuleTarget_attribute attr, Element p) {
Element e = new Element(x.getCpString(attr.attribute_name_index));
e.add(x.getCpString(attr.version_index));
e.add(x.getCpString(attr.os_name_index));
e.add(x.getCpString(attr.os_arch_index));
e.add(x.getCpString(attr.os_version_index));
e.trimToSize();
p.add(e);
return null;