From fef7fccad025a6c8b83751140f386e32901ae410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 9 Feb 2022 19:38:41 +0100 Subject: [PATCH 01/36] Add initial implementation of --require-complete-classdef --- .../svm-driver/native-image.properties | 4 +- ...AbstractNativeImageClassLoaderSupport.java | 2 +- .../oracle/svm/hosted/ImageClassLoader.java | 2 +- .../hosted/NativeImageClassLoaderSupport.java | 2 +- .../RequireCompleteClassDefFeature.java | 120 ++++++++++++++++++ 5 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RequireCompleteClassDefFeature.java diff --git a/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties b/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties index a3883325fe9c..4674c7e29428 100644 --- a/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties +++ b/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties @@ -1,2 +1,4 @@ ImageName = native-image -Args = -H:-ParseRuntimeOptions --initialize-at-build-time=com.oracle.svm.driver +Args = -H:-ParseRuntimeOptions \ + --initialize-at-build-time=com.oracle.svm.driver \ + --require-complete-classdef 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 index 0902bc345c2c..61ed4a9159c3 100644 --- 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 @@ -108,7 +108,7 @@ public ClassLoader getClassLoader() { protected abstract List applicationModulePath(); - protected abstract Optional findModule(String moduleName); + protected abstract Optional findModule(String moduleName); private HostedOptionParser hostedOptionParser; private OptionValues parsedHostedOptions; 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 ebb8d2fb3dbf..9392c38b1b9e 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 @@ -428,7 +428,7 @@ public Optional getMainClassFromModule(Object module) { return classLoaderSupport.getMainClassFromModule(module); } - public Optional findModule(String moduleName) { + public Optional findModule(String moduleName) { return classLoaderSupport.findModule(moduleName); } 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 3e6d0f93f392..07dfd5a3b54d 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 @@ -47,7 +47,7 @@ protected List applicationModulePath() { } @Override - protected Optional findModule(String moduleName) { + protected Optional findModule(String moduleName) { return Optional.empty(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RequireCompleteClassDefFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RequireCompleteClassDefFeature.java new file mode 100644 index 000000000000..f11abdc35c9c --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RequireCompleteClassDefFeature.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.hosted; + +import java.lang.module.ModuleFinder; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import org.graalvm.collections.Pair; +import org.graalvm.compiler.options.Option; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.option.APIOption; +import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.option.LocatableMultiOptionValue; +import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.util.UserError; + +@AutomaticFeature +public final class RequireCompleteClassDefFeature implements Feature { + + static final class Options { + @APIOption(name = "require-complete-classdef", defaultValue = "")// + @Option(help = "Require types to be fully defined at image build-time. If used without args, all classes in the scope the option are required to be fully defined.")// + public static final HostedOptionKey RequireCompleteClassDef = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); + } + + private final Set requireCompletePackageOrClass = new HashSet<>(); + private final Set requireCompleteModules = new HashSet<>(); + private boolean requireCompleteAll = false; + + Map moduleFromURI; + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + var loader = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getImageClassLoader(); + + moduleFromURI = ModuleFinder.of(loader.applicationModulePath().toArray(Path[]::new)).findAll().stream() + .filter(mRef -> mRef.location().isPresent()) + .collect(Collectors.toUnmodifiableMap(mRef -> mRef.location().get(), mRef -> loader.findModule(mRef.descriptor().name()).get())); + + Options.RequireCompleteClassDef.getValue().getValuesWithOrigins().forEach(this::extractRequireCompleteClasses); + + System.out.println("DONE"); + } + + private void extractRequireCompleteClasses(Pair valueOrigin) { + var value = valueOrigin.getLeft(); + if (value.isEmpty()) { + String origin = valueOrigin.getRight(); + if (origin == null) { + requireCompleteAll = true; + return; + } + var originURI = originURI(origin); + if (originURI == null) { + throw notUsedInModulePath(origin); + } + if (originURI.getScheme().equals("jar")) { + var specific = originURI.getSchemeSpecificPart(); + var specificJarFile = specific.substring(0, specific.lastIndexOf('!')); + var specificInner = specific.substring(specific.lastIndexOf('!') + 1); + originURI = originURI(specificJarFile); + } + var originModule = moduleFromURI.get(originURI); + if (originModule == null) { + throw notUsedInModulePath(origin); + } + requireCompleteModules.add(originModule); + } else { + requireCompletePackageOrClass.addAll(Arrays.asList(SubstrateUtil.split(value, ","))); + } + } + + private URI originURI(String origin) { + Objects.requireNonNull(origin); + try { + return new URI(origin); + } catch (URISyntaxException x) { + return null; + } + } + + private UserError.UserException notUsedInModulePath(String origin) { + return UserError.abort("Using %s without args is only allowed on module-path. Actual location %s", + SubstrateOptionsParser.commandArgument(Options.RequireCompleteClassDef, ""), origin); + } +} From 233f7c14a27486971757a52f5549af8583f5222d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Mon, 14 Feb 2022 16:10:31 +0100 Subject: [PATCH 02/36] Make SubstrateOptionsParser.commandArgument smarter when option is requested with its default value --- .../oracle/svm/core/option/SubstrateOptionsParser.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java index 226e7205d3a0..62fada7890e8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java @@ -208,7 +208,13 @@ public static String commandArgument(OptionKey option, String value, String a if (apiOption.fixedValue().length == 0) { if (apiOptionWithValue == null) { /* First APIOption that accepts value is selected as fallback */ - apiOptionWithValue = optionName + apiOption.valueSeparator()[0] + value; + if (Arrays.equals(apiOption.defaultValue(), new String[]{value})) { + /* Option with default value. Use short form */ + apiOptionWithValue = optionName; + } else { + /* Option with custom value. Use form with valueSeparator */ + apiOptionWithValue = optionName + apiOption.valueSeparator()[0] + value; + } } } else if (apiOption.fixedValue()[0].equals(value)) { /* Return requested option expressed as fixed-value APIOption */ From 51fb6fc7b763a1c6d4f1fb5888a9813e882f1644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 16 Feb 2022 09:18:52 +0100 Subject: [PATCH 03/36] Generalize OptionOrigin use introduced for LinkAtBuildTime option --- .../option/LocatableMultiOptionValue.java | 8 +- .../oracle/svm/core/option/OptionOrigin.java | 140 ++++++++++++++++ .../oracle/svm/core/option/OptionUtils.java | 49 ++++++ .../svm-driver/native-image.properties | 2 +- .../svm/hosted/LinkAtBuildTimeFeature.java | 154 ++++++++++++++++++ .../oracle/svm/hosted/ModuleLayerFeature.java | 12 +- .../RequireCompleteClassDefFeature.java | 120 -------------- .../ClassInitializationFeature.java | 3 +- .../hosted/classinitialization/InitKind.java | 9 +- ...veImageClassLoaderSupportJDK11OrLater.java | 9 +- .../native-image.properties | 3 +- .../com.oracle.svm.junit/svm-junit.packages | 21 +++ 12 files changed, 387 insertions(+), 143 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RequireCompleteClassDefFeature.java create mode 100644 substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/LocatableMultiOptionValue.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/LocatableMultiOptionValue.java index 176087b6115f..bea02f2d6e32 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/LocatableMultiOptionValue.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/LocatableMultiOptionValue.java @@ -29,10 +29,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.oracle.svm.common.option.LocatableOption; -import com.oracle.svm.common.option.MultiOptionValue; import org.graalvm.collections.Pair; +import com.oracle.svm.common.option.LocatableOption; +import com.oracle.svm.common.option.MultiOptionValue; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ClassUtil; @@ -76,8 +76,8 @@ public List values() { return values.stream().map(Pair::getLeft).collect(Collectors.toList()); } - public Stream> getValuesWithOrigins() { - return values.stream(); + public Stream> getValuesWithOrigins() { + return values.stream().map(pair -> Pair.create(pair.getLeft(), OptionOrigin.of(pair.getRight()))); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java new file mode 100644 index 000000000000..2746f886d8d9 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.option; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; + +public abstract class OptionOrigin { + + public URI container() { + return null; + } + + public Path location() { + return null; + } + + public static OptionOrigin of(String origin) { + + if (origin == null) { + return CommandLineOptionOrigin.singleton; + } + + URI originURI = originURI(origin); + if (originURI == null) { + return new UnsupportedOptionOrigin(origin); + } + + switch (originURI.getScheme()) { + case "jar": + return new JarOptionOrigin(originURI); + case "file": + String rawPath = originURI.getPath(); + if (Files.isDirectory(Path.of(rawPath))) { + return new DirectoryOptionOrigin(rawPath); + } + return new UnsupportedOptionOrigin(origin); + default: + return new UnsupportedOptionOrigin(origin); + } + } + + protected static URI originURI(String origin) { + try { + return new URI(origin); + } catch (URISyntaxException x) { + return null; + } + } + + public static final class CommandLineOptionOrigin extends OptionOrigin { + + private static CommandLineOptionOrigin singleton = new CommandLineOptionOrigin(); + + @Override + public String toString() { + return "command line"; + } + } + + public static final class UnsupportedOptionOrigin extends OptionOrigin { + + protected final String rawOrigin; + + public UnsupportedOptionOrigin(String rawOrigin) { + this.rawOrigin = rawOrigin; + } + + @Override + public String toString() { + return rawOrigin; + } + } + + protected static abstract class URIOptionOrigin extends OptionOrigin { + + protected URI container; + + @Override + public URI container() { + return container; + } + + protected Path location; + + @Override + public Path location() { + return location; + } + + @Override + public String toString() { + return String.format("'%s' in '%s'", location(), container()); + } + } + + public static final class JarOptionOrigin extends URIOptionOrigin { + protected JarOptionOrigin(URI rawOrigin) { + var specific = rawOrigin.getSchemeSpecificPart(); + int sep = specific.lastIndexOf('!'); + var origin = specific.substring(0, sep); + container = URIOptionOrigin.originURI(origin); + location = Path.of(specific.substring(sep + 1)); + } + } + + public static final class DirectoryOptionOrigin extends URIOptionOrigin { + protected DirectoryOptionOrigin(String rawPath) { + String metaInf = "/META-INF/"; + int index = rawPath.indexOf(metaInf); + container = Path.of(rawPath.substring(0, index)).toUri(); + location = Path.of(rawPath.substring(index)); + } + } +} \ No newline at end of file diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java index 4fe1d767c9c8..e33efb33d88d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java @@ -24,12 +24,23 @@ */ package com.oracle.svm.core.option; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.graalvm.compiler.options.OptionKey; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.util.UserError; /** * This class contains static helper methods related to options. @@ -67,4 +78,42 @@ public static List flatten(String delimiter, List values) { } return result; } + + public static List resolveOptionValueRedirection(OptionKey option, String optionValue, OptionOrigin origin) { + return Arrays.asList(SubstrateUtil.split(optionValue, ",")).stream() + .flatMap(entry -> { + try { + return resolveOptionValueRedirectionFlatMap(entry, origin); + } catch (IOException e) { + throw UserError.abort(e, "Option '%s' from %s contains invalid option value redirection.", + SubstrateOptionsParser.commandArgument(option, optionValue), origin); + } + }) + .collect(Collectors.toList()); + } + + private static Stream resolveOptionValueRedirectionFlatMap(String entry, OptionOrigin origin) throws IOException { + if (entry.trim().startsWith("@")) { + URI jarFileURI = URI.create("jar:" + origin.container()); + FileSystem probeJarFS; + try { + probeJarFS = FileSystems.newFileSystem(jarFileURI, Collections.emptyMap()); + } catch (UnsupportedOperationException e) { + throw new FileNotFoundException(); + } + if (probeJarFS != null) { + try (FileSystem jarFS = probeJarFS) { + var normalizedRedirPath = origin.location().getParent().resolve(entry.substring(1)).normalize(); + var pathInJarFS = jarFS.getPath(normalizedRedirPath.toString()); + if (Files.exists(pathInJarFS)) { + return Files.readAllLines(pathInJarFS).stream(); + } + throw new FileNotFoundException(pathInJarFS.toString()); + } + } + throw new FileNotFoundException(jarFileURI.toString()); + } else { + return Stream.of(entry); + } + } } diff --git a/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties b/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties index 4674c7e29428..fbd174007fa6 100644 --- a/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties +++ b/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties @@ -1,4 +1,4 @@ ImageName = native-image Args = -H:-ParseRuntimeOptions \ --initialize-at-build-time=com.oracle.svm.driver \ - --require-complete-classdef + --link-at-build-time \ No newline at end of file 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 new file mode 100644 index 000000000000..f924d447a56e --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.hosted; + +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.net.URI; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.graalvm.collections.Pair; +import org.graalvm.compiler.options.Option; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.svm.core.ClassLoaderSupport; +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.option.APIOption; +import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.option.LocatableMultiOptionValue; +import com.oracle.svm.core.option.OptionOrigin; +import com.oracle.svm.core.option.OptionUtils; +import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.util.UserError; + +@AutomaticFeature +public final class LinkAtBuildTimeFeature implements Feature { + + static final class Options { + @APIOption(name = "link-at-build-time", defaultValue = "")// + @Option(help = "Require types to be fully defined at image build-time. If used without args, all classes in scope of the option are required to be fully defined.")// + public static final HostedOptionKey LinkAtBuildTime = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); + } + + private final String javaIdentifier = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"; + private final Pattern validOptionValue = Pattern.compile(javaIdentifier + "(\\." + javaIdentifier + ")*"); + + private final Set requireCompletePackageOrClass = new HashSet<>(); + private final Set requireCompleteModules = new HashSet<>(); + private boolean requireCompleteAll; + + Map uriModuleMap; + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + var loader = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getImageClassLoader(); + + uriModuleMap = ModuleFinder.of(loader.applicationModulePath().toArray(Path[]::new)).findAll().stream() + .filter(mRef -> mRef.location().isPresent()) + .collect(Collectors.toUnmodifiableMap(mRef -> mRef.location().get(), mRef -> loader.findModule(mRef.descriptor().name()).get())); + + Options.LinkAtBuildTime.getValue().getValuesWithOrigins().forEach(this::extractOptionValue); + } + + private void extractOptionValue(Pair valueOrigin) { + var value = valueOrigin.getLeft(); + OptionOrigin origin = valueOrigin.getRight(); + if (value.isEmpty()) { + if (origin instanceof OptionOrigin.CommandLineOptionOrigin) { + requireCompleteAll = true; + return; + } + if (origin instanceof OptionOrigin.UnsupportedOptionOrigin) { + throw notOnModulePath(origin); + } + var originModule = uriModuleMap.get(origin.container()); + if (originModule == null) { + throw notOnModulePath(origin); + } + requireCompleteModules.add(originModule); + } else { + for (String entry : OptionUtils.resolveOptionValueRedirection(Options.LinkAtBuildTime, value, origin)) { + if (validOptionValue.matcher(entry).matches()) { + requireCompletePackageOrClass.add(entry); + } else { + throw UserError.abort("Entry '%s' in option '%s' (from location '%s') is neither a package nor a fully qualified classname.", + entry, SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, value), origin); + } + } + } + } + + private UserError.UserException notOnModulePath(OptionOrigin origin) { + return UserError.abort("Using '%s' without args only allowed on module-path. %s not part of module-path.", + SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, ""), origin); + } + + public boolean requiresCompleteDefinition(Class clazz) { + if (requireCompleteAll || clazz.isArray()) { + return true; + } + + if (!ImageSingletons.lookup(ClassLoaderSupport.class).isNativeImageClassLoader(clazz.getClassLoader())) { + return true; + } + assert !clazz.isPrimitive() : "Primitive classes are not loaded via NativeImageClassLoader"; + + var module = clazz.getModule(); + if (module.isNamed() && (requireCompleteModules.contains(module) || isModuleSynthetic(module))) { + return true; + } + + return requireCompletePackageOrClass.contains(clazz.getName()) || requireCompletePackageOrClass.contains(clazz.getPackageName()) || clazz.isSynthetic(); + } + + public static boolean isModuleSynthetic(Module m) { + return m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC); + } + + @Override + public void afterAnalysis(AfterAnalysisAccess a) { + // Test results of requiresCompleteDefinition(Class clazz) on whole AnalysisUniverse + FeatureImpl.AfterAnalysisAccessImpl access = (FeatureImpl.AfterAnalysisAccessImpl) a; + for (AnalysisType analysisType : access.getUniverse().getTypes()) { + System.out.print(analysisType.toJavaName()); + Class analysisClass = analysisType.getJavaClass(); + if (analysisClass == null) { + System.out.print(" (no Class)"); + } else { + boolean requiresCompleteDefinition = requiresCompleteDefinition(analysisClass); + System.out.print(" requiresCompleteDefinition: " + requiresCompleteDefinition); + } + System.out.println(); + } + } +} 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 26ab03dc48aa..56a9b6f9a9fc 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 @@ -158,7 +158,7 @@ public void afterAnalysis(AfterAnalysisAccess access) { Set analysisReachableSyntheticModules = analysisReachableNamedModules .stream() - .filter(ModuleLayerFeatureUtils::isModuleSynthetic) + .filter(LinkAtBuildTimeFeature::isModuleSynthetic) .collect(Collectors.toSet()); Set allReachableModules = analysisReachableNamedModules @@ -251,7 +251,7 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag continue; } Module runtimeTo = e2.getValue().runtimeModule; - if (ModuleLayerFeatureUtils.isModuleSynthetic(hostedFrom) || hostedFrom.canRead(hostedTo)) { + if (LinkAtBuildTimeFeature.isModuleSynthetic(hostedFrom) || hostedFrom.canRead(hostedTo)) { moduleLayerFeatureUtils.addReads(runtimeFrom, runtimeTo); if (hostedFrom == builderModule) { for (Module appModule : applicationModules) { @@ -260,7 +260,7 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag } } for (String pn : runtimeFrom.getPackages()) { - if (ModuleLayerFeatureUtils.isModuleSynthetic(hostedFrom) || hostedFrom.isOpen(pn, hostedTo)) { + if (LinkAtBuildTimeFeature.isModuleSynthetic(hostedFrom) || hostedFrom.isOpen(pn, hostedTo)) { moduleLayerFeatureUtils.addOpens(runtimeFrom, pn, runtimeTo); if (hostedTo == builderModule) { for (Module appModule : applicationModules) { @@ -268,7 +268,7 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag } } } - if (ModuleLayerFeatureUtils.isModuleSynthetic(hostedFrom) || hostedFrom.isExported(pn, hostedTo)) { + if (LinkAtBuildTimeFeature.isModuleSynthetic(hostedFrom) || hostedFrom.isExported(pn, hostedTo)) { moduleLayerFeatureUtils.addExports(runtimeFrom, pn, runtimeTo); if (hostedTo == builderModule) { for (Module appModule : applicationModules) { @@ -420,10 +420,6 @@ private static Field findFieldByName(Field[] fields, String name) { return Arrays.stream(fields).filter(f -> f.getName().equals(name)).findAny().orElseThrow(VMError::shouldNotReachHere); } - static boolean isModuleSynthetic(Module m) { - return m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC); - } - /** * This method creates Module instances that will populate the runtime boot module layer of * the image. This implementation is copy-pasted from Module#defineModules(Configuration, diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RequireCompleteClassDefFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RequireCompleteClassDefFeature.java deleted file mode 100644 index f11abdc35c9c..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RequireCompleteClassDefFeature.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.oracle.svm.hosted; - -import java.lang.module.ModuleFinder; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import org.graalvm.collections.Pair; -import org.graalvm.compiler.options.Option; -import org.graalvm.nativeimage.hosted.Feature; - -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.option.APIOption; -import com.oracle.svm.core.option.HostedOptionKey; -import com.oracle.svm.core.option.LocatableMultiOptionValue; -import com.oracle.svm.core.option.SubstrateOptionsParser; -import com.oracle.svm.core.util.UserError; - -@AutomaticFeature -public final class RequireCompleteClassDefFeature implements Feature { - - static final class Options { - @APIOption(name = "require-complete-classdef", defaultValue = "")// - @Option(help = "Require types to be fully defined at image build-time. If used without args, all classes in the scope the option are required to be fully defined.")// - public static final HostedOptionKey RequireCompleteClassDef = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); - } - - private final Set requireCompletePackageOrClass = new HashSet<>(); - private final Set requireCompleteModules = new HashSet<>(); - private boolean requireCompleteAll = false; - - Map moduleFromURI; - - @Override - public void beforeAnalysis(BeforeAnalysisAccess access) { - var loader = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getImageClassLoader(); - - moduleFromURI = ModuleFinder.of(loader.applicationModulePath().toArray(Path[]::new)).findAll().stream() - .filter(mRef -> mRef.location().isPresent()) - .collect(Collectors.toUnmodifiableMap(mRef -> mRef.location().get(), mRef -> loader.findModule(mRef.descriptor().name()).get())); - - Options.RequireCompleteClassDef.getValue().getValuesWithOrigins().forEach(this::extractRequireCompleteClasses); - - System.out.println("DONE"); - } - - private void extractRequireCompleteClasses(Pair valueOrigin) { - var value = valueOrigin.getLeft(); - if (value.isEmpty()) { - String origin = valueOrigin.getRight(); - if (origin == null) { - requireCompleteAll = true; - return; - } - var originURI = originURI(origin); - if (originURI == null) { - throw notUsedInModulePath(origin); - } - if (originURI.getScheme().equals("jar")) { - var specific = originURI.getSchemeSpecificPart(); - var specificJarFile = specific.substring(0, specific.lastIndexOf('!')); - var specificInner = specific.substring(specific.lastIndexOf('!') + 1); - originURI = originURI(specificJarFile); - } - var originModule = moduleFromURI.get(originURI); - if (originModule == null) { - throw notUsedInModulePath(origin); - } - requireCompleteModules.add(originModule); - } else { - requireCompletePackageOrClass.addAll(Arrays.asList(SubstrateUtil.split(value, ","))); - } - } - - private URI originURI(String origin) { - Objects.requireNonNull(origin); - try { - return new URI(origin); - } catch (URISyntaxException x) { - return null; - } - } - - private UserError.UserException notUsedInModulePath(String origin) { - return UserError.abort("Using %s without args is only allowed on module-path. Actual location %s", - SubstrateOptionsParser.commandArgument(Options.RequireCompleteClassDef, ""), origin); - } -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java index 2f9872d24203..07e77635581d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java @@ -61,6 +61,7 @@ import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.option.OptionOrigin; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.snippets.SnippetRuntime; import com.oracle.svm.core.util.UserError; @@ -81,7 +82,7 @@ public static void processClassInitializationOptions(ClassInitializationSupport ClassInitializationOptions.ClassInitialization.getValue().getValuesWithOrigins().forEach(entry -> { for (String info : entry.getLeft().split(",")) { boolean noMatches = Arrays.stream(InitKind.values()).noneMatch(v -> info.endsWith(v.suffix())); - String origin = entry.getRight(); + OptionOrigin origin = entry.getRight(); if (noMatches) { throw UserError.abort("Element in class initialization configuration must end in %s, %s, or %s. Found: %s (from %s)", RUN_TIME.suffix(), RERUN.suffix(), BUILD_TIME.suffix(), info, origin); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/InitKind.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/InitKind.java index f7db249faa24..e876be3a1b9f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/InitKind.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/InitKind.java @@ -32,6 +32,8 @@ import org.graalvm.collections.Pair; +import com.oracle.svm.core.option.OptionOrigin; + /** * The initialization kind for a class. The order of the enum values matters, {@link #max} depends * on it. @@ -62,7 +64,7 @@ String suffix() { return SEPARATOR + name().toLowerCase(); } - Consumer stringConsumer(ClassInitializationSupport support, String origin) { + Consumer stringConsumer(ClassInitializationSupport support, OptionOrigin origin) { if (this == RUN_TIME) { return name -> support.initializeAtRunTime(name, reason(origin, name)); } else if (this == RERUN) { @@ -81,9 +83,8 @@ Consumer stringConsumer(ClassInitializationSupport support, String origi } } - private static String reason(String origin, String name) { - String prefix = "from "; - return (origin == null ? prefix + "the command line" : prefix + origin) + " with '" + name + "'"; + private static String reason(OptionOrigin origin, String name) { + return "from " + origin + " with '" + name + "'"; } static Pair strip(String input) { 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 index 1a02c225c494..111835a98fc6 100644 --- 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 @@ -54,6 +54,7 @@ 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; @@ -285,7 +286,7 @@ static List allLayers(ModuleLayer moduleLayer) { } private Stream processOption(OptionValues parsedHostedOptions, OptionKey specificOption) { - Stream> valuesWithOrigins = specificOption.getValue(parsedHostedOptions).getValuesWithOrigins(); + Stream> valuesWithOrigins = specificOption.getValue(parsedHostedOptions).getValuesWithOrigins(); Stream parsedOptions = valuesWithOrigins.flatMap(valWithOrig -> { try { return Stream.of(asAddExportsAndOpensAndReadsFormatValue(specificOption, valWithOrig)); @@ -318,8 +319,8 @@ private AddExportsAndOpensAndReadsFormatValue(Module module, String packageName, } } - private AddExportsAndOpensAndReadsFormatValue asAddExportsAndOpensAndReadsFormatValue(OptionKey option, Pair valueOrigin) { - String optionOrigin = valueOrigin.getRight(); + private AddExportsAndOpensAndReadsFormatValue asAddExportsAndOpensAndReadsFormatValue(OptionKey option, Pair valueOrigin) { + OptionOrigin optionOrigin = valueOrigin.getRight(); String optionValue = valueOrigin.getLeft(); boolean reads = option.equals(NativeImageClassLoaderOptions.AddReads); @@ -361,7 +362,7 @@ private AddExportsAndOpensAndReadsFormatValue asAddExportsAndOpensAndReadsFormat return new AddExportsAndOpensAndReadsFormatValue(module, packageName, targetModules); } - private static UserError.UserException userErrorAddExportsAndOpensAndReads(OptionKey option, String origin, String value, String detailMessage) { + 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); } diff --git a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties index 14446be07331..5bc72b062925 100644 --- a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties +++ b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties @@ -1 +1,2 @@ -Args = --add-reads=org.graalvm.nativeimage.junitsupport=ALL-UNNAMED +Args = --add-reads=org.graalvm.nativeimage.junitsupport=ALL-UNNAMED \ + --link-at-build-time=@svm-junit.packages diff --git a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages new file mode 100644 index 000000000000..e8e54e6a3d4f --- /dev/null +++ b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages @@ -0,0 +1,21 @@ +com.oracle.mxtool.junit +com.oracle.svm.junit +junit.framework +junit.runner +org.hamcrest +org.hamcrest.core +org.hamcrest.internal +org.junit +org.junit +org.junit.internal +org.junit.internal +org.junit.internal.builders +org.junit.internal.runners.model +org.junit.internal.runners.statements +org.junit.rules +org.junit.runner +org.junit.runner.manipulation +org.junit.runner.notification +org.junit.runners +org.junit.runners.model +org.junit.validator \ No newline at end of file From 3eb9031b1b1c12e01786279de9e5d85939475300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 16 Feb 2022 10:42:38 +0100 Subject: [PATCH 04/36] Add jdk/internal/reflect/GeneratedSerializationConstructorAccessor* handling --- .../src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 f924d447a56e..ef2e0cb61189 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 @@ -77,6 +77,14 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { .filter(mRef -> mRef.location().isPresent()) .collect(Collectors.toUnmodifiableMap(mRef -> mRef.location().get(), mRef -> loader.findModule(mRef.descriptor().name()).get())); + /* + * SerializationBuilder.newConstructorForSerialization() creates synthetic + * jdk/internal/reflect/GeneratedSerializationConstructorAccessor* classes that do not have + * the synthetic modifier set (clazz.isSynthetic() returns false for such classes). Any + * class with package-name jdk.internal.reflect should be treated as link-at-build-time. + */ + requireCompletePackageOrClass.add("jdk.internal.reflect"); + Options.LinkAtBuildTime.getValue().getValuesWithOrigins().forEach(this::extractOptionValue); } From 5ab1d98c4f09d4b0eec9c6fd43c9e02810267943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 16 Feb 2022 15:44:26 +0100 Subject: [PATCH 05/36] Support @ also for directory-based class-path and module-path entries --- .../oracle/svm/core/option/OptionOrigin.java | 69 ++++++++++++++++--- .../oracle/svm/core/option/OptionUtils.java | 32 ++------- 2 files changed, 67 insertions(+), 34 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index 2746f886d8d9..d345b4c92200 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -25,10 +25,18 @@ package com.oracle.svm.core.option; +import java.io.FileNotFoundException; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +import com.oracle.svm.core.util.VMError; public abstract class OptionOrigin { @@ -40,6 +48,10 @@ public Path location() { return null; } + public List getRedirectionValues(Path valuesFile) throws IOException { + throw new UnsupportedOperationException(); + } + public static OptionOrigin of(String origin) { if (origin == null) { @@ -55,9 +67,9 @@ public static OptionOrigin of(String origin) { case "jar": return new JarOptionOrigin(originURI); case "file": - String rawPath = originURI.getPath(); - if (Files.isDirectory(Path.of(rawPath))) { - return new DirectoryOptionOrigin(rawPath); + Path originPath = Path.of(originURI); + if (Files.isReadable(originPath)) { + return new DirectoryOptionOrigin(originPath); } return new UnsupportedOptionOrigin(origin); default: @@ -125,16 +137,55 @@ protected JarOptionOrigin(URI rawOrigin) { int sep = specific.lastIndexOf('!'); var origin = specific.substring(0, sep); container = URIOptionOrigin.originURI(origin); - location = Path.of(specific.substring(sep + 1)); + location = Path.of(specific.substring(sep + 2)); + } + + @Override + public List getRedirectionValues(Path valuesFile) throws IOException { + URI jarFileURI = URI.create("jar:" + container()); + FileSystem probeJarFS; + try { + probeJarFS = FileSystems.newFileSystem(jarFileURI, Collections.emptyMap()); + } catch (UnsupportedOperationException e) { + throw new FileNotFoundException(); + } + if (probeJarFS != null) { + try (FileSystem jarFS = probeJarFS) { + var normalizedRedirPath = location().getParent().resolve(valuesFile).normalize(); + var pathInJarFS = jarFS.getPath(normalizedRedirPath.toString()); + if (Files.exists(pathInJarFS)) { + return Files.readAllLines(pathInJarFS); + } + throw new FileNotFoundException(pathInJarFS.toString()); + } + } + throw new FileNotFoundException(jarFileURI.toString()); } } public static final class DirectoryOptionOrigin extends URIOptionOrigin { - protected DirectoryOptionOrigin(String rawPath) { - String metaInf = "/META-INF/"; - int index = rawPath.indexOf(metaInf); - container = Path.of(rawPath.substring(0, index)).toUri(); - location = Path.of(rawPath.substring(index)); + protected DirectoryOptionOrigin(Path originPath) { + int pathPos = 0; + int metaInfPos = -1; + for (Path entry : originPath) { + if ("META-INF".equals(entry.toString())) { + metaInfPos = pathPos; + break; + } + ++pathPos; + } + VMError.guarantee(metaInfPos > 0, "invalid directory origin"); + container = originPath.getRoot().resolve(originPath.subpath(0, metaInfPos)).toUri(); + location = originPath.subpath(metaInfPos, originPath.getNameCount()); + } + + @Override + public List getRedirectionValues(Path valuesFile) throws IOException { + var normalizedRedirPath = Path.of(container()).resolve(location()).getParent().resolve(valuesFile).normalize(); + if (Files.exists(normalizedRedirPath)) { + return Files.readAllLines(normalizedRedirPath); + } + throw new FileNotFoundException(normalizedRedirPath.toString()); } } } \ No newline at end of file diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java index e33efb33d88d..7c522cf06352 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java @@ -24,12 +24,7 @@ */ package com.oracle.svm.core.option; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -84,7 +79,7 @@ public static List resolveOptionValueRedirection(OptionKey option, St .flatMap(entry -> { try { return resolveOptionValueRedirectionFlatMap(entry, origin); - } catch (IOException e) { + } catch (Exception e) { throw UserError.abort(e, "Option '%s' from %s contains invalid option value redirection.", SubstrateOptionsParser.commandArgument(option, optionValue), origin); } @@ -92,26 +87,13 @@ public static List resolveOptionValueRedirection(OptionKey option, St .collect(Collectors.toList()); } - private static Stream resolveOptionValueRedirectionFlatMap(String entry, OptionOrigin origin) throws IOException { + private static Stream resolveOptionValueRedirectionFlatMap(String entry, OptionOrigin origin) throws Exception { if (entry.trim().startsWith("@")) { - URI jarFileURI = URI.create("jar:" + origin.container()); - FileSystem probeJarFS; - try { - probeJarFS = FileSystems.newFileSystem(jarFileURI, Collections.emptyMap()); - } catch (UnsupportedOperationException e) { - throw new FileNotFoundException(); + Path valuesFile = Path.of(entry.substring(1)); + if (valuesFile.isAbsolute()) { + throw new IllegalArgumentException("Option option value redirection file '" + valuesFile + "' is absolute path"); } - if (probeJarFS != null) { - try (FileSystem jarFS = probeJarFS) { - var normalizedRedirPath = origin.location().getParent().resolve(entry.substring(1)).normalize(); - var pathInJarFS = jarFS.getPath(normalizedRedirPath.toString()); - if (Files.exists(pathInJarFS)) { - return Files.readAllLines(pathInJarFS).stream(); - } - throw new FileNotFoundException(pathInJarFS.toString()); - } - } - throw new FileNotFoundException(jarFileURI.toString()); + return origin.getRedirectionValues(valuesFile).stream(); } else { return Stream.of(entry); } From 802265b7ee53502916765e5f764ae9f0a0a67cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 16 Feb 2022 16:59:46 +0100 Subject: [PATCH 06/36] Handle macro options like command line options --- .../oracle/svm/core/option/OptionOrigin.java | 26 ++++- .../oracle/svm/core/option/OptionUtils.java | 56 ++++++++++ .../svm/driver/DefaultOptionHandler.java | 4 +- .../com/oracle/svm/driver/MacroOption.java | 103 +++++------------- .../oracle/svm/driver/MacroOptionHandler.java | 2 +- .../com/oracle/svm/driver/NativeImage.java | 6 +- 6 files changed, 114 insertions(+), 83 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index d345b4c92200..4f1afebe9b98 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -60,6 +60,10 @@ public static OptionOrigin of(String origin) { URI originURI = originURI(origin); if (originURI == null) { + String macroName = MacroOptionOrigin.macroName(origin); + if (macroName != null) { + return new MacroOptionOrigin(macroName); + } return new UnsupportedOptionOrigin(origin); } @@ -85,7 +89,7 @@ protected static URI originURI(String origin) { } } - public static final class CommandLineOptionOrigin extends OptionOrigin { + public static class CommandLineOptionOrigin extends OptionOrigin { private static CommandLineOptionOrigin singleton = new CommandLineOptionOrigin(); @@ -95,6 +99,26 @@ public String toString() { } } + public static final class MacroOptionOrigin extends CommandLineOptionOrigin { + + private static final String PREFIX = OptionUtils.MacroOptionKind.Macro.getDescriptionPrefix(true); + + private final String name; + + MacroOptionOrigin(String name) { + this.name = name; + } + + public static String macroName(String rawOrigin) { + return rawOrigin.startsWith(PREFIX) ? rawOrigin.substring(PREFIX.length()) : null; + } + + @Override + public String toString() { + return "macro option '" + name + "'"; + } + } + public static final class UnsupportedOptionOrigin extends OptionOrigin { protected final String rawOrigin; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java index 7c522cf06352..9f828e4b1e67 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java @@ -98,4 +98,60 @@ private static Stream resolveOptionValueRedirectionFlatMap(Str return Stream.of(entry); } } + + public enum MacroOptionKind { + + Language("languages", true), + Tool("tools", true), + Macro("macros", false); + + public static final String macroOptionPrefix = "--"; + + public final String subdir; + public final boolean allowAll; + + MacroOptionKind(String subdir, boolean allowAll) { + this.subdir = subdir; + this.allowAll = allowAll; + } + + public static MacroOptionKind fromSubdir(String subdir) { + for (MacroOptionKind kind : MacroOptionKind.values()) { + if (kind.subdir.equals(subdir)) { + return kind; + } + } + throw new InvalidMacroException("No MacroOptionKind for subDir: " + subdir); + } + + public static MacroOptionKind fromString(String kindName) { + for (MacroOptionKind kind : MacroOptionKind.values()) { + if (kind.toString().equals(kindName)) { + return kind; + } + } + throw new InvalidMacroException("No MacroOptionKind for kindName: " + kindName); + } + + public String getDescriptionPrefix(boolean commandLineStyle) { + StringBuilder sb = new StringBuilder(); + if (commandLineStyle) { + sb.append(macroOptionPrefix); + } + sb.append(this).append(":"); + return sb.toString(); + } + + @Override + public String toString() { + return name().toLowerCase(); + } + } + + @SuppressWarnings("serial") + public static final class InvalidMacroException extends RuntimeException { + public InvalidMacroException(String arg0) { + super(arg0); + } + } } diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java index 3401ef093644..ec48cae2ab39 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java @@ -36,7 +36,7 @@ import org.graalvm.compiler.options.OptionType; -import com.oracle.svm.driver.MacroOption.MacroOptionKind; +import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.driver.NativeImage.ArgumentQueue; class DefaultOptionHandler extends NativeImage.OptionHandler { @@ -105,7 +105,7 @@ public boolean consume(ArgumentQueue args) { nativeImage.showMessage(helpExtraText); nativeImage.apiOptionHandler.printOptions(nativeImage::showMessage, true); nativeImage.showNewline(); - nativeImage.optionRegistry.showOptions(MacroOptionKind.Macro, true, nativeImage::showMessage); + nativeImage.optionRegistry.showOptions(OptionUtils.MacroOptionKind.Macro, true, nativeImage::showMessage); nativeImage.showNewline(); System.exit(0); return true; diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOption.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOption.java index 1057f6b286b3..89068c6fab6d 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOption.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOption.java @@ -42,46 +42,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.driver.NativeImage.BuildConfiguration; import com.oracle.svm.driver.metainf.NativeImageMetaInfWalker; final class MacroOption { - enum MacroOptionKind { - Language("languages", true), - Tool("tools", true), - Macro("macros", false); - - final String subdir; - final boolean allowAll; - - MacroOptionKind(String subdir, boolean allowAll) { - this.subdir = subdir; - this.allowAll = allowAll; - } - - static MacroOptionKind fromSubdir(String subdir) { - for (MacroOptionKind kind : MacroOptionKind.values()) { - if (kind.subdir.equals(subdir)) { - return kind; - } - } - throw new InvalidMacroException("No MacroOptionKind for subDir: " + subdir); - } - - static MacroOptionKind fromString(String kindName) { - for (MacroOptionKind kind : MacroOptionKind.values()) { - if (kind.toString().equals(kindName)) { - return kind; - } - } - throw new InvalidMacroException("No MacroOptionKind for kindName: " + kindName); - } - - @Override - public String toString() { - return name().toLowerCase(); - } - } Path getOptionDirectory() { return optionDirectory; @@ -91,34 +56,20 @@ String getOptionName() { return optionName; } - private static final String macroOptionPrefix = "--"; - String getDescription(boolean commandLineStyle) { - StringBuilder sb = new StringBuilder(); - if (commandLineStyle) { - sb.append(macroOptionPrefix); - } - sb.append(kind.toString()).append(":").append(getOptionName()); - return sb.toString(); - } - - @SuppressWarnings("serial") - static final class InvalidMacroException extends RuntimeException { - InvalidMacroException(String arg0) { - super(arg0); - } + return kind.getDescriptionPrefix(commandLineStyle) + getOptionName(); } @SuppressWarnings("serial") static final class VerboseInvalidMacroException extends RuntimeException { - private final MacroOptionKind forKind; + private final OptionUtils.MacroOptionKind forKind; private final MacroOption context; VerboseInvalidMacroException(String arg0, MacroOption context) { this(arg0, null, context); } - VerboseInvalidMacroException(String arg0, MacroOptionKind forKind, MacroOption context) { + VerboseInvalidMacroException(String arg0, OptionUtils.MacroOptionKind forKind, MacroOption context) { super(arg0); this.forKind = forKind; this.context = context; @@ -218,12 +169,12 @@ boolean isEnabledFromCommandline() { } static final class Registry { - private final Map> supported = new HashMap<>(); + private final Map> supported = new HashMap<>(); private final LinkedHashSet enabled = new LinkedHashSet<>(); - private static Map> collectMacroOptions(Path rootDir) throws IOException { - Map> result = new HashMap<>(); - for (MacroOptionKind kind : MacroOptionKind.values()) { + private static Map> collectMacroOptions(Path rootDir) throws IOException { + Map> result = new HashMap<>(); + for (OptionUtils.MacroOptionKind kind : OptionUtils.MacroOptionKind.values()) { Path optionsDir = rootDir.resolve(kind.subdir); Map collectedOptions = Collections.emptyMap(); if (Files.isDirectory(optionsDir)) { @@ -238,7 +189,7 @@ private static Map> collectMacroOption } Registry() { - for (MacroOptionKind kind : MacroOptionKind.values()) { + for (OptionUtils.MacroOptionKind kind : OptionUtils.MacroOptionKind.values()) { supported.put(kind, new HashMap<>()); } } @@ -250,21 +201,21 @@ void addMacroOptionRoot(Path rootDir) { supported.get(optionKind).putAll(optionMap); }); } catch (IOException e) { - throw new InvalidMacroException("Error while discovering supported MacroOptions in " + rootDir + ": " + e.getMessage()); + throw new OptionUtils.InvalidMacroException("Error while discovering supported MacroOptions in " + rootDir + ": " + e.getMessage()); } } - Set getAvailableOptions(MacroOptionKind forKind) { + Set getAvailableOptions(OptionUtils.MacroOptionKind forKind) { return supported.get(forKind).keySet(); } - void showOptions(MacroOptionKind forKind, boolean commandLineStyle, Consumer lineOut) { + void showOptions(OptionUtils.MacroOptionKind forKind, boolean commandLineStyle, Consumer lineOut) { List optionsToShow = new ArrayList<>(); - for (MacroOptionKind kind : MacroOptionKind.values()) { + for (OptionUtils.MacroOptionKind kind : OptionUtils.MacroOptionKind.values()) { if (forKind != null && !kind.equals(forKind)) { continue; } - if (forKind == null && kind == MacroOptionKind.Macro) { + if (forKind == null && kind == OptionUtils.MacroOptionKind.Macro) { // skip non-API macro options by default continue; } @@ -272,7 +223,7 @@ void showOptions(MacroOptionKind forKind, boolean commandLineStyle, Consumer addedCheck, MacroOption context, Consumer enabler) { String specString; if (context == null) { - if (optionString.startsWith(macroOptionPrefix)) { - specString = optionString.substring(macroOptionPrefix.length()); + if (optionString.startsWith(OptionUtils.MacroOptionKind.macroOptionPrefix)) { + specString = optionString.substring(OptionUtils.MacroOptionKind.macroOptionPrefix.length()); } else { return false; } @@ -315,9 +266,9 @@ boolean enableOption(BuildConfiguration config, String optionString, HashSet getEnabledOptions(MacroOptionKind kind) { + LinkedHashSet getEnabledOptions(OptionUtils.MacroOptionKind kind) { return enabled.stream().filter(eo -> kind.equals(eo.option.kind)).collect(Collectors.toCollection(LinkedHashSet::new)); } - Stream getEnabledOptionsStream(MacroOptionKind kind, MacroOptionKind... otherKinds) { - EnumSet kindSet = EnumSet.of(kind, otherKinds); + Stream getEnabledOptionsStream(OptionUtils.MacroOptionKind kind, OptionUtils.MacroOptionKind... otherKinds) { + EnumSet kindSet = EnumSet.of(kind, otherKinds); return enabled.stream().filter(eo -> kindSet.contains(eo.option.kind)); } @@ -411,7 +362,7 @@ EnabledOption getEnabledOption(MacroOption option) { private final String optionName; private final Path optionDirectory; - final MacroOptionKind kind; + final OptionUtils.MacroOptionKind kind; private final Map properties; private static MacroOption create(Path macroOptionDirectory) { @@ -423,7 +374,7 @@ private static MacroOption create(Path macroOptionDirectory) { } private MacroOption(Path optionDirectory) { - this.kind = MacroOptionKind.fromSubdir(optionDirectory.getParent().getFileName().toString()); + this.kind = OptionUtils.MacroOptionKind.fromSubdir(optionDirectory.getParent().getFileName().toString()); this.optionName = optionDirectory.getFileName().toString(); this.optionDirectory = optionDirectory; this.properties = NativeImage.loadProperties(optionDirectory.resolve(NativeImageMetaInfWalker.nativeImagePropertiesFilename)); diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java index 7be4b92c6627..68f93bdb510c 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java @@ -29,9 +29,9 @@ import java.util.HashSet; import com.oracle.svm.core.OS; +import com.oracle.svm.core.option.OptionUtils.InvalidMacroException; import com.oracle.svm.core.util.ClasspathUtils; import com.oracle.svm.driver.MacroOption.AddedTwiceException; -import com.oracle.svm.driver.MacroOption.InvalidMacroException; import com.oracle.svm.driver.MacroOption.VerboseInvalidMacroException; import com.oracle.svm.driver.NativeImage.ArgumentQueue; import com.oracle.svm.driver.NativeImage.BuildConfiguration; diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java index bc2c934c5ec8..76d05fae9c25 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java @@ -77,11 +77,11 @@ import com.oracle.svm.core.OS; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.ClasspathUtils; import com.oracle.svm.core.util.VMError; import com.oracle.svm.driver.MacroOption.EnabledOption; -import com.oracle.svm.driver.MacroOption.MacroOptionKind; import com.oracle.svm.driver.MacroOption.Registry; import com.oracle.svm.driver.metainf.MetaInfFileType; import com.oracle.svm.driver.metainf.NativeImageMetaInfResourceProcessor; @@ -821,7 +821,7 @@ private void completeOptionArgs() { } /* Determine if truffle is needed- any MacroOption of kind Language counts */ - enabledLanguages = optionRegistry.getEnabledOptions(MacroOptionKind.Language); + enabledLanguages = optionRegistry.getEnabledOptions(OptionUtils.MacroOptionKind.Language); /* Provide more memory for image building if we have more than one language. */ if (enabledLanguages.size() > 1) { @@ -1190,7 +1190,7 @@ private boolean shouldAddCWDToCP() { return false; } - Optional explicitMacroOption = optionRegistry.getEnabledOptions(MacroOptionKind.Macro).stream().filter(EnabledOption::isEnabledFromCommandline).findAny(); + Optional explicitMacroOption = optionRegistry.getEnabledOptions(OptionUtils.MacroOptionKind.Macro).stream().filter(EnabledOption::isEnabledFromCommandline).findAny(); /* If we have any explicit macro options, we do not put "." on classpath */ if (explicitMacroOption.isPresent()) { return false; From 13f45e98530450aa9635480315318c27e8c44d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 16 Feb 2022 17:02:22 +0100 Subject: [PATCH 07/36] Disable debug output --- .../src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java | 4 ++++ 1 file changed, 4 insertions(+) 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 ef2e0cb61189..de053dfe0826 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 @@ -145,6 +145,10 @@ public static boolean isModuleSynthetic(Module m) { @Override public void afterAnalysis(AfterAnalysisAccess a) { + if (a != null) { + return; + } + // Test results of requiresCompleteDefinition(Class clazz) on whole AnalysisUniverse FeatureImpl.AfterAnalysisAccessImpl access = (FeatureImpl.AfterAnalysisAccessImpl) a; for (AnalysisType analysisType : access.getUniverse().getTypes()) { From 9bf4e609900d0fb6b292e4c3590813ef21b1a217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 16 Feb 2022 17:24:42 +0100 Subject: [PATCH 08/36] Style fix --- .../src/com/oracle/svm/core/option/OptionOrigin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index 4f1afebe9b98..8fdb336de697 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -48,7 +48,7 @@ public Path location() { return null; } - public List getRedirectionValues(Path valuesFile) throws IOException { + public List getRedirectionValues(@SuppressWarnings("unused") Path valuesFile) throws IOException { throw new UnsupportedOperationException(); } From 14ef9fd5b386d5198113b4d66d40e0945a80d07b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 09:47:38 +0100 Subject: [PATCH 09/36] Cache ClassLoaderSupport ImageSingletons --- .../src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 de053dfe0826..2a9fe2677652 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 @@ -67,12 +67,14 @@ static final class Options { private final Set requireCompleteModules = new HashSet<>(); private boolean requireCompleteAll; - Map uriModuleMap; + private ClassLoaderSupport classLoaderSupport; + private Map uriModuleMap; @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - var loader = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getImageClassLoader(); + classLoaderSupport = ImageSingletons.lookup(ClassLoaderSupport.class); + var loader = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getImageClassLoader(); uriModuleMap = ModuleFinder.of(loader.applicationModulePath().toArray(Path[]::new)).findAll().stream() .filter(mRef -> mRef.location().isPresent()) .collect(Collectors.toUnmodifiableMap(mRef -> mRef.location().get(), mRef -> loader.findModule(mRef.descriptor().name()).get())); @@ -126,7 +128,7 @@ public boolean requiresCompleteDefinition(Class clazz) { return true; } - if (!ImageSingletons.lookup(ClassLoaderSupport.class).isNativeImageClassLoader(clazz.getClassLoader())) { + if (!classLoaderSupport.isNativeImageClassLoader(clazz.getClassLoader())) { return true; } assert !clazz.isPrimitive() : "Primitive classes are not loaded via NativeImageClassLoader"; From bf43e745d94be1c8c1fd1e9869590501c1947b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 09:58:45 +0100 Subject: [PATCH 10/36] Make requiresCompleteDefinition package-private --- .../src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2a9fe2677652..3cf47f69cbe4 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 @@ -123,7 +123,7 @@ private UserError.UserException notOnModulePath(OptionOrigin origin) { SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, ""), origin); } - public boolean requiresCompleteDefinition(Class clazz) { + boolean requiresCompleteDefinition(Class clazz) { if (requireCompleteAll || clazz.isArray()) { return true; } From 7fc230384a4fd693e6a7491bd69ed771a9e03f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 10:03:04 +0100 Subject: [PATCH 11/36] Remove duplicate entries from svm-junit.packages --- .../com.oracle.svm.junit/svm-junit.packages | 2 -- 1 file changed, 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages index e8e54e6a3d4f..4fec0f61dd0b 100644 --- a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages +++ b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages @@ -6,8 +6,6 @@ org.hamcrest org.hamcrest.core org.hamcrest.internal org.junit -org.junit -org.junit.internal org.junit.internal org.junit.internal.builders org.junit.internal.runners.model From 99fdb33df9ece3b508cd4971f6c8880160ec0cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 10:18:00 +0100 Subject: [PATCH 12/36] Style fix --- .../src/com/oracle/svm/core/option/OptionOrigin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index 8fdb336de697..c8e2b31369a4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -49,7 +49,7 @@ public Path location() { } public List getRedirectionValues(@SuppressWarnings("unused") Path valuesFile) throws IOException { - throw new UnsupportedOperationException(); + throw new IOException(new UnsupportedOperationException()); } public static OptionOrigin of(String origin) { From c42a49342911bdb40bae11f015985e03007a8241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 11:11:17 +0100 Subject: [PATCH 13/36] Consolidate Exception handling in resolveOptionValueRedirectionFlatMap --- .../oracle/svm/core/option/OptionOrigin.java | 25 ++++++++++--------- .../oracle/svm/core/option/OptionUtils.java | 22 ++++++++-------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index c8e2b31369a4..4557129bd9c3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -159,6 +159,7 @@ public static final class JarOptionOrigin extends URIOptionOrigin { protected JarOptionOrigin(URI rawOrigin) { var specific = rawOrigin.getSchemeSpecificPart(); int sep = specific.lastIndexOf('!'); + VMError.guarantee(sep > 0, "invalid jar origin"); var origin = specific.substring(0, sep); container = URIOptionOrigin.originURI(origin); location = Path.of(specific.substring(sep + 2)); @@ -171,19 +172,19 @@ public List getRedirectionValues(Path valuesFile) throws IOException { try { probeJarFS = FileSystems.newFileSystem(jarFileURI, Collections.emptyMap()); } catch (UnsupportedOperationException e) { - throw new FileNotFoundException(); + probeJarFS = null; } - if (probeJarFS != null) { - try (FileSystem jarFS = probeJarFS) { - var normalizedRedirPath = location().getParent().resolve(valuesFile).normalize(); - var pathInJarFS = jarFS.getPath(normalizedRedirPath.toString()); - if (Files.exists(pathInJarFS)) { - return Files.readAllLines(pathInJarFS); - } - throw new FileNotFoundException(pathInJarFS.toString()); + if (probeJarFS == null) { + throw new IOException("Unable to create jar file system for " + jarFileURI); + } + try (FileSystem jarFS = probeJarFS) { + var normalizedRedirPath = location().getParent().resolve(valuesFile).normalize(); + var pathInJarFS = jarFS.getPath(normalizedRedirPath.toString()); + if (Files.isReadable(pathInJarFS)) { + return Files.readAllLines(pathInJarFS); } + throw new FileNotFoundException("Unable to read " + pathInJarFS + " from jar file system " + jarFS); } - throw new FileNotFoundException(jarFileURI.toString()); } } @@ -206,10 +207,10 @@ protected DirectoryOptionOrigin(Path originPath) { @Override public List getRedirectionValues(Path valuesFile) throws IOException { var normalizedRedirPath = Path.of(container()).resolve(location()).getParent().resolve(valuesFile).normalize(); - if (Files.exists(normalizedRedirPath)) { + if (Files.isReadable(normalizedRedirPath)) { return Files.readAllLines(normalizedRedirPath); } - throw new FileNotFoundException(normalizedRedirPath.toString()); + throw new FileNotFoundException("Unable to read file from " + normalizedRedirPath); } } } \ No newline at end of file diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java index 9f828e4b1e67..11e3e0133e89 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.option; +import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -76,24 +77,23 @@ public static List flatten(String delimiter, List values) { public static List resolveOptionValueRedirection(OptionKey option, String optionValue, OptionOrigin origin) { return Arrays.asList(SubstrateUtil.split(optionValue, ",")).stream() - .flatMap(entry -> { - try { - return resolveOptionValueRedirectionFlatMap(entry, origin); - } catch (Exception e) { - throw UserError.abort(e, "Option '%s' from %s contains invalid option value redirection.", - SubstrateOptionsParser.commandArgument(option, optionValue), origin); - } - }) + .flatMap(entry -> resolveOptionValueRedirectionFlatMap(option, optionValue, origin, entry)) .collect(Collectors.toList()); } - private static Stream resolveOptionValueRedirectionFlatMap(String entry, OptionOrigin origin) throws Exception { + private static Stream resolveOptionValueRedirectionFlatMap(OptionKey option, String optionValue, OptionOrigin origin, String entry) { if (entry.trim().startsWith("@")) { Path valuesFile = Path.of(entry.substring(1)); if (valuesFile.isAbsolute()) { - throw new IllegalArgumentException("Option option value redirection file '" + valuesFile + "' is absolute path"); + throw UserError.abort("Option '%s' from %s contains value redirection file '%s' that is an absolute path.", + SubstrateOptionsParser.commandArgument(option, optionValue), origin, valuesFile); + } + try { + return origin.getRedirectionValues(valuesFile).stream(); + } catch (IOException e) { + throw UserError.abort(e, "Option '%s' from %s contains invalid option value redirection.", + SubstrateOptionsParser.commandArgument(option, optionValue), origin); } - return origin.getRedirectionValues(valuesFile).stream(); } else { return Stream.of(entry); } From 1648216245fe1395be7400aaf0b52b630ab83acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 10:18:11 +0100 Subject: [PATCH 14/36] Support all variants of MacroOptionOrigin and remove UnsupportedOptionOrigin --- .../option/LocatableMultiOptionValue.java | 2 +- .../oracle/svm/core/option/OptionOrigin.java | 70 +++++++++++-------- .../oracle/svm/core/option/OptionUtils.java | 4 +- .../svm/hosted/LinkAtBuildTimeFeature.java | 14 ++-- 4 files changed, 48 insertions(+), 42 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/LocatableMultiOptionValue.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/LocatableMultiOptionValue.java index bea02f2d6e32..fa3607a57726 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/LocatableMultiOptionValue.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/LocatableMultiOptionValue.java @@ -77,7 +77,7 @@ public List values() { } public Stream> getValuesWithOrigins() { - return values.stream().map(pair -> Pair.create(pair.getLeft(), OptionOrigin.of(pair.getRight()))); + return values.stream().map(pair -> Pair.create(pair.getLeft(), OptionOrigin.from(pair.getRight()))); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index 4557129bd9c3..acedc6aea1ce 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -48,11 +48,15 @@ public Path location() { return null; } + public boolean commandLineLike() { + return false; + } + public List getRedirectionValues(@SuppressWarnings("unused") Path valuesFile) throws IOException { throw new IOException(new UnsupportedOperationException()); } - public static OptionOrigin of(String origin) { + public static OptionOrigin from(String origin) { if (origin == null) { return CommandLineOptionOrigin.singleton; @@ -60,24 +64,23 @@ public static OptionOrigin of(String origin) { URI originURI = originURI(origin); if (originURI == null) { - String macroName = MacroOptionOrigin.macroName(origin); - if (macroName != null) { - return new MacroOptionOrigin(macroName); + var macroOption = MacroOptionOrigin.from(origin); + if (macroOption != null) { + return macroOption; } - return new UnsupportedOptionOrigin(origin); + throw VMError.shouldNotReachHere("Unsupported OptionOrigin: " + origin); } - switch (originURI.getScheme()) { case "jar": return new JarOptionOrigin(originURI); case "file": Path originPath = Path.of(originURI); - if (Files.isReadable(originPath)) { - return new DirectoryOptionOrigin(originPath); + if (!Files.isReadable(originPath)) { + VMError.shouldNotReachHere("Directory origin with path that cannot be read: " + originPath); } - return new UnsupportedOptionOrigin(origin); + return new DirectoryOptionOrigin(originPath); default: - return new UnsupportedOptionOrigin(origin); + throw VMError.shouldNotReachHere("OptionOrigin of unsupported scheme: " + originURI); } } @@ -93,43 +96,48 @@ public static class CommandLineOptionOrigin extends OptionOrigin { private static CommandLineOptionOrigin singleton = new CommandLineOptionOrigin(); + private CommandLineOptionOrigin() { + } + + @Override + public boolean commandLineLike() { + return true; + } + @Override public String toString() { return "command line"; } } - public static final class MacroOptionOrigin extends CommandLineOptionOrigin { - - private static final String PREFIX = OptionUtils.MacroOptionKind.Macro.getDescriptionPrefix(true); + public static final class MacroOptionOrigin extends OptionOrigin { - private final String name; + public final OptionUtils.MacroOptionKind kind; + public final String name; - MacroOptionOrigin(String name) { + private MacroOptionOrigin(OptionUtils.MacroOptionKind kind, String name) { + this.kind = kind; this.name = name; } - public static String macroName(String rawOrigin) { - return rawOrigin.startsWith(PREFIX) ? rawOrigin.substring(PREFIX.length()) : null; + public static MacroOptionOrigin from(String rawOrigin) { + for (OptionUtils.MacroOptionKind kind : OptionUtils.MacroOptionKind.values()) { + String prefix = kind.getDescriptionPrefix(true); + if (rawOrigin.startsWith(prefix)) { + return new MacroOptionOrigin(kind, rawOrigin.substring(prefix.length())); + } + } + return null; } @Override - public String toString() { - return "macro option '" + name + "'"; - } - } - - public static final class UnsupportedOptionOrigin extends OptionOrigin { - - protected final String rawOrigin; - - public UnsupportedOptionOrigin(String rawOrigin) { - this.rawOrigin = rawOrigin; + public boolean commandLineLike() { + return OptionUtils.MacroOptionKind.Macro.equals(kind); } @Override public String toString() { - return rawOrigin; + return kind + " option '" + name + "'"; } } @@ -159,7 +167,7 @@ public static final class JarOptionOrigin extends URIOptionOrigin { protected JarOptionOrigin(URI rawOrigin) { var specific = rawOrigin.getSchemeSpecificPart(); int sep = specific.lastIndexOf('!'); - VMError.guarantee(sep > 0, "invalid jar origin"); + VMError.guarantee(sep > 0, "Invalid jar origin"); var origin = specific.substring(0, sep); container = URIOptionOrigin.originURI(origin); location = Path.of(specific.substring(sep + 2)); @@ -199,7 +207,7 @@ protected DirectoryOptionOrigin(Path originPath) { } ++pathPos; } - VMError.guarantee(metaInfPos > 0, "invalid directory origin"); + VMError.guarantee(metaInfPos > 0, "Invalid directory origin"); container = originPath.getRoot().resolve(originPath.subpath(0, metaInfPos)).toUri(); location = originPath.subpath(metaInfPos, originPath.getNameCount()); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java index 11e3e0133e89..a7e12b9511f5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java @@ -85,13 +85,13 @@ private static Stream resolveOptionValueRedirectionFlatMap(Opt if (entry.trim().startsWith("@")) { Path valuesFile = Path.of(entry.substring(1)); if (valuesFile.isAbsolute()) { - throw UserError.abort("Option '%s' from %s contains value redirection file '%s' that is an absolute path.", + throw UserError.abort("Option '%s' provided by %s contains value redirection file '%s' that is an absolute path.", SubstrateOptionsParser.commandArgument(option, optionValue), origin, valuesFile); } try { return origin.getRedirectionValues(valuesFile).stream(); } catch (IOException e) { - throw UserError.abort(e, "Option '%s' from %s contains invalid option value redirection.", + throw UserError.abort(e, "Option '%s' provided by %s contains invalid option value redirection.", SubstrateOptionsParser.commandArgument(option, optionValue), origin); } } else { 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 3cf47f69cbe4..af6b9d919caf 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 @@ -94,24 +94,22 @@ private void extractOptionValue(Pair valueOrigin) { var value = valueOrigin.getLeft(); OptionOrigin origin = valueOrigin.getRight(); if (value.isEmpty()) { - if (origin instanceof OptionOrigin.CommandLineOptionOrigin) { + if (origin.commandLineLike()) { requireCompleteAll = true; return; } - if (origin instanceof OptionOrigin.UnsupportedOptionOrigin) { - throw notOnModulePath(origin); - } var originModule = uriModuleMap.get(origin.container()); - if (originModule == null) { - throw notOnModulePath(origin); + if (originModule != null) { + requireCompleteModules.add(originModule); + return; } - requireCompleteModules.add(originModule); + throw notOnModulePath(origin); } else { for (String entry : OptionUtils.resolveOptionValueRedirection(Options.LinkAtBuildTime, value, origin)) { if (validOptionValue.matcher(entry).matches()) { requireCompletePackageOrClass.add(entry); } else { - throw UserError.abort("Entry '%s' in option '%s' (from location '%s') is neither a package nor a fully qualified classname.", + throw UserError.abort("Entry '%s' in option '%s' provided by '%s' is neither a package nor a fully qualified classname.", entry, SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, value), origin); } } From 0b6593fb47850428338e92bcce74dfacd4facb0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 16:11:42 +0100 Subject: [PATCH 15/36] Inline single-use method --- .../src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 af6b9d919caf..4bec52804ad6 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 @@ -103,7 +103,8 @@ private void extractOptionValue(Pair valueOrigin) { requireCompleteModules.add(originModule); return; } - throw notOnModulePath(origin); + throw UserError.abort("Using '%s' without args only allowed on module-path. %s not part of module-path.", + SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, value), origin); } else { for (String entry : OptionUtils.resolveOptionValueRedirection(Options.LinkAtBuildTime, value, origin)) { if (validOptionValue.matcher(entry).matches()) { @@ -116,11 +117,6 @@ private void extractOptionValue(Pair valueOrigin) { } } - private UserError.UserException notOnModulePath(OptionOrigin origin) { - return UserError.abort("Using '%s' without args only allowed on module-path. %s not part of module-path.", - SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, ""), origin); - } - boolean requiresCompleteDefinition(Class clazz) { if (requireCompleteAll || clazz.isArray()) { return true; From 9179718fa00726968ffc743c0a7f3fb33168912f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 16:12:05 +0100 Subject: [PATCH 16/36] Remove debug code --- .../svm/hosted/LinkAtBuildTimeFeature.java | 22 ------------------- 1 file changed, 22 deletions(-) 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 4bec52804ad6..2e860689d849 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 @@ -40,7 +40,6 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; -import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.ClassLoaderSupport; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.option.APIOption; @@ -138,25 +137,4 @@ boolean requiresCompleteDefinition(Class clazz) { public static boolean isModuleSynthetic(Module m) { return m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC); } - - @Override - public void afterAnalysis(AfterAnalysisAccess a) { - if (a != null) { - return; - } - - // Test results of requiresCompleteDefinition(Class clazz) on whole AnalysisUniverse - FeatureImpl.AfterAnalysisAccessImpl access = (FeatureImpl.AfterAnalysisAccessImpl) a; - for (AnalysisType analysisType : access.getUniverse().getTypes()) { - System.out.print(analysisType.toJavaName()); - Class analysisClass = analysisType.getJavaClass(); - if (analysisClass == null) { - System.out.print(" (no Class)"); - } else { - boolean requiresCompleteDefinition = requiresCompleteDefinition(analysisClass); - System.out.print(" requiresCompleteDefinition: " + requiresCompleteDefinition); - } - System.out.println(); - } - } } From 6cba4926812b164768bb38ce35758b44cd06de7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 17:25:08 +0100 Subject: [PATCH 17/36] Record where class and package link-at-build-time options are coming from --- .../oracle/svm/core/option/OptionOrigin.java | 47 +++++++++++++++++-- .../svm/hosted/LinkAtBuildTimeFeature.java | 9 ++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index acedc6aea1ce..434b4bfb0d83 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -35,6 +35,7 @@ import java.nio.file.Path; import java.util.Collections; import java.util.List; +import java.util.Objects; import com.oracle.svm.core.util.VMError; @@ -92,13 +93,23 @@ protected static URI originURI(String origin) { } } - public static class CommandLineOptionOrigin extends OptionOrigin { + public static final class CommandLineOptionOrigin extends OptionOrigin { private static CommandLineOptionOrigin singleton = new CommandLineOptionOrigin(); private CommandLineOptionOrigin() { } + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CommandLineOptionOrigin; + } + @Override public boolean commandLineLike() { return true; @@ -120,6 +131,21 @@ private MacroOptionOrigin(OptionUtils.MacroOptionKind kind, String name) { this.name = name; } + @Override + public int hashCode() { + return Objects.hash(kind, name); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof MacroOptionOrigin) { + var that = (MacroOptionOrigin) obj; + return Objects.equals(this.kind, that.kind) && + Objects.equals(this.name, that.name); + } + return false; + } + public static MacroOptionOrigin from(String rawOrigin) { for (OptionUtils.MacroOptionKind kind : OptionUtils.MacroOptionKind.values()) { String prefix = kind.getDescriptionPrefix(true); @@ -141,7 +167,7 @@ public String toString() { } } - protected static abstract class URIOptionOrigin extends OptionOrigin { + protected abstract static class URIOptionOrigin extends OptionOrigin { protected URI container; @@ -157,6 +183,21 @@ public Path location() { return location; } + @Override + public int hashCode() { + return Objects.hash(container, location); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof URIOptionOrigin) { + var that = (URIOptionOrigin) obj; + return Objects.equals(this.container, that.container) && + Objects.equals(this.location, that.location); + } + return false; + } + @Override public String toString() { return String.format("'%s' in '%s'", location(), container()); @@ -221,4 +262,4 @@ public List getRedirectionValues(Path valuesFile) throws IOException { throw new FileNotFoundException("Unable to read file from " + normalizedRedirPath); } } -} \ No newline at end of file +} 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 2e860689d849..4b752790d182 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 @@ -29,6 +29,7 @@ import java.lang.module.ModuleFinder; import java.net.URI; import java.nio.file.Path; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -62,7 +63,7 @@ static final class Options { private final String javaIdentifier = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"; private final Pattern validOptionValue = Pattern.compile(javaIdentifier + "(\\." + javaIdentifier + ")*"); - private final Set requireCompletePackageOrClass = new HashSet<>(); + private final Map> requireCompletePackageOrClass = new HashMap<>(); private final Set requireCompleteModules = new HashSet<>(); private boolean requireCompleteAll; @@ -84,7 +85,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { * the synthetic modifier set (clazz.isSynthetic() returns false for such classes). Any * class with package-name jdk.internal.reflect should be treated as link-at-build-time. */ - requireCompletePackageOrClass.add("jdk.internal.reflect"); + requireCompletePackageOrClass.put("jdk.internal.reflect", null); Options.LinkAtBuildTime.getValue().getValuesWithOrigins().forEach(this::extractOptionValue); } @@ -107,7 +108,7 @@ private void extractOptionValue(Pair valueOrigin) { } else { for (String entry : OptionUtils.resolveOptionValueRedirection(Options.LinkAtBuildTime, value, origin)) { if (validOptionValue.matcher(entry).matches()) { - requireCompletePackageOrClass.add(entry); + requireCompletePackageOrClass.computeIfAbsent(entry, unused -> new HashSet<>()).add(origin); } else { throw UserError.abort("Entry '%s' in option '%s' provided by '%s' is neither a package nor a fully qualified classname.", entry, SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, value), origin); @@ -131,7 +132,7 @@ boolean requiresCompleteDefinition(Class clazz) { return true; } - return requireCompletePackageOrClass.contains(clazz.getName()) || requireCompletePackageOrClass.contains(clazz.getPackageName()) || clazz.isSynthetic(); + return requireCompletePackageOrClass.containsKey(clazz.getName()) || requireCompletePackageOrClass.containsKey(clazz.getPackageName()) || clazz.isSynthetic(); } public static boolean isModuleSynthetic(Module m) { From 880e231ba9533387dac3ea0813a163ffc5bdc181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 17 Feb 2022 18:49:52 +0100 Subject: [PATCH 18/36] Use qualified --link-at-build-time for svm-driver to allow building on classpath --- .../com.oracle.substratevm/svm-driver/native-image.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties b/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties index fbd174007fa6..a597237df010 100644 --- a/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties +++ b/substratevm/src/com.oracle.svm.driver/resources/META-INF/native-image/com.oracle.substratevm/svm-driver/native-image.properties @@ -1,4 +1,4 @@ ImageName = native-image Args = -H:-ParseRuntimeOptions \ --initialize-at-build-time=com.oracle.svm.driver \ - --link-at-build-time \ No newline at end of file + --link-at-build-time=com.oracle.svm.driver,com.oracle.svm.driver.metainf \ No newline at end of file From 71ede25c453df4dc09e5b613bb86aa1cd1c416d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Mon, 21 Feb 2022 11:23:50 +0100 Subject: [PATCH 19/36] Do not default synthetic modules and classes to link-at-build-time --- .../oracle/svm/hosted/LinkAtBuildTimeFeature.java | 9 ++------- .../com/oracle/svm/hosted/ModuleLayerFeature.java | 12 ++++++++---- 2 files changed, 10 insertions(+), 11 deletions(-) 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 4b752790d182..ebdbb4ff400b 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 @@ -25,7 +25,6 @@ package com.oracle.svm.hosted; -import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.net.URI; import java.nio.file.Path; @@ -128,14 +127,10 @@ boolean requiresCompleteDefinition(Class clazz) { assert !clazz.isPrimitive() : "Primitive classes are not loaded via NativeImageClassLoader"; var module = clazz.getModule(); - if (module.isNamed() && (requireCompleteModules.contains(module) || isModuleSynthetic(module))) { + if (module.isNamed() && (requireCompleteModules.contains(module))) { return true; } - return requireCompletePackageOrClass.containsKey(clazz.getName()) || requireCompletePackageOrClass.containsKey(clazz.getPackageName()) || clazz.isSynthetic(); - } - - public static boolean isModuleSynthetic(Module m) { - return m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC); + return requireCompletePackageOrClass.containsKey(clazz.getName()) || requireCompletePackageOrClass.containsKey(clazz.getPackageName()); } } 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 56a9b6f9a9fc..a53678ae14c7 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 @@ -158,7 +158,7 @@ public void afterAnalysis(AfterAnalysisAccess access) { Set analysisReachableSyntheticModules = analysisReachableNamedModules .stream() - .filter(LinkAtBuildTimeFeature::isModuleSynthetic) + .filter(ModuleLayerFeature::isModuleSynthetic) .collect(Collectors.toSet()); Set allReachableModules = analysisReachableNamedModules @@ -251,7 +251,7 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag continue; } Module runtimeTo = e2.getValue().runtimeModule; - if (LinkAtBuildTimeFeature.isModuleSynthetic(hostedFrom) || hostedFrom.canRead(hostedTo)) { + if (isModuleSynthetic(hostedFrom) || hostedFrom.canRead(hostedTo)) { moduleLayerFeatureUtils.addReads(runtimeFrom, runtimeTo); if (hostedFrom == builderModule) { for (Module appModule : applicationModules) { @@ -260,7 +260,7 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag } } for (String pn : runtimeFrom.getPackages()) { - if (LinkAtBuildTimeFeature.isModuleSynthetic(hostedFrom) || hostedFrom.isOpen(pn, hostedTo)) { + if (isModuleSynthetic(hostedFrom) || hostedFrom.isOpen(pn, hostedTo)) { moduleLayerFeatureUtils.addOpens(runtimeFrom, pn, runtimeTo); if (hostedTo == builderModule) { for (Module appModule : applicationModules) { @@ -268,7 +268,7 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag } } } - if (LinkAtBuildTimeFeature.isModuleSynthetic(hostedFrom) || hostedFrom.isExported(pn, hostedTo)) { + if (isModuleSynthetic(hostedFrom) || hostedFrom.isExported(pn, hostedTo)) { moduleLayerFeatureUtils.addExports(runtimeFrom, pn, runtimeTo); if (hostedTo == builderModule) { for (Module appModule : applicationModules) { @@ -284,6 +284,10 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag } } + private static boolean isModuleSynthetic(Module m) { + return m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC); + } + private static List findApplicationModules(ModuleLayer runtimeBootLayer, List applicationModulePath) { List applicationModules = new ArrayList<>(); List applicationModuleNames; From eab70622b40a72a2232afc3a3296850b10da93e0 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Wed, 16 Feb 2022 17:41:55 -0800 Subject: [PATCH 20/36] Replace usages of AllowIncompleteClasspath --- .../ReflectionConfigurationParser.java | 16 ++-- .../svm/hosted/ConfigurationTypeResolver.java | 15 +--- .../oracle/svm/hosted/FallbackFeature.java | 1 - .../svm/hosted/LinkAtBuildTimeFeature.java | 5 ++ .../svm/hosted/LinkAtBuildTimeSupport.java | 75 +++++++++++++++++++ .../oracle/svm/hosted/NativeImageOptions.java | 12 +-- .../oracle/svm/hosted/ResourcesFeature.java | 2 +- .../src/com/oracle/svm/hosted/SVMHost.java | 21 +++--- .../ConfigurableClassInitialization.java | 52 ++++++------- .../config/ConfigurationParserUtils.java | 3 +- .../phases/SharedGraphBuilderPhase.java | 21 ++---- .../reflect/proxy/DynamicProxySupport.java | 7 +- .../proxy/hosted/DynamicProxyFeature.java | 3 +- .../hosted/SerializationFeature.java | 21 +++--- .../Target_java_lang_reflect_Executable.java | 21 +++--- 15 files changed, 157 insertions(+), 118 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeSupport.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java index 61a4c37a04d9..f7aefb9edc2b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java @@ -47,20 +47,18 @@ public final class ReflectionConfigurationParser extends ConfigurationParser private static final String CONSTRUCTOR_NAME = ""; private final ReflectionConfigurationParserDelegate delegate; - private final boolean allowIncompleteClasspath; private static final List OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS = Arrays.asList("allDeclaredConstructors", "allPublicConstructors", "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields", "allDeclaredClasses", "allPermittedSubclasses", "allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY, "queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods"); public ReflectionConfigurationParser(ReflectionConfigurationParserDelegate delegate) { - this(delegate, false, true); + this(delegate, true); } - public ReflectionConfigurationParser(ReflectionConfigurationParserDelegate delegate, boolean allowIncompleteClasspath, boolean strictConfiguration) { + public ReflectionConfigurationParser(ReflectionConfigurationParserDelegate delegate, boolean strictConfiguration) { super(strictConfiguration); this.delegate = delegate; - this.allowIncompleteClasspath = allowIncompleteClasspath; } @Override @@ -282,19 +280,15 @@ private String formatMethod(T clazz, String methodName, List paramTypes) { return delegate.getTypeName(clazz) + '.' + methodName + '(' + parameterTypeNames + ')'; } - private void handleError(String message) { + private static void handleError(String message) { handleError(message, null); } - private void handleError(String msg, Throwable cause) { + private static void handleError(String msg, Throwable cause) { String message = msg; if (cause != null) { message += " Reason: " + formatError(cause) + '.'; } - if (allowIncompleteClasspath) { - System.err.println("Warning: " + message); - } else { - throw new JSONParserException(message + " To allow unresolvable reflection configuration, use option --allow-incomplete-classpath"); - } + System.err.println("Warning: " + message); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConfigurationTypeResolver.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConfigurationTypeResolver.java index e4a323443b0b..b749d9bfbcb1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConfigurationTypeResolver.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConfigurationTypeResolver.java @@ -25,19 +25,16 @@ package com.oracle.svm.hosted; import com.oracle.svm.core.TypeResult; -import com.oracle.svm.core.util.json.JSONParserException; import jdk.vm.ci.meta.MetaUtil; public final class ConfigurationTypeResolver { private final String configurationType; private final ImageClassLoader classLoader; - private final boolean allowIncompleteClasspath; - public ConfigurationTypeResolver(String configurationType, ImageClassLoader classLoader, boolean allowIncompleteClasspath) { + public ConfigurationTypeResolver(String configurationType, ImageClassLoader classLoader) { this.configurationType = configurationType; this.classLoader = classLoader; - this.allowIncompleteClasspath = allowIncompleteClasspath; } public Class resolveType(String typeName) { @@ -48,16 +45,8 @@ public Class resolveType(String typeName) { } TypeResult> typeResult = classLoader.findClass(name); if (!typeResult.isPresent()) { - handleError("Could not resolve " + name + " for " + configurationType + "."); + System.err.println("Warning: Could not resolve " + name + " for " + configurationType + "."); } return typeResult.get(); } - - private void handleError(String message) { - if (allowIncompleteClasspath) { - System.err.println("Warning: " + message); - } else { - throw new JSONParserException(message + " To allow unresolvable " + configurationType + ", use option --allow-incomplete-classpath"); - } - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java index 8e8375fa8c0f..4ad048da5bd3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java @@ -293,7 +293,6 @@ public void beforeAnalysis(BeforeAnalysisAccess a) { public void afterAnalysis(AfterAnalysisAccess a) { if (SubstrateOptions.FallbackThreshold.getValue() == SubstrateOptions.NoFallback || NativeImageOptions.ReportUnsupportedElementsAtRuntime.getValue() || - NativeImageOptions.AllowIncompleteClasspath.getValue() || SubstrateOptions.SharedLibrary.getValue()) { /* * Any of the above ensures we unconditionally allow stand-alone image to be generated. 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 ebdbb4ff400b..88ef69f3039d 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 @@ -69,6 +69,11 @@ static final class Options { private ClassLoaderSupport classLoaderSupport; private Map uriModuleMap; + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(LinkAtBuildTimeSupport.class, new LinkAtBuildTimeSupport(this)); + } + @Override public void beforeAnalysis(BeforeAnalysisAccess access) { classLoaderSupport = ImageSingletons.lookup(ClassLoaderSupport.class); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeSupport.java new file mode 100644 index 000000000000..4312822e1ad4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeSupport.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.hosted; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; + +import jdk.vm.ci.meta.ResolvedJavaType; + +public final class LinkAtBuildTimeSupport { + + private final LinkAtBuildTimeFeature feature; + + LinkAtBuildTimeSupport(LinkAtBuildTimeFeature feature) { + this.feature = feature; + } + + public static LinkAtBuildTimeSupport singleton() { + return ImageSingletons.lookup(LinkAtBuildTimeSupport.class); + } + + public boolean linkAtBuildTime(ResolvedJavaType type) { + Class clazz = ((OriginalClassProvider) type).getJavaClass(); + if (clazz == null) { + /* + * Some kind of synthetic class coming from a substitution. We assume all such classes + * are linked at build time. + */ + return true; + } + return linkAtBuildTime(clazz); + } + + public boolean linkAtBuildTime(Class clazz) { + return feature.requiresCompleteDefinition(clazz); + } + + public String errorMessageFor(ResolvedJavaType type) { + Class clazz = ((OriginalClassProvider) type).getJavaClass(); + if (clazz == null) { + return "This error is reported at image build time because class " + type.toJavaName(true) + " is registered for linking at image build time."; + } + return errorMessageFor(clazz); + } + + public String errorMessageFor(Class clazz) { + return "This error is reported at image build time because class " + clazz.getTypeName() + " is registered for linking at image build time by "; + // TODO add information from locatable option where the class is registered for build time + // initialization. + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java index 49a4abddaf51..14c4a831967b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java @@ -35,7 +35,6 @@ import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionValues; -import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.reports.ReportUtils; import com.oracle.graal.pointsto.util.CompletionExecutor; import com.oracle.svm.core.SubstrateOptions; @@ -111,14 +110,9 @@ public class NativeImageOptions { @Option(help = "Report usage of unsupported methods and fields at run time when they are accessed the first time, instead of as an error during image building", type = User)// public static final HostedOptionKey ReportUnsupportedElementsAtRuntime = new HostedOptionKey<>(false); - @APIOption(name = "allow-incomplete-classpath")// - @Option(help = "Allow image building with an incomplete class path: report type resolution errors at run time when they are accessed the first time, instead of during image building", type = User)// - public static final HostedOptionKey AllowIncompleteClasspath = new HostedOptionKey<>(false) { - @Override - protected void onValueUpdate(EconomicMap, Object> values, Boolean oldValue, Boolean newValue) { - PointstoOptions.UnresolvedIsError.update(values, !newValue); - } - }; + @APIOption(name = "allow-incomplete-classpath", deprecated = "Allowing an incomplete classpath is now the default. Use --link-at-build-time to report linking errors at image build time for a class or package.")// + @Option(help = "Deprecated", type = User)// + static final HostedOptionKey AllowIncompleteClasspath = new HostedOptionKey<>(false); @SuppressWarnings("all") private static boolean areAssertionsEnabled() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index 7d1252f684d7..49cf6fd31173 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -174,7 +174,7 @@ public void afterRegistration(AfterRegistrationAccess a) { FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a; imageClassLoader = access.getImageClassLoader(); ImageSingletons.add(ResourcesRegistry.class, - new ResourcesRegistryImpl(new ConfigurationTypeResolver("resource configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue()))); + new ResourcesRegistryImpl(new ConfigurationTypeResolver("resource configuration", imageClassLoader))); } private static ResourcesRegistryImpl resourceRegistryImpl() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index dae4db125f77..5739b20104d3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -123,6 +123,7 @@ public class SVMHost extends HostVM { private final Map> forbiddenTypes; private final Platform platform; private final ClassInitializationSupport classInitializationSupport; + private final LinkAtBuildTimeSupport linkAtBuildTimeSupport; private final HostedStringDeduplication stringTable; private final UnsafeAutomaticSubstitutionProcessor automaticSubstitutions; private final SnippetReflectionProvider originalSnippetReflection; @@ -151,6 +152,7 @@ public SVMHost(OptionValues options, ClassLoader classLoader, ClassInitializatio this.forbiddenTypes = setupForbiddenTypes(options); this.automaticSubstitutions = automaticSubstitutions; this.platform = platform; + this.linkAtBuildTimeSupport = LinkAtBuildTimeSupport.singleton(); } private static Map> setupForbiddenTypes(OptionValues options) { @@ -289,7 +291,8 @@ public boolean isInitialized(AnalysisType type) { @Override public GraphBuilderConfiguration updateGraphBuilderConfiguration(GraphBuilderConfiguration config, AnalysisMethod method) { - return config.withRetainLocalVariables(retainLocalVariables()); + return config.withRetainLocalVariables(retainLocalVariables()) + .withUnresolvedIsError(linkAtBuildTimeSupport.linkAtBuildTime(method.getDeclaringClass())); } private boolean retainLocalVariables() { @@ -389,13 +392,13 @@ private DynamicHub createHub(AnalysisType type) { return dynamicHub; } - private static Object isLocalClass(Class javaClass) { + private Object isLocalClass(Class javaClass) { try { return javaClass.isLocalClass(); } catch (InternalError e) { return e; } catch (LinkageError e) { - if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { + if (!linkAtBuildTimeSupport.linkAtBuildTime(javaClass)) { return e; } else { return unsupportedMethod(javaClass, "isLocalClass"); @@ -407,13 +410,13 @@ private static Object isLocalClass(Class javaClass) { * @return boolean if class is available or LinkageError if class' parents are not on the * classpath or InternalError if the class is invalid. */ - private static Object isAnonymousClass(Class javaClass) { + private Object isAnonymousClass(Class javaClass) { try { return javaClass.isAnonymousClass(); } catch (InternalError e) { return e; } catch (LinkageError e) { - if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { + if (!linkAtBuildTimeSupport.linkAtBuildTime(javaClass)) { return e; } else { return unsupportedMethod(javaClass, "isAnonymousClass"); @@ -421,11 +424,9 @@ private static Object isAnonymousClass(Class javaClass) { } } - private static Object unsupportedMethod(Class javaClass, String methodName) { - String message = "Discovered a type for which " + methodName + " can't be called: " + javaClass.getTypeName() + - ". To avoid this issue at build time use the " + - SubstrateOptionsParser.commandArgument(NativeImageOptions.AllowIncompleteClasspath, "+") + - " option. The LinkageError will then be reported at run time when this method is called for the first time."; + private Object unsupportedMethod(Class javaClass, String methodName) { + String message = "Discovered a type for which " + methodName + " cannot be called: " + javaClass.getTypeName() + ". " + + linkAtBuildTimeSupport.errorMessageFor(javaClass); throw new UnsupportedFeatureException(message); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java index 7555c61554cf..09e3d014b293 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java @@ -50,7 +50,7 @@ import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ImageClassLoader; -import com.oracle.svm.hosted.NativeImageOptions; +import com.oracle.svm.hosted.LinkAtBuildTimeSupport; import jdk.internal.misc.Unsafe; import jdk.vm.ci.meta.MetaAccessProvider; @@ -177,40 +177,40 @@ private InitKind ensureClassInitialized(Class clazz, boolean allowErrors) { Unsafe.getUnsafe().ensureClassInitialized(clazz); return InitKind.BUILD_TIME; } catch (NoClassDefFoundError ex) { - if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { - if (!allowErrors) { - System.out.println("Warning: class initialization of class " + clazz.getTypeName() + " failed with exception " + - ex.getClass().getTypeName() + (ex.getMessage() == null ? "" : ": " + ex.getMessage()) + ". This class will be initialized at run time because option " + - SubstrateOptionsParser.commandArgument(NativeImageOptions.AllowIncompleteClasspath, "+") + " is used for image building. " + - instructionsToInitializeAtRuntime(clazz)); - } + if (allowErrors) { + return InitKind.RUN_TIME; + } else if (!LinkAtBuildTimeSupport.singleton().linkAtBuildTime(clazz)) { + System.out.println("Warning: class initialization of class " + clazz.getTypeName() + " failed with exception " + + ex.getClass().getTypeName() + (ex.getMessage() == null ? "" : ": " + ex.getMessage()) + ". This class will be initialized at run time. " + + instructionsToInitializeAtRuntime(clazz)); return InitKind.RUN_TIME; } else { - return reportInitializationError(allowErrors, clazz, ex); - + return reportInitializationError("Class initialization of " + clazz.getTypeName() + " failed. " + + LinkAtBuildTimeSupport.singleton().errorMessageFor(clazz) + " " + + instructionsToInitializeAtRuntime(clazz), clazz, ex); } } catch (Throwable t) { - return reportInitializationError(allowErrors, clazz, t); + if (allowErrors) { + return InitKind.RUN_TIME; + } else { + return reportInitializationError("Class initialization of " + clazz.getTypeName() + " failed. " + + instructionsToInitializeAtRuntime(clazz), clazz, t); + } } } - private InitKind reportInitializationError(boolean allowErrors, Class clazz, Throwable t) { - if (allowErrors) { + private InitKind reportInitializationError(String msg, Class clazz, Throwable t) { + if (unsupportedFeatures != null) { + /* + * Report an unsupported feature during static analysis, so that we can collect multiple + * error messages without aborting analysis immediately. Returning InitKind.RUN_TIME + * ensures that analysis can continue, even though eventually an error is reported (so + * no image will be created). + */ + unsupportedFeatures.addMessage(clazz.getTypeName(), null, msg, null, t); return InitKind.RUN_TIME; } else { - String msg = String.format("Class initialization of %s failed. %s", clazz.getTypeName(), instructionsToInitializeAtRuntime(clazz)); - if (unsupportedFeatures != null) { - /* - * Report an unsupported feature during static analysis, so that we can collect - * multiple error messages without aborting analysis immediately. Returning - * InitKind.RUN_TIME ensures that analysis can continue, even though eventually an - * error is reported (so no image will be created). - */ - unsupportedFeatures.addMessage(clazz.getTypeName(), null, msg, null, t); - return InitKind.RUN_TIME; - } else { - throw UserError.abort(t, "%s", msg); - } + throw UserError.abort(t, "%s", msg); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java index 944df94ab0e3..731b2a8196b9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java @@ -53,13 +53,12 @@ import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.json.JSONParserException; import com.oracle.svm.hosted.ImageClassLoader; -import com.oracle.svm.hosted.NativeImageOptions; public final class ConfigurationParserUtils { public static ReflectionConfigurationParser>> create(ReflectionRegistry registry, ImageClassLoader imageClassLoader) { return new ReflectionConfigurationParser<>(new ReflectionRegistryAdapter(registry, imageClassLoader), - NativeImageOptions.AllowIncompleteClasspath.getValue(), ConfigurationFiles.Options.StrictConfiguration.getValue()); + ConfigurationFiles.Options.StrictConfiguration.getValue()); } /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java index d23d4fe15bcb..b1ec28b92303 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java @@ -51,12 +51,11 @@ import com.oracle.svm.core.deopt.DeoptimizationSupport; import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.nodes.SubstrateMethodCallTargetNode; -import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.UserError.UserException; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ExceptionSynthesizer; -import com.oracle.svm.hosted.NativeImageOptions; +import com.oracle.svm.hosted.LinkAtBuildTimeSupport; import jdk.vm.ci.meta.JavaField; import jdk.vm.ci.meta.JavaKind; @@ -88,7 +87,7 @@ public abstract static class SharedBytecodeParser extends BytecodeParser { protected SharedBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext, boolean explicitExceptionEdges) { - this(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, explicitExceptionEdges, NativeImageOptions.AllowIncompleteClasspath.getValue()); + this(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, explicitExceptionEdges, !LinkAtBuildTimeSupport.singleton().linkAtBuildTime(method.getDeclaringClass())); } protected SharedBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, @@ -189,9 +188,8 @@ protected void handleIllegalNewInstance(JavaType type) { if (allowIncompleteClassPath) { ExceptionSynthesizer.throwException(this, InstantiationError.class, type.toJavaName()); } else { - String message = "Cannot instantiate " + type.toJavaName() + - ". To diagnose the issue you can use the " + allowIncompleteClassPathOption() + - " option. The instantiation error is then reported at run time."; + String message = "Cannot instantiate " + type.toJavaName() + ". " + + LinkAtBuildTimeSupport.singleton().errorMessageFor(method.getDeclaringClass()); throw new TypeInstantiationException(message); } } @@ -294,17 +292,12 @@ private void handleUnresolvedMethod(JavaMethod javaMethod) { } } - private static void reportUnresolvedElement(String elementKind, String elementAsString) { - String message = "Discovered unresolved " + elementKind + " during parsing: " + elementAsString + - ". To diagnose the issue you can use the " + allowIncompleteClassPathOption() + - " option. The missing " + elementKind + " is then reported at run time when it is accessed the first time."; + private void reportUnresolvedElement(String elementKind, String elementAsString) { + String message = "Discovered unresolved " + elementKind + " during parsing: " + elementAsString + ". " + + LinkAtBuildTimeSupport.singleton().errorMessageFor(method.getDeclaringClass()); throw new UnresolvedElementException(message); } - private static String allowIncompleteClassPathOption() { - return SubstrateOptionsParser.commandArgument(NativeImageOptions.AllowIncompleteClasspath, "+"); - } - @Override protected void emitCheckForInvokeSuperSpecial(ValueNode[] args) { /* Not implemented in SVM (GR-4854) */ diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java index 0038bd48f8f4..488832341a76 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java @@ -40,7 +40,6 @@ import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.ReflectionUtil; @@ -100,11 +99,7 @@ public void addProxyClass(Class... interfaces) { try { clazz = getJdkProxyClass(classLoader, intfs); } catch (Throwable e) { - if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { - return e; - } else { - throw e; - } + return e; } /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java index e25e8d11c795..c265f3d901c4 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java @@ -41,7 +41,6 @@ import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; -import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.reflect.hosted.ReflectionFeature; import com.oracle.svm.reflect.proxy.DynamicProxySupport; @@ -63,7 +62,7 @@ public void duringSetup(DuringSetupAccess a) { ImageClassLoader imageClassLoader = access.getImageClassLoader(); DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(imageClassLoader.getClassLoader()); ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport); - ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("resource configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue()); + ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("resource configuration", imageClassLoader); ProxyRegistry proxyRegistry = new ProxyRegistry(typeResolver, dynamicProxySupport, imageClassLoader); ImageSingletons.add(ProxyRegistry.class, proxyRegistry); ProxyConfigurationParser parser = new ProxyConfigurationParser(proxyRegistry, ConfigurationFiles.Options.StrictConfiguration.getValue()); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java index 90d1f738b42c..3f17f3fcd21e 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java @@ -49,15 +49,6 @@ import java.util.Map; import java.util.Set; -import com.oracle.graal.pointsto.phases.NoClassInitializationPlugin; -import com.oracle.graal.pointsto.util.GraalAccess; -import jdk.vm.ci.hotspot.HotSpotObjectConstant; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.graph.iterators.NodeIterable; import org.graalvm.compiler.java.GraphBuilderPhase; @@ -75,6 +66,8 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; +import com.oracle.graal.pointsto.phases.NoClassInitializationPlugin; +import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.configure.ConfigurationFile; import com.oracle.svm.core.configure.ConfigurationFiles; @@ -88,7 +81,6 @@ import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; -import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.reflect.hosted.ReflectionFeature; import com.oracle.svm.reflect.serialize.SerializationRegistry; @@ -96,6 +88,13 @@ import com.oracle.svm.util.ReflectionUtil; import jdk.internal.reflect.ReflectionFactory; +import jdk.vm.ci.hotspot.HotSpotObjectConstant; +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; @AutomaticFeature public class SerializationFeature implements Feature { @@ -112,7 +111,7 @@ public List> getRequiredFeatures() { public void duringSetup(DuringSetupAccess a) { FeatureImpl.DuringSetupAccessImpl access = (FeatureImpl.DuringSetupAccessImpl) a; ImageClassLoader imageClassLoader = access.getImageClassLoader(); - ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("serialization configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue()); + ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("serialization configuration", imageClassLoader); SerializationDenyRegistry serializationDenyRegistry = new SerializationDenyRegistry(typeResolver); serializationBuilder = new SerializationBuilder(serializationDenyRegistry, access, typeResolver); ImageSingletons.add(RuntimeSerializationSupport.class, serializationBuilder); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java index d9345e90d191..f56e3a90520f 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java @@ -41,9 +41,8 @@ import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.NativeImageOptions; +import com.oracle.svm.hosted.LinkAtBuildTimeSupport; import com.oracle.svm.reflect.hosted.ReflectionObjectReplacer; import jdk.vm.ci.meta.MetaAccessProvider; @@ -253,7 +252,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedReceiverType, receiver); + return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedReceiverType, executable); } } @@ -266,7 +265,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedParameterTypes, receiver); + return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedParameterTypes, executable); } } @@ -279,7 +278,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedReturnType, receiver); + return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedReturnType, executable); } } @@ -292,24 +291,22 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedExceptionTypes, receiver); + return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedExceptionTypes, executable); } } private static final class AnnotatedTypeEncoder { - static Object encodeAnnotationTypes(Supplier supplier, Object receiver) { + static Object encodeAnnotationTypes(Supplier supplier, Executable executable) { try { return supplier.get(); } catch (InternalError e) { return e; } catch (LinkageError e) { - if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { + if (!LinkAtBuildTimeSupport.singleton().linkAtBuildTime(executable.getDeclaringClass())) { return e; } - Executable culprit = (Executable) receiver; - String message = "Encountered an error while processing annotated types for type: " + culprit.getDeclaringClass().getName() + ", executable: " + culprit.getName() + ". " + - "To avoid the issue at build time, use " + SubstrateOptionsParser.commandArgument(NativeImageOptions.AllowIncompleteClasspath, "+") + ". " + - "The error is then reported at runtime when these annotated types are first accessed."; + String message = "Encountered an error while processing annotated types for type: " + executable.getDeclaringClass().getName() + ", executable: " + executable.getName() + ". " + + LinkAtBuildTimeSupport.singleton().errorMessageFor(executable.getDeclaringClass()); throw new UnsupportedOperationException(message, e); } } From e4da0b6ffad66b4dcae41ede501a4e90bd5e8d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 22 Feb 2022 16:29:28 +0100 Subject: [PATCH 21/36] Add info why class is seen as requiring link-at-build-time --- .../oracle/svm/core/option/OptionOrigin.java | 2 +- .../svm/hosted/LinkAtBuildTimeFeature.java | 38 +++++++++++++++---- .../svm/hosted/LinkAtBuildTimeSupport.java | 7 ++-- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index 434b4bfb0d83..71f5a39a4b14 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -95,7 +95,7 @@ protected static URI originURI(String origin) { public static final class CommandLineOptionOrigin extends OptionOrigin { - private static CommandLineOptionOrigin singleton = new CommandLineOptionOrigin(); + public static final CommandLineOptionOrigin singleton = new CommandLineOptionOrigin(); private CommandLineOptionOrigin() { } 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 88ef69f3039d..0886068745bc 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 @@ -28,6 +28,7 @@ import java.lang.module.ModuleFinder; import java.net.URI; import java.nio.file.Path; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -62,6 +63,8 @@ static final class Options { private final String javaIdentifier = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"; private final Pattern validOptionValue = Pattern.compile(javaIdentifier + "(\\." + javaIdentifier + ")*"); + private final Set reasonCommandLine = Collections.singleton(OptionOrigin.CommandLineOptionOrigin.singleton); + private final Map> requireCompletePackageOrClass = new HashMap<>(); private final Set requireCompleteModules = new HashSet<>(); private boolean requireCompleteAll; @@ -121,21 +124,42 @@ private void extractOptionValue(Pair valueOrigin) { } } - boolean requiresCompleteDefinition(Class clazz) { - if (requireCompleteAll || clazz.isArray()) { - return true; + boolean linkAtBuildTime(Class clazz) { + return linkAtBuildTimeImpl(clazz) != null; + } + + @SuppressWarnings("unchecked") + String linkAtBuildTimeReason(Class clazz) { + Object reason = linkAtBuildTimeImpl(clazz); + if (reason == null) { + return null; + } + if (reason instanceof String) { + return (String) reason; + } + Set origins = (Set) reason; + return origins.stream().map(OptionOrigin::toString).collect(Collectors.joining(" and ")); + } + + private Object linkAtBuildTimeImpl(Class clazz) { + if (requireCompleteAll) { + return reasonCommandLine; } - if (!classLoaderSupport.isNativeImageClassLoader(clazz.getClassLoader())) { - return true; + if (clazz.isArray() || !classLoaderSupport.isNativeImageClassLoader(clazz.getClassLoader())) { + return "system default"; } assert !clazz.isPrimitive() : "Primitive classes are not loaded via NativeImageClassLoader"; var module = clazz.getModule(); if (module.isNamed() && (requireCompleteModules.contains(module))) { - return true; + return module.toString(); } - return requireCompletePackageOrClass.containsKey(clazz.getName()) || requireCompletePackageOrClass.containsKey(clazz.getPackageName()); + Set origins = requireCompletePackageOrClass.get(clazz.getName()); + if (origins != null) { + return origins; + } + return requireCompletePackageOrClass.get(clazz.getPackageName()); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeSupport.java index 4312822e1ad4..403ba68be543 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LinkAtBuildTimeSupport.java @@ -56,7 +56,7 @@ public boolean linkAtBuildTime(ResolvedJavaType type) { } public boolean linkAtBuildTime(Class clazz) { - return feature.requiresCompleteDefinition(clazz); + return feature.linkAtBuildTime(clazz); } public String errorMessageFor(ResolvedJavaType type) { @@ -68,8 +68,7 @@ public String errorMessageFor(ResolvedJavaType type) { } public String errorMessageFor(Class clazz) { - return "This error is reported at image build time because class " + clazz.getTypeName() + " is registered for linking at image build time by "; - // TODO add information from locatable option where the class is registered for build time - // initialization. + assert feature.linkAtBuildTime(clazz); + return "This error is reported at image build time because class " + clazz.getTypeName() + " is registered for linking at image build time by " + feature.linkAtBuildTimeReason(clazz); } } From 93183a7da9ae702c13144f5664d02705d8c4a178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 22 Feb 2022 19:07:11 +0100 Subject: [PATCH 22/36] Style fix --- .../src/com/oracle/svm/hosted/SVMHost.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 5739b20104d3..80079be95792 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -292,7 +292,7 @@ public boolean isInitialized(AnalysisType type) { @Override public GraphBuilderConfiguration updateGraphBuilderConfiguration(GraphBuilderConfiguration config, AnalysisMethod method) { return config.withRetainLocalVariables(retainLocalVariables()) - .withUnresolvedIsError(linkAtBuildTimeSupport.linkAtBuildTime(method.getDeclaringClass())); + .withUnresolvedIsError(linkAtBuildTimeSupport.linkAtBuildTime(method.getDeclaringClass())); } private boolean retainLocalVariables() { @@ -426,7 +426,7 @@ private Object isAnonymousClass(Class javaClass) { private Object unsupportedMethod(Class javaClass, String methodName) { String message = "Discovered a type for which " + methodName + " cannot be called: " + javaClass.getTypeName() + ". " + - linkAtBuildTimeSupport.errorMessageFor(javaClass); + linkAtBuildTimeSupport.errorMessageFor(javaClass); throw new UnsupportedFeatureException(message); } From 1c1ef87e3122e4ca2bb9c63348ca4085ed994680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 23 Feb 2022 10:00:10 +0100 Subject: [PATCH 23/36] Ensure LinkAtBuildTimeSupport is already available after afterRegistration --- .../oracle/svm/hosted/LinkAtBuildTimeFeature.java | 12 ++++++++---- ...va => ClassLoaderSupportFeatureJDK11OrLater.java} | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) rename substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/{ClassLoaderSupportImplJDK11OrLater.java => ClassLoaderSupportFeatureJDK11OrLater.java} (97%) 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 0886068745bc..86dc134f47ce 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 @@ -31,6 +31,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; @@ -50,6 +51,7 @@ 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 { @@ -73,15 +75,15 @@ static final class Options { private Map uriModuleMap; @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(LinkAtBuildTimeSupport.class, new LinkAtBuildTimeSupport(this)); + public List> getRequiredFeatures() { + return Collections.singletonList(ClassLoaderSupportFeatureJDK11OrLater.class); } @Override - public void beforeAnalysis(BeforeAnalysisAccess access) { + public void afterRegistration(AfterRegistrationAccess access) { classLoaderSupport = ImageSingletons.lookup(ClassLoaderSupport.class); - var loader = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getImageClassLoader(); + var loader = ((FeatureImpl.AfterRegistrationAccessImpl) access).getImageClassLoader(); uriModuleMap = ModuleFinder.of(loader.applicationModulePath().toArray(Path[]::new)).findAll().stream() .filter(mRef -> mRef.location().isPresent()) .collect(Collectors.toUnmodifiableMap(mRef -> mRef.location().get(), mRef -> loader.findModule(mRef.descriptor().name()).get())); @@ -95,6 +97,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { requireCompletePackageOrClass.put("jdk.internal.reflect", null); Options.LinkAtBuildTime.getValue().getValuesWithOrigins().forEach(this::extractOptionValue); + + ImageSingletons.add(LinkAtBuildTimeSupport.class, new LinkAtBuildTimeSupport(this)); } private void extractOptionValue(Pair valueOrigin) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportImplJDK11OrLater.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java similarity index 97% rename from substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportImplJDK11OrLater.java rename to substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java index ac77b1d2147c..c021061f41f6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportImplJDK11OrLater.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ClassLoaderSupportFeatureJDK11OrLater.java @@ -52,7 +52,7 @@ import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.util.ModuleSupport; -public final class ClassLoaderSupportImplJDK11OrLater extends ClassLoaderSupportImpl { +final class ClassLoaderSupportImplJDK11OrLater extends ClassLoaderSupportImpl { private final NativeImageClassLoaderSupportJDK11OrLater classLoaderSupport; private final Map> packageToModules; @@ -165,7 +165,7 @@ private void addToPackageNameModules(Module moduleName, String packageName) { } @AutomaticFeature -class ClassLoaderSupportFeatureJDK11OrLater implements Feature { +public class ClassLoaderSupportFeatureJDK11OrLater implements Feature { @Override public void afterRegistration(AfterRegistrationAccess a) { FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a; From 0537b3e9a8a53ffe956adfa9ade9bd85ce0d676e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 1 Mar 2022 13:55:24 +0100 Subject: [PATCH 24/36] Ensure all images of a GraalVM build require --link-at-build-time --- sdk/mx.sdk/mx_sdk_vm_impl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/mx.sdk/mx_sdk_vm_impl.py b/sdk/mx.sdk/mx_sdk_vm_impl.py index 41c32ec8243e..ce909d3815a0 100644 --- a/sdk/mx.sdk/mx_sdk_vm_impl.py +++ b/sdk/mx.sdk/mx_sdk_vm_impl.py @@ -1247,6 +1247,7 @@ def contents(self): image_config = self.subject.image_config build_args = [ '--no-fallback', + '--link-at-build-time', '--initialize-at-build-time=org,com,net,jdk,javax,java,sun,apple', '-H:+AssertInitializationSpecifiedForAllClasses', '-H:+EnforceMaxRuntimeCompileMethods', From 23815188c35543be1f2b64b587b0aeeddf53ed0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 1 Mar 2022 14:42:17 +0100 Subject: [PATCH 25/36] Document OptionOrigin.getRedirectionValues --- .../src/com/oracle/svm/core/option/OptionOrigin.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index 71f5a39a4b14..22666a43ae2e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -53,6 +53,11 @@ public boolean commandLineLike() { return false; } + /** + * Return the option values contained in the redirection file specified by the given path. + * Depending on the specific kind of OptionOrigin the method to retrieve those values can + * require different implementations. + */ public List getRedirectionValues(@SuppressWarnings("unused") Path valuesFile) throws IOException { throw new IOException(new UnsupportedOperationException()); } From 6de6a9f9a9b7c779b1a7256ba86ca2d14194c3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 1 Mar 2022 14:42:45 +0100 Subject: [PATCH 26/36] Rename resolveOptionValueRedirectionFlatMap --- .../src/com/oracle/svm/core/option/OptionUtils.java | 6 +++--- .../src/com/oracle/svm/hosted/LinkAtBuildTimeFeature.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java index a7e12b9511f5..f446c89d1e27 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionUtils.java @@ -75,13 +75,13 @@ public static List flatten(String delimiter, List values) { return result; } - public static List resolveOptionValueRedirection(OptionKey option, String optionValue, OptionOrigin origin) { + public static List resolveOptionValuesRedirection(OptionKey option, String optionValue, OptionOrigin origin) { return Arrays.asList(SubstrateUtil.split(optionValue, ",")).stream() - .flatMap(entry -> resolveOptionValueRedirectionFlatMap(option, optionValue, origin, entry)) + .flatMap(entry -> resolveOptionValueRedirection(option, optionValue, origin, entry)) .collect(Collectors.toList()); } - private static Stream resolveOptionValueRedirectionFlatMap(OptionKey option, String optionValue, OptionOrigin origin, String entry) { + private static Stream resolveOptionValueRedirection(OptionKey option, String optionValue, OptionOrigin origin, String entry) { if (entry.trim().startsWith("@")) { Path valuesFile = Path.of(entry.substring(1)); if (valuesFile.isAbsolute()) { 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 86dc134f47ce..58b81e13dcd0 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 @@ -117,7 +117,7 @@ private void extractOptionValue(Pair valueOrigin) { throw UserError.abort("Using '%s' without args only allowed on module-path. %s not part of module-path.", SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, value), origin); } else { - for (String entry : OptionUtils.resolveOptionValueRedirection(Options.LinkAtBuildTime, value, origin)) { + for (String entry : OptionUtils.resolveOptionValuesRedirection(Options.LinkAtBuildTime, value, origin)) { if (validOptionValue.matcher(entry).matches()) { requireCompletePackageOrClass.computeIfAbsent(entry, unused -> new HashSet<>()).add(origin); } else { From 45a8a136db6c645f720e6a622e43e2a1b480873d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 1 Mar 2022 16:38:21 +0100 Subject: [PATCH 27/36] Allow individual launchers to opt-out of --link-at-build-time --- sdk/mx.sdk/mx_sdk_vm.py | 3 ++- sdk/mx.sdk/mx_sdk_vm_impl.py | 3 ++- vm/mx.vm/mx_vm.py | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/mx.sdk/mx_sdk_vm.py b/sdk/mx.sdk/mx_sdk_vm.py index 54c5aa5534b9..a9c95545fbd1 100644 --- a/sdk/mx.sdk/mx_sdk_vm.py +++ b/sdk/mx.sdk/mx_sdk_vm.py @@ -159,7 +159,7 @@ def add_relative_home_path(self, language, path): class LauncherConfig(AbstractNativeImageConfig): def __init__(self, destination, jar_distributions, main_class, build_args, is_main_launcher=True, default_symlinks=True, is_sdk_launcher=False, custom_launcher_script=None, extra_jvm_args=None, - use_modules=None, main_module=None, option_vars=None, home_finder=True, **kwargs): + use_modules=None, main_module=None, link_at_build_time=True, option_vars=None, home_finder=True, **kwargs): """ :param str main_class :param bool is_main_launcher @@ -174,6 +174,7 @@ def __init__(self, destination, jar_distributions, main_class, build_args, is_ma self.main_module = main_module assert self.use_modules is None or self.main_module self.main_class = main_class + self.link_at_build_time = link_at_build_time self.is_main_launcher = is_main_launcher self.default_symlinks = default_symlinks self.is_sdk_launcher = is_sdk_launcher diff --git a/sdk/mx.sdk/mx_sdk_vm_impl.py b/sdk/mx.sdk/mx_sdk_vm_impl.py index ce909d3815a0..c1ffd29fd85d 100644 --- a/sdk/mx.sdk/mx_sdk_vm_impl.py +++ b/sdk/mx.sdk/mx_sdk_vm_impl.py @@ -1247,7 +1247,6 @@ def contents(self): image_config = self.subject.image_config build_args = [ '--no-fallback', - '--link-at-build-time', '--initialize-at-build-time=org,com,net,jdk,javax,java,sun,apple', '-H:+AssertInitializationSpecifiedForAllClasses', '-H:+EnforceMaxRuntimeCompileMethods', @@ -1257,6 +1256,8 @@ def contents(self): build_args += ['-ea', '-H:-AOTInline', '-H:+PreserveFramePointer', '-H:-DeleteLocalSymbols'] if _get_svm_support().is_debug_supported(): build_args += ['-g'] + if getattr(image_config, 'link_at_build_time', True): + build_args += ['--link-at-build-time'] graalvm_dist = get_final_graalvm_distribution() graalvm_location = dirname(graalvm_dist.find_single_source_location('dependency:' + self.subject.name)) diff --git a/vm/mx.vm/mx_vm.py b/vm/mx.vm/mx_vm.py index d7082f2c0007..ffe6dbfcb854 100644 --- a/vm/mx.vm/mx_vm.py +++ b/vm/mx.vm/mx_vm.py @@ -64,6 +64,7 @@ ], dir_jars=True, main_class="org.graalvm.component.installer.ComponentInstaller", + link_at_build_time=False, build_args=[], # Please see META-INF/native-image in the project for custom build options for native-image is_sdk_launcher=True, From a0d139d65f96ee8b32f685d293b30321c8e7a6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 2 Mar 2022 16:42:29 +0100 Subject: [PATCH 28/36] Implement strict checking of --link-at-build-time arguments --- .../svm-junit.packages | 0 ...AbstractNativeImageClassLoaderSupport.java | 41 ++++++++++++++++++- .../oracle/svm/hosted/ImageClassLoader.java | 9 ++++ .../svm/hosted/LinkAtBuildTimeFeature.java | 16 +++++--- ...veImageClassLoaderSupportJDK11OrLater.java | 2 +- 5 files changed, 60 insertions(+), 8 deletions(-) rename substratevm/{src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit => mx.substratevm}/svm-junit.packages (100%) diff --git a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages b/substratevm/mx.substratevm/svm-junit.packages similarity index 100% rename from substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/svm-junit.packages rename to substratevm/mx.substratevm/svm-junit.packages 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 index 61ed4a9159c3..1f11f26d185e 100644 --- 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 @@ -55,6 +55,8 @@ 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; @@ -68,11 +70,18 @@ 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 = Collections.unmodifiableList(Arrays.stream(classPathClassLoader.getURLs()).map(Util::urlToPath).collect(Collectors.toList())); @@ -132,6 +141,14 @@ 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); + } + protected abstract void processClassLoaderOptions(); public abstract void propagateQualifiedExports(String fromTargetModule, String toTargetModule); @@ -252,7 +269,7 @@ 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(null, fileName, fileSystemSeparatorChar)); + executor.execute(() -> handleClassFileName(root.toUri(), null, fileName, fileSystemSeparatorChar)); } return FileVisitResult.CONTINUE; } @@ -295,12 +312,32 @@ private String strippedClassFileName(String fileName) { return result.substring(0, result.length() - CLASS_EXTENSION.length()); } - protected void handleClassFileName(Object module, String fileName, char fileSystemSeparatorChar) { + protected void handleClassFileName(URI location, 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(location); + if (classNames == null) { + classNames = EconomicSet.create(); + classes.put(location, classNames); + } + classNames.add(className); + } + int packageSep = className.lastIndexOf('.'); + String packageName = packageSep > 0 ? className.substring(0, packageSep) : ""; + synchronized (packages) { + EconomicSet packageNames = packages.get(location); + if (packageNames == null) { + packageNames = EconomicSet.create(); + packages.put(location, packageNames); + } + packageNames.add(packageName); + } + Class clazz = null; try { clazz = imageClassLoader.forName(className, module); 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 9392c38b1b9e..0c4f9e15e30e 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 @@ -33,6 +33,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.net.URI; import java.net.URL; import java.nio.file.Path; import java.util.ArrayList; @@ -443,4 +444,12 @@ public String getMainClassNotFoundErrorMessage(String className) { private static String pathsToString(List paths) { return paths.stream().map(n -> String.valueOf(n)).collect(Collectors.joining(File.pathSeparator)); } + + public EconomicSet classes(URI container) { + return classLoaderSupport.classes(container); + } + + public EconomicSet packages(URI container) { + return classLoaderSupport.packages(container); + } } 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 58b81e13dcd0..cf1a2b7c7215 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 @@ -72,6 +72,7 @@ static final class Options { private boolean requireCompleteAll; private ClassLoaderSupport classLoaderSupport; + private ImageClassLoader imageClassLoader; private Map uriModuleMap; @Override @@ -83,10 +84,10 @@ public List> getRequiredFeatures() { public void afterRegistration(AfterRegistrationAccess access) { classLoaderSupport = ImageSingletons.lookup(ClassLoaderSupport.class); - var loader = ((FeatureImpl.AfterRegistrationAccessImpl) access).getImageClassLoader(); - uriModuleMap = ModuleFinder.of(loader.applicationModulePath().toArray(Path[]::new)).findAll().stream() + imageClassLoader = ((FeatureImpl.AfterRegistrationAccessImpl) access).getImageClassLoader(); + uriModuleMap = ModuleFinder.of(imageClassLoader.applicationModulePath().toArray(Path[]::new)).findAll().stream() .filter(mRef -> mRef.location().isPresent()) - .collect(Collectors.toUnmodifiableMap(mRef -> mRef.location().get(), mRef -> loader.findModule(mRef.descriptor().name()).get())); + .collect(Collectors.toUnmodifiableMap(mRef -> mRef.location().get(), mRef -> imageClassLoader.findModule(mRef.descriptor().name()).get())); /* * SerializationBuilder.newConstructorForSerialization() creates synthetic @@ -104,12 +105,13 @@ public void afterRegistration(AfterRegistrationAccess access) { private void extractOptionValue(Pair valueOrigin) { var value = valueOrigin.getLeft(); OptionOrigin origin = valueOrigin.getRight(); + URI container = origin.container(); if (value.isEmpty()) { if (origin.commandLineLike()) { requireCompleteAll = true; return; } - var originModule = uriModuleMap.get(origin.container()); + var originModule = uriModuleMap.get(container); if (originModule != null) { requireCompleteModules.add(originModule); return; @@ -119,9 +121,13 @@ private void extractOptionValue(Pair valueOrigin) { } else { for (String entry : OptionUtils.resolveOptionValuesRedirection(Options.LinkAtBuildTime, value, origin)) { if (validOptionValue.matcher(entry).matches()) { + if (!origin.commandLineLike() && !imageClassLoader.classes(container).contains(entry) && !imageClassLoader.packages(container).contains(entry)) { + throw UserError.abort("Option '%s' provided by %s contains '%s'. No such package or class name found in '%s'.", + SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, value), origin, entry, container); + } requireCompletePackageOrClass.computeIfAbsent(entry, unused -> new HashSet<>()).add(origin); } else { - throw UserError.abort("Entry '%s' in option '%s' provided by '%s' is neither a package nor a fully qualified classname.", + throw UserError.abort("Entry '%s' in option '%s' provided by %s is neither a package nor a fully qualified classname.", entry, SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTime, value), origin); } } 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 index 111835a98fc6..c774451ca8af 100644 --- 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 @@ -430,7 +430,7 @@ private void initModule(ModuleReference moduleReference) { Module module = optionalModule.get(); moduleReader.list().forEach(moduleResource -> { if (moduleResource.endsWith(CLASS_EXTENSION)) { - executor.execute(() -> handleClassFileName(module, moduleResource, '/')); + executor.execute(() -> handleClassFileName(moduleReference.location().get(), module, moduleResource, '/')); } }); } catch (IOException e) { From bf1b83a084f9623862dbc3ca2462ed60777efb58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 3 Mar 2022 10:47:08 +0100 Subject: [PATCH 29/36] Add support for @ use in macro-options --- .../oracle/svm/core/option/OptionOrigin.java | 42 +++++++++++++------ .../oracle/svm/driver/MacroOptionHandler.java | 3 +- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index 22666a43ae2e..e54989b1aacf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -130,10 +130,13 @@ public static final class MacroOptionOrigin extends OptionOrigin { public final OptionUtils.MacroOptionKind kind; public final String name; + public final Path optionDirectory; - private MacroOptionOrigin(OptionUtils.MacroOptionKind kind, String name) { + private MacroOptionOrigin(OptionUtils.MacroOptionKind kind, String name, URI optionDirectory) { this.kind = kind; this.name = name; + VMError.guarantee(optionDirectory != null, "Invalid optionDirectory origin"); + this.optionDirectory = Path.of(optionDirectory); } @Override @@ -155,7 +158,15 @@ public static MacroOptionOrigin from(String rawOrigin) { for (OptionUtils.MacroOptionKind kind : OptionUtils.MacroOptionKind.values()) { String prefix = kind.getDescriptionPrefix(true); if (rawOrigin.startsWith(prefix)) { - return new MacroOptionOrigin(kind, rawOrigin.substring(prefix.length())); + int optionDirectorySep = rawOrigin.indexOf('@'); + VMError.guarantee(optionDirectorySep > 0, "Missing macro-option optionDirectory origin"); + String optionDirectory = rawOrigin.substring(optionDirectorySep + 1); + int argumentOriginSep = optionDirectory.indexOf('@'); + if (argumentOriginSep > 0) { + /* Strip optional trailing argumentOrigin */ + optionDirectory = optionDirectory.substring(0, argumentOriginSep); + } + return new MacroOptionOrigin(kind, rawOrigin.substring(prefix.length()), originURI(optionDirectory)); } } return null; @@ -170,6 +181,12 @@ public boolean commandLineLike() { public String toString() { return kind + " option '" + name + "'"; } + + @Override + public List getRedirectionValues(Path valuesFile) throws IOException { + var normalizedRedirPath = optionDirectory.resolve(valuesFile).normalize(); + return getRedirectionValuesFromPath(normalizedRedirPath); + } } protected abstract static class URIOptionOrigin extends OptionOrigin { @@ -219,6 +236,7 @@ protected JarOptionOrigin(URI rawOrigin) { location = Path.of(specific.substring(sep + 2)); } + @SuppressWarnings("try") @Override public List getRedirectionValues(Path valuesFile) throws IOException { URI jarFileURI = URI.create("jar:" + container()); @@ -231,13 +249,9 @@ public List getRedirectionValues(Path valuesFile) throws IOException { if (probeJarFS == null) { throw new IOException("Unable to create jar file system for " + jarFileURI); } - try (FileSystem jarFS = probeJarFS) { + try (FileSystem fs = probeJarFS) { var normalizedRedirPath = location().getParent().resolve(valuesFile).normalize(); - var pathInJarFS = jarFS.getPath(normalizedRedirPath.toString()); - if (Files.isReadable(pathInJarFS)) { - return Files.readAllLines(pathInJarFS); - } - throw new FileNotFoundException("Unable to read " + pathInJarFS + " from jar file system " + jarFS); + return getRedirectionValuesFromPath(normalizedRedirPath); } } } @@ -261,10 +275,14 @@ protected DirectoryOptionOrigin(Path originPath) { @Override public List getRedirectionValues(Path valuesFile) throws IOException { var normalizedRedirPath = Path.of(container()).resolve(location()).getParent().resolve(valuesFile).normalize(); - if (Files.isReadable(normalizedRedirPath)) { - return Files.readAllLines(normalizedRedirPath); - } - throw new FileNotFoundException("Unable to read file from " + normalizedRedirPath); + return getRedirectionValuesFromPath(normalizedRedirPath); + } + } + + private static List getRedirectionValuesFromPath(Path normalizedRedirPath) throws IOException { + if (Files.isReadable(normalizedRedirPath)) { + return Files.readAllLines(normalizedRedirPath); } + throw new FileNotFoundException("Unable to read file from " + normalizedRedirPath.toUri()); } } diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java index 68f93bdb510c..d38cd4f2f8af 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java @@ -116,8 +116,9 @@ private void applyEnabled(MacroOption.EnabledOption enabledOption, String argume enabledOption.forEachPropertyValue(config, "JavaArgs", nativeImage::addImageBuilderJavaArgs); String origin = enabledOption.getOption().getDescription(true); + origin += "@" + enabledOption.getOption().getOptionDirectory().toUri(); if (argumentOrigin != null) { - origin += " from " + argumentOrigin; + origin += "@" + argumentOrigin; } NativeImage.NativeImageArgsProcessor args = nativeImage.new NativeImageArgsProcessor(origin); enabledOption.forEachPropertyValue(config, "Args", args); From ed4525dbc7d663303910286b853694429223319f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 3 Mar 2022 10:49:41 +0100 Subject: [PATCH 30/36] Move @-use from c.o.s.junit native-image.properties to junit macro-option --- substratevm/mx.substratevm/macro-junit.properties | 3 ++- substratevm/mx.substratevm/macro-junitcp.properties | 3 ++- substratevm/mx.substratevm/suite.py | 2 ++ substratevm/mx.substratevm/svm-junit.packages | 1 - .../com.oracle.svm.junit/native-image.properties | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/substratevm/mx.substratevm/macro-junit.properties b/substratevm/mx.substratevm/macro-junit.properties index 6f802d0e00a0..dec7f4d637fe 100644 --- a/substratevm/mx.substratevm/macro-junit.properties +++ b/substratevm/mx.substratevm/macro-junit.properties @@ -8,4 +8,5 @@ ImageClasspath = ${.}/junit-tool.jar:${.}/junit.jar:${.}/hamcrest.jar Args = -H:Features=com.oracle.svm.junit.JUnitFeature \ -H:Class=com.oracle.svm.junit.SVMJUnitRunner \ -H:TestFile=${*} \ - --initialize-at-build-time=org.junit,com.oracle.mxtool.junit.MxJUnitRequest + --initialize-at-build-time=org.junit,com.oracle.mxtool.junit.MxJUnitRequest \ + --link-at-build-time=@svm-junit.packages \ No newline at end of file diff --git a/substratevm/mx.substratevm/macro-junitcp.properties b/substratevm/mx.substratevm/macro-junitcp.properties index 3fb002782ec9..b5ed54d7cf28 100644 --- a/substratevm/mx.substratevm/macro-junitcp.properties +++ b/substratevm/mx.substratevm/macro-junitcp.properties @@ -7,4 +7,5 @@ ImageClasspath = ${.}/junit-support.jar:${.}/junit-tool.jar:${.}/junit.jar:${.}/ Args = -H:Features=com.oracle.svm.junit.JUnitFeature \ -H:Class=com.oracle.svm.junit.SVMJUnitRunner \ -H:TestFile=${*} \ - --initialize-at-build-time=org.junit,com.oracle.mxtool.junit.MxJUnitRequest + --initialize-at-build-time=org.junit,com.oracle.mxtool.junit.MxJUnitRequest \ + --link-at-build-time=@svm-junit.packages \ No newline at end of file diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index f35a483e8e35..dde0dd16cf7b 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -1603,6 +1603,7 @@ "description" : "Native-image based junit testing support", "layout" : { "native-image.properties" : "file:mx.substratevm/macro-junit.properties", + "svm-junit.packages" : "file:mx.substratevm/svm-junit.packages", }, }, @@ -1611,6 +1612,7 @@ "description" : "Native-image based junit testing support but with running image-builder on classpath", "layout" : { "native-image.properties" : "file:mx.substratevm/macro-junitcp.properties", + "svm-junit.packages" : "file:mx.substratevm/svm-junit.packages", }, }, diff --git a/substratevm/mx.substratevm/svm-junit.packages b/substratevm/mx.substratevm/svm-junit.packages index 4fec0f61dd0b..1da0ff3a3cd6 100644 --- a/substratevm/mx.substratevm/svm-junit.packages +++ b/substratevm/mx.substratevm/svm-junit.packages @@ -1,5 +1,4 @@ com.oracle.mxtool.junit -com.oracle.svm.junit junit.framework junit.runner org.hamcrest diff --git a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties index 5bc72b062925..d06f997c45b8 100644 --- a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties +++ b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties @@ -1,2 +1,2 @@ Args = --add-reads=org.graalvm.nativeimage.junitsupport=ALL-UNNAMED \ - --link-at-build-time=@svm-junit.packages + --link-at-build-time=com.oracle.svm.junit \ No newline at end of file From 29ad09555f2d37cecf9db207dbbd0476639d307c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 3 Mar 2022 12:18:28 +0100 Subject: [PATCH 31/36] Change SharedBytecodeParser allowIncompleteClassPath to linkAtBuildTime and adjust comments --- .../ClassInitializerGraphBuilderPhase.java | 2 +- .../phases/SharedGraphBuilderPhase.java | 48 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializerGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializerGraphBuilderPhase.java index d69f1a86881e..ef3f598940db 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializerGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializerGraphBuilderPhase.java @@ -54,7 +54,7 @@ protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodePar static class ClassInitializerBytecodeParser extends SharedBytecodeParser { ClassInitializerBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) { - super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, true, true); + super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, true, false); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java index b1ec28b92303..ee291935020d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java @@ -83,18 +83,18 @@ protected void run(StructuredGraph graph) { public abstract static class SharedBytecodeParser extends BytecodeParser { private final boolean explicitExceptionEdges; - private final boolean allowIncompleteClassPath; + private final boolean linkAtBuildTime; protected SharedBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext, boolean explicitExceptionEdges) { - this(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, explicitExceptionEdges, !LinkAtBuildTimeSupport.singleton().linkAtBuildTime(method.getDeclaringClass())); + this(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, explicitExceptionEdges, LinkAtBuildTimeSupport.singleton().linkAtBuildTime(method.getDeclaringClass())); } protected SharedBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, - IntrinsicContext intrinsicContext, boolean explicitExceptionEdges, boolean allowIncompleteClasspath) { + IntrinsicContext intrinsicContext, boolean explicitExceptionEdges, boolean linkAtBuildTime) { super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext); this.explicitExceptionEdges = explicitExceptionEdges; - this.allowIncompleteClassPath = allowIncompleteClasspath; + this.linkAtBuildTime = linkAtBuildTime; } @Override @@ -182,15 +182,15 @@ protected JavaType maybeEagerlyResolve(JavaType type, ResolvedJavaType accessing @Override protected void handleIllegalNewInstance(JavaType type) { /* - * If --allow-incomplete-classpath is set defer the error reporting to runtime, - * otherwise report the error during image building. + * If linkAtBuildTime was set for type, report the error during image building, + * otherwise defer the error reporting to runtime. */ - if (allowIncompleteClassPath) { - ExceptionSynthesizer.throwException(this, InstantiationError.class, type.toJavaName()); - } else { + if (linkAtBuildTime) { String message = "Cannot instantiate " + type.toJavaName() + ". " + LinkAtBuildTimeSupport.singleton().errorMessageFor(method.getDeclaringClass()); throw new TypeInstantiationException(message); + } else { + ExceptionSynthesizer.throwException(this, InstantiationError.class, type.toJavaName()); } } @@ -246,13 +246,13 @@ protected void handleUnresolvedInvoke(JavaMethod javaMethod, InvokeKind invokeKi private void handleUnresolvedType(JavaType type) { /* - * If --allow-incomplete-classpath is set defer the error reporting to runtime, - * otherwise report the error during image building. + * If linkAtBuildTime was set for type, report the error during image building, + * otherwise defer the error reporting to runtime. */ - if (allowIncompleteClassPath) { - ExceptionSynthesizer.throwException(this, NoClassDefFoundError.class, type.toJavaName()); - } else { + if (linkAtBuildTime) { reportUnresolvedElement("type", type.toJavaName()); + } else { + ExceptionSynthesizer.throwException(this, NoClassDefFoundError.class, type.toJavaName()); } } @@ -263,13 +263,13 @@ private void handleUnresolvedField(JavaField field) { handleUnresolvedType(declaringClass); } else { /* - * If --allow-incomplete-classpath is set defer the error reporting to runtime, - * otherwise report the error during image building. + * If linkAtBuildTime was set for type, report the error during image building, + * otherwise defer the error reporting to runtime. */ - if (allowIncompleteClassPath) { - ExceptionSynthesizer.throwException(this, NoSuchFieldError.class, field.format("%H.%n")); - } else { + if (linkAtBuildTime) { reportUnresolvedElement("field", field.format("%H.%n")); + } else { + ExceptionSynthesizer.throwException(this, NoSuchFieldError.class, field.format("%H.%n")); } } } @@ -281,13 +281,13 @@ private void handleUnresolvedMethod(JavaMethod javaMethod) { handleUnresolvedType(declaringClass); } else { /* - * If --allow-incomplete-classpath is set defer the error reporting to runtime, - * otherwise report the error during image building. + * If linkAtBuildTime was set for type, report the error during image building, + * otherwise defer the error reporting to runtime. */ - if (allowIncompleteClassPath) { - ExceptionSynthesizer.throwException(this, NoSuchMethodError.class, javaMethod.format("%H.%n(%P)")); - } else { + if (linkAtBuildTime) { reportUnresolvedElement("method", javaMethod.format("%H.%n(%P)")); + } else { + ExceptionSynthesizer.throwException(this, NoSuchMethodError.class, javaMethod.format("%H.%n(%P)")); } } } From 4dd15406730b480bab93092b5247345ccc79bdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 3 Mar 2022 16:36:31 +0100 Subject: [PATCH 32/36] Fix strict checking of --link-at-build-time arguments for jar-files on classpath --- ...AbstractNativeImageClassLoaderSupport.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) 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 index 1f11f26d185e..9bb9e12ea135 100644 --- 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 @@ -227,7 +227,8 @@ private Set getExcludeDirectories() { private void loadClassesFromPath(Path path) { if (ClasspathUtils.isJar(path)) { try { - URI jarURI = new URI("jar:" + path.toAbsolutePath().toUri()); + URI container = path.toAbsolutePath().toUri(); + URI jarURI = new URI("jar:" + container); FileSystem probeJarFileSystem; try { probeJarFileSystem = FileSystems.newFileSystem(jarURI, Collections.emptyMap()); @@ -237,7 +238,7 @@ private void loadClassesFromPath(Path path) { } if (probeJarFileSystem != null) { try (FileSystem jarFileSystem = probeJarFileSystem) { - loadClassesFromPath(jarFileSystem.getPath("/"), Collections.emptySet()); + loadClassesFromPath(container, jarFileSystem.getPath("/"), Collections.emptySet()); } } } catch (ClosedByInterruptException ignored) { @@ -246,13 +247,14 @@ private void loadClassesFromPath(Path path) { throw shouldNotReachHere(e); } } else { - loadClassesFromPath(path, excludeDirectories); + URI container = path.toUri(); + loadClassesFromPath(container, path, excludeDirectories); } } protected static final String CLASS_EXTENSION = ".class"; - private void loadClassesFromPath(Path root, Set excludes) { + private void loadClassesFromPath(URI container, Path root, Set excludes) { FileVisitor visitor = new SimpleFileVisitor<>() { private final char fileSystemSeparatorChar = root.getFileSystem().getSeparator().charAt(0); @@ -269,7 +271,7 @@ 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(root.toUri(), null, fileName, fileSystemSeparatorChar)); + executor.execute(() -> handleClassFileName(container, null, fileName, fileSystemSeparatorChar)); } return FileVisitResult.CONTINUE; } @@ -312,7 +314,7 @@ private String strippedClassFileName(String fileName) { return result.substring(0, result.length() - CLASS_EXTENSION.length()); } - protected void handleClassFileName(URI location, Object module, String fileName, char fileSystemSeparatorChar) { + protected void handleClassFileName(URI container, Object module, String fileName, char fileSystemSeparatorChar) { String strippedClassFileName = strippedClassFileName(fileName); if (strippedClassFileName.equals("module-info")) { return; @@ -320,20 +322,20 @@ protected void handleClassFileName(URI location, Object module, String fileName, String className = strippedClassFileName.replace(fileSystemSeparatorChar, '.'); synchronized (classes) { - EconomicSet classNames = classes.get(location); + EconomicSet classNames = classes.get(container); if (classNames == null) { classNames = EconomicSet.create(); - classes.put(location, classNames); + 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(location); + EconomicSet packageNames = packages.get(container); if (packageNames == null) { packageNames = EconomicSet.create(); - packages.put(location, packageNames); + packages.put(container, packageNames); } packageNames.add(packageName); } From 4e330cd2128b6c273be49c2d8c47d5449d41170f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Mon, 7 Mar 2022 13:08:50 +0100 Subject: [PATCH 33/36] Implement --link-at-build-time-paths --- ...AbstractNativeImageClassLoaderSupport.java | 4 +++ .../oracle/svm/hosted/ImageClassLoader.java | 4 +++ .../svm/hosted/LinkAtBuildTimeFeature.java | 36 +++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) 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 index 9bb9e12ea135..ef6c26e20125 100644 --- 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 @@ -149,6 +149,10 @@ 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); 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 0c4f9e15e30e..98c3c6c6d97e 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 @@ -452,4 +452,8 @@ public EconomicSet classes(URI container) { public EconomicSet packages(URI container) { return classLoaderSupport.packages(container); } + + public boolean noEntryForURI(EconomicSet set) { + return classLoaderSupport.noEntryForURI(set); + } } 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 cf1a2b7c7215..9604245a1426 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 @@ -25,6 +25,7 @@ package com.oracle.svm.hosted; +import java.io.File; import java.lang.module.ModuleFinder; import java.net.URI; import java.nio.file.Path; @@ -37,12 +38,14 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.graalvm.collections.EconomicSet; import org.graalvm.collections.Pair; import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.ClassLoaderSupport; +import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.option.APIOption; import com.oracle.svm.core.option.HostedOptionKey; @@ -60,6 +63,10 @@ static final class Options { @APIOption(name = "link-at-build-time", defaultValue = "")// @Option(help = "Require types to be fully defined at image build-time. If used without args, all classes in scope of the option are required to be fully defined.")// public static final HostedOptionKey LinkAtBuildTime = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); + + @APIOption(name = "link-at-build-time-paths")// + @Option(help = "Require all types in given class or module-path entries to be fully defined at image build-time.")// + public static final HostedOptionKey LinkAtBuildTimePaths = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); } private final String javaIdentifier = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"; @@ -97,12 +104,13 @@ public void afterRegistration(AfterRegistrationAccess access) { */ requireCompletePackageOrClass.put("jdk.internal.reflect", null); - Options.LinkAtBuildTime.getValue().getValuesWithOrigins().forEach(this::extractOptionValue); + Options.LinkAtBuildTime.getValue().getValuesWithOrigins().forEach(this::extractLinkAtBuildTimeOptionValue); + Options.LinkAtBuildTimePaths.getValue().getValuesWithOrigins().forEach(this::extractLinkAtBuildTimePathsOptionValue); ImageSingletons.add(LinkAtBuildTimeSupport.class, new LinkAtBuildTimeSupport(this)); } - private void extractOptionValue(Pair valueOrigin) { + private void extractLinkAtBuildTimeOptionValue(Pair valueOrigin) { var value = valueOrigin.getLeft(); OptionOrigin origin = valueOrigin.getRight(); URI container = origin.container(); @@ -134,6 +142,30 @@ private void extractOptionValue(Pair valueOrigin) { } } + private void extractLinkAtBuildTimePathsOptionValue(Pair valueOrigin) { + var value = valueOrigin.getLeft(); + OptionOrigin origin = valueOrigin.getRight(); + if (!origin.commandLineLike()) { + throw UserError.abort("Using '%s' is only allowed on command line.", + SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTimePaths, value), origin); + } + if (value.isEmpty()) { + throw UserError.abort("Using '%s' requires directory or jar-file path arguments.", + SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTimePaths, value), origin); + } + for (String pathStr : SubstrateUtil.split(value, File.pathSeparator)) { + Path path = Path.of(pathStr); + EconomicSet packages = imageClassLoader.packages(path.toAbsolutePath().normalize().toUri()); + if (imageClassLoader.noEntryForURI(packages)) { + throw UserError.abort("Option '%s' provided by %s contains entry '%s'. No such entry exists on class or module-path.", + SubstrateOptionsParser.commandArgument(Options.LinkAtBuildTimePaths, value), origin, pathStr); + } + for (String pkg : packages) { + requireCompletePackageOrClass.put(pkg, Collections.singleton(origin)); + } + } + } + boolean linkAtBuildTime(Class clazz) { return linkAtBuildTimeImpl(clazz) != null; } From 7838db3d14c4f9b1006aa76231a179c630857707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Mon, 7 Mar 2022 14:14:10 +0100 Subject: [PATCH 34/36] Fully document --link-at-build-time and --link-at-build-time-paths --- .../svm/hosted/LinkAtBuildTimeFeature.java | 4 ++-- .../hosted/doc-files/LinkAtBuildTimeHelp.txt | 19 +++++++++++++++++++ .../doc-files/LinkAtBuildTimePathsHelp.txt | 11 +++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LinkAtBuildTimeHelp.txt create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LinkAtBuildTimePathsHelp.txt 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 9604245a1426..51aae4b1a226 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 @@ -61,11 +61,11 @@ public final class LinkAtBuildTimeFeature implements Feature { static final class Options { @APIOption(name = "link-at-build-time", defaultValue = "")// - @Option(help = "Require types to be fully defined at image build-time. If used without args, all classes in scope of the option are required to be fully defined.")// + @Option(help = "file:doc-files/LinkAtBuildTimeHelp.txt")// public static final HostedOptionKey LinkAtBuildTime = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); @APIOption(name = "link-at-build-time-paths")// - @Option(help = "Require all types in given class or module-path entries to be fully defined at image build-time.")// + @Option(help = "file:doc-files/LinkAtBuildTimePathsHelp.txt")// public static final HostedOptionKey LinkAtBuildTimePaths = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LinkAtBuildTimeHelp.txt b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LinkAtBuildTimeHelp.txt new file mode 100644 index 000000000000..2ed511ce962a --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LinkAtBuildTimeHelp.txt @@ -0,0 +1,19 @@ +Require types to be fully defined at image build-time. If used without args, all classes in scope of the option are required to be fully defined. + +Using --link-at-build-time without arguments is only allowed on command line or when embedded in a +native-image.properties file of some zip/jar file on the module-path (but not on class-path). + +In the module path case, the option will cause all classes of the module to be required to be +fully defined at image build-time. If used without arguments on command line all classes are +required to be fully defined at image build-time. + +Using --link-at-build-time with arguments is allowed in every scope: + + 1. On command line + 2. Embedded in a native-image.properties file of some zip/jar file on module-path + 3. Embedded in a native-image.properties file of some zip/jar file on class-path + +If the option is embedded in native-image.properties file in some zip/jar file all class-names +and package-names passed to the option have to be found in the zip/jar files the option is embedded +in. Using --link-at-build-time with arguments on command line does not have that restriction. + diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LinkAtBuildTimePathsHelp.txt b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LinkAtBuildTimePathsHelp.txt new file mode 100644 index 000000000000..d8188a550ed2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LinkAtBuildTimePathsHelp.txt @@ -0,0 +1,11 @@ +Require all types in given class or module-path entries to be fully defined at image build-time. + +This option requires arguments that are of the same type as the +arguments passed via -p (--module-path) or -cp (--class-path): + + --link-at-build-time-paths + +The given entries are searched and all classes inside are registered as --link-at-build-time classes. + +This option is only allowed to be used on command line. I.e. the option will be rejected if it is provided +by Args of a native-image.properties file embedded in a zip/jar file. From c3227c8984f5f732ae36ef78699a7bb14a10dd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 8 Mar 2022 09:54:19 +0100 Subject: [PATCH 35/36] Make OptionOrigin classes package-private --- .../oracle/svm/core/option/OptionOrigin.java | 294 +++++++++--------- .../svm/hosted/LinkAtBuildTimeFeature.java | 2 +- 2 files changed, 148 insertions(+), 148 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java index e54989b1aacf..9dcb2627f8ff 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/OptionOrigin.java @@ -41,6 +41,8 @@ public abstract class OptionOrigin { + public static final OptionOrigin commandLineOptionOriginSingleton = new CommandLineOptionOrigin(); + public URI container() { return null; } @@ -65,7 +67,7 @@ public List getRedirectionValues(@SuppressWarnings("unused") Path values public static OptionOrigin from(String origin) { if (origin == null) { - return CommandLineOptionOrigin.singleton; + return commandLineOptionOriginSingleton; } URI originURI = originURI(origin); @@ -98,191 +100,189 @@ protected static URI originURI(String origin) { } } - public static final class CommandLineOptionOrigin extends OptionOrigin { + static List getRedirectionValuesFromPath(Path normalizedRedirPath) throws IOException { + if (Files.isReadable(normalizedRedirPath)) { + return Files.readAllLines(normalizedRedirPath); + } + throw new FileNotFoundException("Unable to read file from " + normalizedRedirPath.toUri()); + } +} - public static final CommandLineOptionOrigin singleton = new CommandLineOptionOrigin(); +final class CommandLineOptionOrigin extends OptionOrigin { - private CommandLineOptionOrigin() { - } + CommandLineOptionOrigin() { + } - @Override - public int hashCode() { - return 0; - } + @Override + public int hashCode() { + return 0; + } - @Override - public boolean equals(Object obj) { - return obj instanceof CommandLineOptionOrigin; - } + @Override + public boolean equals(Object obj) { + return obj instanceof CommandLineOptionOrigin; + } - @Override - public boolean commandLineLike() { - return true; - } + @Override + public boolean commandLineLike() { + return true; + } - @Override - public String toString() { - return "command line"; - } + @Override + public String toString() { + return "command line"; } +} - public static final class MacroOptionOrigin extends OptionOrigin { +final class MacroOptionOrigin extends OptionOrigin { - public final OptionUtils.MacroOptionKind kind; - public final String name; - public final Path optionDirectory; + public final OptionUtils.MacroOptionKind kind; + public final String name; + public final Path optionDirectory; - private MacroOptionOrigin(OptionUtils.MacroOptionKind kind, String name, URI optionDirectory) { - this.kind = kind; - this.name = name; - VMError.guarantee(optionDirectory != null, "Invalid optionDirectory origin"); - this.optionDirectory = Path.of(optionDirectory); - } + private MacroOptionOrigin(OptionUtils.MacroOptionKind kind, String name, URI optionDirectory) { + this.kind = kind; + this.name = name; + VMError.guarantee(optionDirectory != null, "Invalid optionDirectory origin"); + this.optionDirectory = Path.of(optionDirectory); + } - @Override - public int hashCode() { - return Objects.hash(kind, name); - } + @Override + public int hashCode() { + return Objects.hash(kind, name); + } - @Override - public boolean equals(Object obj) { - if (obj instanceof MacroOptionOrigin) { - var that = (MacroOptionOrigin) obj; - return Objects.equals(this.kind, that.kind) && - Objects.equals(this.name, that.name); - } - return false; + @Override + public boolean equals(Object obj) { + if (obj instanceof MacroOptionOrigin) { + var that = (MacroOptionOrigin) obj; + return Objects.equals(this.kind, that.kind) && + Objects.equals(this.name, that.name); } + return false; + } - public static MacroOptionOrigin from(String rawOrigin) { - for (OptionUtils.MacroOptionKind kind : OptionUtils.MacroOptionKind.values()) { - String prefix = kind.getDescriptionPrefix(true); - if (rawOrigin.startsWith(prefix)) { - int optionDirectorySep = rawOrigin.indexOf('@'); - VMError.guarantee(optionDirectorySep > 0, "Missing macro-option optionDirectory origin"); - String optionDirectory = rawOrigin.substring(optionDirectorySep + 1); - int argumentOriginSep = optionDirectory.indexOf('@'); - if (argumentOriginSep > 0) { - /* Strip optional trailing argumentOrigin */ - optionDirectory = optionDirectory.substring(0, argumentOriginSep); - } - return new MacroOptionOrigin(kind, rawOrigin.substring(prefix.length()), originURI(optionDirectory)); + public static MacroOptionOrigin from(String rawOrigin) { + for (OptionUtils.MacroOptionKind kind : OptionUtils.MacroOptionKind.values()) { + String prefix = kind.getDescriptionPrefix(true); + if (rawOrigin.startsWith(prefix)) { + int optionDirectorySep = rawOrigin.indexOf('@'); + VMError.guarantee(optionDirectorySep > 0, "Missing macro-option optionDirectory origin"); + String optionDirectory = rawOrigin.substring(optionDirectorySep + 1); + int argumentOriginSep = optionDirectory.indexOf('@'); + if (argumentOriginSep > 0) { + /* Strip optional trailing argumentOrigin */ + optionDirectory = optionDirectory.substring(0, argumentOriginSep); } + return new MacroOptionOrigin(kind, rawOrigin.substring(prefix.length()), originURI(optionDirectory)); } - return null; - } - - @Override - public boolean commandLineLike() { - return OptionUtils.MacroOptionKind.Macro.equals(kind); } + return null; + } - @Override - public String toString() { - return kind + " option '" + name + "'"; - } + @Override + public boolean commandLineLike() { + return OptionUtils.MacroOptionKind.Macro.equals(kind); + } - @Override - public List getRedirectionValues(Path valuesFile) throws IOException { - var normalizedRedirPath = optionDirectory.resolve(valuesFile).normalize(); - return getRedirectionValuesFromPath(normalizedRedirPath); - } + @Override + public String toString() { + return kind + " option '" + name + "'"; } - protected abstract static class URIOptionOrigin extends OptionOrigin { + @Override + public List getRedirectionValues(Path valuesFile) throws IOException { + var normalizedRedirPath = optionDirectory.resolve(valuesFile).normalize(); + return getRedirectionValuesFromPath(normalizedRedirPath); + } +} - protected URI container; +abstract class URIOptionOrigin extends OptionOrigin { - @Override - public URI container() { - return container; - } + protected URI container; - protected Path location; + @Override + public URI container() { + return container; + } - @Override - public Path location() { - return location; - } + protected Path location; - @Override - public int hashCode() { - return Objects.hash(container, location); - } + @Override + public Path location() { + return location; + } - @Override - public boolean equals(Object obj) { - if (obj instanceof URIOptionOrigin) { - var that = (URIOptionOrigin) obj; - return Objects.equals(this.container, that.container) && - Objects.equals(this.location, that.location); - } - return false; - } + @Override + public int hashCode() { + return Objects.hash(container, location); + } - @Override - public String toString() { - return String.format("'%s' in '%s'", location(), container()); + @Override + public boolean equals(Object obj) { + if (obj instanceof URIOptionOrigin) { + var that = (URIOptionOrigin) obj; + return Objects.equals(this.container, that.container) && + Objects.equals(this.location, that.location); } + return false; } - public static final class JarOptionOrigin extends URIOptionOrigin { - protected JarOptionOrigin(URI rawOrigin) { - var specific = rawOrigin.getSchemeSpecificPart(); - int sep = specific.lastIndexOf('!'); - VMError.guarantee(sep > 0, "Invalid jar origin"); - var origin = specific.substring(0, sep); - container = URIOptionOrigin.originURI(origin); - location = Path.of(specific.substring(sep + 2)); - } + @Override + public String toString() { + return String.format("'%s' in '%s'", location(), container()); + } +} - @SuppressWarnings("try") - @Override - public List getRedirectionValues(Path valuesFile) throws IOException { - URI jarFileURI = URI.create("jar:" + container()); - FileSystem probeJarFS; - try { - probeJarFS = FileSystems.newFileSystem(jarFileURI, Collections.emptyMap()); - } catch (UnsupportedOperationException e) { - probeJarFS = null; - } - if (probeJarFS == null) { - throw new IOException("Unable to create jar file system for " + jarFileURI); - } - try (FileSystem fs = probeJarFS) { - var normalizedRedirPath = location().getParent().resolve(valuesFile).normalize(); - return getRedirectionValuesFromPath(normalizedRedirPath); - } - } +final class JarOptionOrigin extends URIOptionOrigin { + protected JarOptionOrigin(URI rawOrigin) { + var specific = rawOrigin.getSchemeSpecificPart(); + int sep = specific.lastIndexOf('!'); + VMError.guarantee(sep > 0, "Invalid jar origin"); + var origin = specific.substring(0, sep); + container = URIOptionOrigin.originURI(origin); + location = Path.of(specific.substring(sep + 2)); } - public static final class DirectoryOptionOrigin extends URIOptionOrigin { - protected DirectoryOptionOrigin(Path originPath) { - int pathPos = 0; - int metaInfPos = -1; - for (Path entry : originPath) { - if ("META-INF".equals(entry.toString())) { - metaInfPos = pathPos; - break; - } - ++pathPos; - } - VMError.guarantee(metaInfPos > 0, "Invalid directory origin"); - container = originPath.getRoot().resolve(originPath.subpath(0, metaInfPos)).toUri(); - location = originPath.subpath(metaInfPos, originPath.getNameCount()); + @SuppressWarnings("try") + @Override + public List getRedirectionValues(Path valuesFile) throws IOException { + URI jarFileURI = URI.create("jar:" + container()); + FileSystem probeJarFS; + try { + probeJarFS = FileSystems.newFileSystem(jarFileURI, Collections.emptyMap()); + } catch (UnsupportedOperationException e) { + probeJarFS = null; } - - @Override - public List getRedirectionValues(Path valuesFile) throws IOException { - var normalizedRedirPath = Path.of(container()).resolve(location()).getParent().resolve(valuesFile).normalize(); + if (probeJarFS == null) { + throw new IOException("Unable to create jar file system for " + jarFileURI); + } + try (FileSystem fs = probeJarFS) { + var normalizedRedirPath = location().getParent().resolve(valuesFile).normalize(); return getRedirectionValuesFromPath(normalizedRedirPath); } } +} - private static List getRedirectionValuesFromPath(Path normalizedRedirPath) throws IOException { - if (Files.isReadable(normalizedRedirPath)) { - return Files.readAllLines(normalizedRedirPath); +final class DirectoryOptionOrigin extends URIOptionOrigin { + protected DirectoryOptionOrigin(Path originPath) { + int pathPos = 0; + int metaInfPos = -1; + for (Path entry : originPath) { + if ("META-INF".equals(entry.toString())) { + metaInfPos = pathPos; + break; + } + ++pathPos; } - throw new FileNotFoundException("Unable to read file from " + normalizedRedirPath.toUri()); + VMError.guarantee(metaInfPos > 0, "Invalid directory origin"); + container = originPath.getRoot().resolve(originPath.subpath(0, metaInfPos)).toUri(); + location = originPath.subpath(metaInfPos, originPath.getNameCount()); + } + + @Override + public List getRedirectionValues(Path valuesFile) throws IOException { + var normalizedRedirPath = Path.of(container()).resolve(location()).getParent().resolve(valuesFile).normalize(); + return getRedirectionValuesFromPath(normalizedRedirPath); } } 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 51aae4b1a226..bdf6e7a17340 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 @@ -72,7 +72,7 @@ static final class Options { private final String javaIdentifier = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"; private final Pattern validOptionValue = Pattern.compile(javaIdentifier + "(\\." + javaIdentifier + ")*"); - private final Set reasonCommandLine = Collections.singleton(OptionOrigin.CommandLineOptionOrigin.singleton); + private final Set reasonCommandLine = Collections.singleton(OptionOrigin.commandLineOptionOriginSingleton); private final Map> requireCompletePackageOrClass = new HashMap<>(); private final Set requireCompleteModules = new HashSet<>(); From 1655cb236bff7d21829e3c3d765d00abb6dcd991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 8 Mar 2022 10:02:52 +0100 Subject: [PATCH 36/36] Add changelog entry --- substratevm/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 54062dd502c7..82ec788db4ee 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -8,6 +8,7 @@ This changelog summarizes major changes to GraalVM Native Image. * Remove support for JDK8. As a result, `JDK8OrEarlier` and `JDK11OrLater` have been deprecated and will be removed in a future release. * (GR-26814) (GR-37018) (GR-37038) Red Hat added support for the following JFR events: `SafepointBegin`, `SafepointEnd`, `GarbageCollection`, `GCPhasePause`, and `GCPhasePauseLevel*`. All GC-related JFR events are currently limited to the serial GC. * (GR-35721) Deprecate `-H:±BuildOutputUseNewStyle` option. The old build output style will be removed in a future release. +* (GR-36905) Allow incomplete classes at build-time is now default. Add --link-at-build-time option and @ support for native-image.properties. Add --link-at-build-time-paths option. ## Version 22.0.0 * (GR-33930) Decouple HostedOptionParser setup from classpath/modulepath scanning (use ServiceLoader for collecting options).