Skip to content

Commit

Permalink
[GR-52333] Refactor and update module system native substitutions
Browse files Browse the repository at this point in the history
PullRequest: graal/17104
  • Loading branch information
ivan-ristovic committed Feb 29, 2024
2 parents 62ac53a + 5664813 commit 2334a13
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 172 deletions.
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

0 comments on commit 2334a13

Please sign in to comment.