undefect. CWE-407 — 92 sites, 42 ecosystems
B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
This commit is contained in:
parent
0a580b313d
commit
db29a08762
1311 changed files with 371202 additions and 1188 deletions
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2025, 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;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.ElementType.TYPE_PARAMETER;
|
||||
|
||||
/**
|
||||
* Indicates that the annotated parameter or type parameter is not expected to be a
|
||||
* Value Based class.
|
||||
* Using a parameter or type parameter of a <a href="../lang/doc-files/ValueBased.html">value-based classes</a>
|
||||
* should produce warnings about behavior that is inconsistent with identity based semantics.
|
||||
*
|
||||
* Note this internal annotation is handled specially by the javac compiler.
|
||||
* To work properly with {@code --release older-release}, it requires special
|
||||
* handling in {@code make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java}
|
||||
* and {@code src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java}.
|
||||
*
|
||||
* @since 25
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value={PARAMETER, TYPE_PARAMETER})
|
||||
public @interface RequiresIdentity {
|
||||
}
|
||||
48
src/java.base/share/classes/jdk/internal/ValueBased.java
Normal file
48
src/java.base/share/classes/jdk/internal/ValueBased.java
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2025, 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;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
|
||||
/**
|
||||
* Indicates the API declaration in question is associated with a Value Based class.
|
||||
* References to <a href="../lang/doc-files/ValueBased.html">value-based classes</a>
|
||||
* should produce warnings about behavior that is inconsistent with value based semantics.
|
||||
*
|
||||
* Note this internal annotation is handled specially by the javac compiler.
|
||||
* To work properly with {@code --release older-release}, it requires special
|
||||
* handling in {@code make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java}
|
||||
* and {@code src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java}.
|
||||
*
|
||||
* @since 16
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value={TYPE})
|
||||
public @interface ValueBased {
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2025, 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 jdk.internal.misc.CDS;
|
||||
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
|
||||
|
||||
/**
|
||||
* Used by ModuleBootstrap for archiving the boot layer.
|
||||
*/
|
||||
@AOTSafeClassInitializer
|
||||
class ArchivedBootLayer {
|
||||
private static ArchivedBootLayer archivedBootLayer;
|
||||
|
||||
private final ModuleLayer bootLayer;
|
||||
|
||||
private ArchivedBootLayer(ModuleLayer bootLayer) {
|
||||
this.bootLayer = bootLayer;
|
||||
}
|
||||
|
||||
ModuleLayer bootLayer() {
|
||||
return bootLayer;
|
||||
}
|
||||
|
||||
static ArchivedBootLayer get() {
|
||||
return archivedBootLayer;
|
||||
}
|
||||
|
||||
static void archive(ModuleLayer layer) {
|
||||
archivedBootLayer = new ArchivedBootLayer(layer);
|
||||
}
|
||||
|
||||
static {
|
||||
CDS.initializeFromArchive(ArchivedBootLayer.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2025, 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.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import jdk.internal.misc.CDS;
|
||||
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
|
||||
|
||||
/**
|
||||
* Used by ModuleBootstrap for archiving the configuration for the boot layer,
|
||||
* and the system module finder.
|
||||
*/
|
||||
@AOTSafeClassInitializer
|
||||
class ArchivedModuleGraph {
|
||||
private static ArchivedModuleGraph archivedModuleGraph;
|
||||
|
||||
private final boolean hasSplitPackages;
|
||||
private final boolean hasIncubatorModules;
|
||||
private final ModuleFinder finder;
|
||||
private final Configuration configuration;
|
||||
private final Function<String, ClassLoader> classLoaderFunction;
|
||||
private final String mainModule;
|
||||
private final Set<String> addModules;
|
||||
|
||||
private ArchivedModuleGraph(boolean hasSplitPackages,
|
||||
boolean hasIncubatorModules,
|
||||
ModuleFinder finder,
|
||||
Configuration configuration,
|
||||
Function<String, ClassLoader> classLoaderFunction,
|
||||
String mainModule,
|
||||
Set<String> addModules) {
|
||||
this.hasSplitPackages = hasSplitPackages;
|
||||
this.hasIncubatorModules = hasIncubatorModules;
|
||||
this.finder = finder;
|
||||
this.configuration = configuration;
|
||||
this.classLoaderFunction = classLoaderFunction;
|
||||
this.mainModule = mainModule;
|
||||
this.addModules = addModules;
|
||||
}
|
||||
|
||||
ModuleFinder finder() {
|
||||
return finder;
|
||||
}
|
||||
|
||||
Configuration configuration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
Function<String, ClassLoader> classLoaderFunction() {
|
||||
return classLoaderFunction;
|
||||
}
|
||||
|
||||
boolean hasSplitPackages() {
|
||||
return hasSplitPackages;
|
||||
}
|
||||
|
||||
boolean hasIncubatorModules() {
|
||||
return hasIncubatorModules;
|
||||
}
|
||||
|
||||
static boolean sameAddModules(Set<String> addModules) {
|
||||
if (archivedModuleGraph.addModules == null || addModules == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (archivedModuleGraph.addModules.size() != addModules.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return archivedModuleGraph.addModules.containsAll(addModules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ArchivedModuleGraph for the given initial module.
|
||||
*/
|
||||
static ArchivedModuleGraph get(String mainModule, Set<String> addModules) {
|
||||
ArchivedModuleGraph graph = archivedModuleGraph;
|
||||
if ((graph != null) && Objects.equals(graph.mainModule, mainModule) && sameAddModules(addModules)) {
|
||||
return graph;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive the module graph for the given initial module.
|
||||
*/
|
||||
static void archive(boolean hasSplitPackages,
|
||||
boolean hasIncubatorModules,
|
||||
ModuleFinder finder,
|
||||
Configuration configuration,
|
||||
Function<String, ClassLoader> classLoaderFunction,
|
||||
String mainModule,
|
||||
Set<String> addModules) {
|
||||
archivedModuleGraph = new ArchivedModuleGraph(hasSplitPackages,
|
||||
hasIncubatorModules,
|
||||
finder,
|
||||
configuration,
|
||||
classLoaderFunction,
|
||||
mainModule,
|
||||
addModules);
|
||||
}
|
||||
|
||||
static {
|
||||
// Legacy CDS archive support (to be deprecated)
|
||||
CDS.initializeFromArchive(ArchivedModuleGraph.class);
|
||||
}
|
||||
}
|
||||
282
src/java.base/share/classes/jdk/internal/module/Builder.java
Normal file
282
src/java.base/share/classes/jdk/internal/module/Builder.java
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Exports;
|
||||
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.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.access.JavaLangModuleAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
/**
|
||||
* This builder is optimized for reconstituting the {@code ModuleDescriptor}s
|
||||
* for system modules. The validation should be done at jlink time.
|
||||
*
|
||||
* 1. skip name validation
|
||||
* 2. ignores dependency hashes.
|
||||
* 3. ModuleDescriptor skips the defensive copy and directly uses the
|
||||
* sets/maps created in this Builder.
|
||||
*
|
||||
* SystemModules should contain modules for the boot layer.
|
||||
*/
|
||||
final class Builder {
|
||||
private static final JavaLangModuleAccess JLMA =
|
||||
SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
// Static cache of the most recently seen Version to cheaply deduplicate
|
||||
// most Version objects. JDK modules have the same version.
|
||||
static Version cachedVersion;
|
||||
|
||||
/**
|
||||
* 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 newRequires(mods, mn, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Exports} for a qualified export, with
|
||||
* the given (and possibly empty) set of modifiers,
|
||||
* to a set of target modules.
|
||||
*/
|
||||
public static Exports newExports(Set<Exports.Modifier> ms,
|
||||
String pn,
|
||||
Set<String> targets) {
|
||||
return JLMA.newExports(ms, pn, targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Opens} for an unqualified open with a given set of
|
||||
* modifiers.
|
||||
*/
|
||||
public static Opens newOpens(Set<Opens.Modifier> ms, String pn) {
|
||||
return JLMA.newOpens(ms, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Opens} for a qualified opens, with
|
||||
* the given (and possibly empty) set of modifiers,
|
||||
* to a set of target modules.
|
||||
*/
|
||||
public static Opens newOpens(Set<Opens.Modifier> ms,
|
||||
String pn,
|
||||
Set<String> targets) {
|
||||
return JLMA.newOpens(ms, pn, targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Exports} for an unqualified export with a given set
|
||||
* of modifiers.
|
||||
*/
|
||||
public static Exports newExports(Set<Exports.Modifier> ms, String pn) {
|
||||
return JLMA.newExports(ms, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Provides} for a service with a given list of
|
||||
* implementation classes.
|
||||
*/
|
||||
public static Provides newProvides(String st, List<String> pcs) {
|
||||
return JLMA.newProvides(st, pcs);
|
||||
}
|
||||
|
||||
final String name;
|
||||
boolean open, synthetic, mandated;
|
||||
Set<Requires> requires;
|
||||
Set<Exports> exports;
|
||||
Set<Opens> opens;
|
||||
Set<String> packages;
|
||||
Set<String> uses;
|
||||
Set<Provides> provides;
|
||||
Version version;
|
||||
String mainClass;
|
||||
|
||||
Builder(String name) {
|
||||
this.name = name;
|
||||
this.requires = Set.of();
|
||||
this.exports = Set.of();
|
||||
this.opens = Set.of();
|
||||
this.provides = Set.of();
|
||||
this.uses = Set.of();
|
||||
}
|
||||
|
||||
Builder open(boolean value) {
|
||||
this.open = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder synthetic(boolean value) {
|
||||
this.synthetic = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder mandated(boolean value) {
|
||||
this.mandated = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets module exports.
|
||||
*/
|
||||
public Builder exports(Exports[] exports) {
|
||||
this.exports = Set.of(exports);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets module opens.
|
||||
*/
|
||||
public Builder opens(Opens[] opens) {
|
||||
this.opens = Set.of(opens);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets module requires.
|
||||
*/
|
||||
public Builder requires(Requires[] requires) {
|
||||
this.requires = Set.of(requires);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of (possible empty) packages.
|
||||
*/
|
||||
public Builder packages(Set<String> packages) {
|
||||
this.packages = packages;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the set of service dependences.
|
||||
*/
|
||||
public Builder uses(Set<String> uses) {
|
||||
this.uses = uses;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets module provides.
|
||||
*/
|
||||
public Builder provides(Provides[] provides) {
|
||||
this.provides = Set.of(provides);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the module version.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code v} is null or cannot be
|
||||
* parsed as a version string
|
||||
*
|
||||
* @see Version#parse(String)
|
||||
*/
|
||||
public Builder version(String v) {
|
||||
Version ver = cachedVersion;
|
||||
if (ver != null && v.equals(ver.toString())) {
|
||||
version = ver;
|
||||
} else {
|
||||
cachedVersion = version = Version.parse(v);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the module main class.
|
||||
*/
|
||||
public Builder mainClass(String mc) {
|
||||
mainClass = mc;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable set of the module modifiers derived from the flags.
|
||||
*/
|
||||
private Set<ModuleDescriptor.Modifier> modifiers() {
|
||||
int n = 0;
|
||||
if (open) n++;
|
||||
if (synthetic) n++;
|
||||
if (mandated) n++;
|
||||
if (n == 0) {
|
||||
return Set.of();
|
||||
} else {
|
||||
ModuleDescriptor.Modifier[] mods = new ModuleDescriptor.Modifier[n];
|
||||
if (open) mods[--n] = ModuleDescriptor.Modifier.OPEN;
|
||||
if (synthetic) mods[--n] = ModuleDescriptor.Modifier.SYNTHETIC;
|
||||
if (mandated) mods[--n] = ModuleDescriptor.Modifier.MANDATED;
|
||||
return Set.of(mods);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@code ModuleDescriptor} from the components.
|
||||
*/
|
||||
public ModuleDescriptor build(int hashCode) {
|
||||
assert name != null;
|
||||
return JLMA.newModuleDescriptor(name,
|
||||
version,
|
||||
modifiers(),
|
||||
requires,
|
||||
exports,
|
||||
opens,
|
||||
uses,
|
||||
provides,
|
||||
packages,
|
||||
mainClass,
|
||||
hashCode);
|
||||
}
|
||||
}
|
||||
247
src/java.base/share/classes/jdk/internal/module/Checks.java
Normal file
247
src/java.base/share/classes/jdk/internal/module/Checks.java
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* Copyright (c) 2009, 2021, 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.util.Set;
|
||||
|
||||
/**
|
||||
* Utility class for checking module, package, and class names.
|
||||
*/
|
||||
|
||||
public final class Checks {
|
||||
|
||||
private Checks() { }
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
String id = name.substring(off, next);
|
||||
if (!isJavaIdentifier(id)) {
|
||||
throw new IllegalArgumentException(name + ": Invalid module name"
|
||||
+ ": '" + id + "' is not a Java identifier");
|
||||
}
|
||||
off = next+1;
|
||||
}
|
||||
String last = name.substring(off);
|
||||
if (!isJavaIdentifier(last)) {
|
||||
throw new IllegalArgumentException(name + ": Invalid module name"
|
||||
+ ": '" + last + "' is not a Java identifier");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 requirePackageName(String name) {
|
||||
return requireTypeName("package name", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given name is a legal package name.
|
||||
*/
|
||||
public static boolean isPackageName(String name) {
|
||||
return isTypeName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a name to ensure that it's a legal qualified class name
|
||||
*
|
||||
* @throws IllegalArgumentException if name is null or not a legal
|
||||
* qualified class name
|
||||
*/
|
||||
public static String requireServiceTypeName(String name) {
|
||||
return requireQualifiedClassName("service type name", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a name to ensure that it's a legal qualified class name.
|
||||
*
|
||||
* @throws IllegalArgumentException if name is null or not a legal
|
||||
* qualified class name
|
||||
*/
|
||||
public static String requireServiceProviderName(String name) {
|
||||
return requireQualifiedClassName("service provider name", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a name to ensure that it's a legal qualified class name in
|
||||
* a named package.
|
||||
*
|
||||
* @throws IllegalArgumentException if name is null or not a legal
|
||||
* qualified class name in a named package
|
||||
*/
|
||||
public static String requireQualifiedClassName(String what, String name) {
|
||||
requireTypeName(what, name);
|
||||
if (name.indexOf('.') == -1)
|
||||
throw new IllegalArgumentException(name + ": is not a qualified name of"
|
||||
+ " a Java class in a named package");
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given name is a legal class name.
|
||||
*/
|
||||
public static boolean isClassName(String name) {
|
||||
return isTypeName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given name is a legal type name.
|
||||
*/
|
||||
private static boolean isTypeName(String name) {
|
||||
int next;
|
||||
int off = 0;
|
||||
while ((next = name.indexOf('.', off)) != -1) {
|
||||
String id = name.substring(off, next);
|
||||
if (!isJavaIdentifier(id))
|
||||
return false;
|
||||
off = next+1;
|
||||
}
|
||||
String last = name.substring(off);
|
||||
return isJavaIdentifier(last);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given name is a legal type name.
|
||||
*
|
||||
* @throws IllegalArgumentException if name is null or not a legal
|
||||
* type name
|
||||
*/
|
||||
private static String requireTypeName(String what, String name) {
|
||||
if (name == null)
|
||||
throw new IllegalArgumentException("Null " + what);
|
||||
int next;
|
||||
int off = 0;
|
||||
while ((next = name.indexOf('.', off)) != -1) {
|
||||
String id = name.substring(off, next);
|
||||
if (!isJavaIdentifier(id)) {
|
||||
throw new IllegalArgumentException(name + ": Invalid " + what
|
||||
+ ": '" + id + "' is not a Java identifier");
|
||||
}
|
||||
off = next + 1;
|
||||
}
|
||||
String last = name.substring(off);
|
||||
if (!isJavaIdentifier(last)) {
|
||||
throw new IllegalArgumentException(name + ": Invalid " + what
|
||||
+ ": '" + last + "' is not a Java identifier");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given string is a legal Java identifier,
|
||||
* otherwise false.
|
||||
*/
|
||||
public static boolean isJavaIdentifier(String str) {
|
||||
if (str.isEmpty() || RESERVED.contains(str))
|
||||
return false;
|
||||
|
||||
int first = Character.codePointAt(str, 0);
|
||||
if (!Character.isJavaIdentifierStart(first))
|
||||
return false;
|
||||
|
||||
int i = Character.charCount(first);
|
||||
while (i < str.length()) {
|
||||
int cp = Character.codePointAt(str, i);
|
||||
if (!Character.isJavaIdentifierPart(cp))
|
||||
return false;
|
||||
i += Character.charCount(cp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// keywords, boolean and null literals, not allowed in identifiers
|
||||
private static final Set<String> RESERVED = Set.of(
|
||||
"abstract",
|
||||
"assert",
|
||||
"boolean",
|
||||
"break",
|
||||
"byte",
|
||||
"case",
|
||||
"catch",
|
||||
"char",
|
||||
"class",
|
||||
"const",
|
||||
"continue",
|
||||
"default",
|
||||
"do",
|
||||
"double",
|
||||
"else",
|
||||
"enum",
|
||||
"extends",
|
||||
"final",
|
||||
"finally",
|
||||
"float",
|
||||
"for",
|
||||
"goto",
|
||||
"if",
|
||||
"implements",
|
||||
"import",
|
||||
"instanceof",
|
||||
"int",
|
||||
"interface",
|
||||
"long",
|
||||
"native",
|
||||
"new",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"return",
|
||||
"short",
|
||||
"static",
|
||||
"strictfp",
|
||||
"super",
|
||||
"switch",
|
||||
"synchronized",
|
||||
"this",
|
||||
"throw",
|
||||
"throws",
|
||||
"transient",
|
||||
"try",
|
||||
"void",
|
||||
"volatile",
|
||||
"while",
|
||||
"true",
|
||||
"false",
|
||||
"null",
|
||||
"_"
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
|
||||
// Constants in module-info.class files
|
||||
|
||||
public class ClassFileConstants {
|
||||
|
||||
private ClassFileConstants() { }
|
||||
|
||||
// Attribute names
|
||||
public static final String MODULE = "Module";
|
||||
public static final String SOURCE_FILE = "SourceFile";
|
||||
public static final String SDE = "SourceDebugExtension";
|
||||
|
||||
public static final String MODULE_PACKAGES = "ModulePackages";
|
||||
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;
|
||||
public static final int ACC_OPEN = 0x0020;
|
||||
public static final int ACC_TRANSITIVE = 0x0020;
|
||||
public static final int ACC_STATIC_PHASE = 0x0040;
|
||||
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;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Defines methods to compute the default set of root modules for the unnamed
|
||||
* module.
|
||||
*/
|
||||
|
||||
public final class DefaultRoots {
|
||||
private DefaultRoots() { }
|
||||
|
||||
/**
|
||||
* Returns the default set of root modules for the unnamed module from the
|
||||
* modules observable with the intersection of two module finders.
|
||||
*
|
||||
* The first module finder should be the module finder that finds modules on
|
||||
* the upgrade module path or among the system modules. The second module
|
||||
* finder should be the module finder that finds all modules on the module
|
||||
* path, or a subset of when using --limit-modules.
|
||||
*/
|
||||
static Set<String> compute(ModuleFinder finder1, ModuleFinder finder2) {
|
||||
return finder1.findAll().stream()
|
||||
.filter(mref -> !ModuleResolution.doNotResolveByDefault(mref))
|
||||
.map(ModuleReference::descriptor)
|
||||
.filter(descriptor -> finder2.find(descriptor.name()).isPresent()
|
||||
&& exportsAPI(descriptor))
|
||||
.map(ModuleDescriptor::name)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default set of root modules for the unnamed module from the
|
||||
* modules observable with the given module finder.
|
||||
*
|
||||
* This method is used by the jlink system modules plugin.
|
||||
*/
|
||||
public static Set<String> compute(ModuleFinder finder) {
|
||||
return compute(finder, finder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given module exports a package to all modules
|
||||
*/
|
||||
private static boolean exportsAPI(ModuleDescriptor descriptor) {
|
||||
return descriptor.exports()
|
||||
.stream()
|
||||
.filter(e -> !e.isQualified())
|
||||
.findAny()
|
||||
.isPresent();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A dummy SystemModules for use with exploded builds or testing.
|
||||
*/
|
||||
|
||||
class ExplodedSystemModules implements SystemModules {
|
||||
@Override
|
||||
public boolean hasSplitPackages() {
|
||||
return true; // not known
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIncubatorModules() {
|
||||
return true; // not known
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleDescriptor[] moduleDescriptors() {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleTarget[] moduleTargets() {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleHashes[] moduleHashes() {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleResolution[] moduleResolutions() {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Set<String>> moduleReads() {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
1227
src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
Normal file
1227
src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2020, 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.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The result of hashing the contents of a number of module artifacts.
|
||||
*/
|
||||
|
||||
public final class ModuleHashes {
|
||||
|
||||
/**
|
||||
* A supplier of a message digest.
|
||||
*/
|
||||
public static interface HashSupplier {
|
||||
byte[] generate(String algorithm);
|
||||
}
|
||||
|
||||
private final String algorithm;
|
||||
private final Map<String, byte[]> nameToHash;
|
||||
|
||||
/**
|
||||
* Creates a {@code ModuleHashes}.
|
||||
*
|
||||
* @param algorithm the algorithm used to create the hashes
|
||||
* @param nameToHash the map of module name to hash value
|
||||
*/
|
||||
ModuleHashes(String algorithm, Map<String, byte[]> nameToHash) {
|
||||
this.algorithm = Objects.requireNonNull(algorithm);
|
||||
this.nameToHash = Collections.unmodifiableMap(nameToHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the algorithm used to hash the modules ("SHA-256" for example).
|
||||
*/
|
||||
public String algorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of module names for which hashes are recorded.
|
||||
*/
|
||||
public Set<String> names() {
|
||||
return nameToHash.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash for the given module name, {@code null}
|
||||
* if there is no hash recorded for the module.
|
||||
*/
|
||||
public byte[] hashFor(String mn) {
|
||||
return nameToHash.get(mn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unmodifiable map of module name to hash
|
||||
*/
|
||||
public Map<String, byte[]> hashes() {
|
||||
return nameToHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a hash from the names and content of a module.
|
||||
*
|
||||
* @param reader the module reader to access the module content
|
||||
* @param algorithm the name of the message digest algorithm to use
|
||||
* @return the hash
|
||||
* @throws IllegalArgumentException if digest algorithm is not supported
|
||||
* @throws UncheckedIOException if an I/O error occurs
|
||||
*/
|
||||
private static byte[] computeHash(ModuleReader reader, String algorithm) {
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance(algorithm);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
byte[] buf = new byte[32*1024];
|
||||
try (Stream<String> stream = reader.list()) {
|
||||
stream.sorted().forEach(rn -> {
|
||||
md.update(rn.getBytes(StandardCharsets.UTF_8));
|
||||
try (InputStream in = reader.open(rn).orElseThrow()) {
|
||||
int n;
|
||||
while ((n = in.read(buf)) > 0) {
|
||||
md.update(buf, 0, n);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
});
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
return md.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a hash from the names and content of a module.
|
||||
*
|
||||
* @param supplier supplies the module reader to access the module content
|
||||
* @param algorithm the name of the message digest algorithm to use
|
||||
* @return the hash
|
||||
* @throws IllegalArgumentException if digest algorithm is not supported
|
||||
* @throws UncheckedIOException if an I/O error occurs
|
||||
*/
|
||||
static byte[] computeHash(Supplier<ModuleReader> supplier, String algorithm) {
|
||||
try (ModuleReader reader = supplier.get()) {
|
||||
return computeHash(reader, algorithm);
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the hash from the names and content of a set of modules. Returns
|
||||
* a {@code ModuleHashes} to encapsulate the result.
|
||||
*
|
||||
* @param mrefs the set of modules
|
||||
* @param algorithm the name of the message digest algorithm to use
|
||||
* @return ModuleHashes that encapsulates the hashes
|
||||
* @throws IllegalArgumentException if digest algorithm is not supported
|
||||
* @throws UncheckedIOException if an I/O error occurs
|
||||
*/
|
||||
static ModuleHashes generate(Set<ModuleReference> mrefs, String algorithm) {
|
||||
Map<String, byte[]> nameToHash = new TreeMap<>();
|
||||
for (ModuleReference mref : mrefs) {
|
||||
try (ModuleReader reader = mref.open()) {
|
||||
byte[] hash = computeHash(reader, algorithm);
|
||||
nameToHash.put(mref.descriptor().name(), hash);
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
}
|
||||
return new ModuleHashes(algorithm, nameToHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = algorithm.hashCode();
|
||||
for (Map.Entry<String, byte[]> e : nameToHash.entrySet()) {
|
||||
h = h * 31 + e.getKey().hashCode();
|
||||
h = h * 31 + Arrays.hashCode(e.getValue());
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof ModuleHashes))
|
||||
return false;
|
||||
ModuleHashes other = (ModuleHashes) obj;
|
||||
if (!algorithm.equals(other.algorithm)
|
||||
|| nameToHash.size() != other.nameToHash.size())
|
||||
return false;
|
||||
for (Map.Entry<String, byte[]> e : nameToHash.entrySet()) {
|
||||
String name = e.getKey();
|
||||
byte[] hash = e.getValue();
|
||||
if (!Arrays.equals(hash, other.nameToHash.get(name)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(algorithm);
|
||||
sb.append(" ");
|
||||
nameToHash.entrySet()
|
||||
.stream()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.forEach(e -> {
|
||||
sb.append(e.getKey());
|
||||
sb.append("=");
|
||||
byte[] ba = e.getValue();
|
||||
for (byte b : ba) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
});
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used by jdk.internal.module.SystemModules class
|
||||
* generated at link time.
|
||||
*/
|
||||
public static class Builder {
|
||||
final String algorithm;
|
||||
final Map<String, byte[]> nameToHash;
|
||||
|
||||
Builder(String algorithm, int initialCapacity) {
|
||||
this.nameToHash = new HashMap<>(initialCapacity);
|
||||
this.algorithm = Objects.requireNonNull(algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the module hash for the given module name
|
||||
*/
|
||||
public Builder hashForModule(String mn, byte[] hash) {
|
||||
nameToHash.put(mn, hash);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@code ModuleHashes}.
|
||||
*/
|
||||
public ModuleHashes build() {
|
||||
if (!nameToHash.isEmpty()) {
|
||||
return new ModuleHashes(algorithm, nameToHash);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2020, 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.PrintStream;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
/**
|
||||
* A Builder to compute ModuleHashes from a given configuration
|
||||
*/
|
||||
public class ModuleHashesBuilder {
|
||||
private final Configuration configuration;
|
||||
private final Set<String> hashModuleCandidates;
|
||||
|
||||
/**
|
||||
* Constructs a ModuleHashesBuilder that finds the packaged modules
|
||||
* from the location of ModuleReference found from the given Configuration.
|
||||
*
|
||||
* @param config Configuration for building module hashes
|
||||
* @param modules the candidate modules to be hashed
|
||||
*/
|
||||
public ModuleHashesBuilder(Configuration config, Set<String> modules) {
|
||||
this.configuration = config;
|
||||
this.hashModuleCandidates = modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of a module M to ModuleHashes for the modules
|
||||
* that depend upon M directly or indirectly.
|
||||
*
|
||||
* The key for each entry in the returned map is a module M that has
|
||||
* no outgoing edges to any of the candidate modules to be hashed
|
||||
* i.e. M is a leaf node in a connected subgraph containing M and
|
||||
* other candidate modules from the module graph filtering
|
||||
* the outgoing edges from M to non-candidate modules.
|
||||
*/
|
||||
public Map<String, ModuleHashes> computeHashes(Set<String> roots) {
|
||||
// build a graph containing the packaged modules and
|
||||
// its transitive dependences matching --hash-modules
|
||||
Graph.Builder<String> builder = new Graph.Builder<>();
|
||||
Deque<ResolvedModule> todo = new ArrayDeque<>(configuration.modules());
|
||||
Set<ResolvedModule> visited = new HashSet<>();
|
||||
ResolvedModule rm;
|
||||
while ((rm = todo.poll()) != null) {
|
||||
if (visited.add(rm)) {
|
||||
builder.addNode(rm.name());
|
||||
for (ResolvedModule dm : rm.reads()) {
|
||||
if (!visited.contains(dm)) {
|
||||
todo.push(dm);
|
||||
}
|
||||
builder.addEdge(rm.name(), dm.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// each node in a transposed graph is a matching packaged module
|
||||
// in which the hash of the modules that depend upon it is recorded
|
||||
Graph<String> transposedGraph = builder.build().transpose();
|
||||
|
||||
// traverse the modules in topological order that will identify
|
||||
// the modules to record the hashes - it is the first matching
|
||||
// module and has not been hashed during the traversal.
|
||||
Set<String> mods = new HashSet<>();
|
||||
Map<String, ModuleHashes> hashes = new TreeMap<>();
|
||||
builder.build()
|
||||
.orderedNodes()
|
||||
.filter(mn -> roots.contains(mn) && !mods.contains(mn))
|
||||
.forEach(mn -> {
|
||||
// Compute hashes of the modules that depend on mn directly and
|
||||
// indirectly excluding itself.
|
||||
Set<String> ns = transposedGraph.dfs(mn)
|
||||
.stream()
|
||||
.filter(n -> !n.equals(mn) && hashModuleCandidates.contains(n))
|
||||
.collect(toSet());
|
||||
mods.add(mn);
|
||||
mods.addAll(ns);
|
||||
|
||||
if (!ns.isEmpty()) {
|
||||
Set<ModuleReference> mrefs = ns.stream()
|
||||
.map(name -> configuration.findModule(name)
|
||||
.orElseThrow(InternalError::new))
|
||||
.map(ResolvedModule::reference)
|
||||
.collect(toSet());
|
||||
hashes.put(mn, ModuleHashes.generate(mrefs, "SHA-256"));
|
||||
}
|
||||
});
|
||||
return hashes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility class
|
||||
*/
|
||||
static class Graph<T> {
|
||||
private final Set<T> nodes;
|
||||
private final Map<T, Set<T>> edges;
|
||||
|
||||
public Graph(Set<T> nodes, Map<T, Set<T>> edges) {
|
||||
this.nodes = Collections.unmodifiableSet(nodes);
|
||||
this.edges = Collections.unmodifiableMap(edges);
|
||||
}
|
||||
|
||||
public Set<T> nodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public Map<T, Set<T>> edges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public Set<T> adjacentNodes(T u) {
|
||||
return edges.get(u);
|
||||
}
|
||||
|
||||
public boolean contains(T u) {
|
||||
return nodes.contains(u);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nodes sorted in topological order.
|
||||
*/
|
||||
public Stream<T> orderedNodes() {
|
||||
TopoSorter<T> sorter = new TopoSorter<>(this);
|
||||
return sorter.result.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses this graph and performs the given action in topological order.
|
||||
*/
|
||||
public void ordered(Consumer<T> action) {
|
||||
TopoSorter<T> sorter = new TopoSorter<>(this);
|
||||
sorter.ordered(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses this graph and performs the given action in reverse topological order.
|
||||
*/
|
||||
public void reverse(Consumer<T> action) {
|
||||
TopoSorter<T> sorter = new TopoSorter<>(this);
|
||||
sorter.reverse(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a transposed graph from this graph.
|
||||
*/
|
||||
public Graph<T> transpose() {
|
||||
Builder<T> builder = new Builder<>();
|
||||
nodes.forEach(builder::addNode);
|
||||
// reverse edges
|
||||
edges.keySet().forEach(u -> {
|
||||
edges.get(u).forEach(v -> builder.addEdge(v, u));
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all nodes reachable from the given root.
|
||||
*/
|
||||
public Set<T> dfs(T root) {
|
||||
return dfs(Set.of(root));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all nodes reachable from the given set of roots.
|
||||
*/
|
||||
public Set<T> dfs(Set<T> roots) {
|
||||
ArrayDeque<T> todo = new ArrayDeque<>(roots);
|
||||
Set<T> visited = new HashSet<>();
|
||||
T u;
|
||||
while ((u = todo.poll()) != null) {
|
||||
if (visited.add(u) && contains(u)) {
|
||||
adjacentNodes(u).stream()
|
||||
.filter(v -> !visited.contains(v))
|
||||
.forEach(todo::push);
|
||||
}
|
||||
}
|
||||
return visited;
|
||||
}
|
||||
|
||||
public void printGraph(PrintStream out) {
|
||||
out.println("graph for " + nodes);
|
||||
nodes
|
||||
.forEach(u -> adjacentNodes(u)
|
||||
.forEach(v -> out.format(" %s -> %s%n", u, v)));
|
||||
}
|
||||
|
||||
static class Builder<T> {
|
||||
final Set<T> nodes = new HashSet<>();
|
||||
final Map<T, Set<T>> edges = new HashMap<>();
|
||||
|
||||
public void addNode(T node) {
|
||||
if (nodes.add(node)) {
|
||||
edges.computeIfAbsent(node, _e -> new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
public void addEdge(T u, T v) {
|
||||
addNode(u);
|
||||
addNode(v);
|
||||
edges.get(u).add(v);
|
||||
}
|
||||
|
||||
public Graph<T> build() {
|
||||
return new Graph<T>(nodes, edges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Topological sort
|
||||
*/
|
||||
private static class TopoSorter<T> {
|
||||
final Deque<T> result = new ArrayDeque<>();
|
||||
final Graph<T> graph;
|
||||
|
||||
TopoSorter(Graph<T> graph) {
|
||||
this.graph = graph;
|
||||
sort();
|
||||
}
|
||||
|
||||
public void ordered(Consumer<T> action) {
|
||||
result.forEach(action);
|
||||
}
|
||||
|
||||
public void reverse(Consumer<T> action) {
|
||||
result.descendingIterator().forEachRemaining(action);
|
||||
}
|
||||
|
||||
private void sort() {
|
||||
Set<T> visited = new HashSet<>();
|
||||
Deque<T> stack = new ArrayDeque<>();
|
||||
// CWE-407 fix: parallel Set for O(1) stack membership test.
|
||||
// Deque.contains() is O(n); stackSet.contains() is O(1).
|
||||
Set<T> stackSet = new HashSet<>();
|
||||
graph.nodes.forEach(node -> visit(node, visited, stack, stackSet));
|
||||
}
|
||||
|
||||
private Set<T> children(T node) {
|
||||
return graph.edges().get(node);
|
||||
}
|
||||
|
||||
private void visit(T node, Set<T> visited, Deque<T> stack, Set<T> stackSet) {
|
||||
if (visited.add(node)) {
|
||||
stack.push(node);
|
||||
stackSet.add(node);
|
||||
children(node).forEach(child -> visit(child, visited, stack, stackSet));
|
||||
stack.pop();
|
||||
stackSet.remove(node);
|
||||
result.addLast(node);
|
||||
}
|
||||
else if (stackSet.contains(node)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cycle detected: " + node + " -> " + children(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1225
src/java.base/share/classes/jdk/internal/module/ModuleInfo.java
Normal file
1225
src/java.base/share/classes/jdk/internal/module/ModuleInfo.java
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2024, 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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.attribute.ModuleAttribute;
|
||||
import java.lang.classfile.attribute.ModuleHashInfo;
|
||||
import java.lang.classfile.attribute.ModuleHashesAttribute;
|
||||
import java.lang.classfile.attribute.ModuleMainClassAttribute;
|
||||
import java.lang.classfile.attribute.ModulePackagesAttribute;
|
||||
import java.lang.classfile.attribute.ModuleResolutionAttribute;
|
||||
import java.lang.classfile.attribute.ModuleTargetAttribute;
|
||||
import java.lang.constant.ModuleDesc;
|
||||
import java.lang.constant.PackageDesc;
|
||||
|
||||
|
||||
/**
|
||||
* Utility class to extend a module-info.class with additional attributes.
|
||||
*/
|
||||
|
||||
public final class ModuleInfoExtender {
|
||||
|
||||
// the input stream to read the original module-info.class
|
||||
private final InputStream in;
|
||||
|
||||
// the packages in the ModulePackages attribute
|
||||
private Set<String> packages;
|
||||
|
||||
// the value for the module version in the Module attribute
|
||||
private Version version;
|
||||
|
||||
// the value of the ModuleMainClass attribute
|
||||
private String mainClass;
|
||||
|
||||
// the value for the ModuleTarget attribute
|
||||
private String targetPlatform;
|
||||
|
||||
// the hashes for the ModuleHashes attribute
|
||||
private ModuleHashes hashes;
|
||||
|
||||
// the value of the ModuleResolution attribute
|
||||
private ModuleResolution moduleResolution;
|
||||
|
||||
private ModuleInfoExtender(InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the packages for the ModulePackages attribute
|
||||
*
|
||||
* @apiNote This method does not check that the package names are legal
|
||||
* package names or that the set of packages is a super set of the
|
||||
* packages in the module.
|
||||
*/
|
||||
public ModuleInfoExtender packages(Set<String> packages) {
|
||||
this.packages = Collections.unmodifiableSet(packages);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the module version in the Module attribute
|
||||
*/
|
||||
public ModuleInfoExtender version(Version version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the ModuleMainClass attribute.
|
||||
*
|
||||
* @apiNote This method does not check that the main class is a legal
|
||||
* class name in a named package.
|
||||
*/
|
||||
public ModuleInfoExtender mainClass(String mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the ModuleTarget attribute.
|
||||
*/
|
||||
public ModuleInfoExtender targetPlatform(String targetPlatform) {
|
||||
this.targetPlatform = targetPlatform;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ModuleHashes attribute will be emitted to the module-info with
|
||||
* the hashes encapsulated in the given {@code ModuleHashes}
|
||||
* object.
|
||||
*/
|
||||
public ModuleInfoExtender hashes(ModuleHashes hashes) {
|
||||
this.hashes = hashes;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the ModuleResolution attribute.
|
||||
*/
|
||||
public ModuleInfoExtender moduleResolution(ModuleResolution mres) {
|
||||
this.moduleResolution = mres;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the modified module-info.class to the given output stream.
|
||||
* Once this method has been called then the Extender object should
|
||||
* be discarded.
|
||||
*/
|
||||
public void write(OutputStream out) throws IOException {
|
||||
// emit to the output stream
|
||||
out.write(toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes of the modified module-info.class.
|
||||
* Once this method has been called then the Extender object should
|
||||
* be discarded.
|
||||
*/
|
||||
public byte[] toByteArray() throws IOException {
|
||||
var cc = ClassFile.of();
|
||||
var cm = cc.parse(in.readAllBytes());
|
||||
Version v = ModuleInfoExtender.this.version;
|
||||
return cc.transformClass(cm, ClassTransform.endHandler(clb -> {
|
||||
// ModuleMainClass attribute
|
||||
if (mainClass != null) {
|
||||
clb.with(ModuleMainClassAttribute.of(ClassDesc.of(mainClass)));
|
||||
}
|
||||
|
||||
// ModulePackages attribute
|
||||
if (packages != null) {
|
||||
List<PackageDesc> packageNames = packages.stream()
|
||||
.sorted()
|
||||
.map(PackageDesc::of)
|
||||
.toList();
|
||||
clb.with(ModulePackagesAttribute.ofNames(packageNames));
|
||||
}
|
||||
|
||||
// ModuleTarget, ModuleResolution and ModuleHashes attributes
|
||||
if (targetPlatform != null) {
|
||||
clb.with(ModuleTargetAttribute.of(targetPlatform));
|
||||
}
|
||||
if (moduleResolution != null) {
|
||||
clb.with(ModuleResolutionAttribute.of(moduleResolution.value()));
|
||||
}
|
||||
if (hashes != null) {
|
||||
clb.with(ModuleHashesAttribute.of(
|
||||
hashes.algorithm(),
|
||||
hashes.hashes().entrySet().stream().map(he ->
|
||||
ModuleHashInfo.of(ModuleDesc.of(
|
||||
he.getKey()),
|
||||
he.getValue())).toList()));
|
||||
}
|
||||
}).andThen((clb, cle) -> {
|
||||
if (v != null && cle instanceof ModuleAttribute ma) {
|
||||
clb.with(ModuleAttribute.of(
|
||||
ma.moduleName(),
|
||||
ma.moduleFlagsMask(),
|
||||
clb.constantPool().utf8Entry(v.toString()),
|
||||
ma.requires(),
|
||||
ma.exports(),
|
||||
ma.opens(),
|
||||
ma.uses(),
|
||||
ma.provides()));
|
||||
} else {
|
||||
clb.accept(cle);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code Extender} that may be used to add additional
|
||||
* attributes to the module-info.class read from the given input
|
||||
* stream.
|
||||
*/
|
||||
public static ModuleInfoExtender newExtender(InputStream in) {
|
||||
return new ModuleInfoExtender(in);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2024, 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.Configuration;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jdk.internal.loader.ClassLoaders;
|
||||
|
||||
/**
|
||||
* Supports the mapping of modules to class loaders. The set of modules mapped
|
||||
* to the boot and platform class loaders is generated at build time from
|
||||
* this source file.
|
||||
*/
|
||||
public final class ModuleLoaderMap {
|
||||
|
||||
/**
|
||||
* Maps the system modules to the built-in class loaders.
|
||||
*/
|
||||
private static final class Mapper implements Function<String, ClassLoader> {
|
||||
|
||||
private static final ClassLoader PLATFORM_CLASSLOADER =
|
||||
ClassLoaders.platformClassLoader();
|
||||
private static final ClassLoader APP_CLASSLOADER =
|
||||
ClassLoaders.appClassLoader();
|
||||
|
||||
private static final String PLATFORM_LOADER_NAME = "PLATFORM";
|
||||
private static final String APP_LOADER_NAME = "APP";
|
||||
|
||||
/**
|
||||
* Map from module name to class loader name. The name is resolved to the
|
||||
* actual class loader in {@code apply}.
|
||||
*/
|
||||
private final Map<String, String> map;
|
||||
|
||||
/**
|
||||
* Creates a Mapper to map module names in the given Configuration to
|
||||
* built-in classloaders.
|
||||
*
|
||||
* As a proxy for the actual classloader, we store an easily archiveable
|
||||
* loader name in the internal map.
|
||||
*/
|
||||
Mapper(Configuration cf) {
|
||||
var map = new HashMap<String, String>();
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
String mn = resolvedModule.name();
|
||||
if (!Modules.bootModules.contains(mn)) {
|
||||
if (Modules.platformModules.contains(mn)) {
|
||||
map.put(mn, PLATFORM_LOADER_NAME);
|
||||
} else {
|
||||
map.put(mn, APP_LOADER_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader apply(String name) {
|
||||
String loader = map.get(name);
|
||||
if (APP_LOADER_NAME.equals(loader)) {
|
||||
return APP_CLASSLOADER;
|
||||
} else if (PLATFORM_LOADER_NAME.equals(loader)) {
|
||||
return PLATFORM_CLASSLOADER;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the modules defined to the boot loader.
|
||||
*/
|
||||
public static Set<String> bootModules() {
|
||||
return Modules.bootModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the modules defined to the platform loader.
|
||||
*/
|
||||
public static Set<String> platformModules() {
|
||||
return Modules.platformModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the modules defined to the application loader which perform native access.
|
||||
*/
|
||||
public static Set<String> nativeAccessModules() {
|
||||
return Modules.nativeAccessModules;
|
||||
}
|
||||
|
||||
private static class Modules {
|
||||
// list of boot modules is generated at build time.
|
||||
private static final Set<String> bootModules =
|
||||
Set.of(new String[] { "@@BOOT_MODULE_NAMES@@" });
|
||||
|
||||
// list of platform modules is generated at build time.
|
||||
private static final Set<String> platformModules =
|
||||
Set.of(new String[] { "@@PLATFORM_MODULE_NAMES@@" });
|
||||
|
||||
// list of jdk modules is generated at build time.
|
||||
private static final Set<String> nativeAccessModules =
|
||||
Set.of(new String[] { "@@NATIVE_ACCESS_MODULE_NAMES@@" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function to map modules in the given configuration to the
|
||||
* built-in class loaders.
|
||||
*/
|
||||
static Function<String, ClassLoader> mappingFunction(Configuration cf) {
|
||||
return new Mapper(cf);
|
||||
}
|
||||
|
||||
/**
|
||||
* When defining modules for a configuration, we only allow defining modules
|
||||
* to the boot or platform classloader if the ClassLoader mapping function
|
||||
* originate from here.
|
||||
*/
|
||||
public static boolean isBuiltinMapper(Function<String, ClassLoader> clf) {
|
||||
return clf instanceof Mapper;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,624 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2025, 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.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Builder;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.loader.Resource;
|
||||
import jdk.internal.access.JavaLangModuleAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import sun.net.www.ParseUtil;
|
||||
|
||||
|
||||
/**
|
||||
* Provides support for patching modules, mostly the boot layer.
|
||||
*/
|
||||
|
||||
public final class ModulePatcher {
|
||||
|
||||
private static final JavaLangModuleAccess JLMA
|
||||
= SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
// module name -> sequence of patches (directories or JAR files)
|
||||
private final Map<String, List<Path>> map;
|
||||
|
||||
/**
|
||||
* Initialize the module patcher with the given map. The map key is
|
||||
* the module name, the value is a list of path strings.
|
||||
*/
|
||||
public ModulePatcher(Map<String, List<String>> input) {
|
||||
if (input.isEmpty()) {
|
||||
this.map = Map.of();
|
||||
} else {
|
||||
Map<String, List<Path>> map = new HashMap<>();
|
||||
for (Map.Entry<String, List<String>> e : input.entrySet()) {
|
||||
String mn = e.getKey();
|
||||
List<Path> paths = e.getValue().stream()
|
||||
.map(Paths::get)
|
||||
.toList();
|
||||
map.put(mn, paths);
|
||||
}
|
||||
this.map = map;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a module reference that interposes on the given module if
|
||||
* needed. If there are no patches for the given module then the module
|
||||
* reference is simply returned. Otherwise the patches for the module
|
||||
* are scanned (to find any new packages) and a new module reference is
|
||||
* returned.
|
||||
*
|
||||
* @throws UncheckedIOException if an I/O error is detected
|
||||
*/
|
||||
public ModuleReference patchIfNeeded(ModuleReference mref) {
|
||||
// if there are no patches for the module then nothing to do
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
String mn = descriptor.name();
|
||||
List<Path> paths = map.get(mn);
|
||||
if (paths == null)
|
||||
return mref;
|
||||
|
||||
// Scan the JAR file or directory tree to get the set of packages.
|
||||
// For automatic modules then packages that do not contain class files
|
||||
// must be ignored.
|
||||
Set<String> packages = new HashSet<>();
|
||||
boolean isAutomatic = descriptor.isAutomatic();
|
||||
try {
|
||||
for (Path file : paths) {
|
||||
if (Files.isRegularFile(file)) {
|
||||
|
||||
// JAR file - do not open as a multi-release JAR as this
|
||||
// is not supported by the boot class loader
|
||||
try (JarFile jf = new JarFile(file.toString())) {
|
||||
jf.stream()
|
||||
.filter(e -> !e.isDirectory()
|
||||
&& (!isAutomatic || e.getName().endsWith(".class")))
|
||||
.map(e -> toPackageName(file, e))
|
||||
.filter(Checks::isPackageName)
|
||||
.forEach(packages::add);
|
||||
}
|
||||
|
||||
} else if (Files.isDirectory(file)) {
|
||||
|
||||
// exploded directory without following sym links
|
||||
Path top = file;
|
||||
try (Stream<Path> stream = Files.find(top, Integer.MAX_VALUE,
|
||||
((path, attrs) -> attrs.isRegularFile()))) {
|
||||
stream.filter(path -> (!isAutomatic
|
||||
|| path.toString().endsWith(".class"))
|
||||
&& !isHidden(path))
|
||||
.map(path -> toPackageName(top, path))
|
||||
.filter(Checks::isPackageName)
|
||||
.forEach(packages::add);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
|
||||
// if there are new packages then we need a new ModuleDescriptor
|
||||
packages.removeAll(descriptor.packages());
|
||||
if (!packages.isEmpty()) {
|
||||
Builder builder = JLMA.newModuleBuilder(descriptor.name(),
|
||||
/*strict*/ descriptor.isAutomatic(),
|
||||
descriptor.modifiers());
|
||||
if (!descriptor.isAutomatic()) {
|
||||
descriptor.requires().forEach(builder::requires);
|
||||
descriptor.exports().forEach(builder::exports);
|
||||
descriptor.opens().forEach(builder::opens);
|
||||
descriptor.uses().forEach(builder::uses);
|
||||
}
|
||||
descriptor.provides().forEach(builder::provides);
|
||||
|
||||
descriptor.version().ifPresent(builder::version);
|
||||
descriptor.mainClass().ifPresent(builder::mainClass);
|
||||
|
||||
// original + new packages
|
||||
builder.packages(descriptor.packages());
|
||||
builder.packages(packages);
|
||||
|
||||
descriptor = builder.build();
|
||||
}
|
||||
|
||||
// return a module reference to the patched module
|
||||
URI location = mref.location().orElse(null);
|
||||
|
||||
ModuleTarget target = null;
|
||||
ModuleHashes recordedHashes = null;
|
||||
ModuleHashes.HashSupplier hasher = null;
|
||||
ModuleResolution mres = null;
|
||||
if (mref instanceof ModuleReferenceImpl) {
|
||||
ModuleReferenceImpl impl = (ModuleReferenceImpl)mref;
|
||||
target = impl.moduleTarget();
|
||||
recordedHashes = impl.recordedHashes();
|
||||
hasher = impl.hasher();
|
||||
mres = impl.moduleResolution();
|
||||
}
|
||||
|
||||
return new ModuleReferenceImpl(descriptor,
|
||||
location,
|
||||
() -> new PatchedModuleReader(paths, mref),
|
||||
this,
|
||||
target,
|
||||
recordedHashes,
|
||||
hasher,
|
||||
mres);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true is this module patcher has patches.
|
||||
*/
|
||||
public boolean hasPatches() {
|
||||
return !map.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the names of the patched modules.
|
||||
*/
|
||||
Set<String> patchedModules() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* A ModuleReader that reads resources from a patched module.
|
||||
*
|
||||
* This class is public so as to expose the findResource method to the
|
||||
* built-in class loaders and avoid locating the resource twice during
|
||||
* class loading (once to locate the resource, the second to gets the
|
||||
* URL for the CodeSource).
|
||||
*/
|
||||
public static class PatchedModuleReader implements ModuleReader {
|
||||
private final List<ResourceFinder> finders;
|
||||
private final ModuleReference mref;
|
||||
private final URL delegateCodeSourceURL;
|
||||
private volatile ModuleReader delegate;
|
||||
private volatile boolean closed;
|
||||
|
||||
/**
|
||||
* Creates the ModuleReader to reads resources in a patched module.
|
||||
*/
|
||||
PatchedModuleReader(List<Path> patches, ModuleReference mref) {
|
||||
List<ResourceFinder> finders = new ArrayList<>();
|
||||
boolean initialized = false;
|
||||
try {
|
||||
for (Path file : patches) {
|
||||
if (Files.isRegularFile(file)) {
|
||||
finders.add(new JarResourceFinder(file));
|
||||
} else {
|
||||
finders.add(new ExplodedResourceFinder(file));
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
} finally {
|
||||
// close all ResourceFinder in the event of an error
|
||||
if (!initialized) closeAll(finders);
|
||||
}
|
||||
|
||||
this.finders = finders;
|
||||
this.mref = mref;
|
||||
this.delegateCodeSourceURL = codeSourceURL(mref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all resource finders.
|
||||
*/
|
||||
private static void closeAll(List<ResourceFinder> finders) {
|
||||
for (ResourceFinder finder : finders) {
|
||||
try { finder.close(); } catch (IOException ioe) { }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code source URL for the given module.
|
||||
*/
|
||||
private static URL codeSourceURL(ModuleReference mref) {
|
||||
try {
|
||||
Optional<URI> ouri = mref.location();
|
||||
if (ouri.isPresent())
|
||||
return ouri.get().toURL();
|
||||
} catch (MalformedURLException e) { }
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ModuleReader to delegate to when the resource is not
|
||||
* found in a patch location.
|
||||
*/
|
||||
private ModuleReader delegate() throws IOException {
|
||||
ModuleReader r = delegate;
|
||||
if (r == null) {
|
||||
synchronized (this) {
|
||||
r = delegate;
|
||||
if (r == null) {
|
||||
delegate = r = mref.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an IOException if the ModuleReader is closed.
|
||||
*/
|
||||
private void ensureOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("ModuleReader is closed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a resources in the patch locations. Returns null if not found
|
||||
* or the name is "module-info.class" as that cannot be overridden.
|
||||
*/
|
||||
private Resource findResourceInPatch(String name) throws IOException {
|
||||
if (!name.equals("module-info.class")) {
|
||||
for (ResourceFinder finder : finders) {
|
||||
Resource r = finder.find(name);
|
||||
if (r != null)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a resource of the given name in the patched module.
|
||||
*/
|
||||
public Resource findResource(String name) throws IOException {
|
||||
assert !closed : "module reader is closed";
|
||||
// patch locations
|
||||
Resource r = findResourceInPatch(name);
|
||||
if (r != null)
|
||||
return r;
|
||||
|
||||
// original module
|
||||
ByteBuffer bb = delegate().read(name).orElse(null);
|
||||
if (bb == null)
|
||||
return null;
|
||||
|
||||
return new Resource() {
|
||||
private <T> T shouldNotGetHere(Class<T> type) {
|
||||
throw new InternalError("should not get here");
|
||||
}
|
||||
@Override
|
||||
public String getName() {
|
||||
return shouldNotGetHere(String.class);
|
||||
}
|
||||
@Override
|
||||
public URL getURL() {
|
||||
return shouldNotGetHere(URL.class);
|
||||
}
|
||||
@Override
|
||||
public URL getCodeSourceURL() {
|
||||
return delegateCodeSourceURL;
|
||||
}
|
||||
@Override
|
||||
public ByteBuffer getByteBuffer() throws IOException {
|
||||
return bb;
|
||||
}
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return shouldNotGetHere(InputStream.class);
|
||||
}
|
||||
@Override
|
||||
public int getContentLength() throws IOException {
|
||||
return shouldNotGetHere(int.class);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<URI> find(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Resource r = findResourceInPatch(name);
|
||||
if (r != null) {
|
||||
URI uri = URI.create(r.getURL().toString());
|
||||
return Optional.of(uri);
|
||||
} else {
|
||||
return delegate().find(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InputStream> open(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Resource r = findResourceInPatch(name);
|
||||
if (r != null) {
|
||||
return Optional.of(r.getInputStream());
|
||||
} else {
|
||||
return delegate().open(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ByteBuffer> read(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Resource r = findResourceInPatch(name);
|
||||
if (r != null) {
|
||||
ByteBuffer bb = r.getByteBuffer();
|
||||
assert !bb.isDirect();
|
||||
return Optional.of(bb);
|
||||
} else {
|
||||
return delegate().read(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(ByteBuffer bb) {
|
||||
if (bb.isDirect()) {
|
||||
try {
|
||||
delegate().release(bb);
|
||||
} catch (IOException ioe) {
|
||||
throw new InternalError(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> list() throws IOException {
|
||||
ensureOpen();
|
||||
Stream<String> s = delegate().list();
|
||||
for (ResourceFinder finder : finders) {
|
||||
s = Stream.concat(s, finder.list());
|
||||
}
|
||||
return s.distinct();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
closeAll(finders);
|
||||
delegate().close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A resource finder that find resources in a patch location.
|
||||
*/
|
||||
private static interface ResourceFinder extends Closeable {
|
||||
Resource find(String name) throws IOException;
|
||||
Stream<String> list() throws IOException;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A ResourceFinder that finds resources in a JAR file.
|
||||
*/
|
||||
private static class JarResourceFinder implements ResourceFinder {
|
||||
private final JarFile jf;
|
||||
private final URL csURL;
|
||||
|
||||
JarResourceFinder(Path path) throws IOException {
|
||||
this.jf = new JarFile(path.toString());
|
||||
this.csURL = path.toUri().toURL();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
jf.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource find(String name) throws IOException {
|
||||
JarEntry entry = jf.getJarEntry(name);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
return new Resource() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@Override
|
||||
public URL getURL() {
|
||||
String encodedPath = ParseUtil.encodePath(name, false);
|
||||
try {
|
||||
@SuppressWarnings("deprecation")
|
||||
var result = new URL("jar:" + csURL + "!/" + encodedPath);
|
||||
return result;
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public URL getCodeSourceURL() {
|
||||
return csURL;
|
||||
}
|
||||
@Override
|
||||
public ByteBuffer getByteBuffer() throws IOException {
|
||||
try (InputStream in = getInputStream()) {
|
||||
byte[] bytes = in.readAllBytes();
|
||||
return ByteBuffer.wrap(bytes);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return jf.getInputStream(entry);
|
||||
}
|
||||
@Override
|
||||
public int getContentLength() throws IOException {
|
||||
long size = entry.getSize();
|
||||
return (size > Integer.MAX_VALUE) ? -1 : (int) size;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> list() throws IOException {
|
||||
return jf.stream().map(JarEntry::getName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A ResourceFinder that finds resources on the file system.
|
||||
*/
|
||||
private static class ExplodedResourceFinder implements ResourceFinder {
|
||||
private final Path dir;
|
||||
|
||||
ExplodedResourceFinder(Path dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() { }
|
||||
|
||||
@Override
|
||||
public Resource find(String name) throws IOException {
|
||||
Path file = Resources.toFilePath(dir, name);
|
||||
if (file != null) {
|
||||
return newResource(name, dir, file);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Resource newResource(String name, Path top, Path file) {
|
||||
return new Resource() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@Override
|
||||
public URL getURL() {
|
||||
try {
|
||||
return file.toUri().toURL();
|
||||
} catch (IOException | IOError e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public URL getCodeSourceURL() {
|
||||
try {
|
||||
return top.toUri().toURL();
|
||||
} catch (IOException | IOError e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public ByteBuffer getByteBuffer() throws IOException {
|
||||
return ByteBuffer.wrap(Files.readAllBytes(file));
|
||||
}
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return Files.newInputStream(file);
|
||||
}
|
||||
@Override
|
||||
public int getContentLength() throws IOException {
|
||||
long size = Files.size(file);
|
||||
return (size > Integer.MAX_VALUE) ? -1 : (int)size;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> list() throws IOException {
|
||||
return Files.walk(dir, Integer.MAX_VALUE)
|
||||
.map(f -> Resources.toResourceName(dir, f))
|
||||
.filter(s -> !s.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Derives a package name from the file path of an entry in an exploded patch
|
||||
*/
|
||||
private static String toPackageName(Path top, Path file) {
|
||||
Path entry = top.relativize(file);
|
||||
Path parent = entry.getParent();
|
||||
if (parent == null) {
|
||||
return warnIfModuleInfo(top, entry.toString());
|
||||
} else {
|
||||
return parent.toString().replace(File.separatorChar, '.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given file exists and is a hidden file
|
||||
*/
|
||||
private boolean isHidden(Path file) {
|
||||
try {
|
||||
return Files.isHidden(file);
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a package name from the name of an entry in a JAR file.
|
||||
*/
|
||||
private static String toPackageName(Path file, JarEntry entry) {
|
||||
String name = entry.getName();
|
||||
int index = name.lastIndexOf("/");
|
||||
if (index == -1) {
|
||||
return warnIfModuleInfo(file, name);
|
||||
} else {
|
||||
return name.substring(0, index).replace('/', '.');
|
||||
}
|
||||
}
|
||||
|
||||
private static String warnIfModuleInfo(Path file, String e) {
|
||||
if (e.equals("module-info.class"))
|
||||
System.err.println("WARNING: " + e + " ignored in patch: " + file);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
789
src/java.base/share/classes/jdk/internal/module/ModulePath.java
Normal file
789
src/java.base/share/classes/jdk/internal/module/ModulePath.java
Normal file
|
|
@ -0,0 +1,789 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, 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.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
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.Builder;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.net.URI;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import sun.nio.cs.UTF_8;
|
||||
|
||||
import jdk.internal.jmod.JmodFile;
|
||||
import jdk.internal.jmod.JmodFile.Section;
|
||||
import jdk.internal.perf.PerfCounter;
|
||||
|
||||
/**
|
||||
* A {@code ModuleFinder} that locates modules on the file system by searching
|
||||
* a sequence of directories or packaged modules. The ModuleFinder can be
|
||||
* created to work in either the run-time or link-time phases. In both cases it
|
||||
* locates modular JAR and exploded modules. When created for link-time then it
|
||||
* additionally locates modules in JMOD files. The ModuleFinder can also
|
||||
* optionally patch any modules that it locates with a ModulePatcher.
|
||||
*/
|
||||
|
||||
public class ModulePath implements ModuleFinder {
|
||||
private static final String MODULE_INFO = "module-info.class";
|
||||
|
||||
// the version to use for multi-release modular JARs
|
||||
private final Runtime.Version releaseVersion;
|
||||
|
||||
// true for the link phase (supports modules packaged in JMOD format)
|
||||
private final boolean isLinkPhase;
|
||||
|
||||
// for patching modules, can be null
|
||||
private final ModulePatcher patcher;
|
||||
|
||||
// the entries on this module path
|
||||
private final Path[] entries;
|
||||
private int next;
|
||||
|
||||
// map of module name to module reference map for modules already located
|
||||
private final Map<String, ModuleReference> cachedModules = new HashMap<>();
|
||||
|
||||
|
||||
private ModulePath(Runtime.Version version,
|
||||
boolean isLinkPhase,
|
||||
ModulePatcher patcher,
|
||||
Path... entries) {
|
||||
this.releaseVersion = version;
|
||||
this.isLinkPhase = isLinkPhase;
|
||||
this.patcher = patcher;
|
||||
this.entries = entries.clone();
|
||||
for (Path entry : this.entries) {
|
||||
Objects.requireNonNull(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ModuleFinder that locates modules on the file system by
|
||||
* searching a sequence of directories and/or packaged modules. The modules
|
||||
* may be patched by the given ModulePatcher.
|
||||
*/
|
||||
public static ModuleFinder of(ModulePatcher patcher, Path... entries) {
|
||||
return new ModulePath(JarFile.runtimeVersion(), false, patcher, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ModuleFinder that locates modules on the file system by
|
||||
* searching a sequence of directories and/or packaged modules.
|
||||
*/
|
||||
public static ModuleFinder of(Path... entries) {
|
||||
return of((ModulePatcher)null, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ModuleFinder that locates modules on the file system by
|
||||
* searching a sequence of directories and/or packaged modules.
|
||||
*
|
||||
* @param version The release version to use for multi-release JAR files
|
||||
* @param isLinkPhase {@code true} if the link phase to locate JMOD files
|
||||
*/
|
||||
public static ModuleFinder of(Runtime.Version version,
|
||||
boolean isLinkPhase,
|
||||
Path... entries) {
|
||||
return new ModulePath(version, isLinkPhase, null, entries);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
|
||||
// try cached modules
|
||||
ModuleReference m = cachedModules.get(name);
|
||||
if (m != null)
|
||||
return Optional.of(m);
|
||||
|
||||
// the module may not have been encountered yet
|
||||
while (hasNextEntry()) {
|
||||
scanNextEntry();
|
||||
m = cachedModules.get(name);
|
||||
if (m != null)
|
||||
return Optional.of(m);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ModuleReference> findAll() {
|
||||
// need to ensure that all entries have been scanned
|
||||
while (hasNextEntry()) {
|
||||
scanNextEntry();
|
||||
}
|
||||
return cachedModules.values().stream().collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if there are additional entries to scan
|
||||
*/
|
||||
private boolean hasNextEntry() {
|
||||
return next < entries.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the next entry on the module path. A no-op if all entries have
|
||||
* already been scanned.
|
||||
*
|
||||
* @throws FindException if an error occurs scanning the next entry
|
||||
*/
|
||||
private void scanNextEntry() {
|
||||
if (hasNextEntry()) {
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
|
||||
Path entry = entries[next];
|
||||
Map<String, ModuleReference> modules = scan(entry);
|
||||
next++;
|
||||
|
||||
// update cache, ignoring duplicates
|
||||
int initialSize = cachedModules.size();
|
||||
for (Map.Entry<String, ModuleReference> e : modules.entrySet()) {
|
||||
cachedModules.putIfAbsent(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
// update counters
|
||||
int added = cachedModules.size() - initialSize;
|
||||
moduleCount.add(added);
|
||||
|
||||
scanTime.addElapsedTimeFrom(t0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan the given module path entry. If the entry is a directory then it is
|
||||
* a directory of modules or an exploded module. If the entry is a regular
|
||||
* file then it is assumed to be a packaged module.
|
||||
*
|
||||
* @throws FindException if an error occurs scanning the entry
|
||||
*/
|
||||
private Map<String, ModuleReference> scan(Path entry) {
|
||||
|
||||
BasicFileAttributes attrs;
|
||||
try {
|
||||
attrs = Files.readAttributes(entry, BasicFileAttributes.class);
|
||||
} catch (NoSuchFileException e) {
|
||||
return Map.of();
|
||||
} catch (IOException ioe) {
|
||||
throw new FindException(ioe);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (attrs.isDirectory()) {
|
||||
Path mi = entry.resolve(MODULE_INFO);
|
||||
if (!Files.exists(mi)) {
|
||||
// assume a directory of modules
|
||||
return scanDirectory(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// packaged or exploded module
|
||||
ModuleReference mref = readModule(entry, attrs);
|
||||
if (mref != null) {
|
||||
String name = mref.descriptor().name();
|
||||
return Map.of(name, mref);
|
||||
}
|
||||
|
||||
// not recognized
|
||||
String msg;
|
||||
if (!isLinkPhase && entry.toString().endsWith(".jmod")) {
|
||||
msg = "JMOD format not supported at execution time";
|
||||
} else {
|
||||
msg = "Module format not recognized";
|
||||
}
|
||||
throw new FindException(msg + ": " + entry);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new FindException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scans the given directory for packaged or exploded modules.
|
||||
*
|
||||
* @return a map of module name to ModuleReference for the modules found
|
||||
* in the directory
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws FindException if an error occurs scanning the entry or the
|
||||
* directory contains two or more modules with the same name
|
||||
*/
|
||||
private Map<String, ModuleReference> scanDirectory(Path dir)
|
||||
throws IOException
|
||||
{
|
||||
// The map of name -> mref of modules found in this directory.
|
||||
Map<String, ModuleReference> nameToReference = new HashMap<>();
|
||||
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
|
||||
for (Path entry : stream) {
|
||||
BasicFileAttributes attrs;
|
||||
try {
|
||||
attrs = Files.readAttributes(entry, BasicFileAttributes.class);
|
||||
} catch (NoSuchFileException ignore) {
|
||||
// file has been removed or moved, ignore for now
|
||||
continue;
|
||||
}
|
||||
|
||||
ModuleReference mref = readModule(entry, attrs);
|
||||
|
||||
// module found
|
||||
if (mref != null) {
|
||||
// can have at most one version of a module in the directory
|
||||
String name = mref.descriptor().name();
|
||||
ModuleReference previous = nameToReference.put(name, mref);
|
||||
if (previous != null) {
|
||||
String fn1 = fileName(mref);
|
||||
String fn2 = fileName(previous);
|
||||
throw new FindException("Two versions of module "
|
||||
+ name + " found in " + dir
|
||||
+ " (" + fn1 + " and " + fn2 + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nameToReference;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a packaged or exploded module, returning a {@code ModuleReference}
|
||||
* to the module. Returns {@code null} if the entry is not recognized.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws FindException if an error occurs parsing its module descriptor
|
||||
*/
|
||||
private ModuleReference readModule(Path entry, BasicFileAttributes attrs)
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
|
||||
// exploded module
|
||||
if (attrs.isDirectory()) {
|
||||
return readExplodedModule(entry); // may return null
|
||||
}
|
||||
|
||||
// JAR or JMOD file
|
||||
if (attrs.isRegularFile()) {
|
||||
String fn = entry.getFileName().toString();
|
||||
boolean isDefaultFileSystem = isDefaultFileSystem(entry);
|
||||
|
||||
// JAR file
|
||||
if (fn.endsWith(".jar")) {
|
||||
if (isDefaultFileSystem) {
|
||||
return readJar(entry);
|
||||
} else {
|
||||
// the JAR file is in a custom file system so
|
||||
// need to copy it to the local file system
|
||||
Path tmpdir = Files.createTempDirectory("mlib");
|
||||
Path target = Files.copy(entry, tmpdir.resolve(fn));
|
||||
return readJar(target);
|
||||
}
|
||||
}
|
||||
|
||||
// JMOD file
|
||||
if (isDefaultFileSystem && isLinkPhase && fn.endsWith(".jmod")) {
|
||||
return readJMod(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} catch (InvalidModuleDescriptorException e) {
|
||||
throw new FindException("Error reading module: " + entry, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string with the file name of the module if possible.
|
||||
* If the module location is not a file URI then return the URI
|
||||
* as a string.
|
||||
*/
|
||||
private String fileName(ModuleReference mref) {
|
||||
URI uri = mref.location().orElse(null);
|
||||
if (uri != null) {
|
||||
if (uri.getScheme().equalsIgnoreCase("file")) {
|
||||
Path file = Path.of(uri);
|
||||
return file.getFileName().toString();
|
||||
} else {
|
||||
return uri.toString();
|
||||
}
|
||||
} else {
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
// -- JMOD files --
|
||||
|
||||
private Set<String> jmodPackages(JmodFile jf) {
|
||||
return jf.stream()
|
||||
.filter(e -> e.section() == Section.CLASSES)
|
||||
.map(JmodFile.Entry::name)
|
||||
.map(this::toPackageName)
|
||||
.flatMap(Optional::stream)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code ModuleReference} to a module in JMOD file on the
|
||||
* file system.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws InvalidModuleDescriptorException
|
||||
*/
|
||||
private ModuleReference readJMod(Path file) throws IOException {
|
||||
try (JmodFile jf = new JmodFile(file)) {
|
||||
ModuleInfo.Attributes attrs;
|
||||
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
|
||||
attrs = ModuleInfo.read(in, () -> jmodPackages(jf));
|
||||
}
|
||||
return ModuleReferences.newJModModule(attrs, file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -- JAR files --
|
||||
|
||||
private static final String SERVICES_PREFIX = "META-INF/services/";
|
||||
|
||||
private static final Attributes.Name AUTOMATIC_MODULE_NAME
|
||||
= new Attributes.Name("Automatic-Module-Name");
|
||||
|
||||
/**
|
||||
* Returns the service type corresponding to the name of a services
|
||||
* configuration file if it is a legal type name.
|
||||
*
|
||||
* For example, if called with "META-INF/services/p.S" then this method
|
||||
* returns a container with the value "p.S".
|
||||
*/
|
||||
private Optional<String> toServiceName(String cf) {
|
||||
assert cf.startsWith(SERVICES_PREFIX);
|
||||
int index = cf.lastIndexOf("/") + 1;
|
||||
if (index < cf.length()) {
|
||||
String prefix = cf.substring(0, index);
|
||||
if (prefix.equals(SERVICES_PREFIX)) {
|
||||
String sn = cf.substring(index);
|
||||
if (Checks.isClassName(sn))
|
||||
return Optional.of(sn);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next line from the given reader and trims it of comments and
|
||||
* leading/trailing white space.
|
||||
*
|
||||
* Returns null if the reader is at EOF.
|
||||
*/
|
||||
private String nextLine(BufferedReader reader) throws IOException {
|
||||
String ln = reader.readLine();
|
||||
if (ln != null) {
|
||||
int ci = ln.indexOf('#');
|
||||
if (ci >= 0)
|
||||
ln = ln.substring(0, ci);
|
||||
ln = ln.trim();
|
||||
}
|
||||
return ln;
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat the given JAR file as a module as follows:
|
||||
*
|
||||
* 1. The value of the Automatic-Module-Name attribute is the module name
|
||||
* 2. The version, and the module name when the Automatic-Module-Name
|
||||
* attribute is not present, is derived from the file ame of the JAR file
|
||||
* 3. All packages are derived from the .class files in the JAR file
|
||||
* 4. The contents of any META-INF/services configuration files are mapped
|
||||
* to "provides" declarations
|
||||
* 5. The Main-Class attribute in the main attributes of the JAR manifest
|
||||
* is mapped to the module descriptor mainClass if possible
|
||||
*/
|
||||
private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
|
||||
throws IOException
|
||||
{
|
||||
// Read Automatic-Module-Name attribute if present
|
||||
Manifest man = jf.getManifest();
|
||||
Attributes attrs = null;
|
||||
String moduleName = null;
|
||||
if (man != null) {
|
||||
attrs = man.getMainAttributes();
|
||||
if (attrs != null) {
|
||||
moduleName = attrs.getValue(AUTOMATIC_MODULE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Derive the version, and the module name if needed, from JAR file name
|
||||
String fn = jf.getName();
|
||||
int i = fn.lastIndexOf(File.separator);
|
||||
if (i != -1)
|
||||
fn = fn.substring(i + 1);
|
||||
|
||||
// drop ".jar"
|
||||
String name = fn.substring(0, fn.length() - 4);
|
||||
String vs = null;
|
||||
|
||||
// find first occurrence of -${NUMBER}. or -${NUMBER}$
|
||||
Matcher matcher = Patterns.DASH_VERSION.matcher(name);
|
||||
if (matcher.find()) {
|
||||
int start = matcher.start();
|
||||
|
||||
// attempt to parse the tail as a version string
|
||||
try {
|
||||
String tail = name.substring(start + 1);
|
||||
ModuleDescriptor.Version.parse(tail);
|
||||
vs = tail;
|
||||
} catch (IllegalArgumentException ignore) { }
|
||||
|
||||
name = name.substring(0, start);
|
||||
}
|
||||
|
||||
// Create builder, using the name derived from file name when
|
||||
// Automatic-Module-Name not present
|
||||
Builder builder;
|
||||
if (moduleName != null) {
|
||||
try {
|
||||
builder = ModuleDescriptor.newAutomaticModule(moduleName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new FindException(AUTOMATIC_MODULE_NAME + ": " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
builder = ModuleDescriptor.newAutomaticModule(cleanModuleName(name));
|
||||
}
|
||||
|
||||
// module version if present
|
||||
if (vs != null)
|
||||
builder.version(vs);
|
||||
|
||||
// scan the names of the entries in the JAR file
|
||||
Map<Boolean, Set<String>> map = jf.versionedStream()
|
||||
.filter(e -> !e.isDirectory())
|
||||
.map(JarEntry::getName)
|
||||
.filter(e -> (e.endsWith(".class") ^ e.startsWith(SERVICES_PREFIX)))
|
||||
.collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
|
||||
Collectors.toSet()));
|
||||
|
||||
Set<String> classFiles = map.get(Boolean.FALSE);
|
||||
Set<String> configFiles = map.get(Boolean.TRUE);
|
||||
|
||||
// the packages containing class files
|
||||
Set<String> packages = classFiles.stream()
|
||||
.map(this::toPackageName)
|
||||
.flatMap(Optional::stream)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// all packages are exported and open
|
||||
builder.packages(packages);
|
||||
|
||||
// map names of service configuration files to service names
|
||||
Set<String> serviceNames = configFiles.stream()
|
||||
.map(this::toServiceName)
|
||||
.flatMap(Optional::stream)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// parse each service configuration file
|
||||
for (String sn : serviceNames) {
|
||||
JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
|
||||
List<String> providerClasses = new ArrayList<>();
|
||||
try (InputStream in = jf.getInputStream(entry)) {
|
||||
BufferedReader reader
|
||||
= new BufferedReader(new InputStreamReader(in, UTF_8.INSTANCE));
|
||||
String cn;
|
||||
while ((cn = nextLine(reader)) != null) {
|
||||
if (!cn.isEmpty()) {
|
||||
String pn = packageName(cn);
|
||||
if (!packages.contains(pn)) {
|
||||
String msg = "Provider class " + cn + " not in JAR file " + fn;
|
||||
throw new InvalidModuleDescriptorException(msg);
|
||||
}
|
||||
providerClasses.add(cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!providerClasses.isEmpty())
|
||||
builder.provides(sn, providerClasses);
|
||||
}
|
||||
|
||||
// Main-Class attribute if it exists
|
||||
if (attrs != null) {
|
||||
String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
|
||||
if (mainClass != null) {
|
||||
mainClass = mainClass.replace('/', '.');
|
||||
if (Checks.isClassName(mainClass)) {
|
||||
String pn = packageName(mainClass);
|
||||
if (packages.contains(pn)) {
|
||||
builder.mainClass(mainClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Patterns used to derive the module name from a JAR file name.
|
||||
*/
|
||||
private static class Patterns {
|
||||
static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
|
||||
static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
|
||||
static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
|
||||
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
|
||||
static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up candidate module name derived from a JAR file name.
|
||||
*/
|
||||
private static String cleanModuleName(String mn) {
|
||||
// replace non-alphanumeric
|
||||
mn = Patterns.NON_ALPHANUM.matcher(mn).replaceAll(".");
|
||||
|
||||
// collapse repeating dots
|
||||
mn = Patterns.REPEATING_DOTS.matcher(mn).replaceAll(".");
|
||||
|
||||
// drop leading dots
|
||||
if (!mn.isEmpty() && mn.charAt(0) == '.')
|
||||
mn = Patterns.LEADING_DOTS.matcher(mn).replaceAll("");
|
||||
|
||||
// drop trailing dots
|
||||
int len = mn.length();
|
||||
if (len > 0 && mn.charAt(len-1) == '.')
|
||||
mn = Patterns.TRAILING_DOTS.matcher(mn).replaceAll("");
|
||||
|
||||
return mn;
|
||||
}
|
||||
|
||||
private Set<String> jarPackages(JarFile jf) {
|
||||
return jf.versionedStream()
|
||||
.filter(e -> !e.isDirectory())
|
||||
.map(JarEntry::getName)
|
||||
.map(this::toPackageName)
|
||||
.flatMap(Optional::stream)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code ModuleReference} to a module in modular JAR file on
|
||||
* the file system.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws FindException
|
||||
* @throws InvalidModuleDescriptorException
|
||||
*/
|
||||
private ModuleReference readJar(Path file) throws IOException {
|
||||
try (JarFile jf = new JarFile(file.toFile(),
|
||||
true, // verify
|
||||
ZipFile.OPEN_READ,
|
||||
releaseVersion))
|
||||
{
|
||||
ModuleInfo.Attributes attrs;
|
||||
JarEntry entry = jf.getJarEntry(MODULE_INFO);
|
||||
if (entry == null) {
|
||||
|
||||
// no module-info.class so treat it as automatic module
|
||||
try {
|
||||
ModuleDescriptor md = deriveModuleDescriptor(jf);
|
||||
attrs = new ModuleInfo.Attributes(md, null, null, null);
|
||||
} catch (RuntimeException e) {
|
||||
throw new FindException("Unable to derive module descriptor for "
|
||||
+ jf.getName(), e);
|
||||
}
|
||||
|
||||
} else {
|
||||
attrs = ModuleInfo.read(jf.getInputStream(entry),
|
||||
() -> jarPackages(jf));
|
||||
}
|
||||
|
||||
return ModuleReferences.newJarModule(attrs, patcher, file);
|
||||
} catch (ZipException e) {
|
||||
throw new FindException("Error reading " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -- exploded directories --
|
||||
|
||||
private Set<String> explodedPackages(Path dir) {
|
||||
String separator = dir.getFileSystem().getSeparator();
|
||||
try (Stream<Path> stream = Files.find(dir, Integer.MAX_VALUE,
|
||||
(path, attrs) -> attrs.isRegularFile() && !isHidden(path))) {
|
||||
return stream.map(dir::relativize)
|
||||
.map(path -> toPackageName(path, separator))
|
||||
.flatMap(Optional::stream)
|
||||
.collect(Collectors.toSet());
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code ModuleReference} to an exploded module on the file
|
||||
* system or {@code null} if {@code module-info.class} not found.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws InvalidModuleDescriptorException
|
||||
*/
|
||||
private ModuleReference readExplodedModule(Path dir) throws IOException {
|
||||
Path mi = dir.resolve(MODULE_INFO);
|
||||
ModuleInfo.Attributes attrs;
|
||||
try (InputStream in = Files.newInputStream(mi)) {
|
||||
attrs = ModuleInfo.read(new BufferedInputStream(in),
|
||||
() -> explodedPackages(dir));
|
||||
} catch (NoSuchFileException e) {
|
||||
// for now
|
||||
return null;
|
||||
}
|
||||
return ModuleReferences.newExplodedModule(attrs, patcher, dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a type name to its package name.
|
||||
*/
|
||||
private static String packageName(String cn) {
|
||||
int index = cn.lastIndexOf('.');
|
||||
return (index == -1) ? "" : cn.substring(0, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the name of an entry in a JAR or ZIP file to a package name.
|
||||
*
|
||||
* @throws InvalidModuleDescriptorException if the name is a class file in
|
||||
* the top-level directory of the JAR/ZIP file (and it's not
|
||||
* module-info.class)
|
||||
*/
|
||||
private Optional<String> toPackageName(String name) {
|
||||
assert !name.endsWith("/");
|
||||
int index = name.lastIndexOf("/");
|
||||
if (index == -1) {
|
||||
if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
|
||||
String msg = name + " found in top-level directory"
|
||||
+ " (unnamed package not allowed in module)";
|
||||
throw new InvalidModuleDescriptorException(msg);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
String pn = name.substring(0, index).replace('/', '.');
|
||||
if (Checks.isPackageName(pn)) {
|
||||
return Optional.of(pn);
|
||||
} else {
|
||||
// not a valid package name
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the relative path of an entry in an exploded module to a package
|
||||
* name.
|
||||
*
|
||||
* @throws InvalidModuleDescriptorException if the name is a class file in
|
||||
* the top-level directory (and it's not module-info.class)
|
||||
*/
|
||||
private Optional<String> toPackageName(Path file, String separator) {
|
||||
assert file.getRoot() == null;
|
||||
|
||||
Path parent = file.getParent();
|
||||
if (parent == null) {
|
||||
String name = file.toString();
|
||||
if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
|
||||
String msg = name + " found in top-level directory"
|
||||
+ " (unnamed package not allowed in module)";
|
||||
throw new InvalidModuleDescriptorException(msg);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
String pn = parent.toString().replace(separator, ".");
|
||||
if (Checks.isPackageName(pn)) {
|
||||
return Optional.of(pn);
|
||||
} else {
|
||||
// not a valid package name
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given file exists and is a hidden file
|
||||
*/
|
||||
private boolean isHidden(Path file) {
|
||||
try {
|
||||
return Files.isHidden(file);
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if a path locates a path in the default file system
|
||||
*/
|
||||
private boolean isDefaultFileSystem(Path path) {
|
||||
return path.getFileSystem().provider()
|
||||
.getScheme().equalsIgnoreCase("file");
|
||||
}
|
||||
|
||||
|
||||
private static final PerfCounter scanTime
|
||||
= PerfCounter.newPerfCounter("jdk.module.finder.modulepath.scanTime");
|
||||
private static final PerfCounter moduleCount
|
||||
= PerfCounter.newPerfCounter("jdk.module.finder.modulepath.modules");
|
||||
}
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 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.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.module.FindException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.net.URI;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A validator to check for errors and conflicts between modules.
|
||||
*/
|
||||
|
||||
class ModulePathValidator {
|
||||
private static final String MODULE_INFO = "module-info.class";
|
||||
private static final String INDENT = " ";
|
||||
|
||||
private final Map<String, ModuleReference> nameToModule;
|
||||
private final Map<String, ModuleReference> packageToModule;
|
||||
private final PrintStream out;
|
||||
|
||||
private int errorCount;
|
||||
|
||||
private ModulePathValidator(PrintStream out) {
|
||||
this.nameToModule = new HashMap<>();
|
||||
this.packageToModule = new HashMap<>();
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans and the validates all modules on the module path. The module path
|
||||
* comprises the upgrade module path, system modules, and the application
|
||||
* module path.
|
||||
*
|
||||
* @param out the print stream for output messages
|
||||
* @return the number of errors found
|
||||
*/
|
||||
static int scanAllModules(PrintStream out) {
|
||||
ModulePathValidator validator = new ModulePathValidator(out);
|
||||
|
||||
// upgrade module path
|
||||
String value = System.getProperty("jdk.module.upgrade.path");
|
||||
if (value != null) {
|
||||
Stream.of(value.split(File.pathSeparator))
|
||||
.map(Path::of)
|
||||
.forEach(validator::scan);
|
||||
}
|
||||
|
||||
// system modules
|
||||
ModuleFinder.ofSystem().findAll().stream()
|
||||
.sorted(Comparator.comparing(ModuleReference::descriptor))
|
||||
.forEach(validator::process);
|
||||
|
||||
// application module path
|
||||
value = System.getProperty("jdk.module.path");
|
||||
if (value != null) {
|
||||
Stream.of(value.split(File.pathSeparator))
|
||||
.map(Path::of)
|
||||
.forEach(validator::scan);
|
||||
}
|
||||
|
||||
return validator.errorCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the module location and name.
|
||||
*/
|
||||
private void printModule(ModuleReference mref) {
|
||||
mref.location()
|
||||
.filter(uri -> !isJrt(uri))
|
||||
.ifPresent(uri -> out.print(uri + " "));
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
out.print(descriptor.name());
|
||||
if (descriptor.isAutomatic())
|
||||
out.print(" automatic");
|
||||
out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the module location and name, checks if the module is
|
||||
* shadowed by a previously seen module, and finally checks for
|
||||
* package conflicts with previously seen modules.
|
||||
*/
|
||||
private void process(ModuleReference mref) {
|
||||
String name = mref.descriptor().name();
|
||||
ModuleReference previous = nameToModule.putIfAbsent(name, mref);
|
||||
if (previous != null) {
|
||||
printModule(mref);
|
||||
out.print(INDENT + "shadowed by ");
|
||||
printModule(previous);
|
||||
} else {
|
||||
boolean first = true;
|
||||
|
||||
// check for package conflicts when not shadowed
|
||||
for (String pkg : mref.descriptor().packages()) {
|
||||
previous = packageToModule.putIfAbsent(pkg, mref);
|
||||
if (previous != null) {
|
||||
if (first) {
|
||||
printModule(mref);
|
||||
first = false;
|
||||
errorCount++;
|
||||
}
|
||||
String mn = previous.descriptor().name();
|
||||
out.println(INDENT + "contains " + pkg
|
||||
+ " conflicts with module " + mn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan an element on a module path. The element is a directory
|
||||
* of modules, an exploded module, or a JAR file.
|
||||
*/
|
||||
private void scan(Path entry) {
|
||||
BasicFileAttributes attrs;
|
||||
try {
|
||||
attrs = Files.readAttributes(entry, BasicFileAttributes.class);
|
||||
} catch (NoSuchFileException ignore) {
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
out.println(entry + " " + ioe);
|
||||
errorCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
String fn = entry.getFileName().toString();
|
||||
if (attrs.isRegularFile() && fn.endsWith(".jar")) {
|
||||
// JAR file, explicit or automatic module
|
||||
scanModule(entry).ifPresent(this::process);
|
||||
} else if (attrs.isDirectory()) {
|
||||
Path mi = entry.resolve(MODULE_INFO);
|
||||
if (Files.exists(mi)) {
|
||||
// exploded module
|
||||
scanModule(entry).ifPresent(this::process);
|
||||
} else {
|
||||
// directory of modules
|
||||
scanDirectory(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the JAR files and exploded modules in a directory.
|
||||
*/
|
||||
private void scanDirectory(Path dir) {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
|
||||
Map<String, Path> moduleToEntry = new HashMap<>();
|
||||
|
||||
for (Path entry : stream) {
|
||||
BasicFileAttributes attrs;
|
||||
try {
|
||||
attrs = Files.readAttributes(entry, BasicFileAttributes.class);
|
||||
} catch (IOException ioe) {
|
||||
out.println(entry + " " + ioe);
|
||||
errorCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
ModuleReference mref = null;
|
||||
|
||||
String fn = entry.getFileName().toString();
|
||||
if (attrs.isRegularFile() && fn.endsWith(".jar")) {
|
||||
mref = scanModule(entry).orElse(null);
|
||||
} else if (attrs.isDirectory()) {
|
||||
Path mi = entry.resolve(MODULE_INFO);
|
||||
if (Files.exists(mi)) {
|
||||
mref = scanModule(entry).orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (mref != null) {
|
||||
String name = mref.descriptor().name();
|
||||
Path previous = moduleToEntry.putIfAbsent(name, entry);
|
||||
if (previous != null) {
|
||||
// same name as other module in the directory
|
||||
printModule(mref);
|
||||
out.println(INDENT + "contains same module as "
|
||||
+ previous.getFileName());
|
||||
errorCount++;
|
||||
} else {
|
||||
process(mref);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
out.println(dir + " " + ioe);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a JAR file or exploded module.
|
||||
*/
|
||||
private Optional<ModuleReference> scanModule(Path entry) {
|
||||
ModuleFinder finder = ModuleFinder.of(entry);
|
||||
try {
|
||||
return finder.findAll().stream().findFirst();
|
||||
} catch (FindException e) {
|
||||
out.println(entry);
|
||||
out.println(INDENT + e.getMessage());
|
||||
Throwable cause = e.getCause();
|
||||
if (cause != null) {
|
||||
out.println(INDENT + cause);
|
||||
}
|
||||
errorCount++;
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given URI is a jrt URI
|
||||
*/
|
||||
private static boolean isJrt(URI uri) {
|
||||
return (uri != null && uri.getScheme().equalsIgnoreCase("jrt"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2022, 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 {
|
||||
|
||||
// location of module
|
||||
private final URI location;
|
||||
|
||||
// the module reader
|
||||
private final Supplier<ModuleReader> readerSupplier;
|
||||
|
||||
// non-null if the module is patched
|
||||
private final ModulePatcher patcher;
|
||||
|
||||
// ModuleTarget if the module is OS/architecture specific
|
||||
private final ModuleTarget target;
|
||||
|
||||
// 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;
|
||||
|
||||
// Single-slot cache of this module's hash to avoid needing to compute
|
||||
// it many times. For correctness under concurrent updates, we need to
|
||||
// wrap the fields updated at the same time with a record.
|
||||
private record CachedHash(byte[] hash, String algorithm) {}
|
||||
private CachedHash cachedHash;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this class.
|
||||
*/
|
||||
public ModuleReferenceImpl(ModuleDescriptor descriptor,
|
||||
URI location,
|
||||
Supplier<ModuleReader> readerSupplier,
|
||||
ModulePatcher patcher,
|
||||
ModuleTarget target,
|
||||
ModuleHashes recordedHashes,
|
||||
ModuleHashes.HashSupplier hasher,
|
||||
ModuleResolution moduleResolution)
|
||||
{
|
||||
super(descriptor, Objects.requireNonNull(location));
|
||||
this.location = location;
|
||||
this.readerSupplier = readerSupplier;
|
||||
this.patcher = patcher;
|
||||
this.target = target;
|
||||
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 ModuleTarget or {@code null} if the no target platform.
|
||||
*/
|
||||
public ModuleTarget moduleTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
CachedHash ch = cachedHash;
|
||||
if (ch != null && ch.algorithm().equals(algorithm)) {
|
||||
return ch.hash();
|
||||
}
|
||||
|
||||
if (hasher == null) {
|
||||
return null;
|
||||
}
|
||||
byte[] hash = hasher.generate(algorithm);
|
||||
cachedHash = new CachedHash(hash, algorithm);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@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() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[module ");
|
||||
sb.append(descriptor().name());
|
||||
sb.append(", location=");
|
||||
sb.append(location);
|
||||
if (isPatched()) sb.append(" (patched)");
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2024, 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.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;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import jdk.internal.jmod.JmodFile;
|
||||
import jdk.internal.module.ModuleHashes.HashSupplier;
|
||||
import sun.net.www.ParseUtil;
|
||||
|
||||
|
||||
/**
|
||||
* A factory for creating ModuleReference implementations where the modules are
|
||||
* packaged as modular JAR file, JMOD files or where the modules are exploded
|
||||
* on the file system.
|
||||
*/
|
||||
|
||||
class ModuleReferences {
|
||||
private ModuleReferences() { }
|
||||
|
||||
/**
|
||||
* Creates a ModuleReference to a possibly-patched module
|
||||
*/
|
||||
private static ModuleReference newModule(ModuleInfo.Attributes attrs,
|
||||
URI uri,
|
||||
Supplier<ModuleReader> supplier,
|
||||
ModulePatcher patcher,
|
||||
HashSupplier hasher) {
|
||||
ModuleReference mref = new ModuleReferenceImpl(attrs.descriptor(),
|
||||
uri,
|
||||
supplier,
|
||||
null,
|
||||
attrs.target(),
|
||||
attrs.recordedHashes(),
|
||||
hasher,
|
||||
attrs.moduleResolution());
|
||||
if (patcher != null)
|
||||
mref = patcher.patchIfNeeded(mref);
|
||||
|
||||
return mref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ModuleReference to a possibly-patched module in a modular JAR.
|
||||
*/
|
||||
static ModuleReference newJarModule(ModuleInfo.Attributes attrs,
|
||||
ModulePatcher patcher,
|
||||
Path file) {
|
||||
URI uri = file.toUri();
|
||||
String fileString = file.toString();
|
||||
Supplier<ModuleReader> supplier = new Supplier<>() {
|
||||
@Override
|
||||
public ModuleReader get() {
|
||||
return new JarModuleReader(fileString, uri);
|
||||
}
|
||||
};
|
||||
HashSupplier hasher = new HashSupplier() {
|
||||
@Override
|
||||
public byte[] generate(String algorithm) {
|
||||
return ModuleHashes.computeHash(supplier, algorithm);
|
||||
}
|
||||
};
|
||||
return newModule(attrs, uri, supplier, patcher, hasher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ModuleReference to a module in a JMOD 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(supplier, a);
|
||||
return newModule(attrs, uri, supplier, null, hasher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ModuleReference to a possibly-patched exploded module.
|
||||
*/
|
||||
static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs,
|
||||
ModulePatcher patcher,
|
||||
Path dir) {
|
||||
Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir);
|
||||
return newModule(attrs, dir.toUri(), supplier, patcher, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A base module reader that encapsulates machinery required to close the
|
||||
* module reader safely.
|
||||
*/
|
||||
abstract static class SafeCloseModuleReader implements ModuleReader {
|
||||
|
||||
// RW lock to support safe close
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Lock readLock = lock.readLock();
|
||||
private final Lock writeLock = lock.writeLock();
|
||||
private boolean closed;
|
||||
|
||||
SafeCloseModuleReader() { }
|
||||
|
||||
/**
|
||||
* Returns a URL to resource. This method is invoked by the find
|
||||
* method to do the actual work of finding the resource.
|
||||
*/
|
||||
abstract Optional<URI> implFind(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns an input stream for reading a resource. This method is
|
||||
* invoked by the open method to do the actual work of opening
|
||||
* an input stream to the resource.
|
||||
*/
|
||||
abstract Optional<InputStream> implOpen(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a stream of the names of resources in the module. This
|
||||
* method is invoked by the list method to do the actual work of
|
||||
* creating the stream.
|
||||
*/
|
||||
abstract Stream<String> implList() throws IOException;
|
||||
|
||||
/**
|
||||
* Closes the module reader. This method is invoked by close to do the
|
||||
* actual work of closing the module reader.
|
||||
*/
|
||||
abstract void implClose() throws IOException;
|
||||
|
||||
@Override
|
||||
public final Optional<URI> find(String name) throws IOException {
|
||||
readLock.lock();
|
||||
try {
|
||||
if (!closed) {
|
||||
return implFind(name);
|
||||
} else {
|
||||
throw new IOException("ModuleReader is closed");
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final Optional<InputStream> open(String name) throws IOException {
|
||||
readLock.lock();
|
||||
try {
|
||||
if (!closed) {
|
||||
return implOpen(name);
|
||||
} else {
|
||||
throw new IOException("ModuleReader is closed");
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Stream<String> list() throws IOException {
|
||||
readLock.lock();
|
||||
try {
|
||||
if (!closed) {
|
||||
return implList();
|
||||
} else {
|
||||
throw new IOException("ModuleReader is closed");
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
writeLock.lock();
|
||||
try {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
implClose();
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A ModuleReader for a modular JAR file.
|
||||
*/
|
||||
static class JarModuleReader extends SafeCloseModuleReader {
|
||||
private final JarFile jf;
|
||||
private final URI uri;
|
||||
|
||||
static JarFile newJarFile(String path) {
|
||||
try {
|
||||
return new JarFile(new File(path),
|
||||
true, // verify
|
||||
ZipFile.OPEN_READ,
|
||||
JarFile.runtimeVersion());
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
JarModuleReader(String path, URI uri) {
|
||||
this.jf = newJarFile(path);
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
private JarEntry getEntry(String name) {
|
||||
return jf.getJarEntry(Objects.requireNonNull(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
Optional<URI> implFind(String name) throws IOException {
|
||||
JarEntry je = getEntry(name);
|
||||
if (je != null) {
|
||||
if (jf.isMultiRelease())
|
||||
name = je.getRealName();
|
||||
if (je.isDirectory() && !name.endsWith("/"))
|
||||
name += "/";
|
||||
String encodedPath = ParseUtil.encodePath(name, false);
|
||||
String uris = "jar:" + uri + "!/" + encodedPath;
|
||||
return Optional.of(URI.create(uris));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Optional<InputStream> implOpen(String name) throws IOException {
|
||||
JarEntry je = getEntry(name);
|
||||
if (je != null) {
|
||||
return Optional.of(jf.getInputStream(je));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Stream<String> implList() throws IOException {
|
||||
// take snapshot to avoid async close
|
||||
List<String> names = jf.versionedStream()
|
||||
.map(JarEntry::getName)
|
||||
.toList();
|
||||
return names.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
void implClose() throws IOException {
|
||||
jf.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A ModuleReader for a JMOD file.
|
||||
*/
|
||||
static class JModModuleReader extends SafeCloseModuleReader {
|
||||
private final JmodFile jf;
|
||||
private final URI uri;
|
||||
|
||||
static JmodFile newJmodFile(Path path) {
|
||||
try {
|
||||
return new JmodFile(path);
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
JModModuleReader(Path path, URI uri) {
|
||||
this.jf = newJmodFile(path);
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
private JmodFile.Entry getEntry(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return jf.getEntry(JmodFile.Section.CLASSES, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
Optional<URI> implFind(String name) {
|
||||
JmodFile.Entry je = getEntry(name);
|
||||
if (je != null) {
|
||||
if (je.isDirectory() && !name.endsWith("/"))
|
||||
name += "/";
|
||||
String encodedPath = ParseUtil.encodePath(name, false);
|
||||
String uris = "jmod:" + uri + "!/" + encodedPath;
|
||||
return Optional.of(URI.create(uris));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Optional<InputStream> implOpen(String name) throws IOException {
|
||||
JmodFile.Entry je = getEntry(name);
|
||||
if (je != null) {
|
||||
return Optional.of(jf.getInputStream(je));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Stream<String> implList() throws IOException {
|
||||
// take snapshot to avoid async close
|
||||
List<String> names = jf.stream()
|
||||
.filter(e -> e.section() == JmodFile.Section.CLASSES)
|
||||
.map(JmodFile.Entry::name)
|
||||
.toList();
|
||||
return names.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
void implClose() throws IOException {
|
||||
jf.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A ModuleReader for an exploded module.
|
||||
*/
|
||||
static class ExplodedModuleReader implements ModuleReader {
|
||||
private final Path dir;
|
||||
private volatile boolean closed;
|
||||
|
||||
ExplodedModuleReader(Path dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws IOException if the module reader is closed;
|
||||
*/
|
||||
private void ensureOpen() throws IOException {
|
||||
if (closed) throw new IOException("ModuleReader is closed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<URI> find(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Path path = Resources.toFilePath(dir, name);
|
||||
if (path != null) {
|
||||
try {
|
||||
return Optional.of(path.toUri());
|
||||
} catch (IOError e) {
|
||||
throw (IOException) e.getCause();
|
||||
}
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InputStream> open(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Path path = Resources.toFilePath(dir, name);
|
||||
if (path != null) {
|
||||
return Optional.of(Files.newInputStream(path));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ByteBuffer> read(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Path path = Resources.toFilePath(dir, name);
|
||||
if (path != null) {
|
||||
return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path)));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> list() throws IOException {
|
||||
ensureOpen();
|
||||
return Files.walk(dir, Integer.MAX_VALUE)
|
||||
.map(f -> Resources.toResourceName(dir, f))
|
||||
.filter(s -> s.length() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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 int value() {
|
||||
return 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 static boolean doNotResolveByDefault(ModuleReference mref) {
|
||||
// get the DO_NOT_RESOLVE_BY_DEFAULT flag, if any
|
||||
if (mref instanceof ModuleReferenceImpl) {
|
||||
ModuleResolution mres = ((ModuleReferenceImpl) mref).moduleResolution();
|
||||
if (mres != null)
|
||||
return mres.doNotResolveByDefault();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasIncubatingWarning(ModuleReference mref) {
|
||||
if (mref instanceof ModuleReferenceImpl) {
|
||||
ModuleResolution mres = ((ModuleReferenceImpl) mref).moduleResolution();
|
||||
if (mres != null)
|
||||
return mres.hasIncubatingWarning();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[value=" + value + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
/**
|
||||
* Represents the module target.
|
||||
*
|
||||
* For now, this is a single value for the target platform, e.g. "linux-x64".
|
||||
*/
|
||||
public final class ModuleTarget {
|
||||
|
||||
private final String targetPlatform;
|
||||
|
||||
public ModuleTarget(String targetPlatform) {
|
||||
this.targetPlatform = targetPlatform;
|
||||
}
|
||||
|
||||
public String targetPlatform() {
|
||||
return targetPlatform;
|
||||
}
|
||||
|
||||
}
|
||||
332
src/java.base/share/classes/jdk/internal/module/Modules.java
Normal file
332
src/java.base/share/classes/jdk/internal/module/Modules.java
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2025, 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.PrintStream;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.access.JavaLangModuleAccess;
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
import jdk.internal.loader.ClassLoaders;
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
/**
|
||||
* A helper class for creating and updating modules. This class is intended to
|
||||
* support command-line options, tests, and the instrumentation API. It is also
|
||||
* used by the VM to load modules or add read edges when agents are instrumenting
|
||||
* code that need to link to supporting classes.
|
||||
*
|
||||
* The parameters that are package names in this API are the fully-qualified
|
||||
* names of the packages as defined in section 6.5.3 of <cite>The Java
|
||||
* Language Specification </cite>, for example, {@code "java.lang"}.
|
||||
*/
|
||||
|
||||
public class Modules {
|
||||
private Modules() { }
|
||||
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
/**
|
||||
* Creates a new Module. The module has the given ModuleDescriptor and
|
||||
* is defined to the given class loader.
|
||||
*
|
||||
* The resulting Module is in a larval state in that it does not read
|
||||
* any other module and does not have any exports.
|
||||
*
|
||||
* The URI is for information purposes only.
|
||||
*/
|
||||
public static Module defineModule(ClassLoader loader,
|
||||
ModuleDescriptor descriptor,
|
||||
URI uri)
|
||||
{
|
||||
return JLA.defineModule(loader, descriptor, uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates m1 to read m2.
|
||||
* Same as m1.addReads(m2) but without a caller check.
|
||||
*/
|
||||
public static void addReads(Module m1, Module m2) {
|
||||
JLA.addReads(m1, m2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update module m to read all unnamed modules.
|
||||
*/
|
||||
public static void addReadsAllUnnamed(Module m) {
|
||||
JLA.addReadsAllUnnamed(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m1 to export a package to module m2.
|
||||
* Same as m1.addExports(pn, m2) but without a caller check
|
||||
*/
|
||||
public static void addExports(Module m1, String pn, Module m2) {
|
||||
JLA.addExports(m1, pn, m2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to export a package unconditionally.
|
||||
*/
|
||||
public static void addExports(Module m, String pn) {
|
||||
JLA.addExports(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to export a package to all unnamed modules.
|
||||
*/
|
||||
public static void addExportsToAllUnnamed(Module m, String pn) {
|
||||
JLA.addExportsToAllUnnamed(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m1 to open a package to module m2.
|
||||
* Same as m1.addOpens(pn, m2) but without a caller check.
|
||||
*/
|
||||
public static void addOpens(Module m1, String pn, Module m2) {
|
||||
JLA.addOpens(m1, pn, m2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to open a package to all unnamed modules.
|
||||
*/
|
||||
public static void addOpensToAllUnnamed(Module m, String pn) {
|
||||
JLA.addOpensToAllUnnamed(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds native access to all unnamed modules.
|
||||
*/
|
||||
public static void addEnableNativeAccessToAllUnnamed() {
|
||||
JLA.addEnableNativeAccessToAllUnnamed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable code in all unnamed modules to mutate final instance fields.
|
||||
*/
|
||||
public static void addEnableFinalMutationToAllUnnamed() {
|
||||
JLA.addEnableFinalMutationToAllUnnamed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable code in a given module to mutate final instance fields.
|
||||
*/
|
||||
public static boolean tryEnableFinalMutation(Module m) {
|
||||
return JLA.tryEnableFinalMutation(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if code in a given module is allowed to mutate final instance fields.
|
||||
*/
|
||||
public static boolean isFinalMutationEnabled(Module m) {
|
||||
return JLA.isFinalMutationEnabled(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a given module has statically exported the given package to a given
|
||||
* other module. "statically exported" means the module declaration, --add-exports on
|
||||
* the command line, or Add-Exports in the main manifest of an executable JAR.
|
||||
*/
|
||||
public static boolean isStaticallyExported(Module m, String pn, Module other) {
|
||||
return JLA.isStaticallyExported(m, pn, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a given module has statically opened the given package to a given
|
||||
* other module. "statically open" means the module declaration, --add-opens on the
|
||||
* command line, or Add-Opens in the main manifest of an executable JAR.
|
||||
*/
|
||||
public static boolean isStaticallyOpened(Module m, String pn, Module other) {
|
||||
return JLA.isStaticallyOpened(m, pn, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to use a service.
|
||||
* Same as m2.addUses(service) but without a caller check.
|
||||
*/
|
||||
public static void addUses(Module m, Class<?> service) {
|
||||
JLA.addUses(m, service);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to provide a service
|
||||
*/
|
||||
public static void addProvides(Module m, Class<?> service, Class<?> impl) {
|
||||
ModuleLayer layer = m.getLayer();
|
||||
|
||||
ClassLoader loader = m.getClassLoader();
|
||||
ClassLoader platformClassLoader = ClassLoaders.platformClassLoader();
|
||||
if (layer == null || loader == null || loader == platformClassLoader) {
|
||||
// update ClassLoader catalog
|
||||
ServicesCatalog catalog;
|
||||
if (loader == null) {
|
||||
catalog = BootLoader.getServicesCatalog();
|
||||
} else {
|
||||
catalog = ServicesCatalog.getServicesCatalog(loader);
|
||||
}
|
||||
catalog.addProvider(m, service, impl);
|
||||
}
|
||||
|
||||
if (layer != null) {
|
||||
// update Layer catalog
|
||||
JLA.getServicesCatalog(layer).addProvider(m, service, impl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a collection of root modules, with service binding and the empty
|
||||
* Configuration as the parent to create a Configuration for the boot layer.
|
||||
*
|
||||
* This method is intended to be used to create the Configuration for the
|
||||
* boot layer during startup or at a link-time.
|
||||
*/
|
||||
public static Configuration newBootLayerConfiguration(ModuleFinder finder,
|
||||
Collection<String> roots,
|
||||
PrintStream traceOutput)
|
||||
{
|
||||
return JLMA.resolveAndBind(finder, roots, traceOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the VM when code in the given Module has been transformed by
|
||||
* an agent and so may have been instrumented to call into supporting
|
||||
* classes on the boot class path or application class path.
|
||||
*/
|
||||
public static void transformedByAgent(Module m) {
|
||||
addReads(m, BootLoader.getUnnamedModule());
|
||||
addReads(m, ClassLoaders.appClassLoader().getUnnamedModule());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the VM to load a system module, typically "java.instrument" or
|
||||
* "jdk.management.agent". If the module is not loaded then it is resolved
|
||||
* and loaded (along with any dependences that weren't previously loaded)
|
||||
* into a child layer.
|
||||
*/
|
||||
public static synchronized Module loadModule(String name) {
|
||||
ModuleLayer top = topLayer;
|
||||
if (top == null)
|
||||
top = ModuleLayer.boot();
|
||||
|
||||
Module module = top.findModule(name).orElse(null);
|
||||
if (module != null) {
|
||||
// module already loaded
|
||||
return module;
|
||||
}
|
||||
|
||||
// resolve the module with the top-most layer as the parent
|
||||
ModuleFinder empty = ModuleFinder.of();
|
||||
ModuleFinder finder = ModuleBootstrap.unlimitedFinder();
|
||||
Set<String> roots = Set.of(name);
|
||||
Configuration cf = top.configuration().resolveAndBind(empty, finder, roots);
|
||||
|
||||
// create the child layer
|
||||
Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
|
||||
ModuleLayer newLayer = top.defineModules(cf, clf);
|
||||
|
||||
// add qualified exports/opens to give access to modules in child layer
|
||||
Map<String, Module> map = newLayer.modules().stream()
|
||||
.collect(Collectors.toMap(Module::getName,
|
||||
Function.identity()));
|
||||
ModuleLayer layer = top;
|
||||
while (layer != null) {
|
||||
for (Module m : layer.modules()) {
|
||||
// qualified exports
|
||||
m.getDescriptor().exports().stream()
|
||||
.filter(ModuleDescriptor.Exports::isQualified)
|
||||
.forEach(e -> e.targets().forEach(target -> {
|
||||
Module other = map.get(target);
|
||||
if (other != null) {
|
||||
addExports(m, e.source(), other);
|
||||
}}));
|
||||
|
||||
// qualified opens
|
||||
m.getDescriptor().opens().stream()
|
||||
.filter(ModuleDescriptor.Opens::isQualified)
|
||||
.forEach(o -> o.targets().forEach(target -> {
|
||||
Module other = map.get(target);
|
||||
if (other != null) {
|
||||
addOpens(m, o.source(), other);
|
||||
}}));
|
||||
}
|
||||
|
||||
List<ModuleLayer> parents = layer.parents();
|
||||
assert parents.size() <= 1;
|
||||
layer = parents.isEmpty() ? null : parents.get(0);
|
||||
}
|
||||
|
||||
// update the built-in class loaders to make the types visible
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleReference mref = resolvedModule.reference();
|
||||
String mn = mref.descriptor().name();
|
||||
ClassLoader cl = clf.apply(mn);
|
||||
if (cl == null) {
|
||||
BootLoader.loadModule(mref);
|
||||
} else {
|
||||
((BuiltinClassLoader) cl).loadModule(mref);
|
||||
}
|
||||
}
|
||||
|
||||
// new top layer
|
||||
topLayer = newLayer;
|
||||
|
||||
// return module
|
||||
return newLayer.findModule(name)
|
||||
.orElseThrow(() -> new InternalError("module not loaded"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the module with the given name in the boot layer or any child
|
||||
* layers created to load the "java.instrument" or "jdk.management.agent"
|
||||
* modules into a running VM.
|
||||
*/
|
||||
public static Optional<Module> findLoadedModule(String name) {
|
||||
ModuleLayer top = topLayer;
|
||||
if (top == null)
|
||||
top = ModuleLayer.boot();
|
||||
return top.findModule(name);
|
||||
}
|
||||
|
||||
// the top-most layer
|
||||
private static volatile ModuleLayer topLayer;
|
||||
|
||||
}
|
||||
177
src/java.base/share/classes/jdk/internal/module/Resources.java
Normal file
177
src/java.base/share/classes/jdk/internal/module/Resources.java
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2023, 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.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
/**
|
||||
* A helper class to support working with resources in modules. Also provides
|
||||
* support for translating resource names to file paths.
|
||||
*/
|
||||
public final class Resources {
|
||||
private Resources() { }
|
||||
|
||||
/**
|
||||
* Return true if a resource can be encapsulated. Resource with names
|
||||
* ending in ".class" or "/" cannot be encapsulated. Resource names
|
||||
* that map to a legal package name can be encapsulated.
|
||||
*/
|
||||
public static boolean canEncapsulate(String name) {
|
||||
int len = name.length();
|
||||
if (len > 6 && name.endsWith(".class")) {
|
||||
return false;
|
||||
} else {
|
||||
return Checks.isPackageName(toPackageName(name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a <em>package name</em> for a resource. The package name
|
||||
* returned by this method may not be a legal package name. This method
|
||||
* returns null if the resource name ends with a "/" (a directory)
|
||||
* or the resource name does not contain a "/".
|
||||
*/
|
||||
public static String toPackageName(String name) {
|
||||
int index = name.lastIndexOf('/');
|
||||
if (index == -1 || index == name.length()-1) {
|
||||
return "";
|
||||
} else {
|
||||
return name.substring(0, index).replace('/', '.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource name corresponding to the relative file path
|
||||
* between {@code dir} and {@code file}. If the file is a directory
|
||||
* then the name will end with a "/", except the top-level directory
|
||||
* where the empty string is returned.
|
||||
*/
|
||||
public static String toResourceName(Path dir, Path file) {
|
||||
String s = dir.relativize(file)
|
||||
.toString()
|
||||
.replace(File.separatorChar, '/');
|
||||
if (!s.isEmpty() && Files.isDirectory(file))
|
||||
s += "/";
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file path to a resource in a file tree. If the resource
|
||||
* name has a trailing "/" then the file path will locate a directory.
|
||||
* Returns {@code null} if the resource does not map to a file in the
|
||||
* tree file.
|
||||
*/
|
||||
public static Path toFilePath(Path dir, String name) throws IOException {
|
||||
boolean expectDirectory = name.endsWith("/");
|
||||
if (expectDirectory) {
|
||||
name = name.substring(0, name.length() - 1); // drop trailing "/"
|
||||
}
|
||||
Path path = toSafeFilePath(dir.getFileSystem(), name);
|
||||
if (path != null) {
|
||||
Path file = dir.resolve(path);
|
||||
try {
|
||||
BasicFileAttributes attrs;
|
||||
attrs = Files.readAttributes(file, BasicFileAttributes.class);
|
||||
if (attrs.isDirectory()
|
||||
|| (!attrs.isDirectory() && !expectDirectory))
|
||||
return file;
|
||||
} catch (NoSuchFileException ignore) { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a resource name to a "safe" file path. Returns {@code null} if
|
||||
* the resource name cannot be converted into a "safe" file path.
|
||||
*
|
||||
* Resource names with empty elements, or elements that are "." or ".."
|
||||
* are rejected, as are resource names that translates to a file path
|
||||
* with a root component.
|
||||
*/
|
||||
private static Path toSafeFilePath(FileSystem fs, String name) {
|
||||
// scan elements of resource name
|
||||
int next;
|
||||
int off = 0;
|
||||
while ((next = name.indexOf('/', off)) != -1) {
|
||||
int len = next - off;
|
||||
if (!mayTranslate(name, off, len)) {
|
||||
return null;
|
||||
}
|
||||
off = next + 1;
|
||||
}
|
||||
int rem = name.length() - off;
|
||||
if (!mayTranslate(name, off, rem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// map resource name to a file path string
|
||||
String pathString;
|
||||
if (File.separatorChar == '/') {
|
||||
pathString = name;
|
||||
} else {
|
||||
// not allowed to embed file separators
|
||||
if (name.contains(File.separator))
|
||||
return null;
|
||||
pathString = name.replace('/', File.separatorChar);
|
||||
}
|
||||
|
||||
// try to convert to a Path
|
||||
Path path;
|
||||
try {
|
||||
path = fs.getPath(pathString);
|
||||
} catch (InvalidPathException e) {
|
||||
// not a valid file path
|
||||
return null;
|
||||
}
|
||||
|
||||
// file path not allowed to have root component
|
||||
return (path.getRoot() == null) ? path : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the element in a resource name is a candidate
|
||||
* to translate to the element of a file path.
|
||||
*/
|
||||
private static boolean mayTranslate(String name, int off, int len) {
|
||||
if (len <= 2) {
|
||||
if (len == 0)
|
||||
return false;
|
||||
boolean starsWithDot = (name.charAt(off) == '.');
|
||||
if (len == 1 && starsWithDot)
|
||||
return false;
|
||||
if (len == 2 && starsWithDot && (name.charAt(off+1) == '.'))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import jdk.internal.loader.ClassLoaderValue;
|
||||
|
||||
/**
|
||||
* A <em>services catalog</em>. Each {@code ClassLoader} and {@code Layer} has
|
||||
* an optional {@code ServicesCatalog} for modules that provide services.
|
||||
*
|
||||
* @apiNote This class will be replaced once the ServiceLoader is further
|
||||
* specified
|
||||
*/
|
||||
public final class ServicesCatalog {
|
||||
|
||||
/**
|
||||
* Represents a service provider in the services catalog.
|
||||
*/
|
||||
public static final class ServiceProvider {
|
||||
private final Module module;
|
||||
private final String providerName;
|
||||
|
||||
public ServiceProvider(Module module, String providerName) {
|
||||
this.module = module;
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public Module module() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public String providerName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(module, providerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
if (!(ob instanceof ServiceProvider))
|
||||
return false;
|
||||
ServiceProvider that = (ServiceProvider)ob;
|
||||
return Objects.equals(this.module, that.module)
|
||||
&& Objects.equals(this.providerName, that.providerName);
|
||||
}
|
||||
}
|
||||
|
||||
// service name -> list of providers
|
||||
private final Map<String, List<ServiceProvider>> map = new ConcurrentHashMap<>(32);
|
||||
|
||||
private ServicesCatalog() { }
|
||||
|
||||
/**
|
||||
* Creates a ServicesCatalog that supports concurrent registration
|
||||
* and lookup
|
||||
*/
|
||||
public static ServicesCatalog create() {
|
||||
return new ServicesCatalog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds service providers for the given service type.
|
||||
*/
|
||||
private void addProviders(String service, ServiceProvider ... providers) {
|
||||
List<ServiceProvider> list = map.get(service);
|
||||
if (list == null) {
|
||||
list = new CopyOnWriteArrayList<>(providers);
|
||||
List<ServiceProvider> prev = map.putIfAbsent(service, list);
|
||||
if (prev != null) {
|
||||
// someone else got there
|
||||
prev.addAll(list);
|
||||
}
|
||||
} else {
|
||||
if (providers.length == 1) {
|
||||
list.add(providers[0]);
|
||||
} else {
|
||||
list.addAll(Arrays.asList(providers));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the providers in the given module in this services catalog.
|
||||
*/
|
||||
public void register(Module module) {
|
||||
ModuleDescriptor descriptor = module.getDescriptor();
|
||||
for (Provides provides : descriptor.provides()) {
|
||||
String service = provides.service();
|
||||
List<String> providerNames = provides.providers();
|
||||
int count = providerNames.size();
|
||||
ServiceProvider[] providers = new ServiceProvider[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
providers[i] = new ServiceProvider(module, providerNames.get(i));
|
||||
}
|
||||
addProviders(service, providers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a provider in the given module to this services catalog.
|
||||
*
|
||||
* @apiNote This method is for use by java.lang.instrument
|
||||
*/
|
||||
public void addProvider(Module module, Class<?> service, Class<?> impl) {
|
||||
addProviders(service.getName(), new ServiceProvider(module, impl.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (possibly empty) list of service providers that implement
|
||||
* the given service type.
|
||||
*/
|
||||
public List<ServiceProvider> findServices(String service) {
|
||||
return map.getOrDefault(service, List.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ServicesCatalog for the given class loader or {@code null}
|
||||
* if there is none.
|
||||
*/
|
||||
public static ServicesCatalog getServicesCatalogOrNull(ClassLoader loader) {
|
||||
return CLV.get(loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ServicesCatalog for the given class loader, creating it if
|
||||
* needed.
|
||||
*/
|
||||
public static ServicesCatalog getServicesCatalog(ClassLoader loader) {
|
||||
// CLV.computeIfAbsent(loader, (cl, clv) -> create());
|
||||
ServicesCatalog catalog = CLV.get(loader);
|
||||
if (catalog == null) {
|
||||
catalog = create();
|
||||
ServicesCatalog previous = CLV.putIfAbsent(loader, catalog);
|
||||
if (previous != null) catalog = previous;
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the given ServicesCatalog with the given class loader.
|
||||
*/
|
||||
public static void putServicesCatalog(ClassLoader loader, ServicesCatalog catalog) {
|
||||
ServicesCatalog previous = CLV.putIfAbsent(loader, catalog);
|
||||
if (previous != null) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
// the ServicesCatalog registered to a class loader
|
||||
private static final ClassLoaderValue<ServicesCatalog> CLV = new ClassLoaderValue<>();
|
||||
}
|
||||
|
|
@ -0,0 +1,565 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import jdk.internal.jimage.ImageReader;
|
||||
import jdk.internal.jimage.ImageReaderFactory;
|
||||
import jdk.internal.access.JavaNetUriAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.util.StaticProperty;
|
||||
import jdk.internal.module.ModuleHashes.HashSupplier;
|
||||
|
||||
/**
|
||||
* The factory for SystemModules objects and for creating ModuleFinder objects
|
||||
* that find modules in the runtime image.
|
||||
*
|
||||
* This class supports initializing the module system when the runtime is an
|
||||
* images build, an exploded build, or an images build with java.base patched
|
||||
* by an exploded java.base. It also supports a testing mode that re-parses
|
||||
* the module-info.class resources in the run-time image.
|
||||
*/
|
||||
|
||||
public final class SystemModuleFinders {
|
||||
private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
|
||||
|
||||
private static final boolean USE_FAST_PATH;
|
||||
static {
|
||||
String value = System.getProperty("jdk.system.module.finder.disableFastPath");
|
||||
if (value == null) {
|
||||
USE_FAST_PATH = true;
|
||||
} else {
|
||||
USE_FAST_PATH = !value.isEmpty() && !Boolean.parseBoolean(value);
|
||||
}
|
||||
}
|
||||
|
||||
// cached ModuleFinder returned from ofSystem
|
||||
private static volatile ModuleFinder cachedSystemModuleFinder;
|
||||
|
||||
private SystemModuleFinders() { }
|
||||
|
||||
/**
|
||||
* Returns the SystemModules object to reconstitute all modules. Returns
|
||||
* null if this is an exploded build or java.base is patched by an exploded
|
||||
* build.
|
||||
*/
|
||||
static SystemModules allSystemModules() {
|
||||
if (USE_FAST_PATH) {
|
||||
return SystemModulesMap.allSystemModules();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SystemModules object to reconstitute the modules for the
|
||||
* given initial module. If the initial module is null then return the
|
||||
* SystemModules object to reconstitute the default modules.
|
||||
*
|
||||
* Return null if there is no SystemModules class for the initial module,
|
||||
* this is an exploded build, or java.base is patched by an exploded build.
|
||||
*/
|
||||
static SystemModules systemModules(String initialModule) {
|
||||
if (USE_FAST_PATH) {
|
||||
if (initialModule == null) {
|
||||
return SystemModulesMap.defaultSystemModules();
|
||||
}
|
||||
|
||||
String[] initialModules = SystemModulesMap.moduleNames();
|
||||
for (int i = 0; i < initialModules.length; i++) {
|
||||
String moduleName = initialModules[i];
|
||||
if (initialModule.equals(moduleName)) {
|
||||
String cn = SystemModulesMap.classNames()[i];
|
||||
try {
|
||||
// one-arg Class.forName as java.base may not be defined
|
||||
Constructor<?> ctor = Class.forName(cn).getConstructor();
|
||||
return (SystemModules) ctor.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ModuleFinder that is backed by the given SystemModules object.
|
||||
*
|
||||
* @apiNote The returned ModuleFinder is thread safe.
|
||||
*/
|
||||
static ModuleFinder of(SystemModules systemModules) {
|
||||
ModuleDescriptor[] descriptors = systemModules.moduleDescriptors();
|
||||
ModuleTarget[] targets = systemModules.moduleTargets();
|
||||
ModuleHashes[] recordedHashes = systemModules.moduleHashes();
|
||||
ModuleResolution[] moduleResolutions = systemModules.moduleResolutions();
|
||||
|
||||
int moduleCount = descriptors.length;
|
||||
ModuleReference[] mrefs = new ModuleReference[moduleCount];
|
||||
@SuppressWarnings(value = {"rawtypes", "unchecked"})
|
||||
Map.Entry<String, ModuleReference>[] map
|
||||
= (Map.Entry<String, ModuleReference>[])new Map.Entry[moduleCount];
|
||||
|
||||
Map<String, byte[]> nameToHash = generateNameToHash(recordedHashes);
|
||||
|
||||
for (int i = 0; i < moduleCount; i++) {
|
||||
String name = descriptors[i].name();
|
||||
HashSupplier hashSupplier = hashSupplier(nameToHash, name);
|
||||
ModuleReference mref = toModuleReference(descriptors[i],
|
||||
targets[i],
|
||||
recordedHashes[i],
|
||||
hashSupplier,
|
||||
moduleResolutions[i]);
|
||||
mrefs[i] = mref;
|
||||
map[i] = Map.entry(name, mref);
|
||||
}
|
||||
|
||||
return new SystemModuleFinder(mrefs, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ModuleFinder to find all system modules. Supports both
|
||||
* images and exploded builds.
|
||||
*
|
||||
* @apiNote Used by ModuleFinder.ofSystem()
|
||||
*/
|
||||
public static ModuleFinder ofSystem() {
|
||||
ModuleFinder finder = cachedSystemModuleFinder;
|
||||
if (finder != null) {
|
||||
return finder;
|
||||
}
|
||||
|
||||
// probe to see if this is an images build
|
||||
String home = StaticProperty.javaHome();
|
||||
Path modules = Path.of(home, "lib", "modules");
|
||||
if (Files.isRegularFile(modules)) {
|
||||
if (USE_FAST_PATH) {
|
||||
SystemModules systemModules = allSystemModules();
|
||||
if (systemModules != null) {
|
||||
finder = of(systemModules);
|
||||
}
|
||||
}
|
||||
|
||||
// fall back to parsing the module-info.class files in image
|
||||
if (finder == null) {
|
||||
finder = ofModuleInfos();
|
||||
}
|
||||
|
||||
cachedSystemModuleFinder = finder;
|
||||
return finder;
|
||||
|
||||
}
|
||||
|
||||
// exploded build (do not cache module finder)
|
||||
Path dir = Path.of(home, "modules");
|
||||
if (!Files.isDirectory(dir))
|
||||
throw new InternalError("Unable to detect the run-time image");
|
||||
return ModulePath.of(ModuleBootstrap.patcher(), dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@code module-info.class} of all modules in the runtime image and
|
||||
* returns a ModuleFinder to find the modules.
|
||||
*
|
||||
* @apiNote The returned ModuleFinder is thread safe.
|
||||
*/
|
||||
private static ModuleFinder ofModuleInfos() {
|
||||
// parse the module-info.class in every module
|
||||
Map<String, ModuleInfo.Attributes> nameToAttributes = new HashMap<>();
|
||||
Map<String, byte[]> nameToHash = new HashMap<>();
|
||||
|
||||
allModuleAttributes().forEach(attrs -> {
|
||||
nameToAttributes.put(attrs.descriptor().name(), attrs);
|
||||
ModuleHashes hashes = attrs.recordedHashes();
|
||||
if (hashes != null) {
|
||||
for (String name : hashes.names()) {
|
||||
nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// create a ModuleReference for each module
|
||||
Set<ModuleReference> mrefs = new HashSet<>();
|
||||
Map<String, ModuleReference> nameToModule = new HashMap<>();
|
||||
for (Map.Entry<String, ModuleInfo.Attributes> e : nameToAttributes.entrySet()) {
|
||||
String mn = e.getKey();
|
||||
ModuleInfo.Attributes attrs = e.getValue();
|
||||
HashSupplier hashSupplier = hashSupplier(nameToHash, mn);
|
||||
ModuleReference mref = toModuleReference(attrs.descriptor(),
|
||||
attrs.target(),
|
||||
attrs.recordedHashes(),
|
||||
hashSupplier,
|
||||
attrs.moduleResolution());
|
||||
mrefs.add(mref);
|
||||
nameToModule.put(mn, mref);
|
||||
}
|
||||
|
||||
return new SystemModuleFinder(mrefs, nameToModule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@code module-info.class} of all modules in the runtime image and
|
||||
* returns a stream of {@link ModuleInfo.Attributes Attributes} for them. The
|
||||
* returned attributes are in no specific order.
|
||||
*/
|
||||
private static Stream<ModuleInfo.Attributes> allModuleAttributes() {
|
||||
// System-wide image reader.
|
||||
ImageReader reader = SystemImage.reader();
|
||||
try {
|
||||
return reader.findNode("/modules")
|
||||
.getChildNames()
|
||||
.map(mn -> readModuleAttributes(reader, mn));
|
||||
} catch (IOException e) {
|
||||
throw new Error("Error reading root /modules entry", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module's "module-info", returning a holder for its class file
|
||||
* attributes. Every module is required to have a valid {@code module-info.class}.
|
||||
*/
|
||||
private static ModuleInfo.Attributes readModuleAttributes(ImageReader reader, String moduleName) {
|
||||
Exception err = null;
|
||||
try {
|
||||
ImageReader.Node node = reader.findNode(moduleName + "/module-info.class");
|
||||
if (node != null && node.isResource()) {
|
||||
return ModuleInfo.read(reader.getResourceBuffer(node), null);
|
||||
}
|
||||
} catch (IOException | UncheckedIOException e) {
|
||||
err = e;
|
||||
}
|
||||
throw new Error("Missing or invalid module-info.class for module: " + moduleName, err);
|
||||
}
|
||||
|
||||
/**
|
||||
* A ModuleFinder that finds module in an array or set of modules.
|
||||
*/
|
||||
private static class SystemModuleFinder implements ModuleFinder {
|
||||
final Set<ModuleReference> mrefs;
|
||||
final Map<String, ModuleReference> nameToModule;
|
||||
|
||||
SystemModuleFinder(ModuleReference[] array,
|
||||
Map.Entry<String, ModuleReference>[] map) {
|
||||
this.mrefs = Set.of(array);
|
||||
this.nameToModule = Map.ofEntries(map);
|
||||
}
|
||||
|
||||
SystemModuleFinder(Set<ModuleReference> mrefs,
|
||||
Map<String, ModuleReference> nameToModule) {
|
||||
this.mrefs = Set.copyOf(mrefs);
|
||||
this.nameToModule = Map.copyOf(nameToModule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return Optional.ofNullable(nameToModule.get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ModuleReference> findAll() {
|
||||
return mrefs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ModuleReference to the system module.
|
||||
*/
|
||||
static ModuleReference toModuleReference(ModuleDescriptor descriptor,
|
||||
ModuleTarget target,
|
||||
ModuleHashes recordedHashes,
|
||||
HashSupplier hasher,
|
||||
ModuleResolution mres) {
|
||||
String mn = descriptor.name();
|
||||
URI uri = JNUA.create("jrt", "/".concat(mn));
|
||||
|
||||
Supplier<ModuleReader> readerSupplier = new Supplier<>() {
|
||||
@Override
|
||||
public ModuleReader get() {
|
||||
return new SystemModuleReader(mn);
|
||||
}
|
||||
};
|
||||
|
||||
ModuleReference mref = new ModuleReferenceImpl(descriptor,
|
||||
uri,
|
||||
readerSupplier,
|
||||
null,
|
||||
target,
|
||||
recordedHashes,
|
||||
hasher,
|
||||
mres);
|
||||
|
||||
// may need a reference to a patched module if --patch-module specified
|
||||
mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
|
||||
|
||||
return mref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a map of module name to hash value.
|
||||
*/
|
||||
static Map<String, byte[]> generateNameToHash(ModuleHashes[] recordedHashes) {
|
||||
Map<String, byte[]> nameToHash = null;
|
||||
|
||||
boolean secondSeen = false;
|
||||
// record the hashes to build HashSupplier
|
||||
for (ModuleHashes mh : recordedHashes) {
|
||||
if (mh != null) {
|
||||
// if only one module contain ModuleHashes, use it
|
||||
if (nameToHash == null) {
|
||||
nameToHash = mh.hashes();
|
||||
} else {
|
||||
if (!secondSeen) {
|
||||
nameToHash = new HashMap<>(nameToHash);
|
||||
secondSeen = true;
|
||||
}
|
||||
nameToHash.putAll(mh.hashes());
|
||||
}
|
||||
}
|
||||
}
|
||||
return (nameToHash != null) ? nameToHash : Map.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a HashSupplier that returns the hash of the given module.
|
||||
*/
|
||||
static HashSupplier hashSupplier(Map<String, byte[]> nameToHash, String name) {
|
||||
byte[] hash = nameToHash.get(name);
|
||||
if (hash != null) {
|
||||
// avoid lambda here
|
||||
return new HashSupplier() {
|
||||
@Override
|
||||
public byte[] generate(String algorithm) {
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder class for the ImageReader.
|
||||
*/
|
||||
private static class SystemImage {
|
||||
static final ImageReader READER = ImageReaderFactory.getImageReader();
|
||||
static ImageReader reader() {
|
||||
return READER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A ModuleReader for reading resources from a module linked into the
|
||||
* run-time image.
|
||||
*/
|
||||
private static class SystemModuleReader implements ModuleReader {
|
||||
private final String module;
|
||||
private volatile boolean closed;
|
||||
|
||||
SystemModuleReader(String module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given resource exists, {@code false}
|
||||
* if not found.
|
||||
*/
|
||||
private boolean containsResource(String module, String name) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
if (closed)
|
||||
throw new IOException("ModuleReader is closed");
|
||||
ImageReader imageReader = SystemImage.reader();
|
||||
return imageReader != null && imageReader.containsResource(module, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<URI> find(String name) throws IOException {
|
||||
if (containsResource(module, name)) {
|
||||
URI u = JNUA.create("jrt", "/" + module + "/" + name);
|
||||
return Optional.of(u);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InputStream> open(String name) throws IOException {
|
||||
return read(name).map(this::toInputStream);
|
||||
}
|
||||
|
||||
private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
|
||||
try {
|
||||
int rem = bb.remaining();
|
||||
byte[] bytes = new byte[rem];
|
||||
bb.get(bytes);
|
||||
return new ByteArrayInputStream(bytes);
|
||||
} finally {
|
||||
release(bb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node for the given resource if found. If the name references
|
||||
* a non-resource node, then {@code null} is returned.
|
||||
*/
|
||||
private ImageReader.Node findResource(ImageReader reader, String name) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
if (closed) {
|
||||
throw new IOException("ModuleReader is closed");
|
||||
}
|
||||
return reader.findResourceNode(module, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ByteBuffer> read(String name) throws IOException {
|
||||
ImageReader reader = SystemImage.reader();
|
||||
return Optional.ofNullable(findResource(reader, name))
|
||||
.map(reader::getResourceBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> list() throws IOException {
|
||||
if (closed)
|
||||
throw new IOException("ModuleReader is closed");
|
||||
|
||||
Spliterator<String> s = new ModuleContentSpliterator(module);
|
||||
return StreamSupport.stream(s, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// nothing else to do
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Spliterator for traversing the resources of a module linked into the
|
||||
* run-time image.
|
||||
*/
|
||||
private static class ModuleContentSpliterator implements Spliterator<String> {
|
||||
final String moduleRoot;
|
||||
final Deque<ImageReader.Node> stack;
|
||||
Iterator<String> iterator;
|
||||
|
||||
ModuleContentSpliterator(String module) throws IOException {
|
||||
moduleRoot = "/modules/" + module;
|
||||
stack = new ArrayDeque<>();
|
||||
|
||||
// push the root node to the stack to get started
|
||||
ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot);
|
||||
if (dir == null || !dir.isDirectory())
|
||||
throw new IOException(moduleRoot + " not a directory");
|
||||
stack.push(dir);
|
||||
iterator = Collections.emptyIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the next non-directory node or {@code null} if
|
||||
* there are no remaining nodes to visit.
|
||||
*/
|
||||
private String next() throws IOException {
|
||||
for (;;) {
|
||||
while (iterator.hasNext()) {
|
||||
String name = iterator.next();
|
||||
ImageReader.Node node = SystemImage.reader().findNode(name);
|
||||
if (node.isDirectory()) {
|
||||
stack.push(node);
|
||||
} else {
|
||||
// strip /modules/$MODULE/ prefix
|
||||
return name.substring(moduleRoot.length() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
ImageReader.Node dir = stack.poll();
|
||||
assert dir.isDirectory();
|
||||
iterator = dir.getChildNames().iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super String> action) {
|
||||
String next;
|
||||
try {
|
||||
next = next();
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
if (next != null) {
|
||||
action.accept(next);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<String> trySplit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A SystemModules object reconstitutes module descriptors and other modules
|
||||
* attributes in an efficient way to avoid parsing module-info.class files at
|
||||
* startup. Implementations of this class are generated by the "system modules"
|
||||
* jlink plugin.
|
||||
*
|
||||
* @see SystemModuleFinders
|
||||
* @see jdk.tools.jlink.internal.plugins.SystemModulesPlugin
|
||||
*/
|
||||
|
||||
interface SystemModules {
|
||||
|
||||
/**
|
||||
* Returns false if the module reconstituted by this SystemModules object
|
||||
* have no overlapping packages. Returns true if there are overlapping
|
||||
* packages or unknown.
|
||||
*/
|
||||
boolean hasSplitPackages();
|
||||
|
||||
/**
|
||||
* Return false if the modules reconstituted by this SystemModules object
|
||||
* do not include any incubator modules. Returns true if there are
|
||||
* incubating modules or unknown.
|
||||
*/
|
||||
boolean hasIncubatorModules();
|
||||
|
||||
/**
|
||||
* Returns the non-empty array of ModuleDescriptor objects.
|
||||
*/
|
||||
ModuleDescriptor[] moduleDescriptors();
|
||||
|
||||
/**
|
||||
* Returns the array of ModuleTarget objects. The array elements correspond
|
||||
* to the array of ModuleDescriptor objects.
|
||||
*/
|
||||
ModuleTarget[] moduleTargets();
|
||||
|
||||
/**
|
||||
* Returns the array of ModuleHashes objects. The array elements correspond
|
||||
* to the array of ModuleDescriptor objects.
|
||||
*/
|
||||
ModuleHashes[] moduleHashes();
|
||||
|
||||
/**
|
||||
* Returns the array of ModuleResolution objects. The array elements correspond
|
||||
* to the array of ModuleDescriptor objects.
|
||||
*/
|
||||
ModuleResolution[] moduleResolutions();
|
||||
|
||||
/**
|
||||
* Returns the map representing readability graph for the modules reconstituted
|
||||
* by this SystemModules object.
|
||||
*/
|
||||
Map<String, Set<String>> moduleReads();
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
/**
|
||||
* This class is generated/overridden at link time to return the names of the
|
||||
* SystemModules classes generated at link time.
|
||||
*
|
||||
* @see SystemModuleFinders
|
||||
* @see jdk.tools.jlink.internal.plugins.SystemModulesPlugin
|
||||
*/
|
||||
|
||||
class SystemModulesMap {
|
||||
|
||||
/**
|
||||
* Returns the SystemModules object to reconstitute all modules or null
|
||||
* if this is an exploded build.
|
||||
*/
|
||||
static SystemModules allSystemModules() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SystemModules object to reconstitute default modules or null
|
||||
* if this is an exploded build.
|
||||
*/
|
||||
static SystemModules defaultSystemModules() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of initial module names identified at link time.
|
||||
*/
|
||||
static String[] moduleNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of SystemModules class names. The elements
|
||||
* correspond to the elements in the array returned by moduleNames().
|
||||
*/
|
||||
static String[] classNames() {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
408
src/java.base/share/classes/module-info.java
Normal file
408
src/java.base/share/classes/module-info.java
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2025, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the foundational APIs of the Java SE Platform.
|
||||
*
|
||||
* <dl class="notes">
|
||||
* <dt>Providers:</dt>
|
||||
* <dd> The JDK implementation of this module provides an implementation of
|
||||
* the {@index jrt jrt} {@linkplain java.nio.file.spi.FileSystemProvider
|
||||
* file system provider} to enumerate and read the class and resource
|
||||
* files in a run-time image.
|
||||
* The jrt file system can be created by calling
|
||||
* {@link java.nio.file.FileSystems#getFileSystem
|
||||
* FileSystems.getFileSystem(URI.create("jrt:/"))}.
|
||||
* </dd>
|
||||
* </dl>
|
||||
*
|
||||
* @toolGuide java java launcher
|
||||
* @toolGuide keytool
|
||||
*
|
||||
* @provides java.nio.file.spi.FileSystemProvider
|
||||
*
|
||||
* @uses java.lang.System.LoggerFinder
|
||||
* @uses java.net.ContentHandlerFactory
|
||||
* @uses java.net.spi.URLStreamHandlerProvider
|
||||
* @uses java.nio.channels.spi.AsynchronousChannelProvider
|
||||
* @uses java.nio.channels.spi.SelectorProvider
|
||||
* @uses java.nio.charset.spi.CharsetProvider
|
||||
* @uses java.nio.file.spi.FileSystemProvider
|
||||
* @uses java.nio.file.spi.FileTypeDetector
|
||||
* @uses java.security.Provider
|
||||
* @uses java.text.spi.BreakIteratorProvider
|
||||
* @uses java.text.spi.CollatorProvider
|
||||
* @uses java.text.spi.DateFormatProvider
|
||||
* @uses java.text.spi.DateFormatSymbolsProvider
|
||||
* @uses java.text.spi.DecimalFormatSymbolsProvider
|
||||
* @uses java.text.spi.NumberFormatProvider
|
||||
* @uses java.time.chrono.AbstractChronology
|
||||
* @uses java.time.chrono.Chronology
|
||||
* @uses java.time.zone.ZoneRulesProvider
|
||||
* @uses java.util.spi.CalendarDataProvider
|
||||
* @uses java.util.spi.CalendarNameProvider
|
||||
* @uses java.util.spi.CurrencyNameProvider
|
||||
* @uses java.util.spi.LocaleNameProvider
|
||||
* @uses java.util.spi.ResourceBundleControlProvider
|
||||
* @uses java.util.spi.ResourceBundleProvider
|
||||
* @uses java.util.spi.TimeZoneNameProvider
|
||||
* @uses java.util.spi.ToolProvider
|
||||
* @uses javax.security.auth.spi.LoginModule
|
||||
*
|
||||
* @moduleGraph
|
||||
* @since 9
|
||||
*/
|
||||
module java.base {
|
||||
|
||||
exports java.io;
|
||||
exports java.lang;
|
||||
exports java.lang.annotation;
|
||||
exports java.lang.classfile;
|
||||
exports java.lang.classfile.attribute;
|
||||
exports java.lang.classfile.constantpool;
|
||||
exports java.lang.classfile.instruction;
|
||||
exports java.lang.constant;
|
||||
exports java.lang.foreign;
|
||||
exports java.lang.invoke;
|
||||
exports java.lang.module;
|
||||
exports java.lang.ref;
|
||||
exports java.lang.reflect;
|
||||
exports java.lang.runtime;
|
||||
exports java.math;
|
||||
exports java.net;
|
||||
exports java.net.spi;
|
||||
exports java.nio;
|
||||
exports java.nio.channels;
|
||||
exports java.nio.channels.spi;
|
||||
exports java.nio.charset;
|
||||
exports java.nio.charset.spi;
|
||||
exports java.nio.file;
|
||||
exports java.nio.file.attribute;
|
||||
exports java.nio.file.spi;
|
||||
exports java.security;
|
||||
exports java.security.cert;
|
||||
exports java.security.interfaces;
|
||||
exports java.security.spec;
|
||||
exports java.text;
|
||||
exports java.text.spi;
|
||||
exports java.time;
|
||||
exports java.time.chrono;
|
||||
exports java.time.format;
|
||||
exports java.time.temporal;
|
||||
exports java.time.zone;
|
||||
exports java.util;
|
||||
exports java.util.concurrent;
|
||||
exports java.util.concurrent.atomic;
|
||||
exports java.util.concurrent.locks;
|
||||
exports java.util.function;
|
||||
exports java.util.jar;
|
||||
exports java.util.random;
|
||||
exports java.util.regex;
|
||||
exports java.util.spi;
|
||||
exports java.util.stream;
|
||||
exports java.util.zip;
|
||||
exports javax.crypto;
|
||||
exports javax.crypto.interfaces;
|
||||
exports javax.crypto.spec;
|
||||
exports javax.net;
|
||||
exports javax.net.ssl;
|
||||
exports javax.security.auth;
|
||||
exports javax.security.auth.callback;
|
||||
exports javax.security.auth.login;
|
||||
exports javax.security.auth.spi;
|
||||
exports javax.security.auth.x500;
|
||||
exports javax.security.cert;
|
||||
|
||||
// additional qualified exports may be inserted at build time
|
||||
// see make/gensrc/GenModuleInfo.gmk
|
||||
|
||||
exports com.sun.crypto.provider to
|
||||
jdk.crypto.cryptoki;
|
||||
exports sun.invoke.util to
|
||||
jdk.compiler;
|
||||
exports com.sun.security.ntlm to
|
||||
java.security.sasl;
|
||||
exports jdk.internal to
|
||||
jdk.incubator.vector;
|
||||
// Note: all modules in the exported list participate in preview features,
|
||||
// normal or reflective. They do not need to be compiled with "--enable-preview"
|
||||
// to use preview features and do not need to suppress "preview" warnings.
|
||||
// It is recommended for any modules that do participate that their
|
||||
// module declaration be annotated with jdk.internal.javac.ParticipatesInPreview.
|
||||
exports jdk.internal.javac to
|
||||
java.compiler,
|
||||
jdk.compiler;
|
||||
exports jdk.internal.access to
|
||||
java.desktop,
|
||||
java.logging,
|
||||
java.management,
|
||||
java.rmi,
|
||||
jdk.charsets,
|
||||
jdk.jartool,
|
||||
jdk.jlink,
|
||||
jdk.jfr,
|
||||
jdk.management,
|
||||
jdk.net,
|
||||
jdk.sctp,
|
||||
jdk.crypto.cryptoki;
|
||||
exports jdk.internal.classfile.components to
|
||||
jdk.jfr;
|
||||
exports jdk.internal.foreign to
|
||||
jdk.incubator.vector;
|
||||
exports jdk.internal.event to
|
||||
jdk.jfr;
|
||||
exports jdk.internal.io to
|
||||
jdk.internal.le,
|
||||
jdk.jshell;
|
||||
exports jdk.internal.jimage to
|
||||
jdk.jlink;
|
||||
exports jdk.internal.jimage.decompressor to
|
||||
jdk.jlink;
|
||||
exports jdk.internal.loader to
|
||||
java.instrument,
|
||||
java.logging,
|
||||
java.naming;
|
||||
exports jdk.internal.jmod to
|
||||
jdk.compiler,
|
||||
jdk.jlink;
|
||||
exports jdk.internal.logger to
|
||||
java.logging;
|
||||
exports jdk.internal.net.quic to
|
||||
java.net.http;
|
||||
exports jdk.internal.org.xml.sax to
|
||||
jdk.jfr;
|
||||
exports jdk.internal.org.xml.sax.helpers to
|
||||
jdk.jfr;
|
||||
exports jdk.internal.misc to
|
||||
java.desktop,
|
||||
java.logging,
|
||||
java.management,
|
||||
java.naming,
|
||||
java.net.http,
|
||||
java.rmi,
|
||||
java.security.jgss,
|
||||
jdk.attach,
|
||||
jdk.charsets,
|
||||
jdk.compiler,
|
||||
jdk.crypto.cryptoki,
|
||||
jdk.incubator.vector,
|
||||
jdk.jfr,
|
||||
jdk.jshell,
|
||||
jdk.nio.mapmode,
|
||||
jdk.unsupported,
|
||||
jdk.internal.vm.ci,
|
||||
jdk.graal.compiler;
|
||||
exports jdk.internal.module to
|
||||
java.instrument,
|
||||
java.management.rmi,
|
||||
jdk.jartool,
|
||||
jdk.compiler,
|
||||
jdk.jfr,
|
||||
jdk.jlink,
|
||||
jdk.jpackage;
|
||||
exports jdk.internal.perf to
|
||||
java.management,
|
||||
jdk.management.agent,
|
||||
jdk.internal.jvmstat;
|
||||
exports jdk.internal.platform to
|
||||
jdk.management,
|
||||
jdk.jfr;
|
||||
exports jdk.internal.ref to
|
||||
java.desktop,
|
||||
java.net.http,
|
||||
jdk.naming.dns;
|
||||
exports jdk.internal.reflect to
|
||||
java.logging,
|
||||
java.sql,
|
||||
java.sql.rowset,
|
||||
jdk.dynalink,
|
||||
jdk.internal.vm.ci,
|
||||
jdk.unsupported;
|
||||
exports jdk.internal.vm to
|
||||
java.management,
|
||||
jdk.internal.jvmstat,
|
||||
jdk.management,
|
||||
jdk.management.agent,
|
||||
jdk.internal.vm.ci,
|
||||
jdk.jfr;
|
||||
exports jdk.internal.vm.annotation to
|
||||
java.instrument,
|
||||
jdk.internal.vm.ci,
|
||||
jdk.incubator.vector,
|
||||
jdk.jfr,
|
||||
jdk.unsupported;
|
||||
exports jdk.internal.vm.vector to
|
||||
jdk.incubator.vector;
|
||||
exports jdk.internal.util.xml to
|
||||
jdk.jfr;
|
||||
exports jdk.internal.util.xml.impl to
|
||||
jdk.jfr;
|
||||
exports jdk.internal.util to
|
||||
java.desktop,
|
||||
java.prefs,
|
||||
java.security.jgss,
|
||||
java.smartcardio,
|
||||
java.naming,
|
||||
java.rmi,
|
||||
java.net.http,
|
||||
jdk.charsets,
|
||||
jdk.incubator.vector,
|
||||
jdk.internal.vm.ci,
|
||||
jdk.httpserver,
|
||||
jdk.jlink,
|
||||
jdk.jpackage,
|
||||
jdk.net,
|
||||
jdk.security.auth;
|
||||
exports sun.net to
|
||||
java.net.http,
|
||||
jdk.naming.dns;
|
||||
exports sun.net.ext to
|
||||
jdk.net;
|
||||
exports sun.net.dns to
|
||||
java.security.jgss,
|
||||
jdk.naming.dns;
|
||||
exports sun.net.util to
|
||||
java.net.http,
|
||||
jdk.jconsole,
|
||||
jdk.sctp;
|
||||
exports sun.net.www to
|
||||
java.net.http,
|
||||
jdk.jartool;
|
||||
exports sun.net.www.protocol.http to
|
||||
java.security.jgss;
|
||||
exports sun.nio.ch to
|
||||
java.management,
|
||||
jdk.crypto.cryptoki,
|
||||
jdk.net,
|
||||
jdk.sctp;
|
||||
exports sun.nio.cs to
|
||||
jdk.charsets;
|
||||
exports sun.nio.fs to
|
||||
jdk.net;
|
||||
exports sun.reflect.annotation to
|
||||
jdk.compiler;
|
||||
exports sun.reflect.generics.reflectiveObjects to
|
||||
java.desktop;
|
||||
exports sun.reflect.misc to
|
||||
java.desktop,
|
||||
java.management;
|
||||
exports sun.security.internal.interfaces to
|
||||
jdk.crypto.cryptoki;
|
||||
exports sun.security.internal.spec to
|
||||
jdk.crypto.cryptoki;
|
||||
exports sun.security.jca to
|
||||
java.security.sasl,
|
||||
java.smartcardio,
|
||||
jdk.crypto.cryptoki,
|
||||
jdk.naming.dns;
|
||||
exports sun.security.pkcs to
|
||||
jdk.jartool;
|
||||
exports sun.security.provider to
|
||||
java.security.jgss,
|
||||
jdk.crypto.cryptoki,
|
||||
jdk.security.auth;
|
||||
exports sun.security.provider.certpath to
|
||||
java.naming,
|
||||
jdk.jartool;
|
||||
exports sun.security.rsa to
|
||||
jdk.crypto.cryptoki;
|
||||
exports sun.security.timestamp to
|
||||
jdk.jartool;
|
||||
exports sun.security.tools to
|
||||
jdk.jartool;
|
||||
exports sun.security.util to
|
||||
java.naming,
|
||||
java.security.jgss,
|
||||
java.security.sasl,
|
||||
java.smartcardio,
|
||||
java.xml.crypto,
|
||||
jdk.crypto.cryptoki,
|
||||
jdk.jartool,
|
||||
jdk.security.auth,
|
||||
jdk.security.jgss;
|
||||
exports sun.security.x509 to
|
||||
jdk.crypto.cryptoki,
|
||||
jdk.jartool;
|
||||
exports sun.security.validator to
|
||||
jdk.jartool;
|
||||
exports sun.util.cldr to
|
||||
jdk.jlink;
|
||||
exports sun.util.locale.provider to
|
||||
java.desktop,
|
||||
jdk.jlink,
|
||||
jdk.localedata;
|
||||
exports sun.util.logging to
|
||||
java.desktop,
|
||||
java.logging,
|
||||
java.prefs;
|
||||
exports sun.util.resources to
|
||||
jdk.localedata;
|
||||
|
||||
// the service types defined by the APIs in this module
|
||||
|
||||
uses java.lang.System.LoggerFinder;
|
||||
uses java.net.ContentHandlerFactory;
|
||||
uses java.net.spi.InetAddressResolverProvider;
|
||||
uses java.net.spi.URLStreamHandlerProvider;
|
||||
uses java.nio.channels.spi.AsynchronousChannelProvider;
|
||||
uses java.nio.channels.spi.SelectorProvider;
|
||||
uses java.nio.charset.spi.CharsetProvider;
|
||||
uses java.nio.file.spi.FileSystemProvider;
|
||||
uses java.nio.file.spi.FileTypeDetector;
|
||||
uses java.security.Provider;
|
||||
uses java.text.spi.BreakIteratorProvider;
|
||||
uses java.text.spi.CollatorProvider;
|
||||
uses java.text.spi.DateFormatProvider;
|
||||
uses java.text.spi.DateFormatSymbolsProvider;
|
||||
uses java.text.spi.DecimalFormatSymbolsProvider;
|
||||
uses java.text.spi.NumberFormatProvider;
|
||||
uses java.time.chrono.AbstractChronology;
|
||||
uses java.time.chrono.Chronology;
|
||||
uses java.time.zone.ZoneRulesProvider;
|
||||
uses java.util.spi.CalendarDataProvider;
|
||||
uses java.util.spi.CalendarNameProvider;
|
||||
uses java.util.spi.CurrencyNameProvider;
|
||||
uses java.util.spi.LocaleNameProvider;
|
||||
uses java.util.spi.ResourceBundleControlProvider;
|
||||
uses java.util.spi.ResourceBundleProvider;
|
||||
uses java.util.spi.TimeZoneNameProvider;
|
||||
uses java.util.spi.ToolProvider;
|
||||
uses javax.security.auth.spi.LoginModule;
|
||||
|
||||
// JDK-internal service types
|
||||
|
||||
uses jdk.internal.io.JdkConsoleProvider;
|
||||
uses jdk.internal.logger.DefaultLoggerFinder;
|
||||
uses sun.text.spi.JavaTimeDateTimePatternProvider;
|
||||
uses sun.util.spi.CalendarProvider;
|
||||
uses sun.util.locale.provider.LocaleDataMetaInfo;
|
||||
uses sun.util.resources.LocaleData.LocaleDataResourceBundleProvider;
|
||||
|
||||
// Built-in service providers that are located via ServiceLoader
|
||||
|
||||
provides java.nio.file.spi.FileSystemProvider with
|
||||
jdk.internal.jrtfs.JrtFileSystemProvider;
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue