From d4cd87f5a51a75bdce815245254883ab2eebacdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Mar 2022 12:01:11 +0100 Subject: [PATCH 1/7] Fold AbstractNativeImageClassLoaderSupport & NativeImageClassLoaderSupportJDK11OrLater into NativeImageClassLoaderSupport --- ...AbstractNativeImageClassLoaderSupport.java | 357 --------- .../svm/hosted/ClassLoaderSupportImpl.java | 4 +- .../oracle/svm/hosted/ImageClassLoader.java | 4 +- .../oracle/svm/hosted/ModuleLayerFeature.java | 5 +- .../hosted/NativeImageClassLoaderOptions.java | 2 +- .../NativeImageClassLoaderPostProcessing.java | 4 +- .../hosted/NativeImageClassLoaderSupport.java | 691 +++++++++++++++++- .../hosted/NativeImageGeneratorRunner.java | 6 +- ...ClassLoaderSupportFeatureJDK11OrLater.java | 13 +- ...veImageClassLoaderSupportJDK11OrLater.java | 446 ----------- 10 files changed, 685 insertions(+), 847 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/AbstractNativeImageClassLoaderSupport.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/NativeImageClassLoaderSupportJDK11OrLater.java diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/AbstractNativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/AbstractNativeImageClassLoaderSupport.java deleted file mode 100644 index 3110de940bcb..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/AbstractNativeImageClassLoaderSupport.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (c) 2020, 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 com.oracle.svm.hosted; - -import static com.oracle.svm.core.util.VMError.shouldNotReachHere; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.channels.ClosedByInterruptException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.FileVisitResult; -import java.nio.file.FileVisitor; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ForkJoinPool; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.graalvm.collections.EconomicMap; -import org.graalvm.collections.EconomicSet; -import org.graalvm.compiler.options.OptionValues; - -import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.util.ClasspathUtils; -import com.oracle.svm.core.util.InterruptImageBuilding; -import com.oracle.svm.core.util.UserError; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.option.HostedOptionParser; - -public abstract class AbstractNativeImageClassLoaderSupport { - - final List imagecp; - private final List buildcp; - private final EconomicMap> classes; - private final EconomicMap> packages; - private final EconomicSet emptySet; - - protected final URLClassLoader classPathClassLoader; - - protected AbstractNativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, String[] classpath) { - - classes = EconomicMap.create(); - packages = EconomicMap.create(); - emptySet = EconomicSet.create(); - - classPathClassLoader = new URLClassLoader(Util.verifyClassPathAndConvertToURLs(classpath), defaultSystemClassLoader); - - imagecp = Arrays.stream(classPathClassLoader.getURLs()) - .map(Util::urlToPath) - .collect(Collectors.toUnmodifiableList()); - String builderClassPathString = System.getProperty("java.class.path"); - String[] builderClassPathEntries = builderClassPathString.isEmpty() ? new String[0] : builderClassPathString.split(File.pathSeparator); - if (Arrays.asList(builderClassPathEntries).contains(".")) { - VMError.shouldNotReachHere("The classpath of " + NativeImageGeneratorRunner.class.getName() + - " must not contain \".\". This can happen implicitly if the builder runs exclusively on the --module-path" + - " but specifies the " + NativeImageGeneratorRunner.class.getName() + " main class without --module."); - } - buildcp = Arrays.stream(builderClassPathEntries) - .map(Paths::get) - .map(Path::toAbsolutePath) - .collect(Collectors.toUnmodifiableList()); - } - - List classpath() { - return Stream.concat(imagecp.stream(), buildcp.stream()).distinct().collect(Collectors.toList()); - } - - List applicationClassPath() { - return imagecp; - } - - public ClassLoader getClassLoader() { - return classPathClassLoader; - } - - protected abstract Class loadClassFromModule(Object module, String className) throws ClassNotFoundException; - - protected abstract Optional getMainClassFromModule(Object module); - - protected abstract List modulepath(); - - protected abstract List applicationModulePath(); - - protected abstract Optional findModule(String moduleName); - - private HostedOptionParser hostedOptionParser; - private OptionValues parsedHostedOptions; - private List remainingArguments; - - public void setupHostedOptionParser(List arguments) { - hostedOptionParser = new HostedOptionParser(getClassLoader()); - remainingArguments = Collections.unmodifiableList((hostedOptionParser.parse(arguments))); - parsedHostedOptions = new OptionValues(hostedOptionParser.getHostedValues()); - } - - public HostedOptionParser getHostedOptionParser() { - return hostedOptionParser; - } - - public List getRemainingArguments() { - return remainingArguments; - } - - public OptionValues getParsedHostedOptions() { - return parsedHostedOptions; - } - - public EconomicSet classes(URI container) { - return classes.get(container, emptySet); - } - - public EconomicSet packages(URI container) { - return packages.get(container, emptySet); - } - - public boolean noEntryForURI(EconomicSet set) { - return set == emptySet; - } - - protected abstract void processClassLoaderOptions(); - - public abstract void propagateQualifiedExports(String fromTargetModule, String toTargetModule); - - protected abstract void initAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoader); - - protected static class Util { - - static URL[] verifyClassPathAndConvertToURLs(String[] classpath) { - Stream pathStream = new LinkedHashSet<>(Arrays.asList(classpath)).stream().flatMap(Util::toClassPathEntries); - return pathStream.map(v -> { - try { - return v.toAbsolutePath().toUri().toURL(); - } catch (MalformedURLException e) { - throw UserError.abort("Invalid classpath element '%s'. Make sure that all paths provided with '%s' are correct.", v, SubstrateOptions.IMAGE_CLASSPATH_PREFIX); - } - }).toArray(URL[]::new); - } - - static Stream toClassPathEntries(String classPathEntry) { - Path entry = ClasspathUtils.stringToClasspath(classPathEntry); - if (entry.endsWith(ClasspathUtils.cpWildcardSubstitute)) { - try { - return Files.list(entry.getParent()).filter(ClasspathUtils::isJar); - } catch (IOException e) { - return Stream.empty(); - } - } - if (Files.isReadable(entry)) { - return Stream.of(entry); - } - return Stream.empty(); - } - - static Path urlToPath(URL url) { - try { - return Paths.get(url.toURI()); - } catch (URISyntaxException e) { - throw VMError.shouldNotReachHere(); - } - } - } - - final Path excludeDirectoriesRoot = Paths.get("/"); - final Set excludeDirectories = getExcludeDirectories(); - - private Set getExcludeDirectories() { - return Stream.of("dev", "sys", "proc", "etc", "var", "tmp", "boot", "lost+found") - .map(excludeDirectoriesRoot::resolve).collect(Collectors.toUnmodifiableSet()); - } - - protected class ClassInit { - - protected final ForkJoinPool executor; - protected final ImageClassLoader imageClassLoader; - - protected ClassInit(ForkJoinPool executor, ImageClassLoader imageClassLoader) { - this.executor = executor; - this.imageClassLoader = imageClassLoader; - } - - protected void init() { - classpath().parallelStream().forEach(this::loadClassesFromPath); - } - - private void loadClassesFromPath(Path path) { - if (ClasspathUtils.isJar(path)) { - try { - URI container = path.toAbsolutePath().toUri(); - URI jarURI = new URI("jar:" + container); - FileSystem probeJarFileSystem; - try { - probeJarFileSystem = FileSystems.newFileSystem(jarURI, Collections.emptyMap()); - } catch (UnsupportedOperationException e) { - /* Silently ignore invalid jar-files on image-classpath */ - probeJarFileSystem = null; - } - if (probeJarFileSystem != null) { - try (FileSystem jarFileSystem = probeJarFileSystem) { - loadClassesFromPath(container, jarFileSystem.getPath("/"), null, Collections.emptySet()); - } - } - } catch (ClosedByInterruptException ignored) { - throw new InterruptImageBuilding(); - } catch (IOException | URISyntaxException e) { - throw shouldNotReachHere(e); - } - } else { - URI container = path.toUri(); - loadClassesFromPath(container, path, excludeDirectoriesRoot, excludeDirectories); - } - } - - protected static final String CLASS_EXTENSION = ".class"; - - private void loadClassesFromPath(URI container, Path root, Path excludeRoot, Set excludes) { - boolean useFilter = root.equals(excludeRoot); - if (useFilter) { - String excludesStr = excludes.stream().map(Path::toString).collect(Collectors.joining(", ")); - System.err.println("Warning: Using directory " + excludeRoot + " on classpath is discouraged." + - " Reading classes/resources from directories " + excludesStr + " will be suppressed."); - } - FileVisitor visitor = new SimpleFileVisitor<>() { - private final char fileSystemSeparatorChar = root.getFileSystem().getSeparator().charAt(0); - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (useFilter && excludes.contains(dir)) { - return FileVisitResult.SKIP_SUBTREE; - } - return super.preVisitDirectory(dir, attrs); - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { - assert !excludes.contains(file.getParent()) : "Visiting file '" + file + "' with excluded parent directory"; - String fileName = root.relativize(file).toString(); - if (fileName.endsWith(CLASS_EXTENSION)) { - executor.execute(() -> handleClassFileName(container, null, fileName, fileSystemSeparatorChar)); - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFileFailed(Path file, IOException exc) { - /* Silently ignore inaccessible files or directories. */ - return FileVisitResult.CONTINUE; - } - }; - - try { - Files.walkFileTree(root, visitor); - } catch (IOException ex) { - throw shouldNotReachHere(ex); - } - } - - /** - * Take a file name from a possibly-multi-versioned jar file and remove the versioning - * information. See https://docs.oracle.com/javase/9/docs/api/java/util/jar/JarFile.html for - * the specification of the versioning strings. - * - * Then, depend on the JDK class loading mechanism to prefer the appropriately-versioned - * class when the class is loaded. The same class name be loaded multiple times, but each - * request will return the same appropriately-versioned class. If a higher-versioned class - * is not available in a lower-versioned JDK, a ClassNotFoundException will be thrown, which - * will be handled appropriately. - */ - private String strippedClassFileName(String fileName) { - final String versionedPrefix = "META-INF/versions/"; - final String versionedSuffix = "/"; - String result = fileName; - if (fileName.startsWith(versionedPrefix)) { - final int versionedSuffixIndex = fileName.indexOf(versionedSuffix, versionedPrefix.length()); - if (versionedSuffixIndex >= 0) { - result = fileName.substring(versionedSuffixIndex + versionedSuffix.length()); - } - } - return result.substring(0, result.length() - CLASS_EXTENSION.length()); - } - - protected void handleClassFileName(URI container, Object module, String fileName, char fileSystemSeparatorChar) { - String strippedClassFileName = strippedClassFileName(fileName); - if (strippedClassFileName.equals("module-info")) { - return; - } - - String className = strippedClassFileName.replace(fileSystemSeparatorChar, '.'); - synchronized (classes) { - EconomicSet classNames = classes.get(container); - if (classNames == null) { - classNames = EconomicSet.create(); - classes.put(container, classNames); - } - classNames.add(className); - } - int packageSep = className.lastIndexOf('.'); - String packageName = packageSep > 0 ? className.substring(0, packageSep) : ""; - synchronized (packages) { - EconomicSet packageNames = packages.get(container); - if (packageNames == null) { - packageNames = EconomicSet.create(); - packages.put(container, packageNames); - } - packageNames.add(packageName); - } - - Class clazz = null; - try { - clazz = imageClassLoader.forName(className, module); - } catch (AssertionError error) { - VMError.shouldNotReachHere(error); - } catch (Throwable t) { - ImageClassLoader.handleClassLoadingError(t); - } - if (clazz != null) { - imageClassLoader.handleClass(clazz); - } - } - } -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java index e4dabf038e49..cee4d36690c2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java @@ -55,10 +55,10 @@ public class ClassLoaderSupportImpl extends ClassLoaderSupport { - private final AbstractNativeImageClassLoaderSupport classLoaderSupport; + private final NativeImageClassLoaderSupport classLoaderSupport; private final ClassLoader imageClassLoader; - protected ClassLoaderSupportImpl(AbstractNativeImageClassLoaderSupport classLoaderSupport) { + protected ClassLoaderSupportImpl(NativeImageClassLoaderSupport classLoaderSupport) { this.classLoaderSupport = classLoaderSupport; this.imageClassLoader = classLoaderSupport.getClassLoader(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java index 98c3c6c6d97e..c45dee0ff2a3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java @@ -70,14 +70,14 @@ public final class ImageClassLoader { } public final Platform platform; - public final AbstractNativeImageClassLoaderSupport classLoaderSupport; + public final NativeImageClassLoaderSupport classLoaderSupport; private final EconomicSet> applicationClasses = EconomicSet.create(); private final EconomicSet> hostedOnlyClasses = EconomicSet.create(); private final EconomicSet systemMethods = EconomicSet.create(); private final EconomicSet systemFields = EconomicSet.create(); - ImageClassLoader(Platform platform, AbstractNativeImageClassLoaderSupport classLoaderSupport) { + ImageClassLoader(Platform platform, NativeImageClassLoaderSupport classLoaderSupport) { this.platform = platform; this.classLoaderSupport = classLoaderSupport; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java index 5a76b041cbad..48c9cdb21b55 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -59,7 +59,6 @@ import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.jdk.BootModuleLayerSupport; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.jdk.NativeImageClassLoaderSupportJDK11OrLater; import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; @@ -190,9 +189,9 @@ private static Stream extractRequiredModuleNames(Module m) { private ModuleLayer synthesizeRuntimeBootLayer(ImageClassLoader cl, Set reachableModules, Set syntheticModules) { /** * For consistent module lookup we reuse the {@link ModuleFinder}s defined and used in - * {@link NativeImageClassLoaderSupportJDK11OrLater}. + * {@link NativeImageClassLoaderSupport}. */ - NativeImageClassLoaderSupportJDK11OrLater classLoaderSupport = (NativeImageClassLoaderSupportJDK11OrLater) cl.classLoaderSupport; + NativeImageClassLoaderSupport classLoaderSupport = cl.classLoaderSupport; ModuleFinder beforeFinder = classLoaderSupport.modulepathModuleFinder; ModuleFinder afterFinder = classLoaderSupport.upgradeAndSystemModuleFinder; Configuration cf = synthesizeRuntimeBootLayerConfiguration(beforeFinder, afterFinder, reachableModules); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderOptions.java index dfc73222cd7e..a1cf23481efe 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderOptions.java @@ -50,7 +50,7 @@ public class NativeImageClassLoaderOptions { public static class ApplyNativeImageClassLoaderOptions implements NativeImageClassLoaderPostProcessing { @Override - public void apply(AbstractNativeImageClassLoaderSupport support) { + public void apply(NativeImageClassLoaderSupport support) { support.processClassLoaderOptions(); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderPostProcessing.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderPostProcessing.java index c9a4b94871de..0c3fb0271b9d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderPostProcessing.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderPostProcessing.java @@ -27,12 +27,12 @@ /** * ServiceLoader interface to allow post-processing tasks that should be performed right after - * {@link AbstractNativeImageClassLoaderSupport} is created. For example, this is used to apply the + * {@link NativeImageClassLoaderSupport} is created. For example, this is used to apply the * native-image classloader options after hosted options are accessible but before * {@link com.oracle.svm.hosted.ImageClassLoader#initAllClasses()} gets called. */ public interface NativeImageClassLoaderPostProcessing { - void apply(AbstractNativeImageClassLoaderSupport support); + void apply(NativeImageClassLoaderSupport support); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index 07dfd5a3b54d..1a5deeae1f1e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -24,55 +24,698 @@ */ package com.oracle.svm.hosted; +import static com.oracle.svm.core.util.VMError.shouldNotReachHere; + +import java.io.File; +import java.io.IOException; +import java.lang.module.Configuration; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.channels.ClosedByInterruptException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ForkJoinPool; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.EconomicSet; +import org.graalvm.collections.Pair; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.options.OptionValues; + +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.option.LocatableMultiOptionValue; +import com.oracle.svm.core.option.OptionOrigin; +import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.util.ClasspathUtils; +import com.oracle.svm.core.util.InterruptImageBuilding; +import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.option.HostedOptionParser; +import com.oracle.svm.util.ModuleSupport; + +import jdk.internal.module.Modules; + +public class NativeImageClassLoaderSupport { + + private final List imagecp; + private final List buildcp; + private final List imagemp; + private final List buildmp; + + private final EconomicMap> classes; + private final EconomicMap> packages; + private final EconomicSet emptySet; + + private final URLClassLoader classPathClassLoader; + private final ClassLoader modulePathClassLoader; + + public final ModuleFinder upgradeAndSystemModuleFinder; + public final ModuleLayer moduleLayerForImageBuild; + public final ModuleFinder modulepathModuleFinder; + + protected NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, String[] classpath, String[] modulePath) { + + classes = EconomicMap.create(); + packages = EconomicMap.create(); + emptySet = EconomicSet.create(); + + classPathClassLoader = new URLClassLoader(Util.verifyClassPathAndConvertToURLs(classpath), defaultSystemClassLoader); + + imagecp = Arrays.stream(classPathClassLoader.getURLs()).map(Util::urlToPath).collect(Collectors.toUnmodifiableList()); + String builderClassPathString = System.getProperty("java.class.path"); + String[] builderClassPathEntries = builderClassPathString.isEmpty() ? new String[0] : builderClassPathString.split(File.pathSeparator); + if (Arrays.asList(builderClassPathEntries).contains(".")) { + VMError.shouldNotReachHere("The classpath of " + NativeImageGeneratorRunner.class.getName() + + " must not contain \".\". This can happen implicitly if the builder runs exclusively on the --module-path" + + " but specifies the " + NativeImageGeneratorRunner.class.getName() + " main class without --module."); + } + buildcp = Arrays.stream(builderClassPathEntries) + .map(Paths::get).map(Path::toAbsolutePath).collect(Collectors.toUnmodifiableList()); + + imagemp = Arrays.stream(modulePath).map(Paths::get).collect(Collectors.toUnmodifiableList()); + buildmp = Optional.ofNullable(System.getProperty("jdk.module.path")).stream() + .flatMap(s -> Arrays.stream(s.split(File.pathSeparator))).map(Paths::get).collect(Collectors.toUnmodifiableList()); + + upgradeAndSystemModuleFinder = createUpgradeAndSystemModuleFinder(); + ModuleLayer moduleLayer = createModuleLayer(imagemp.toArray(Path[]::new), classPathClassLoader); + adjustBootLayerQualifiedExports(moduleLayer); + moduleLayerForImageBuild = moduleLayer; + + modulePathClassLoader = getSingleClassloader(moduleLayer); + + modulepathModuleFinder = ModuleFinder.of(modulepath().toArray(Path[]::new)); + } + + List classpath() { + return Stream.concat(imagecp.stream(), buildcp.stream()).collect(Collectors.toList()); + } + + List applicationClassPath() { + return imagecp; + } + + public ClassLoader getClassLoader() { + return modulePathClassLoader; + } + + public void initAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoader) { + new ClassInitWithModules(executor, imageClassLoader).init(); + } + + private HostedOptionParser hostedOptionParser; + private OptionValues parsedHostedOptions; + private List remainingArguments; + + public void setupHostedOptionParser(List arguments) { + hostedOptionParser = new HostedOptionParser(getClassLoader()); + remainingArguments = Collections.unmodifiableList((hostedOptionParser.parse(arguments))); + parsedHostedOptions = new OptionValues(hostedOptionParser.getHostedValues()); + } + + public HostedOptionParser getHostedOptionParser() { + return hostedOptionParser; + } + + public List getRemainingArguments() { + return remainingArguments; + } + + public OptionValues getParsedHostedOptions() { + return parsedHostedOptions; + } + + public EconomicSet classes(URI container) { + return classes.get(container, emptySet); + } + + public EconomicSet packages(URI container) { + return packages.get(container, emptySet); + } + + public boolean noEntryForURI(EconomicSet set) { + return set == emptySet; + } + + protected static class Util { + + static URL[] verifyClassPathAndConvertToURLs(String[] classpath) { + Stream pathStream = new LinkedHashSet<>(Arrays.asList(classpath)).stream().flatMap(Util::toClassPathEntries); + return pathStream.map(v -> { + try { + return v.toAbsolutePath().toUri().toURL(); + } catch (MalformedURLException e) { + throw UserError.abort("Invalid classpath element '%s'. Make sure that all paths provided with '%s' are correct.", v, SubstrateOptions.IMAGE_CLASSPATH_PREFIX); + } + }).toArray(URL[]::new); + } + + static Stream toClassPathEntries(String classPathEntry) { + Path entry = ClasspathUtils.stringToClasspath(classPathEntry); + if (entry.endsWith(ClasspathUtils.cpWildcardSubstitute)) { + try { + return Files.list(entry.getParent()).filter(ClasspathUtils::isJar); + } catch (IOException e) { + return Stream.empty(); + } + } + if (Files.isReadable(entry)) { + return Stream.of(entry); + } + return Stream.empty(); + } + + static Path urlToPath(URL url) { + try { + return Paths.get(url.toURI()); + } catch (URISyntaxException e) { + throw VMError.shouldNotReachHere(); + } + } + } + + private ModuleLayer createModuleLayer(Path[] modulePaths, ClassLoader parent) { + ModuleFinder modulePathsFinder = ModuleFinder.of(modulePaths); + Set moduleNames = modulePathsFinder.findAll().stream().map(moduleReference -> moduleReference.descriptor().name()).collect(Collectors.toSet()); + + /** + * When building a moduleLayer for the module-path passed to native-image we need to be able + * to resolve against system modules that are not used by the moduleLayer in which the + * image-builder got loaded into. To do so we use {@link upgradeAndSystemModuleFinder} as + * {@code ModuleFinder after} in + * {@link Configuration#resolve(ModuleFinder, ModuleFinder, Collection)}. + */ + Configuration configuration = ModuleLayer.boot().configuration().resolve(modulePathsFinder, upgradeAndSystemModuleFinder, moduleNames); + /** + * For the modules we want to build an image for, a ModuleLayer is needed that can be + * accessed with a single classloader so we can use it for {@link ImageClassLoader}. + */ + return ModuleLayer.defineModulesWithOneLoader(configuration, List.of(ModuleLayer.boot()), parent).layer(); + } + + /** + * Gets a finder that locates the upgrade modules and the system modules, in that order. Upgrade + * modules are used when mx environment variable {@code MX_BUILD_EXPLODED=true} is used. + */ + private static ModuleFinder createUpgradeAndSystemModuleFinder() { + ModuleFinder finder = ModuleFinder.ofSystem(); + ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path"); + if (upgradeModulePath != null) { + finder = ModuleFinder.compose(upgradeModulePath, finder); + } + return finder; + } -public class NativeImageClassLoaderSupport extends AbstractNativeImageClassLoaderSupport { + /** + * Creates a finder from a module path specified by the {@code prop} system property. + */ + private static ModuleFinder finderFor(String prop) { + String s = System.getProperty(prop); + if (s == null || s.isEmpty()) { + return null; + } else { + String[] dirs = s.split(File.pathSeparator); + Path[] paths = new Path[dirs.length]; + int i = 0; + for (String dir : dirs) { + paths[i++] = Path.of(dir); + } + return ModuleFinder.of(paths); + } + } + + private static void adjustBootLayerQualifiedExports(ModuleLayer layer) { + /* + * For all qualified exports packages of modules in the the boot layer we check if layer + * contains modules that satisfy such qualified exports. If we find a match we perform a + * addExports. + */ + for (Module module : ModuleLayer.boot().modules()) { + for (ModuleDescriptor.Exports export : module.getDescriptor().exports()) { + for (String target : export.targets()) { + Optional optExportTargetModule = layer.findModule(target); + if (optExportTargetModule.isEmpty()) { + continue; + } + Module exportTargetModule = optExportTargetModule.get(); + if (module.isExported(export.source(), exportTargetModule)) { + continue; + } + Modules.addExports(module, export.source(), exportTargetModule); + } + } + } + } - NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, String[] classpath, @SuppressWarnings("unused") String[] modulePath) { - super(defaultSystemClassLoader, classpath); + private ClassLoader getSingleClassloader(ModuleLayer moduleLayer) { + ClassLoader singleClassloader = classPathClassLoader; + for (Module module : moduleLayer.modules()) { + ClassLoader moduleClassLoader = module.getClassLoader(); + if (singleClassloader == classPathClassLoader) { + singleClassloader = moduleClassLoader; + } else { + VMError.guarantee(singleClassloader == moduleClassLoader); + } + } + return singleClassloader; + } + + private static void implAddReadsAllUnnamed(Module module) { + try { + Method implAddReadsAllUnnamed = Module.class.getDeclaredMethod("implAddReadsAllUnnamed"); + ModuleSupport.openModuleByClass(Module.class, NativeImageClassLoaderSupport.class); + implAddReadsAllUnnamed.setAccessible(true); + implAddReadsAllUnnamed.invoke(module); + } catch (ReflectiveOperationException | NoSuchElementException e) { + VMError.shouldNotReachHere("Could reflectively call Module.implAddReadsAllUnnamed", e); + } } - @Override protected List modulepath() { - return Collections.emptyList(); + return Stream.concat(imagemp.stream(), buildmp.stream()).collect(Collectors.toList()); } - @Override protected List applicationModulePath() { - return Collections.emptyList(); + return imagemp; } - @Override - protected Optional findModule(String moduleName) { - return Optional.empty(); + public Optional findModule(String moduleName) { + return moduleLayerForImageBuild.findModule(moduleName); } - @Override - protected void processClassLoaderOptions() { - /* Nothing to do for Java 8 */ + void processClassLoaderOptions() { + OptionValues optionValues = getParsedHostedOptions(); + + processOption(optionValues, NativeImageClassLoaderOptions.AddExports).forEach(val -> { + if (val.targetModules.isEmpty()) { + Modules.addExportsToAllUnnamed(val.module, val.packageName); + } else { + for (Module targetModule : val.targetModules) { + Modules.addExports(val.module, val.packageName, targetModule); + } + } + }); + processOption(optionValues, NativeImageClassLoaderOptions.AddOpens).forEach(val -> { + if (val.targetModules.isEmpty()) { + Modules.addOpensToAllUnnamed(val.module, val.packageName); + } else { + for (Module targetModule : val.targetModules) { + Modules.addOpens(val.module, val.packageName, targetModule); + } + } + }); + processOption(optionValues, NativeImageClassLoaderOptions.AddReads).forEach(val -> { + if (val.targetModules.isEmpty()) { + implAddReadsAllUnnamed(val.module); + } else { + for (Module targetModule : val.targetModules) { + Modules.addReads(val.module, targetModule); + } + } + }); } - @Override public void propagateQualifiedExports(String fromTargetModule, String toTargetModule) { - /* Nothing to do for Java 8 */ + Optional optFromTarget = moduleLayerForImageBuild.findModule(fromTargetModule); + Optional optToTarget = moduleLayerForImageBuild.findModule(toTargetModule); + VMError.guarantee(optFromTarget.isPresent() && optToTarget.isPresent()); + Module toTarget = optToTarget.get(); + Module fromTarget = optFromTarget.get(); + + allLayers(moduleLayerForImageBuild).stream().flatMap(layer -> layer.modules().stream()).forEach(m -> { + if (!m.equals(toTarget)) { + for (String p : m.getPackages()) { + if (m.isExported(p, fromTarget)) { + Modules.addExports(m, p, toTarget); + } + if (m.isOpen(p, fromTarget)) { + Modules.addOpens(m, p, toTarget); + } + } + } + }); } - @Override - protected Class loadClassFromModule(Object module, String className) throws ClassNotFoundException { - throw new UnsupportedOperationException("NativeImageClassLoader for Java 8 does not support modules"); + public static List allLayers(ModuleLayer moduleLayer) { + /** Implementation taken from {@link ModuleLayer#layers()} */ + List allLayers = new ArrayList<>(); + Set visited = new HashSet<>(); + Deque stack = new ArrayDeque<>(); + visited.add(moduleLayer); + stack.push(moduleLayer); + + while (!stack.isEmpty()) { + ModuleLayer layer = stack.pop(); + allLayers.add(layer); + + // push in reverse order + for (int i = layer.parents().size() - 1; i >= 0; i--) { + ModuleLayer parent = layer.parents().get(i); + if (!visited.contains(parent)) { + visited.add(parent); + stack.push(parent); + } + } + } + return allLayers; } - @Override - protected Optional getMainClassFromModule(Object module) { - return Optional.empty(); + private Stream processOption(OptionValues parsedHostedOptions, OptionKey specificOption) { + Stream> valuesWithOrigins = specificOption.getValue(parsedHostedOptions).getValuesWithOrigins(); + Stream parsedOptions = valuesWithOrigins.flatMap(valWithOrig -> { + try { + return Stream.of(asAddExportsAndOpensAndReadsFormatValue(specificOption, valWithOrig)); + } catch (UserError.UserException e) { + if (ModuleSupport.modulePathBuild) { + throw e; + } else { + /* + * Until we switch to always running the image-builder on module-path we have to + * be tolerant if invalid --add-exports -add-opens or --add-reads options are + * used. GR-30433 + */ + System.out.println("Warning: " + e.getMessage()); + return Stream.empty(); + } + } + }); + return parsedOptions; } - @Override - public void initAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoader) { - new ClassInit(executor, imageClassLoader).init(); + private static final class AddExportsAndOpensAndReadsFormatValue { + private final Module module; + private final String packageName; + private final List targetModules; + + private AddExportsAndOpensAndReadsFormatValue(Module module, String packageName, List targetModules) { + this.module = module; + this.packageName = packageName; + this.targetModules = targetModules; + } + } + + private AddExportsAndOpensAndReadsFormatValue asAddExportsAndOpensAndReadsFormatValue(OptionKey option, Pair valueOrigin) { + OptionOrigin optionOrigin = valueOrigin.getRight(); + String optionValue = valueOrigin.getLeft(); + + boolean reads = option.equals(NativeImageClassLoaderOptions.AddReads); + String format = reads ? NativeImageClassLoaderOptions.AddReadsFormat : NativeImageClassLoaderOptions.AddExportsAndOpensFormat; + String syntaxErrorMessage = " Allowed value format: " + format; + + String[] modulePackageAndTargetModules = optionValue.split("=", 2); + if (modulePackageAndTargetModules.length != 2) { + throw userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, syntaxErrorMessage); + } + String modulePackage = modulePackageAndTargetModules[0]; + String targetModuleNames = modulePackageAndTargetModules[1]; + + String[] moduleAndPackage = modulePackage.split("/"); + if (moduleAndPackage.length > 1 + (reads ? 0 : 1)) { + throw userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, syntaxErrorMessage); + } + String moduleName = moduleAndPackage[0]; + String packageName = moduleAndPackage.length > 1 ? moduleAndPackage[1] : null; + + List targetModuleNamesList = Arrays.asList(targetModuleNames.split(",")); + if (targetModuleNamesList.isEmpty()) { + throw userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, syntaxErrorMessage); + } + + Module module = findModule(moduleName).orElseThrow(() -> { + return userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, " Specified module '" + moduleName + "' is unknown."); + }); + List targetModules; + if (targetModuleNamesList.contains("ALL-UNNAMED")) { + targetModules = Collections.emptyList(); + } else { + targetModules = targetModuleNamesList.stream().map(mn -> { + return findModule(mn).orElseThrow(() -> { + throw userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, " Specified target-module '" + mn + "' is unknown."); + }); + }).collect(Collectors.toList()); + } + return new AddExportsAndOpensAndReadsFormatValue(module, packageName, targetModules); + } + + private static UserError.UserException userErrorAddExportsAndOpensAndReads(OptionKey option, OptionOrigin origin, String value, String detailMessage) { + Objects.requireNonNull(detailMessage, "missing detailMessage"); + return UserError.abort("Invalid option %s provided by %s.%s", SubstrateOptionsParser.commandArgument(option, value), origin, detailMessage); + } + + Class loadClassFromModule(Object module, String className) { + assert module instanceof Module : "Argument `module` is not an instance of java.lang.Module"; + Module m = (Module) module; + assert isModuleClassLoader(modulePathClassLoader, m.getClassLoader()) : "Argument `module` is java.lang.Module from unknown ClassLoader"; + return Class.forName(m, className); + } + + private static boolean isModuleClassLoader(ClassLoader loader, ClassLoader moduleClassLoader) { + if (moduleClassLoader == loader) { + return true; + } else { + if (loader == null) { + return false; + } + return isModuleClassLoader(loader.getParent(), moduleClassLoader); + } + } + + Optional getMainClassFromModule(Object module) { + assert module instanceof Module : "Argument `module` is not an instance of java.lang.Module"; + return ((Module) module).getDescriptor().mainClass(); + } + + private class ClassInit { + + protected final ForkJoinPool executor; + protected final ImageClassLoader imageClassLoader; + + protected ClassInit(ForkJoinPool executor, ImageClassLoader imageClassLoader) { + this.executor = executor; + this.imageClassLoader = imageClassLoader; + } + + protected void init() { + Set uniquePaths = new TreeSet<>(Comparator.comparing(this::toRealPath)); + uniquePaths.addAll(classpath()); + uniquePaths.parallelStream().forEach(path -> loadClassesFromPath(path)); + } + + private Path toRealPath(Path p) { + try { + return p.toRealPath(); + } catch (IOException e) { + throw VMError.shouldNotReachHere("Path.toRealPath failed for " + p, e); + } + } + + private final Set excludeDirectories = getExcludeDirectories(); + + private Set getExcludeDirectories() { + Path root = Paths.get("/"); + return Stream.of("dev", "sys", "proc", "etc", "var", "tmp", "boot", "lost+found") + .map(root::resolve).collect(Collectors.toSet()); + } + + private void loadClassesFromPath(Path path) { + if (ClasspathUtils.isJar(path)) { + try { + URI container = path.toAbsolutePath().toUri(); + URI jarURI = new URI("jar:" + container); + FileSystem probeJarFileSystem; + try { + probeJarFileSystem = FileSystems.newFileSystem(jarURI, Collections.emptyMap()); + } catch (UnsupportedOperationException e) { + /* Silently ignore invalid jar-files on image-classpath */ + probeJarFileSystem = null; + } + if (probeJarFileSystem != null) { + try (FileSystem jarFileSystem = probeJarFileSystem) { + loadClassesFromPath(container, jarFileSystem.getPath("/"), Collections.emptySet()); + } + } + } catch (ClosedByInterruptException ignored) { + throw new InterruptImageBuilding(); + } catch (IOException | URISyntaxException e) { + throw shouldNotReachHere(e); + } + } else { + URI container = path.toUri(); + loadClassesFromPath(container, path, excludeDirectories); + } + } + + protected static final String CLASS_EXTENSION = ".class"; + + private void loadClassesFromPath(URI container, Path root, Set excludes) { + FileVisitor visitor = new SimpleFileVisitor<>() { + private final char fileSystemSeparatorChar = root.getFileSystem().getSeparator().charAt(0); + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (excludes.contains(dir)) { + return FileVisitResult.SKIP_SUBTREE; + } + return super.preVisitDirectory(dir, attrs); + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + assert !excludes.contains(file.getParent()) : "Visiting file '" + file + "' with excluded parent directory"; + String fileName = root.relativize(file).toString(); + if (fileName.endsWith(CLASS_EXTENSION)) { + executor.execute(() -> handleClassFileName(container, null, fileName, fileSystemSeparatorChar)); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + /* Silently ignore inaccessible files or directories. */ + return FileVisitResult.CONTINUE; + } + }; + + try { + Files.walkFileTree(root, visitor); + } catch (IOException ex) { + throw shouldNotReachHere(ex); + } + } + + /** + * Take a file name from a possibly-multi-versioned jar file and remove the versioning + * information. See https://docs.oracle.com/javase/9/docs/api/java/util/jar/JarFile.html for + * the specification of the versioning strings. + * + * Then, depend on the JDK class loading mechanism to prefer the appropriately-versioned + * class when the class is loaded. The same class name be loaded multiple times, but each + * request will return the same appropriately-versioned class. If a higher-versioned class + * is not available in a lower-versioned JDK, a ClassNotFoundException will be thrown, which + * will be handled appropriately. + */ + private String strippedClassFileName(String fileName) { + final String versionedPrefix = "META-INF/versions/"; + final String versionedSuffix = "/"; + String result = fileName; + if (fileName.startsWith(versionedPrefix)) { + final int versionedSuffixIndex = fileName.indexOf(versionedSuffix, versionedPrefix.length()); + if (versionedSuffixIndex >= 0) { + result = fileName.substring(versionedSuffixIndex + versionedSuffix.length()); + } + } + return result.substring(0, result.length() - CLASS_EXTENSION.length()); + } + + protected void handleClassFileName(URI container, Object module, String fileName, char fileSystemSeparatorChar) { + String strippedClassFileName = strippedClassFileName(fileName); + if (strippedClassFileName.equals("module-info")) { + return; + } + + String className = strippedClassFileName.replace(fileSystemSeparatorChar, '.'); + synchronized (classes) { + EconomicSet classNames = classes.get(container); + if (classNames == null) { + classNames = EconomicSet.create(); + classes.put(container, classNames); + } + classNames.add(className); + } + int packageSep = className.lastIndexOf('.'); + String packageName = packageSep > 0 ? className.substring(0, packageSep) : ""; + synchronized (packages) { + EconomicSet packageNames = packages.get(container); + if (packageNames == null) { + packageNames = EconomicSet.create(); + packages.put(container, packageNames); + } + packageNames.add(packageName); + } + + Class clazz = null; + try { + clazz = imageClassLoader.forName(className, module); + } catch (AssertionError error) { + VMError.shouldNotReachHere(error); + } catch (Throwable t) { + ImageClassLoader.handleClassLoadingError(t); + } + if (clazz != null) { + imageClassLoader.handleClass(clazz); + } + } + } + + private class ClassInitWithModules extends ClassInit { + + ClassInitWithModules(ForkJoinPool executor, ImageClassLoader imageClassLoader) { + super(executor, imageClassLoader); + } + + @Override + protected void init() { + List requiresInit = Arrays.asList( + "jdk.internal.vm.ci", "jdk.internal.vm.compiler", "com.oracle.graal.graal_enterprise", + "org.graalvm.sdk", "org.graalvm.truffle"); + + for (ModuleReference moduleReference : upgradeAndSystemModuleFinder.findAll()) { + if (requiresInit.contains(moduleReference.descriptor().name())) { + initModule(moduleReference); + } + } + for (ModuleReference moduleReference : modulepathModuleFinder.findAll()) { + initModule(moduleReference); + } + + super.init(); + } + + private void initModule(ModuleReference moduleReference) { + Optional optionalModule = findModule(moduleReference.descriptor().name()); + if (optionalModule.isEmpty()) { + return; + } + try (ModuleReader moduleReader = moduleReference.open()) { + Module module = optionalModule.get(); + moduleReader.list().forEach(moduleResource -> { + if (moduleResource.endsWith(CLASS_EXTENSION)) { + executor.execute(() -> handleClassFileName(moduleReference.location().orElseThrow(), module, moduleResource, '/')); + } + }); + } catch (IOException e) { + throw new RuntimeException("Unable get list of resources in module" + moduleReference.descriptor().name(), e); + } + } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java index 08af81b72649..aa605cef423a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java @@ -72,7 +72,6 @@ import com.oracle.svm.hosted.analysis.NativeImagePointsToAnalysis; import com.oracle.svm.hosted.code.CEntryPointData; import com.oracle.svm.hosted.image.AbstractImage.NativeImageKind; -import com.oracle.svm.hosted.jdk.NativeImageClassLoaderSupportJDK11OrLater; import com.oracle.svm.hosted.option.HostedOptionParser; import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.ModuleSupport; @@ -178,8 +177,7 @@ public static void uninstallNativeImageClassLoader() { */ public static ImageClassLoader installNativeImageClassLoader(String[] classpath, String[] modulepath, List arguments) { NativeImageSystemClassLoader nativeImageSystemClassLoader = NativeImageSystemClassLoader.singleton(); - AbstractNativeImageClassLoaderSupport nativeImageClassLoaderSupport = new NativeImageClassLoaderSupportJDK11OrLater(nativeImageSystemClassLoader.defaultSystemClassLoader, classpath, - modulepath); + NativeImageClassLoaderSupport nativeImageClassLoaderSupport = new NativeImageClassLoaderSupport(nativeImageSystemClassLoader.defaultSystemClassLoader, classpath, modulepath); nativeImageClassLoaderSupport.setupHostedOptionParser(arguments); /* Perform additional post-processing with the created nativeImageClassLoaderSupport */ for (NativeImageClassLoaderPostProcessing postProcessing : ServiceLoader.load(NativeImageClassLoaderPostProcessing.class)) { @@ -194,7 +192,7 @@ public static ImageClassLoader installNativeImageClassLoader(String[] classpath, */ nativeImageSystemClassLoader.setNativeImageClassLoader(nativeImageClassLoader); - if (!nativeImageClassLoaderSupport.imagecp.isEmpty()) { + if (!ModuleSupport.modulePathBuild) { ModuleSupport.openModuleByClass(JavaVersionUtil.class, null); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java index 7ff6056679a6..d028a036bbff 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java @@ -50,14 +50,15 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ClassLoaderSupportImpl; import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.NativeImageClassLoaderSupport; import com.oracle.svm.util.ModuleSupport; final class ClassLoaderSupportImplJDK11OrLater extends ClassLoaderSupportImpl { - private final NativeImageClassLoaderSupportJDK11OrLater classLoaderSupport; + private final NativeImageClassLoaderSupport classLoaderSupport; private final Map> packageToModules; - ClassLoaderSupportImplJDK11OrLater(NativeImageClassLoaderSupportJDK11OrLater classLoaderSupport) { + ClassLoaderSupportImplJDK11OrLater(NativeImageClassLoaderSupport classLoaderSupport) { super(classLoaderSupport); this.classLoaderSupport = classLoaderSupport; packageToModules = new HashMap<>(); @@ -67,7 +68,7 @@ final class ClassLoaderSupportImplJDK11OrLater extends ClassLoaderSupportImpl { @Override public void collectResources(ResourceCollector resourceCollector) { /* Collect resources from modules */ - NativeImageClassLoaderSupportJDK11OrLater.allLayers(classLoaderSupport.moduleLayerForImageBuild).stream() + NativeImageClassLoaderSupport.allLayers(classLoaderSupport.moduleLayerForImageBuild).stream() .flatMap(moduleLayer -> moduleLayer.configuration().modules().stream()) .forEach(resolvedModule -> collectResourceFromModule(resourceCollector, resolvedModule)); @@ -136,8 +137,8 @@ private static String packageName(String bundleName) { return bundleName.substring(0, classSep); } - private void buildPackageToModulesMap(NativeImageClassLoaderSupportJDK11OrLater cls) { - for (ModuleLayer layer : NativeImageClassLoaderSupportJDK11OrLater.allLayers(cls.moduleLayerForImageBuild)) { + private void buildPackageToModulesMap(NativeImageClassLoaderSupport cls) { + for (ModuleLayer layer : NativeImageClassLoaderSupport.allLayers(cls.moduleLayerForImageBuild)) { for (Module module : layer.modules()) { for (String packageName : module.getDescriptor().packages()) { addToPackageNameModules(module, packageName); @@ -169,6 +170,6 @@ public class ClassLoaderSupportFeatureJDK11OrLater implements Feature { @Override public void afterRegistration(AfterRegistrationAccess a) { FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a; - ImageSingletons.add(ClassLoaderSupport.class, new ClassLoaderSupportImplJDK11OrLater((NativeImageClassLoaderSupportJDK11OrLater) access.getImageClassLoader().classLoaderSupport)); + ImageSingletons.add(ClassLoaderSupport.class, new ClassLoaderSupportImplJDK11OrLater(access.getImageClassLoader().classLoaderSupport)); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/NativeImageClassLoaderSupportJDK11OrLater.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/NativeImageClassLoaderSupportJDK11OrLater.java deleted file mode 100644 index c774451ca8af..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/NativeImageClassLoaderSupportJDK11OrLater.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (c) 2020, 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 com.oracle.svm.hosted.jdk; - -import java.io.File; -import java.io.IOException; -import java.lang.module.Configuration; -import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleFinder; -import java.lang.module.ModuleReader; -import java.lang.module.ModuleReference; -import java.lang.reflect.Method; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Deque; -import java.util.HashSet; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ForkJoinPool; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.graalvm.collections.Pair; -import org.graalvm.compiler.options.OptionKey; -import org.graalvm.compiler.options.OptionValues; - -import com.oracle.svm.core.option.LocatableMultiOptionValue; -import com.oracle.svm.core.option.OptionOrigin; -import com.oracle.svm.core.option.SubstrateOptionsParser; -import com.oracle.svm.core.util.UserError; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.AbstractNativeImageClassLoaderSupport; -import com.oracle.svm.hosted.ImageClassLoader; -import com.oracle.svm.hosted.NativeImageClassLoaderOptions; -import com.oracle.svm.util.ModuleSupport; - -import jdk.internal.module.Modules; - -public class NativeImageClassLoaderSupportJDK11OrLater extends AbstractNativeImageClassLoaderSupport { - - private final List imagemp; - private final List buildmp; - - private final ClassLoader classLoader; - - public final ModuleFinder upgradeAndSystemModuleFinder; - public final ModuleLayer moduleLayerForImageBuild; - - public final ModuleFinder modulepathModuleFinder; - - public NativeImageClassLoaderSupportJDK11OrLater(ClassLoader defaultSystemClassLoader, String[] classpath, String[] modulePath) { - super(defaultSystemClassLoader, classpath); - - imagemp = Arrays.stream(modulePath).map(Paths::get).collect(Collectors.toUnmodifiableList()); - buildmp = Optional.ofNullable(System.getProperty("jdk.module.path")).stream() - .flatMap(s -> Arrays.stream(s.split(File.pathSeparator))).map(Paths::get).collect(Collectors.toUnmodifiableList()); - - upgradeAndSystemModuleFinder = createUpgradeAndSystemModuleFinder(); - ModuleLayer moduleLayer = createModuleLayer(imagemp.toArray(Path[]::new), classPathClassLoader); - adjustBootLayerQualifiedExports(moduleLayer); - moduleLayerForImageBuild = moduleLayer; - - classLoader = getSingleClassloader(moduleLayer); - - modulepathModuleFinder = ModuleFinder.of(modulepath().toArray(Path[]::new)); - } - - private ModuleLayer createModuleLayer(Path[] modulePaths, ClassLoader parent) { - ModuleFinder modulePathsFinder = ModuleFinder.of(modulePaths); - Set moduleNames = modulePathsFinder.findAll().stream().map(moduleReference -> moduleReference.descriptor().name()).collect(Collectors.toSet()); - - /** - * When building a moduleLayer for the module-path passed to native-image we need to be able - * to resolve against system modules that are not used by the moduleLayer in which the - * image-builder got loaded into. To do so we use {@link upgradeAndSystemModuleFinder} as - * {@code ModuleFinder after} in - * {@link Configuration#resolve(ModuleFinder, ModuleFinder, Collection)}. - */ - Configuration configuration = ModuleLayer.boot().configuration().resolve(modulePathsFinder, upgradeAndSystemModuleFinder, moduleNames); - /** - * For the modules we want to build an image for, a ModuleLayer is needed that can be - * accessed with a single classloader so we can use it for {@link ImageClassLoader}. - */ - return ModuleLayer.defineModulesWithOneLoader(configuration, List.of(ModuleLayer.boot()), parent).layer(); - } - - /** - * Gets a finder that locates the upgrade modules and the system modules, in that order. Upgrade - * modules are used when mx environment variable {@code MX_BUILD_EXPLODED=true} is used. - */ - private static ModuleFinder createUpgradeAndSystemModuleFinder() { - ModuleFinder finder = ModuleFinder.ofSystem(); - ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path"); - if (upgradeModulePath != null) { - finder = ModuleFinder.compose(upgradeModulePath, finder); - } - return finder; - } - - /** - * Creates a finder from a module path specified by the {@code prop} system property. - */ - private static ModuleFinder finderFor(String prop) { - String s = System.getProperty(prop); - if (s == null || s.isEmpty()) { - return null; - } else { - String[] dirs = s.split(File.pathSeparator); - Path[] paths = new Path[dirs.length]; - int i = 0; - for (String dir : dirs) { - paths[i++] = Path.of(dir); - } - return ModuleFinder.of(paths); - } - } - - private static void adjustBootLayerQualifiedExports(ModuleLayer layer) { - /* - * For all qualified exports packages of modules in the the boot layer we check if layer - * contains modules that satisfy such qualified exports. If we find a match we perform a - * addExports. - */ - for (Module module : ModuleLayer.boot().modules()) { - for (ModuleDescriptor.Exports export : module.getDescriptor().exports()) { - for (String target : export.targets()) { - Optional optExportTargetModule = layer.findModule(target); - if (optExportTargetModule.isEmpty()) { - continue; - } - Module exportTargetModule = optExportTargetModule.get(); - if (module.isExported(export.source(), exportTargetModule)) { - continue; - } - Modules.addExports(module, export.source(), exportTargetModule); - } - } - } - } - - private ClassLoader getSingleClassloader(ModuleLayer moduleLayer) { - ClassLoader singleClassloader = classPathClassLoader; - for (Module module : moduleLayer.modules()) { - ClassLoader moduleClassLoader = module.getClassLoader(); - if (singleClassloader == classPathClassLoader) { - singleClassloader = moduleClassLoader; - } else { - VMError.guarantee(singleClassloader == moduleClassLoader); - } - } - return singleClassloader; - } - - private static void implAddReadsAllUnnamed(Module module) { - try { - Method implAddReadsAllUnnamed = Module.class.getDeclaredMethod("implAddReadsAllUnnamed"); - ModuleSupport.openModuleByClass(Module.class, NativeImageClassLoaderSupportJDK11OrLater.class); - implAddReadsAllUnnamed.setAccessible(true); - implAddReadsAllUnnamed.invoke(module); - } catch (ReflectiveOperationException | NoSuchElementException e) { - VMError.shouldNotReachHere("Could reflectively call Module.implAddReadsAllUnnamed", e); - } - } - - @Override - protected List modulepath() { - return Stream.concat(imagemp.stream(), buildmp.stream()).collect(Collectors.toList()); - } - - @Override - protected List applicationModulePath() { - return imagemp; - } - - @Override - protected Optional findModule(String moduleName) { - return moduleLayerForImageBuild.findModule(moduleName); - } - - @Override - protected void processClassLoaderOptions() { - OptionValues optionValues = getParsedHostedOptions(); - - processOption(optionValues, NativeImageClassLoaderOptions.AddExports).forEach(val -> { - if (val.targetModules.isEmpty()) { - Modules.addExportsToAllUnnamed(val.module, val.packageName); - } else { - for (Module targetModule : val.targetModules) { - Modules.addExports(val.module, val.packageName, targetModule); - } - } - }); - processOption(optionValues, NativeImageClassLoaderOptions.AddOpens).forEach(val -> { - if (val.targetModules.isEmpty()) { - Modules.addOpensToAllUnnamed(val.module, val.packageName); - } else { - for (Module targetModule : val.targetModules) { - Modules.addOpens(val.module, val.packageName, targetModule); - } - } - }); - processOption(optionValues, NativeImageClassLoaderOptions.AddReads).forEach(val -> { - if (val.targetModules.isEmpty()) { - implAddReadsAllUnnamed(val.module); - } else { - for (Module targetModule : val.targetModules) { - Modules.addReads(val.module, targetModule); - } - } - }); - } - - @Override - public void propagateQualifiedExports(String fromTargetModule, String toTargetModule) { - Optional optFromTarget = moduleLayerForImageBuild.findModule(fromTargetModule); - Optional optToTarget = moduleLayerForImageBuild.findModule(toTargetModule); - VMError.guarantee(!optFromTarget.isEmpty() && !optToTarget.isEmpty()); - Module toTarget = optToTarget.get(); - Module fromTarget = optFromTarget.get(); - - allLayers(moduleLayerForImageBuild).stream().flatMap(layer -> layer.modules().stream()).forEach(m -> { - if (!m.equals(toTarget)) { - for (String p : m.getPackages()) { - if (m.isExported(p, fromTarget)) { - Modules.addExports(m, p, toTarget); - } - if (m.isOpen(p, fromTarget)) { - Modules.addOpens(m, p, toTarget); - } - } - } - }); - } - - static List allLayers(ModuleLayer moduleLayer) { - /** Implementation taken from {@link ModuleLayer#layers()} */ - List allLayers = new ArrayList<>(); - Set visited = new HashSet<>(); - Deque stack = new ArrayDeque<>(); - visited.add(moduleLayer); - stack.push(moduleLayer); - - while (!stack.isEmpty()) { - ModuleLayer layer = stack.pop(); - allLayers.add(layer); - - // push in reverse order - for (int i = layer.parents().size() - 1; i >= 0; i--) { - ModuleLayer parent = layer.parents().get(i); - if (!visited.contains(parent)) { - visited.add(parent); - stack.push(parent); - } - } - } - return allLayers; - } - - private Stream processOption(OptionValues parsedHostedOptions, OptionKey specificOption) { - Stream> valuesWithOrigins = specificOption.getValue(parsedHostedOptions).getValuesWithOrigins(); - Stream parsedOptions = valuesWithOrigins.flatMap(valWithOrig -> { - try { - return Stream.of(asAddExportsAndOpensAndReadsFormatValue(specificOption, valWithOrig)); - } catch (UserError.UserException e) { - if (ModuleSupport.modulePathBuild) { - throw e; - } else { - /* - * Until we switch to always running the image-builder on module-path we have to - * be tolerant if invalid --add-exports -add-opens or --add-reads options are - * used. GR-30433 - */ - System.out.println("Warning: " + e.getMessage()); - return Stream.empty(); - } - } - }); - return parsedOptions; - } - - private static final class AddExportsAndOpensAndReadsFormatValue { - private final Module module; - private final String packageName; - private final List targetModules; - - private AddExportsAndOpensAndReadsFormatValue(Module module, String packageName, List targetModules) { - this.module = module; - this.packageName = packageName; - this.targetModules = targetModules; - } - } - - private AddExportsAndOpensAndReadsFormatValue asAddExportsAndOpensAndReadsFormatValue(OptionKey option, Pair valueOrigin) { - OptionOrigin optionOrigin = valueOrigin.getRight(); - String optionValue = valueOrigin.getLeft(); - - boolean reads = option.equals(NativeImageClassLoaderOptions.AddReads); - String format = reads ? NativeImageClassLoaderOptions.AddReadsFormat : NativeImageClassLoaderOptions.AddExportsAndOpensFormat; - String syntaxErrorMessage = " Allowed value format: " + format; - - String[] modulePackageAndTargetModules = optionValue.split("=", 2); - if (modulePackageAndTargetModules.length != 2) { - throw userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, syntaxErrorMessage); - } - String modulePackage = modulePackageAndTargetModules[0]; - String targetModuleNames = modulePackageAndTargetModules[1]; - - String[] moduleAndPackage = modulePackage.split("/"); - if (moduleAndPackage.length > 1 + (reads ? 0 : 1)) { - throw userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, syntaxErrorMessage); - } - String moduleName = moduleAndPackage[0]; - String packageName = moduleAndPackage.length > 1 ? moduleAndPackage[1] : null; - - List targetModuleNamesList = Arrays.asList(targetModuleNames.split(",")); - if (targetModuleNamesList.isEmpty()) { - throw userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, syntaxErrorMessage); - } - - Module module = findModule(moduleName).orElseThrow(() -> { - return userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, " Specified module '" + moduleName + "' is unknown."); - }); - List targetModules; - if (targetModuleNamesList.contains("ALL-UNNAMED")) { - targetModules = Collections.emptyList(); - } else { - targetModules = targetModuleNamesList.stream().map(mn -> { - return findModule(mn).orElseThrow(() -> { - throw userErrorAddExportsAndOpensAndReads(option, optionOrigin, optionValue, " Specified target-module '" + mn + "' is unknown."); - }); - }).collect(Collectors.toList()); - } - return new AddExportsAndOpensAndReadsFormatValue(module, packageName, targetModules); - } - - private static UserError.UserException userErrorAddExportsAndOpensAndReads(OptionKey option, OptionOrigin origin, String value, String detailMessage) { - Objects.requireNonNull(detailMessage, "missing detailMessage"); - return UserError.abort("Invalid option %s provided by %s.%s", SubstrateOptionsParser.commandArgument(option, value), origin, detailMessage); - } - - @Override - protected Class loadClassFromModule(Object module, String className) throws ClassNotFoundException { - assert module instanceof Module : "Argument `module` is not an instance of java.lang.Module"; - Module m = (Module) module; - assert isModuleClassLoader(classLoader, m.getClassLoader()) : "Argument `module` is java.lang.Module from unknown ClassLoader"; - return Class.forName(m, className); - } - - private static boolean isModuleClassLoader(ClassLoader loader, ClassLoader moduleClassLoader) { - if (moduleClassLoader == loader) { - return true; - } else { - if (loader == null) { - return false; - } - return isModuleClassLoader(loader.getParent(), moduleClassLoader); - } - } - - @Override - protected Optional getMainClassFromModule(Object module) { - assert module instanceof Module : "Argument `module` is not an instance of java.lang.Module"; - return ((Module) module).getDescriptor().mainClass(); - } - - @Override - public ClassLoader getClassLoader() { - return classLoader; - } - - private class ClassInitWithModules extends ClassInit { - - ClassInitWithModules(ForkJoinPool executor, ImageClassLoader imageClassLoader) { - super(executor, imageClassLoader); - } - - @Override - protected void init() { - List requiresInit = Arrays.asList( - "jdk.internal.vm.ci", "jdk.internal.vm.compiler", "com.oracle.graal.graal_enterprise", - "org.graalvm.sdk", "org.graalvm.truffle"); - - for (ModuleReference moduleReference : upgradeAndSystemModuleFinder.findAll()) { - if (requiresInit.contains(moduleReference.descriptor().name())) { - initModule(moduleReference); - } - } - for (ModuleReference moduleReference : modulepathModuleFinder.findAll()) { - initModule(moduleReference); - } - - super.init(); - } - - private void initModule(ModuleReference moduleReference) { - Optional optionalModule = findModule(moduleReference.descriptor().name()); - if (optionalModule.isEmpty()) { - return; - } - try (ModuleReader moduleReader = moduleReference.open()) { - Module module = optionalModule.get(); - moduleReader.list().forEach(moduleResource -> { - if (moduleResource.endsWith(CLASS_EXTENSION)) { - executor.execute(() -> handleClassFileName(moduleReference.location().get(), module, moduleResource, '/')); - } - }); - } catch (IOException e) { - throw new RuntimeException("Unable get list of resources in module" + moduleReference.descriptor().name(), e); - } - } - } - - @Override - public void initAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoader) { - new ClassInitWithModules(executor, imageClassLoader).init(); - } -} From b25c477326ae0671c1eed31477fddbdf50dcd352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Mar 2022 12:12:18 +0100 Subject: [PATCH 2/7] Fold ClassLoaderSupportImplJDK11OrLater into ClassLoaderSupportImpl --- ...er.java => ClassLoaderSupportFeature.java} | 127 ++++++++++++-- .../svm/hosted/ClassLoaderSupportImpl.java | 165 ------------------ .../svm/hosted/LinkAtBuildTimeFeature.java | 3 +- 3 files changed, 116 insertions(+), 179 deletions(-) rename substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/{jdk/ClassLoaderSupportFeatureJDK11OrLater.java => ClassLoaderSupportFeature.java} (55%) delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportFeature.java similarity index 55% rename from substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java rename to substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportFeature.java index d028a036bbff..981eec9d9bb6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportFeature.java @@ -23,15 +23,23 @@ * questions. */ -package com.oracle.svm.hosted.jdk; +package com.oracle.svm.hosted; +import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR; + +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.lang.module.ResolvedModule; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -40,31 +48,41 @@ import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; +import java.util.function.Predicate; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.ClassLoaderSupport; import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.util.ClasspathUtils; +import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.ClassLoaderSupportImpl; -import com.oracle.svm.hosted.FeatureImpl; -import com.oracle.svm.hosted.NativeImageClassLoaderSupport; import com.oracle.svm.util.ModuleSupport; -final class ClassLoaderSupportImplJDK11OrLater extends ClassLoaderSupportImpl { +class ClassLoaderSupportImpl extends ClassLoaderSupport { private final NativeImageClassLoaderSupport classLoaderSupport; + private final ClassLoader imageClassLoader; + private final Map> packageToModules; - ClassLoaderSupportImplJDK11OrLater(NativeImageClassLoaderSupport classLoaderSupport) { - super(classLoaderSupport); + protected ClassLoaderSupportImpl(NativeImageClassLoaderSupport classLoaderSupport) { this.classLoaderSupport = classLoaderSupport; + this.imageClassLoader = classLoaderSupport.getClassLoader(); packageToModules = new HashMap<>(); buildPackageToModulesMap(classLoaderSupport); } + @Override + protected boolean isNativeImageClassLoaderImpl(ClassLoader loader) { + return loader == imageClassLoader || loader instanceof NativeImageSystemClassLoader; + } + @Override public void collectResources(ResourceCollector resourceCollector) { /* Collect resources from modules */ @@ -73,7 +91,17 @@ public void collectResources(ResourceCollector resourceCollector) { .forEach(resolvedModule -> collectResourceFromModule(resourceCollector, resolvedModule)); /* Collect remaining resources from classpath */ - super.collectResources(resourceCollector); + for (Path classpathFile : classLoaderSupport.classpath()) { + try { + if (Files.isDirectory(classpathFile)) { + scanDirectory(classpathFile, resourceCollector, classLoaderSupport); + } else if (ClasspathUtils.isJar(classpathFile)) { + scanJar(classpathFile, resourceCollector); + } + } catch (IOException ex) { + throw UserError.abort("Unable to handle classpath element '%s'. Make sure that all classpath entries are either directories or valid jar files.", classpathFile); + } + } } private static void collectResourceFromModule(ResourceCollector resourceCollector, ResolvedModule resolvedModule) { @@ -98,6 +126,81 @@ private static void collectResourceFromModule(ResourceCollector resourceCollecto } } + private static void scanDirectory(Path root, ResourceCollector collector, NativeImageClassLoaderSupport support) throws IOException { + Map> matchedDirectoryResources = new HashMap<>(); + Set allEntries = new HashSet<>(); + + ArrayDeque queue = new ArrayDeque<>(); + queue.push(root); + while (!queue.isEmpty()) { + Path entry = queue.pop(); + + /* Resources always use / as the separator, as do our resource inclusion patterns */ + String relativeFilePath; + if (entry != root) { + relativeFilePath = root.relativize(entry).toString().replace(File.separatorChar, RESOURCES_INTERNAL_PATH_SEPARATOR); + allEntries.add(relativeFilePath); + } else { + relativeFilePath = ""; + } + + if (Files.isDirectory(entry)) { + if (collector.isIncluded(null, relativeFilePath)) { + matchedDirectoryResources.put(relativeFilePath, new ArrayList<>()); + } + try (Stream pathStream = Files.list(entry)) { + Stream filtered = pathStream; + if (support.excludeDirectoriesRoot.equals(entry)) { + filtered = filtered.filter(Predicate.not(support.excludeDirectories::contains)); + } + filtered.forEach(queue::push); + } + } else { + if (collector.isIncluded(null, relativeFilePath)) { + try (InputStream is = Files.newInputStream(entry)) { + collector.addResource(null, relativeFilePath, is, false); + } + } + } + } + + for (String entry : allEntries) { + int last = entry.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); + String key = last == -1 ? "" : entry.substring(0, last); + List dirContent = matchedDirectoryResources.get(key); + if (dirContent != null && !dirContent.contains(entry)) { + dirContent.add(entry.substring(last + 1)); + } + } + + matchedDirectoryResources.forEach((dir, content) -> { + content.sort(Comparator.naturalOrder()); + collector.addDirectoryResource(null, dir, String.join(System.lineSeparator(), content), false); + }); + } + + private static void scanJar(Path jarPath, ResourceCollector collector) throws IOException { + try (JarFile jf = new JarFile(jarPath.toFile())) { + Enumeration entries = jf.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.isDirectory()) { + String dirName = entry.getName().substring(0, entry.getName().length() - 1); + if (collector.isIncluded(null, dirName)) { + // Register the directory with empty content to preserve Java behavior + collector.addDirectoryResource(null, dirName, "", true); + } + } else { + if (collector.isIncluded(null, entry.getName())) { + try (InputStream is = jf.getInputStream(entry)) { + collector.addResource(null, entry.getName(), is, true); + } + } + } + } + } + } + @Override public List getResourceBundle(String bundleSpec, Locale locale) { String[] specParts = bundleSpec.split(":", 2); @@ -119,11 +222,11 @@ public List getResourceBundle(String bundleSpec, Locale locale) } if (modules.isEmpty()) { /* If bundle is not located in any module get it via classloader (from ALL_UNNAMED) */ - return super.getResourceBundle(bundleName, locale); + return Collections.singletonList(ResourceBundle.getBundle(bundleName, locale, imageClassLoader)); } ArrayList resourceBundles = new ArrayList<>(); for (Module module : modules) { - ModuleSupport.exportAndOpenPackageToClass(module.getName(), packageName, false, ClassLoaderSupportImplJDK11OrLater.class); + ModuleSupport.exportAndOpenPackageToClass(module.getName(), packageName, false, ClassLoaderSupportImpl.class); resourceBundles.add(ResourceBundle.getBundle(bundleName, locale, module)); } return resourceBundles; @@ -166,10 +269,10 @@ private void addToPackageNameModules(Module moduleName, String packageName) { } @AutomaticFeature -public class ClassLoaderSupportFeatureJDK11OrLater implements Feature { +public class ClassLoaderSupportFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess a) { FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a; - ImageSingletons.add(ClassLoaderSupport.class, new ClassLoaderSupportImplJDK11OrLater(access.getImageClassLoader().classLoaderSupport)); + ImageSingletons.add(ClassLoaderSupport.class, new ClassLoaderSupportImpl(access.getImageClassLoader().classLoaderSupport)); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java deleted file mode 100644 index cee4d36690c2..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2021, 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 com.oracle.svm.hosted; - -import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.function.Predicate; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.stream.Stream; - -import com.oracle.svm.core.ClassLoaderSupport; -import com.oracle.svm.core.util.ClasspathUtils; -import com.oracle.svm.core.util.UserError; - -public class ClassLoaderSupportImpl extends ClassLoaderSupport { - - private final NativeImageClassLoaderSupport classLoaderSupport; - private final ClassLoader imageClassLoader; - - protected ClassLoaderSupportImpl(NativeImageClassLoaderSupport classLoaderSupport) { - this.classLoaderSupport = classLoaderSupport; - this.imageClassLoader = classLoaderSupport.getClassLoader(); - } - - @Override - protected boolean isNativeImageClassLoaderImpl(ClassLoader loader) { - return loader == imageClassLoader || loader instanceof NativeImageSystemClassLoader; - } - - @Override - public void collectResources(ResourceCollector resourceCollector) { - for (Path classpathFile : classLoaderSupport.classpath()) { - try { - if (Files.isDirectory(classpathFile)) { - scanDirectory(classpathFile, resourceCollector, classLoaderSupport); - } else if (ClasspathUtils.isJar(classpathFile)) { - scanJar(classpathFile, resourceCollector); - } - } catch (IOException ex) { - throw UserError.abort("Unable to handle classpath element '%s'. Make sure that all classpath entries are either directories or valid jar files.", classpathFile); - } - } - } - - private static void scanDirectory(Path root, ResourceCollector collector, AbstractNativeImageClassLoaderSupport support) throws IOException { - Map> matchedDirectoryResources = new HashMap<>(); - Set allEntries = new HashSet<>(); - - ArrayDeque queue = new ArrayDeque<>(); - queue.push(root); - while (!queue.isEmpty()) { - Path entry = queue.pop(); - - /* Resources always use / as the separator, as do our resource inclusion patterns */ - String relativeFilePath; - if (entry != root) { - relativeFilePath = root.relativize(entry).toString().replace(File.separatorChar, RESOURCES_INTERNAL_PATH_SEPARATOR); - allEntries.add(relativeFilePath); - } else { - relativeFilePath = ""; - } - - if (Files.isDirectory(entry)) { - if (collector.isIncluded(null, relativeFilePath)) { - matchedDirectoryResources.put(relativeFilePath, new ArrayList<>()); - } - try (Stream pathStream = Files.list(entry)) { - Stream filtered = pathStream; - if (support.excludeDirectoriesRoot.equals(entry)) { - filtered = filtered.filter(Predicate.not(support.excludeDirectories::contains)); - } - filtered.forEach(queue::push); - } - } else { - if (collector.isIncluded(null, relativeFilePath)) { - try (InputStream is = Files.newInputStream(entry)) { - collector.addResource(null, relativeFilePath, is, false); - } - } - } - } - - for (String entry : allEntries) { - int last = entry.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); - String key = last == -1 ? "" : entry.substring(0, last); - List dirContent = matchedDirectoryResources.get(key); - if (dirContent != null && !dirContent.contains(entry)) { - dirContent.add(entry.substring(last + 1)); - } - } - - matchedDirectoryResources.forEach((dir, content) -> { - content.sort(Comparator.naturalOrder()); - collector.addDirectoryResource(null, dir, String.join(System.lineSeparator(), content), false); - }); - } - - private static void scanJar(Path jarPath, ResourceCollector collector) throws IOException { - try (JarFile jf = new JarFile(jarPath.toFile())) { - Enumeration entries = jf.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - if (entry.isDirectory()) { - String dirName = entry.getName().substring(0, entry.getName().length() - 1); - if (collector.isIncluded(null, dirName)) { - // Register the directory with empty content to preserve Java behavior - collector.addDirectoryResource(null, dirName, "", true); - } - } else { - if (collector.isIncluded(null, entry.getName())) { - try (InputStream is = jf.getInputStream(entry)) { - collector.addResource(null, entry.getName(), is, true); - } - } - } - } - } - } - - @Override - public List getResourceBundle(String bundleName, Locale locale) { - return Collections.singletonList(ResourceBundle.getBundle(bundleName, locale, imageClassLoader)); - } -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java index bdf6e7a17340..09f401d4ab45 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java @@ -54,7 +54,6 @@ import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.UserError; -import com.oracle.svm.hosted.jdk.ClassLoaderSupportFeatureJDK11OrLater; @AutomaticFeature public final class LinkAtBuildTimeFeature implements Feature { @@ -84,7 +83,7 @@ static final class Options { @Override public List> getRequiredFeatures() { - return Collections.singletonList(ClassLoaderSupportFeatureJDK11OrLater.class); + return Collections.singletonList(ClassLoaderSupportFeature.class); } @Override From a5e40adfbc3ab67407f6c4d4e0a2b0c256fe7b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Mar 2022 14:03:56 +0100 Subject: [PATCH 3/7] Remove dead code --- .../AccessControlContextReplacerFeature.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/AccessControlContextReplacerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/AccessControlContextReplacerFeature.java index b4c32b7f7b16..f914ed444e95 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/AccessControlContextReplacerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/AccessControlContextReplacerFeature.java @@ -24,13 +24,6 @@ */ package com.oracle.svm.hosted.jdk; -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.jdk.AccessControllerUtil; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.util.ReflectionUtil; -import org.graalvm.compiler.serviceprovider.JavaVersionUtil; -import org.graalvm.nativeimage.hosted.Feature; - import java.security.AccessControlContext; import java.security.DomainCombiner; import java.security.Permission; @@ -38,6 +31,14 @@ import java.util.HashMap; import java.util.Map; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.jdk.AccessControllerUtil; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.util.ReflectionUtil; + @AutomaticFeature @SuppressWarnings({"unused"}) class AccessControlContextReplacerFeature implements Feature { @@ -81,9 +82,6 @@ public void duringSetup(DuringSetupAccess access) { allowContextIfExists("java.util.Calendar$CalendarAccessControlContext", "INSTANCE"); allowContextIfExists("javax.management.monitor.Monitor", "noPermissionsACC"); - if (JavaVersionUtil.JAVA_SPEC <= 8) { - allowContextIfExists("sun.misc.InnocuousThread", "ACC"); - } if (JavaVersionUtil.JAVA_SPEC >= 11) { allowContextIfExists("java.security.AccessController$AccHolder", "innocuousAcc"); allowContextIfExists("java.util.concurrent.ForkJoinPool$DefaultForkJoinWorkerThreadFactory", "ACC"); From af33976ebaee1b393fb12c4ef32a6a855a4ffefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Mar 2022 17:19:56 +0100 Subject: [PATCH 4/7] Remove redundant parameter --- .../svm/hosted/NativeImageClassLoaderSupport.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index 1a5deeae1f1e..8b8eeebc29d0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -334,9 +334,7 @@ public Optional findModule(String moduleName) { } void processClassLoaderOptions() { - OptionValues optionValues = getParsedHostedOptions(); - - processOption(optionValues, NativeImageClassLoaderOptions.AddExports).forEach(val -> { + processOption(NativeImageClassLoaderOptions.AddExports).forEach(val -> { if (val.targetModules.isEmpty()) { Modules.addExportsToAllUnnamed(val.module, val.packageName); } else { @@ -345,7 +343,7 @@ void processClassLoaderOptions() { } } }); - processOption(optionValues, NativeImageClassLoaderOptions.AddOpens).forEach(val -> { + processOption(NativeImageClassLoaderOptions.AddOpens).forEach(val -> { if (val.targetModules.isEmpty()) { Modules.addOpensToAllUnnamed(val.module, val.packageName); } else { @@ -354,7 +352,7 @@ void processClassLoaderOptions() { } } }); - processOption(optionValues, NativeImageClassLoaderOptions.AddReads).forEach(val -> { + processOption(NativeImageClassLoaderOptions.AddReads).forEach(val -> { if (val.targetModules.isEmpty()) { implAddReadsAllUnnamed(val.module); } else { @@ -410,7 +408,7 @@ public static List allLayers(ModuleLayer moduleLayer) { return allLayers; } - private Stream processOption(OptionValues parsedHostedOptions, OptionKey specificOption) { + private Stream processOption(OptionKey specificOption) { Stream> valuesWithOrigins = specificOption.getValue(parsedHostedOptions).getValuesWithOrigins(); Stream parsedOptions = valuesWithOrigins.flatMap(valWithOrig -> { try { From 7ac37741877f2a5d03015c4cab6d54b4b60a6519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Mar 2022 18:36:06 +0100 Subject: [PATCH 5/7] Only open package JavaVersionUtil where needed --- .../svm/hosted/NativeImageGeneratorRunner.java | 4 ---- .../native-image.properties | 1 + .../src/com/oracle/svm/test/jfr/JfrTest.java | 18 ++++++++++++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java index aa605cef423a..f1f70a69599a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java @@ -192,10 +192,6 @@ public static ImageClassLoader installNativeImageClassLoader(String[] classpath, */ nativeImageSystemClassLoader.setNativeImageClassLoader(nativeImageClassLoader); - if (!ModuleSupport.modulePathBuild) { - ModuleSupport.openModuleByClass(JavaVersionUtil.class, null); - } - /* * Iterating all classes can already trigger class initialization: We need annotation * information, which triggers class initialization of annotation classes and enum classes diff --git a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties index dfcb80a207a4..d6f7981996ce 100644 --- a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties +++ b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties @@ -5,6 +5,7 @@ Args= \ --features=com.oracle.svm.test.AbstractServiceLoaderTest$TestFeature \ --features=com.oracle.svm.test.NoProviderConstructorServiceLoaderTest$TestFeature \ --features=com.oracle.svm.test.NativeImageResourceUtils$TestFeature \ + --features=com.oracle.svm.test.jfr.JFRTestFeature \ --add-opens=java.base/java.lang=ALL-UNNAMED \ -H:+AllowVMInspection \ --add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.containers=ALL-UNNAMED diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/JfrTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/JfrTest.java index 5e8c40a4264f..56b5a2205519 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/JfrTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/JfrTest.java @@ -28,18 +28,21 @@ import static org.junit.Assume.assumeTrue; -import com.oracle.svm.core.jfr.HasJfrSupport; +import java.util.HashSet; + import org.graalvm.nativeimage.ImageInfo; +import org.graalvm.nativeimage.hosted.Feature; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import com.oracle.svm.core.jfr.HasJfrSupport; import com.oracle.svm.test.jfr.utils.Jfr; import com.oracle.svm.test.jfr.utils.JfrFileParser; import com.oracle.svm.test.jfr.utils.LocalJfr; +import com.oracle.svm.util.ModuleSupport; -import java.util.HashSet; import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordingFile; @@ -125,3 +128,14 @@ protected void checkRecording() throws AssertionError { } } } + +class JFRTestFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + /* + * Use of org.graalvm.compiler.serviceprovider.JavaVersionUtil.JAVA_SPEC in + * com.oracle.svm.test.jfr.utils.poolparsers.ClassConstantPoolParser.parse + */ + ModuleSupport.exportAndOpenPackageToClass("jdk.internal.vm.compiler", "org.graalvm.compiler.serviceprovider", false, JFRTestFeature.class); + } +} From b848c3340455579570af0b3e103e4efbbf97a801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Mon, 21 Mar 2022 12:15:21 +0100 Subject: [PATCH 6/7] Fix after rebase --- .../hosted/NativeImageClassLoaderSupport.java | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index 8b8eeebc29d0..ddf325915bfe 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -53,7 +53,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.Deque; import java.util.HashSet; import java.util.LinkedHashSet; @@ -62,7 +61,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.ForkJoinPool; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -112,7 +110,10 @@ protected NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, St classPathClassLoader = new URLClassLoader(Util.verifyClassPathAndConvertToURLs(classpath), defaultSystemClassLoader); - imagecp = Arrays.stream(classPathClassLoader.getURLs()).map(Util::urlToPath).collect(Collectors.toUnmodifiableList()); + imagecp = Arrays.stream(classPathClassLoader.getURLs()) + .map(Util::urlToPath) + .collect(Collectors.toUnmodifiableList()); + String builderClassPathString = System.getProperty("java.class.path"); String[] builderClassPathEntries = builderClassPathString.isEmpty() ? new String[0] : builderClassPathString.split(File.pathSeparator); if (Arrays.asList(builderClassPathEntries).contains(".")) { @@ -121,11 +122,18 @@ protected NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, St " but specifies the " + NativeImageGeneratorRunner.class.getName() + " main class without --module."); } buildcp = Arrays.stream(builderClassPathEntries) - .map(Paths::get).map(Path::toAbsolutePath).collect(Collectors.toUnmodifiableList()); + .map(Path::of) + .map(Path::toAbsolutePath) + .collect(Collectors.toUnmodifiableList()); + + imagemp = Arrays.stream(modulePath) + .map(Path::of) + .collect(Collectors.toUnmodifiableList()); - imagemp = Arrays.stream(modulePath).map(Paths::get).collect(Collectors.toUnmodifiableList()); buildmp = Optional.ofNullable(System.getProperty("jdk.module.path")).stream() - .flatMap(s -> Arrays.stream(s.split(File.pathSeparator))).map(Paths::get).collect(Collectors.toUnmodifiableList()); + .flatMap(s -> Arrays.stream(s.split(File.pathSeparator))) + .map(Path::of) + .collect(Collectors.toUnmodifiableList()); upgradeAndSystemModuleFinder = createUpgradeAndSystemModuleFinder(); ModuleLayer moduleLayer = createModuleLayer(imagemp.toArray(Path[]::new), classPathClassLoader); @@ -138,7 +146,7 @@ protected NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, St } List classpath() { - return Stream.concat(imagecp.stream(), buildcp.stream()).collect(Collectors.toList()); + return Stream.concat(imagecp.stream(), buildcp.stream()).distinct().collect(Collectors.toList()); } List applicationClassPath() { @@ -322,7 +330,7 @@ private static void implAddReadsAllUnnamed(Module module) { } protected List modulepath() { - return Stream.concat(imagemp.stream(), buildmp.stream()).collect(Collectors.toList()); + return Stream.concat(imagemp.stream(), buildmp.stream()).collect(Collectors.toUnmodifiableList()); } protected List applicationModulePath() { @@ -513,6 +521,14 @@ Optional getMainClassFromModule(Object module) { return ((Module) module).getDescriptor().mainClass(); } + final Path excludeDirectoriesRoot = Paths.get("/"); + final Set excludeDirectories = getExcludeDirectories(); + + private Set getExcludeDirectories() { + return Stream.of("dev", "sys", "proc", "etc", "var", "tmp", "boot", "lost+found") + .map(excludeDirectoriesRoot::resolve).collect(Collectors.toUnmodifiableSet()); + } + private class ClassInit { protected final ForkJoinPool executor; @@ -524,25 +540,7 @@ protected ClassInit(ForkJoinPool executor, ImageClassLoader imageClassLoader) { } protected void init() { - Set uniquePaths = new TreeSet<>(Comparator.comparing(this::toRealPath)); - uniquePaths.addAll(classpath()); - uniquePaths.parallelStream().forEach(path -> loadClassesFromPath(path)); - } - - private Path toRealPath(Path p) { - try { - return p.toRealPath(); - } catch (IOException e) { - throw VMError.shouldNotReachHere("Path.toRealPath failed for " + p, e); - } - } - - private final Set excludeDirectories = getExcludeDirectories(); - - private Set getExcludeDirectories() { - Path root = Paths.get("/"); - return Stream.of("dev", "sys", "proc", "etc", "var", "tmp", "boot", "lost+found") - .map(root::resolve).collect(Collectors.toSet()); + classpath().parallelStream().forEach(this::loadClassesFromPath); } private void loadClassesFromPath(Path path) { @@ -559,7 +557,7 @@ private void loadClassesFromPath(Path path) { } if (probeJarFileSystem != null) { try (FileSystem jarFileSystem = probeJarFileSystem) { - loadClassesFromPath(container, jarFileSystem.getPath("/"), Collections.emptySet()); + loadClassesFromPath(container, jarFileSystem.getPath("/"), null, Collections.emptySet()); } } } catch (ClosedByInterruptException ignored) { @@ -569,19 +567,25 @@ private void loadClassesFromPath(Path path) { } } else { URI container = path.toUri(); - loadClassesFromPath(container, path, excludeDirectories); + loadClassesFromPath(container, path, excludeDirectoriesRoot, excludeDirectories); } } protected static final String CLASS_EXTENSION = ".class"; - private void loadClassesFromPath(URI container, Path root, Set excludes) { + private void loadClassesFromPath(URI container, Path root, Path excludeRoot, Set excludes) { + boolean useFilter = root.equals(excludeRoot); + if (useFilter) { + String excludesStr = excludes.stream().map(Path::toString).collect(Collectors.joining(", ")); + System.err.println("Warning: Using directory " + excludeRoot + " on classpath is discouraged." + + " Reading classes/resources from directories " + excludesStr + " will be suppressed."); + } FileVisitor visitor = new SimpleFileVisitor<>() { private final char fileSystemSeparatorChar = root.getFileSystem().getSeparator().charAt(0); @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (excludes.contains(dir)) { + if (useFilter && excludes.contains(dir)) { return FileVisitResult.SKIP_SUBTREE; } return super.preVisitDirectory(dir, attrs); From dd28063e11fff8eebc8283f16454bb65759f3864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Mon, 21 Mar 2022 14:17:08 +0100 Subject: [PATCH 7/7] Fold ClassInitWithModules into ClassInit --- .../hosted/NativeImageClassLoaderSupport.java | 74 ++++++++----------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index ddf325915bfe..1c264368b80c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -158,7 +158,7 @@ public ClassLoader getClassLoader() { } public void initAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoader) { - new ClassInitWithModules(executor, imageClassLoader).init(); + new ClassInit(executor, imageClassLoader).init(); } private HostedOptionParser hostedOptionParser; @@ -540,9 +540,39 @@ protected ClassInit(ForkJoinPool executor, ImageClassLoader imageClassLoader) { } protected void init() { + List requiresInit = Arrays.asList( + "jdk.internal.vm.ci", "jdk.internal.vm.compiler", "com.oracle.graal.graal_enterprise", + "org.graalvm.sdk", "org.graalvm.truffle"); + + for (ModuleReference moduleReference : upgradeAndSystemModuleFinder.findAll()) { + if (requiresInit.contains(moduleReference.descriptor().name())) { + initModule(moduleReference); + } + } + for (ModuleReference moduleReference : modulepathModuleFinder.findAll()) { + initModule(moduleReference); + } + classpath().parallelStream().forEach(this::loadClassesFromPath); } + private void initModule(ModuleReference moduleReference) { + Optional optionalModule = findModule(moduleReference.descriptor().name()); + if (optionalModule.isEmpty()) { + return; + } + try (ModuleReader moduleReader = moduleReference.open()) { + Module module = optionalModule.get(); + moduleReader.list().forEach(moduleResource -> { + if (moduleResource.endsWith(CLASS_EXTENSION)) { + executor.execute(() -> handleClassFileName(moduleReference.location().orElseThrow(), module, moduleResource, '/')); + } + }); + } catch (IOException e) { + throw new RuntimeException("Unable get list of resources in module" + moduleReference.descriptor().name(), e); + } + } + private void loadClassesFromPath(Path path) { if (ClasspathUtils.isJar(path)) { try { @@ -678,46 +708,4 @@ protected void handleClassFileName(URI container, Object module, String fileName } } } - - private class ClassInitWithModules extends ClassInit { - - ClassInitWithModules(ForkJoinPool executor, ImageClassLoader imageClassLoader) { - super(executor, imageClassLoader); - } - - @Override - protected void init() { - List requiresInit = Arrays.asList( - "jdk.internal.vm.ci", "jdk.internal.vm.compiler", "com.oracle.graal.graal_enterprise", - "org.graalvm.sdk", "org.graalvm.truffle"); - - for (ModuleReference moduleReference : upgradeAndSystemModuleFinder.findAll()) { - if (requiresInit.contains(moduleReference.descriptor().name())) { - initModule(moduleReference); - } - } - for (ModuleReference moduleReference : modulepathModuleFinder.findAll()) { - initModule(moduleReference); - } - - super.init(); - } - - private void initModule(ModuleReference moduleReference) { - Optional optionalModule = findModule(moduleReference.descriptor().name()); - if (optionalModule.isEmpty()) { - return; - } - try (ModuleReader moduleReader = moduleReference.open()) { - Module module = optionalModule.get(); - moduleReader.list().forEach(moduleResource -> { - if (moduleResource.endsWith(CLASS_EXTENSION)) { - executor.execute(() -> handleClassFileName(moduleReference.location().orElseThrow(), module, moduleResource, '/')); - } - }); - } catch (IOException e) { - throw new RuntimeException("Unable get list of resources in module" + moduleReference.descriptor().name(), e); - } - } - } }