Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-52333] Refactor and update module system native substitutions #8472

Merged
merged 1 commit into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,150 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.lang.model.SourceVersion;

import com.oracle.svm.core.SubstrateUtil;

public final class ModuleUtil {
private ModuleUtil() {
public final class ModuleNative {
private ModuleNative() {
}

/**
* Re-implementations of native methods from {@code src/hotspot/share/classfile/modules.cpp}.
* See {@link Target_java_lang_Module} for more information on module system native
* substitutions.
*/

/**
* {@code Modules::define_module}.
*/
public static void defineModule(Module module, boolean isOpen, Object[] pns) {
if (Objects.isNull(module)) {
throw new NullPointerException("Null module object");
}

if (Objects.isNull(module.getName())) {
throw new IllegalArgumentException("Module name cannot be null");
}

if (module.getName().equals("java.base")) {
if (isOpen) {
// Checkstyle: stop
throw new AssertionError("java.base module cannot be open");
// Checkstyle: resume
}

for (Object pn : pns) {
checkPackageNameForModule(pn, "java.base");
}

if (module.getClassLoader() != null) {
throw new IllegalArgumentException("Class loader must be the boot class loader");
}

synchronized (moduleLock) {
boolean duplicateJavaBase = bootLayerContainsModule("java.base");
if (duplicateJavaBase) {
throw new InternalError("Module java.base is already defined");
}
}

return;
}

ClassLoader loader = module.getClassLoader();
if (Objects.nonNull(loader) && loader.getClass().getName().equals("jdk.internal.reflect.DelegatingClassLoader")) {
throw new IllegalArgumentException("Class loader is an invalid delegating class loader");
}

boolean javaPkgDisallowed = !Objects.isNull(loader) && !Objects.equals(loader, ClassLoader.getPlatformClassLoader());
for (Object pn : pns) {
checkPackageNameForModule(pn, module.getName());
if (javaPkgDisallowed && isPackageNameForbidden(pn.toString())) {
throw new IllegalArgumentException("Class loader (instance of): " + loader.getClass().getName() +
" tried to define prohibited package name: " + pn);
}
}

String definedPackage = null;
boolean moduleAlreadyDefined;
synchronized (moduleLock) {
moduleAlreadyDefined = isModuleDefinedToLoader(loader, module.getName());
if (!moduleAlreadyDefined) {
List<String> definedPackages = getPackagesDefinedToLoader(loader);
for (Object pn : pns) {
String pnString = pn.toString();
if (definedPackages.contains(pnString)) {
definedPackage = pnString;
break;
}
}
}
}

if (moduleAlreadyDefined) {
throw new IllegalStateException("Module " + module.getName() + " is already defined");
} else if (Objects.nonNull(definedPackage)) {
Module moduleContainingDefinedPackage = SubstrateUtil.cast(getModuleContainingPackage(loader, definedPackage), Module.class);
if (moduleContainingDefinedPackage.isNamed()) {
throw new IllegalStateException("Package " + definedPackage + " is already in another module, " + moduleContainingDefinedPackage.getName() + ", defined to the class loader");
} else {
throw new IllegalStateException("Package " + definedPackage + " is already in the unnamed module defined to the class loader");
}
}

synchronized (moduleLock) {
addDefinedModule(loader, module);
}
}

/**
* {@code Modules::add_reads_module}.
*/
public static void addReads(Module from, @SuppressWarnings("unused") Module to) {
checkIsNull(from, FROM_MODULE_TAG);
}

/**
* {@code Modules::add_module_exports_qualified}.
*/
public static void addExports(Module from, String pn, Module to) {
checkIsNull(to, TO_MODULE_TAG);
addExportsToAll(from, pn);
}

/**
* {@code Modules::add_module_exports}.
*/
public static void addExportsToAll(Module from, String pn) {
checkIsNull(pn, PACKAGE_TAG);
checkIsNull(from, FROM_MODULE_TAG);
checkIsPackageContainedInModule(pn, from, FROM_MODULE_TAG);
}

/**
* {@code Modules::add_module_exports_to_all_unnamed}.
*/
public static void addExportsToAllUnnamed(Module module, String pn) {
checkIsNull(module, MODULE_TAG);
checkIsNull(pn, PACKAGE_TAG);
checkIsPackageContainedInModule(pn, module, MODULE_TAG);
}

/**
* Module bookkeeping and utility methods used by substitutions.
*/

private static final String PACKAGE_TAG = "module";
private static final String MODULE_TAG = "module";
private static final String FROM_MODULE_TAG = "from_" + MODULE_TAG;
private static final String TO_MODULE_TAG = "to_" + MODULE_TAG;
private static final Object moduleLock = new Object();
private static final Map<ClassLoader, Set<Module>> definedModules = new HashMap<>();

public static Map<ClassLoader, Set<Module>> getDefinedModules() {
if (definedModules.size() == 0) {
private static Map<ClassLoader, Set<Module>> getDefinedModules() {
if (definedModules.isEmpty()) {
for (Module module : ModuleLayer.boot().modules()) {
Set<Module> modules = definedModules.get(module.getClassLoader());
if (Objects.isNull(modules)) {
Expand All @@ -59,35 +188,37 @@ public static Map<ClassLoader, Set<Module>> getDefinedModules() {
return definedModules;
}

public static void checkFromModuleAndPackageNullability(Module from, String pn) {
if (Objects.isNull(from)) {
throw new NullPointerException("The from_module is null");
}

if (Objects.isNull(pn)) {
throw new NullPointerException("The package is null");
private static void checkIsNull(Object o, String tag) {
if (Objects.isNull(o)) {
throw new NullPointerException(tag + " is null");
}
}

public static boolean isPackageNameForbidden(String pn) {
private static boolean isPackageNameForbidden(String pn) {
if (!pn.startsWith("java")) {
return false;
}
char trailingChar = pn.length() < 5 ? '.' : pn.charAt("java".length());
return trailingChar == '.';
}

public static boolean isValidPackageName(String pn) {
private static void checkPackageNameForModule(Object pn, String module) {
if (Objects.isNull(pn) || !(pn instanceof String pnString)) {
throw new IllegalArgumentException("Bad package name");
}

// It is OK to use SourceVersion.isName here even though it calls String.split()
// because pattern "\\." will take the fast path in the String.split() method
return Objects.nonNull(pn) && SourceVersion.isName(pn);
if (!SourceVersion.isName(pnString)) {
throw new IllegalArgumentException("Invalid package name: " + pnString + " for module: " + module);
}
}

public static boolean isModuleDefinedToLoader(ClassLoader loader, String moduleName) {
private static boolean isModuleDefinedToLoader(ClassLoader loader, String moduleName) {
return getDefinedModules().getOrDefault(loader, Set.of()).stream().anyMatch(m -> m.getName().equals(moduleName));
}

public static void addDefinedModule(ClassLoader loader, Module module) {
private static void addDefinedModule(ClassLoader loader, Module module) {
Set<Module> modules = getDefinedModules().get(loader);
if (Objects.isNull(modules)) {
modules = new HashSet<>();
Expand All @@ -98,30 +229,23 @@ public static void addDefinedModule(ClassLoader loader, Module module) {
}
}

public static void checkIsPackageContainedInModule(String pn, Module module) {
ClassLoader loader = module.getClassLoader() == null ? ClassLoader.getPlatformClassLoader() : module.getClassLoader();
Package definedPackage = loader.getDefinedPackage(pn);
if (definedPackage != null) {
Target_java_lang_NamedPackage namedPackage = SubstrateUtil.cast(definedPackage, Target_java_lang_NamedPackage.class);
Module actualModule = namedPackage.module;
if (!actualModule.equals(module)) {
throw new IllegalArgumentException("Package " + pn + " found in module " + actualModule.getName() +
", not in module: " + module.getName());
}
private static void checkIsPackageContainedInModule(String pn, Module module, String tag) {
if (!module.isNamed() || module.getDescriptor().isOpen()) {
return;
}
if (!module.getPackages().contains(pn)) {
throw new IllegalArgumentException("Package " + pn + " not found in from_module " + module.getName());
throw new IllegalArgumentException("Package " + pn + " not found in " + tag + " " + module.getName());
}
}

public static List<String> getPackagesDefinedToLoader(ClassLoader loader) {
private static List<String> getPackagesDefinedToLoader(ClassLoader loader) {
return getDefinedModules().getOrDefault(loader, Set.of())
.stream()
.flatMap(m -> m.getPackages().stream())
.collect(Collectors.toUnmodifiableList());
.toList();
}

public static Object getModuleContainingPackage(ClassLoader loader, String pn) {
private static Object getModuleContainingPackage(ClassLoader loader, String pn) {
return getDefinedModules().getOrDefault(loader, Set.of())
.stream()
.filter(m -> m.getPackages().contains(pn))
Expand All @@ -132,84 +256,4 @@ public static boolean bootLayerContainsModule(String name) {
return ModuleLayer.boot().modules().stream().anyMatch(m -> m.getName().equals(name));
}

public static void defineModule(Module module, boolean isOpen, List<String> pns) {
if (Objects.isNull(module)) {
throw new NullPointerException("Null module object");
}

if (Objects.isNull(module.getName())) {
throw new IllegalArgumentException("Module name cannot be null");
}

if (module.getName().equals("java.base")) {
if (isOpen) {
throw new AssertionError("The java.base module cannot be open");
}

for (String pn : pns) {
if (!ModuleUtil.isValidPackageName(pn)) {
throw new IllegalArgumentException("Invalid package name: " + pn + " for module: java.base");
}
}

if (module.getClassLoader() != null) {
throw new IllegalArgumentException("Class loader must be the boot class loader");
}

synchronized (moduleLock) {
boolean duplicateJavaBase = ModuleUtil.bootLayerContainsModule("java.base");
if (duplicateJavaBase) {
throw new InternalError("Module java.base is already defined");
}
}

return;
}

ClassLoader loader = module.getClassLoader();
if (Objects.isNull(loader) || loader.getClass().getName().equals("jdk.internal.reflect.DelegatingClassLoader")) {
throw new IllegalArgumentException("Class loader is an invalid delegating class loader");
}

for (String pn : pns) {
if (!ModuleUtil.isValidPackageName(pn)) {
throw new IllegalArgumentException("Invalid package name: " + pn + " for module: " + module.getName());
}

if (loader != ClassLoader.getPlatformClassLoader() && ModuleUtil.isPackageNameForbidden(pn)) {
throw new IllegalArgumentException("Class loader (instance of): " + loader.getClass().getName() +
" tried to define prohibited package name: " + pn);
}
}

String definedPackage = null;
boolean moduleAlreadyDefined;
synchronized (moduleLock) {
moduleAlreadyDefined = ModuleUtil.isModuleDefinedToLoader(loader, module.getName());
if (!moduleAlreadyDefined) {
List<String> definedPackages = ModuleUtil.getPackagesDefinedToLoader(loader);
for (String pn : pns) {
if (definedPackages.contains(pn)) {
definedPackage = pn;
break;
}
}
}
}

if (moduleAlreadyDefined) {
throw new IllegalStateException("Module " + module.getName() + " is already defined");
} else if (Objects.nonNull(definedPackage)) {
Module moduleContainingDefinedPackage = SubstrateUtil.cast(ModuleUtil.getModuleContainingPackage(loader, definedPackage), Module.class);
if (moduleContainingDefinedPackage.isNamed()) {
throw new IllegalStateException("Package " + definedPackage + " is already in another module, " + moduleContainingDefinedPackage.getName() + ", defined to the class loader");
} else {
throw new IllegalStateException("Package " + definedPackage + " is already in the unnamed module defined to the class loader");
}
}

synchronized (moduleLock) {
ModuleUtil.addDefinedModule(loader, module);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,23 @@
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
import com.oracle.svm.core.heap.UnknownObjectField;

/**
* Runtime module support singleton, containing the runtime boot module layer. The boot module layer
* is synthesized by a feature during native image generation, after analysis (as module layer
* synthesizing requires analysis information). For convenience, this singleton also contains
* hosted-only hosted-to-runtime module mappers used by other parts of the module system during the
* image build. These are important, as every hosted module has its own synthesized runtime
* counterpart. The lookup function is implemented inside the module layer synthesis feature. See
* {@code ModuleLayerFeature} for more information.
*/
public final class RuntimeModuleSupport {

public static RuntimeModuleSupport instance() {
return ImageSingletons.lookup(RuntimeModuleSupport.class);
}

@UnknownObjectField(availability = AfterHostedUniverse.class) private ModuleLayer bootLayer;
@UnknownObjectField(availability = AfterHostedUniverse.class) //
private ModuleLayer bootLayer;

@Platforms(Platform.HOSTED_ONLY.class) //
private Function<Module, Module> hostedToRuntimeModuleMapper;
Expand Down
Loading
Loading