Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.oracle.graal.pointsto.util.AnalysisError;
import com.oracle.graal.pointsto.util.AnalysisFuture;
import com.oracle.graal.pointsto.util.AtomicUtils;
import com.oracle.svm.common.meta.GuaranteeFolded;

import jdk.graal.compiler.debug.GraalError;
import jdk.vm.ci.code.BytecodePosition;
Expand Down Expand Up @@ -217,6 +218,7 @@ public void cleanupAfterAnalysis() {
}

public boolean registerAsAccessed(Object reason) {
checkGuaranteeFolded();
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as accessed needs to provide a valid reason.";
Expand All @@ -231,6 +233,7 @@ public boolean registerAsAccessed(Object reason) {
* @param reason the reason why this field is read, non-null
*/
public boolean registerAsRead(Object reason) {
checkGuaranteeFolded();
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as read needs to provide a valid reason.";
Expand All @@ -250,6 +253,7 @@ public boolean registerAsRead(Object reason) {
* @param reason the reason why this field is written, non-null
*/
public boolean registerAsWritten(Object reason) {
checkGuaranteeFolded();
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as written needs to provide a valid reason.";
Expand All @@ -264,6 +268,14 @@ public boolean registerAsWritten(Object reason) {
});
}

public boolean isGuaranteeFolded() {
return getAnnotation(GuaranteeFolded.class) != null;
}

public void checkGuaranteeFolded() {
AnalysisError.guarantee(!isGuaranteeFolded(), "A field that is guaranteed to always be folded is seen as accessed: %s. ", this);
}

public void registerAsFolded(Object reason) {
getDeclaringClass().registerAsReachable(this);

Expand All @@ -275,6 +287,7 @@ public void registerAsFolded(Object reason) {
}

public boolean registerAsUnsafeAccessed(Object reason) {
checkGuaranteeFolded();
assert isValidReason(reason) : "Registering a field as unsafe accessed needs to provide a valid reason.";
registerAsAccessed(reason);
/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025, 2025, 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.common.meta;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Verifies that loads of the annotated field are always folded in run time code. This annotation
* doesn't influence the folding logic itself, it just ensures that the annotated fields are not
* present in the image.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface GuaranteeFolded {
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,21 @@

import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.EconomicSet;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.util.ClassUtil;
import com.oracle.svm.util.StringUtil;

import jdk.graal.compiler.options.EnumMultiOptionKey;
import jdk.graal.compiler.options.OptionDescriptor;
import jdk.graal.compiler.options.OptionDescriptors;
import jdk.graal.compiler.options.OptionKey;
import jdk.graal.compiler.options.OptionType;
import jdk.graal.compiler.options.OptionsParser;

import com.oracle.svm.util.ClassUtil;
import com.oracle.svm.util.StringUtil;

public class CommonOptionParser {
@Platforms(Platform.HOSTED_ONLY.class) //
public static final String HOSTED_OPTION_PREFIX = "-H:";
public static final String RUNTIME_OPTION_PREFIX = "-R:";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;

import com.oracle.svm.common.meta.GuaranteeFolded;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
Expand All @@ -69,7 +70,7 @@ public class SubstrateUtil {
/**
* Field that is true during native image generation, but false at run time.
*/
public static final boolean HOSTED;
@GuaranteeFolded public static final boolean HOSTED;

static {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
public abstract class SystemPropertiesSupport implements RuntimeSystemPropertiesSupport {

/** System properties that are taken from the VM hosting the image generator. */
@Platforms(Platform.HOSTED_ONLY.class) //
private static final String[] HOSTED_PROPERTIES = {
"java.version",
"java.version.date",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
*/
public class SubstrateOptionsParser {

@Platforms(Platform.HOSTED_ONLY.class) //
public static final String HOSTED_OPTION_PREFIX = CommonOptionParser.HOSTED_OPTION_PREFIX;
public static final String RUNTIME_OPTION_PREFIX = CommonOptionParser.RUNTIME_OPTION_PREFIX;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,20 @@
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.graalvm.collections.EconomicSet;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.BuildPhaseProvider;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.TypeResult;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.LogUtils;
import com.oracle.svm.util.ReflectionUtil;

Expand All @@ -62,6 +66,8 @@ public final class ImageClassLoader {
private final EconomicSet<Class<?>> hostedOnlyClasses = EconomicSet.create();
private final EconomicSet<Method> systemMethods = EconomicSet.create();
private final EconomicSet<Field> systemFields = EconomicSet.create();
/** Modules containing all {@code svm.core} and {@code svm.hosted} classes. */
private Set<Module> builderModules;

ImageClassLoader(Platform platform, NativeImageClassLoaderSupport classLoaderSupport) {
this.platform = platform;
Expand Down Expand Up @@ -430,4 +436,17 @@ public EconomicSet<String> packages(URI container) {
public boolean noEntryForURI(EconomicSet<String> set) {
return classLoaderSupport.noEntryForURI(set);
}

public Set<Module> getBuilderModules() {
assert builderModules != null : "Builder modules not yet initialized.";
return builderModules;
}

public void initBuilderModules() {
VMError.guarantee(BuildPhaseProvider.isFeatureRegistrationFinished() && ImageSingletons.contains(VMFeature.class),
"Querying builder modules is only possible after feature registration is finished.");
Module m0 = ImageSingletons.lookup(VMFeature.class).getClass().getModule();
Module m1 = SVMHost.class.getModule();
builderModules = m0.equals(m1) ? Set.of(m0) : Set.of(m0, m1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.FileSystems;
Expand Down Expand Up @@ -941,6 +942,8 @@ protected void setupNativeImage(String imageName, OptionValues options, Map<Meth
featureHandler.registerFeatures(loader, debug);
BuildPhaseProvider.markFeatureRegistrationFinished();

loader.initBuilderModules();

AfterRegistrationAccessImpl access = new AfterRegistrationAccessImpl(featureHandler, loader, originalMetaAccess, mainEntryPoint, debug);
featureHandler.forEachFeature(feature -> feature.afterRegistration(access));
setDefaultLibCIfMissing();
Expand Down Expand Up @@ -1656,17 +1659,17 @@ private void checkUniverse() {
if (SubstrateOptions.VerifyNamingConventions.getValue()) {
for (AnalysisMethod method : aUniverse.getMethods()) {
if ((method.isInvoked() || method.isReachable()) && method.getAnnotation(Fold.class) == null) {
checkName(method.format("%H.%n(%p)"), method, bb);
checkName(bb, method);
}
}
for (AnalysisField field : aUniverse.getFields()) {
if (field.isAccessed()) {
checkName(field.format("%H.%n"), null, bb);
checkName(bb, field);
}
}
for (AnalysisType type : aUniverse.getTypes()) {
if (type.isReachable()) {
checkName(type.toJavaName(true), null, bb);
checkName(bb, type);
}
}
}
Expand Down Expand Up @@ -1698,32 +1701,47 @@ protected void checkForInvalidCallsToEntryPoints() {
// the unsupported features are reported after checkUniverse is invoked
}

public static void checkName(String name, AnalysisMethod method, BigBang bb) {
public static void checkName(BigBang bb, AnalysisMethod method) {
String format = method.format("%H.%n(%p)");
checkName(bb, method, format);
}

public static void checkName(BigBang bb, AnalysisField field) {
String format = field.format("%H.%n");
checkName(bb, null, format);
}

public static void checkName(BigBang bb, Field field) {
String format = field.getType().getName() + "." + field.getName();
checkName(bb, null, format);
}

public static void checkName(BigBang bb, AnalysisType type) {
String format = type.toJavaName(true);
checkName(bb, null, format);
}

private static void checkName(BigBang bb, AnalysisMethod method, String format) {
/*
* We do not want any parts of the native image generator in the generated image. Therefore,
* no element whose name contains "hosted" must be seen as reachable by the static analysis.
* The same holds for "host VM" elements, which come from the hosting VM, unless they are
* JDK internal types.
*/
String message = checkName(name);
if (message != null) {
if (bb != null) {
bb.getUnsupportedFeatures().addMessage(name, method, message);
} else {
throw new UnsupportedFeatureException(message);
}
String lformat = format.toLowerCase(Locale.ROOT);
if (lformat.contains("hosted")) {
report(bb, format, method, "Hosted element used at run time: " + format + ".");
} else if (!lformat.startsWith("jdk.internal") && lformat.contains("hotspot")) {
report(bb, format, method, "HotSpot element used at run time: " + format + ".");
}
}

public static String checkName(String name) {
String lname = name.toLowerCase(Locale.ROOT);
String message = null;
if (lname.contains("hosted")) {
message = "Hosted element used at run time: " + name;
} else if (!name.startsWith("jdk.internal") && lname.contains("hotspot")) {
message = "HotSpot element used at run time: " + name;
private static void report(BigBang bb, String key, AnalysisMethod method, String message) {
if (bb != null) {
bb.getUnsupportedFeatures().addMessage(key, method, message);
} else {
throw new UnsupportedFeatureException(message);
}
return message;
}

@SuppressWarnings("try")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,6 @@ public void beforeUniverseBuilding(BeforeUniverseBuildingAccess access) {
}
}

public Set<Module> getBuilderModules() {
Module m0 = ImageSingletons.lookup(VMFeature.class).getClass().getModule();
Module m1 = SVMHost.class.getModule();
return m0.equals(m1) ? Set.of(m0) : Set.of(m0, m1);
}

private final Set<AnalysisType> triggeredTypes = new HashSet<>();
private final Set<AnalysisMethod> triggeredMethods = new HashSet<>();

Expand Down
Loading