Skip to content

Commit

Permalink
[GR-59661] Further refine CustomGraalClassLoader support for libgraal…
Browse files Browse the repository at this point in the history
… building.

PullRequest: graal/19281
  • Loading branch information
olpaw committed Nov 14, 2024
2 parents 100855c + 4f9263d commit 48a3929
Show file tree
Hide file tree
Showing 14 changed files with 174 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,13 @@
import java.nio.file.Path;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
Expand All @@ -59,8 +58,9 @@
/**
* A classloader, that reads class files and resources from a jimage file at image build time.
*/
@SuppressWarnings("unused")
@Platforms(Platform.HOSTED_ONLY.class)
final class HostedLibGraalClassLoader extends ClassLoader {
final class HostedLibGraalClassLoader extends ClassLoader implements LibGraalClassLoaderBase {

private static final String JAVA_HOME_PROPERTY_KEY = "jdk.graal.internal.libgraal.javahome";
private static final String JAVA_HOME_PROPERTY_VALUE = System.getProperty(JAVA_HOME_PROPERTY_KEY, System.getProperty("java.home"));
Expand All @@ -79,7 +79,7 @@ final class HostedLibGraalClassLoader extends ClassLoader {
* Map from the {@linkplain Class#forName(String) name} of a class to the image path of its
* class file.
*/
private final Map<String, String> classes = new HashMap<>();
private final Map<String, String> classes;

/**
* Map from a service name to a list of providers.
Expand Down Expand Up @@ -113,7 +113,6 @@ public HostedLibGraalClassLoader() {
super(LibGraalClassLoader.LOADER_NAME, Feature.class.getClassLoader());
libGraalJavaHome = Path.of(JAVA_HOME_PROPERTY_VALUE);

Map<String, String> modulesMap = new HashMap<>();
try {
/*
* Access to jdk.internal.jimage classes is needed by this Classloader implementation.
Expand All @@ -129,6 +128,9 @@ public HostedLibGraalClassLoader() {
Modules.addExports(javaBaseModule, "jdk.internal.vm", unnamedModuleOfThisLoader);
Modules.addExports(javaBaseModule, "jdk.internal.misc", unnamedModuleOfThisLoader);

Map<String, String> modulesMap = new HashMap<>();
Map<String, String> classesMap = new HashMap<>();

Path imagePath = libGraalJavaHome.resolve(Path.of("lib", "modules"));
this.imageReader = BasicImageReader.open(imagePath);
for (var entry : imageReader.getEntryNames()) {
Expand All @@ -140,50 +142,38 @@ public HostedLibGraalClassLoader() {
resources.put(resource, entry);
if (resource.endsWith(".class")) {
String className = resource.substring(0, resource.length() - ".class".length()).replace('/', '.');
classes.put(className, entry);
if (resource.equals("module-info.class")) {
ModuleDescriptor md = ModuleDescriptor.read(imageReader.getResourceBuffer(imageReader.findLocation(entry)));
for (var p : md.provides()) {
services.computeIfAbsent(p.service(), k -> new ArrayList<>()).addAll(p.providers());
}
} else {
classesMap.put(className, entry);
modulesMap.put(className, module);
}
}
}
}
}

modules = Map.copyOf(modulesMap);
classes = Map.copyOf(classesMap);

} catch (IOException e) {
throw new RuntimeException(e);
throw GraalError.shouldNotReachHere(e);
}
this.modules = Map.copyOf(modulesMap);
}

/**
* Gets an unmodifiable map from the {@linkplain Class#forName(String) name} of a class to the
* name of its enclosing module. Reflectively accessed by
* {@code LibGraalFeature.OptionCollector#afterAnalysis(AfterAnalysisAccess)}.
*/
@SuppressWarnings("unused")
@Override
public Map<String, String> getModules() {
return modules;
}

/* Allow image builder to perform registration action on each class this loader provides. */
@SuppressWarnings("unused")
public void forEachClass(Consumer<Class<?>> action) {
for (String className : classes.keySet()) {
if (className.equals("module-info")) {
continue;
}
try {
var clazz = loadClass(className);
action.accept(clazz);
} catch (ClassNotFoundException e) {
throw GraalError.shouldNotReachHere(e, LibGraalClassLoader.LOADER_NAME + " could not load class " + className);
}
}

@Override
public Set<String> getAllClassNames() {
return classes.keySet();
}

@Override
Expand Down Expand Up @@ -262,24 +252,11 @@ protected URL findResource(String name) {

@Override
protected Enumeration<URL> findResources(String name) throws IOException {
return new Enumeration<>() {
private URL next = findResource(name);

@Override
public boolean hasMoreElements() {
return (next != null);
}

@Override
public URL nextElement() {
if (next == null) {
throw new NoSuchElementException();
}
URL u = next;
next = null;
return u;
}
};
URL resource = findResource(name);
if (resource == null) {
return Collections.emptyEnumeration();
}
return Collections.enumeration(List.of(resource));
}

/**
Expand Down Expand Up @@ -340,12 +317,13 @@ public String getContentType() {
}
}

/**
* @return instance of ClassLoader that should be seen at image-runtime if a class was loaded at
* image-buildtime by this classloader.
*/
@SuppressWarnings("unused")
public static ClassLoader getRuntimeClassLoader() {
@Override
public HostedLibGraalClassLoader getClassLoader() {
return this;
}

@Override
public LibGraalClassLoader getRuntimeClassLoader() {
return LibGraalClassLoader.singleton;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 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
Expand All @@ -23,22 +23,32 @@
* questions.
*/

package com.oracle.svm.hosted;
package jdk.graal.compiler.hotspot.libgraal;

import org.graalvm.nativeimage.ImageSingletons;
import java.util.Map;
import java.util.Set;

import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.hub.ClassForNameSupport;
import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton;
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
import com.oracle.svm.hosted.FeatureImpl.AfterRegistrationAccessImpl;
public interface LibGraalClassLoaderBase {

@AutomaticallyRegisteredFeature
public final class ClassForNameSupportFeature implements InternalFeature, FeatureSingleton, UnsavedSingleton {
@Override
public void afterRegistration(AfterRegistrationAccess access) {
ClassLoader customLoader = ((AfterRegistrationAccessImpl) access).getImageClassLoader().classLoaderSupport.getCustomLoader();
ImageSingletons.add(ClassForNameSupport.class, new ClassForNameSupport(customLoader));
}
/**
* @return instance of ClassLoader that implements this interface.
*/
ClassLoader getClassLoader();

/**
* @return instance of ClassLoader that should be seen at image-runtime if a class was loaded at
* image-buildtime by this classloader.
*/
ClassLoader getRuntimeClassLoader();

/**
* Gets an unmodifiable map from the {@linkplain Class#forName(String) name} of a class to the
* name of its enclosing module.
*/
Map<String, String> getModules();

/**
* Get unmodifiable set of fully qualified names of all classes this loader can load.
*/
Set<String> getAllClassNames();
}
1 change: 1 addition & 0 deletions substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,7 @@ def prevent_build_path_in_libgraal():

## Pass via JVM args opening up of packages needed for image builder early on
'-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.hotspot=ALL-UNNAMED',
'-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.hotspot.libgraal=ALL-UNNAMED',
'-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.options=ALL-UNNAMED',
'-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.truffle=ALL-UNNAMED',
'-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.truffle.hotspot=ALL-UNNAMED',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,21 @@

import com.oracle.svm.core.configure.ConditionalRuntimeValue;
import com.oracle.svm.core.configure.RuntimeConditionSet;
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
import com.oracle.svm.core.util.ImageHeapMap;
import com.oracle.svm.core.util.VMError;

@AutomaticallyRegisteredImageSingleton
public final class ClassForNameSupport implements MultiLayeredImageSingleton, UnsavedSingleton {

private ClassLoader customLoader;
private ClassLoader libGraalLoader;

public ClassForNameSupport(ClassLoader customLoader) {
setCustomLoader(customLoader);
}

public void setCustomLoader(ClassLoader customLoader) {
this.customLoader = customLoader;
public void setLibGraalLoader(ClassLoader libGraalLoader) {
this.libGraalLoader = libGraalLoader;
}

public static ClassForNameSupport singleton() {
Expand Down Expand Up @@ -126,7 +124,7 @@ accessible through the builder class loader, and it was already registered by na

@Platforms(HOSTED_ONLY.class)
private boolean isLibGraalClass(Class<?> clazz) {
return customLoader != null && clazz.getClassLoader() == customLoader;
return libGraalLoader != null && clazz.getClassLoader() == libGraalLoader;
}

public static ConditionalRuntimeValue<Object> updateConditionalValue(ConditionalRuntimeValue<Object> existingConditionalValue, Object newValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.graal.hotspot.GetCompilerConfig;
import com.oracle.svm.graal.hotspot.GetJNIConfig;
import com.oracle.svm.hosted.ClassLoaderFeature;
import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl;
Expand All @@ -72,6 +71,7 @@
import jdk.graal.compiler.debug.DebugContext;
import jdk.graal.compiler.hotspot.CompilerConfigurationFactory;
import jdk.graal.compiler.hotspot.libgraal.BuildTime;
import jdk.graal.compiler.hotspot.libgraal.LibGraalClassLoaderBase;
import jdk.graal.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin;
import jdk.graal.compiler.options.OptionDescriptor;
import jdk.graal.compiler.options.OptionKey;
Expand Down Expand Up @@ -109,6 +109,8 @@ public List<Class<? extends Feature>> getRequiredFeatures() {

final MethodHandles.Lookup mhl = MethodHandles.lookup();

LibGraalClassLoaderBase libGraalClassLoader;

/**
* Loader used for loading classes from the guest GraalVM.
*/
Expand Down Expand Up @@ -183,8 +185,9 @@ public void afterRegistration(AfterRegistrationAccess access) {
// org.graalvm.nativeimage.impl.IsolateSupport
accessModulesToClass(ModuleSupport.Access.EXPORT, LibGraalFeature.class, "org.graalvm.nativeimage");

loader = createHostedLibGraalClassLoader(access);
ImageSingletons.lookup(ClassForNameSupport.class).setCustomLoader(loader);
libGraalClassLoader = createHostedLibGraalClassLoader(access);
loader = libGraalClassLoader.getClassLoader();
ImageSingletons.lookup(ClassForNameSupport.class).setLibGraalLoader(loader);

buildTimeClass = loadClassOrFail("jdk.graal.compiler.hotspot.libgraal.BuildTime");

Expand All @@ -206,10 +209,10 @@ public void afterRegistration(AfterRegistrationAccess access) {
}

@SuppressWarnings("unchecked")
private static ClassLoader createHostedLibGraalClassLoader(AfterRegistrationAccess access) {
private static LibGraalClassLoaderBase createHostedLibGraalClassLoader(AfterRegistrationAccess access) {
var hostedLibGraalClassLoaderClass = access.findClassByName("jdk.graal.compiler.hotspot.libgraal.HostedLibGraalClassLoader");
ModuleSupport.accessPackagesToClass(Access.EXPORT, hostedLibGraalClassLoaderClass, false, "java.base", "jdk.internal.module");
return ReflectionUtil.newInstance((Class<ClassLoader>) hostedLibGraalClassLoaderClass);
return ReflectionUtil.newInstance((Class<LibGraalClassLoaderBase>) hostedLibGraalClassLoaderClass);
}

private static void accessModulesToClass(ModuleSupport.Access access, Class<?> accessingClass, String... moduleNames) {
Expand All @@ -231,7 +234,7 @@ public void duringSetup(DuringSetupAccess access) {
* HostedLibGraalClassLoader provides runtime-replacement loader instance. Make sure
* HostedLibGraalClassLoader gets replaced by customRuntimeLoader instance in image.
*/
ClassLoader customRuntimeLoader = ClassLoaderFeature.getCustomRuntimeClassLoader(loader);
ClassLoader customRuntimeLoader = libGraalClassLoader.getRuntimeClassLoader();
access.registerObjectReplacer(obj -> obj == loader ? customRuntimeLoader : obj);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
*/
package com.oracle.svm.hosted;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

Expand All @@ -33,6 +32,7 @@
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability;
import com.oracle.svm.core.hub.ClassForNameSupport;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl;
Expand All @@ -41,6 +41,7 @@
import com.oracle.svm.hosted.jdk.HostedClassLoaderPackageManagement;
import com.oracle.svm.util.ReflectionUtil;

import jdk.graal.compiler.hotspot.libgraal.LibGraalClassLoaderBase;
import jdk.internal.loader.ClassLoaders;
import jdk.vm.ci.meta.JavaConstant;

Expand Down Expand Up @@ -120,15 +121,18 @@ public void duringSetup(DuringSetupAccess access) {

var config = (FeatureImpl.DuringSetupAccessImpl) access;
if (ImageLayerBuildingSupport.firstImageBuild()) {
ClassLoader customLoader = ((DuringSetupAccessImpl) access).imageClassLoader.classLoaderSupport.getCustomLoader();
if (customLoader != null) {
ClassLoader customRuntimeLoader = getCustomRuntimeClassLoader(customLoader);
if (customRuntimeLoader != null) {
LibGraalClassLoaderBase libGraalLoader = ((DuringSetupAccessImpl) access).imageClassLoader.classLoaderSupport.getLibGraalLoader();
if (libGraalLoader != null) {
ClassLoader libGraalClassLoader = libGraalLoader.getClassLoader();
ClassForNameSupport.singleton().setLibGraalLoader(libGraalClassLoader);

ClassLoader runtimeLibGraalClassLoader = libGraalLoader.getRuntimeClassLoader();
if (runtimeLibGraalClassLoader != null) {
/*
* CustomLoader provides runtime-replacement ClassLoader instance. Make sure
* customLoader gets replaced by customRuntimeLoader instance in image.
* LibGraalLoader provides runtime-replacement ClassLoader instance. Make sure
* LibGraalLoader gets replaced by runtimeLibGraalClassLoader instance in image.
*/
access.registerObjectReplacer(obj -> obj == customLoader ? customRuntimeLoader : obj);
access.registerObjectReplacer(obj -> obj == libGraalClassLoader ? runtimeLibGraalClassLoader : obj);
}
}
access.registerObjectReplacer(this::runtimeClassLoaderObjectReplacer);
Expand All @@ -146,12 +150,6 @@ public void duringSetup(DuringSetupAccess access) {
}
}

public static ClassLoader getCustomRuntimeClassLoader(ClassLoader customLoader) {
Class<? extends ClassLoader> customLoaderClass = customLoader.getClass();
Method getRuntimeClassLoaderMethod = ReflectionUtil.lookupMethod(true, customLoaderClass, "getRuntimeClassLoader");
return getRuntimeClassLoaderMethod != null ? ReflectionUtil.invokeMethod(getRuntimeClassLoaderMethod, null) : null;
}

@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
var packagesField = ReflectionUtil.lookupField(ClassLoader.class, "packages");
Expand Down
Loading

0 comments on commit 48a3929

Please sign in to comment.