Skip to content

Commit bbdf41b

Browse files
committed
[GR-60740] [GR-61079] Refactor field inclusion policy in base layer and improve name checks mechanism.
PullRequest: graal/19767
2 parents 0463e0e + 41c8156 commit bbdf41b

File tree

12 files changed

+154
-47
lines changed

12 files changed

+154
-47
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import com.oracle.graal.pointsto.util.AnalysisError;
4141
import com.oracle.graal.pointsto.util.AnalysisFuture;
4242
import com.oracle.graal.pointsto.util.AtomicUtils;
43+
import com.oracle.svm.common.meta.GuaranteeFolded;
4344

4445
import jdk.graal.compiler.debug.GraalError;
4546
import jdk.vm.ci.code.BytecodePosition;
@@ -217,6 +218,7 @@ public void cleanupAfterAnalysis() {
217218
}
218219

219220
public boolean registerAsAccessed(Object reason) {
221+
checkGuaranteeFolded();
220222
getDeclaringClass().registerAsReachable(this);
221223

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

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

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

271+
public boolean isGuaranteeFolded() {
272+
return getAnnotation(GuaranteeFolded.class) != null;
273+
}
274+
275+
public void checkGuaranteeFolded() {
276+
AnalysisError.guarantee(!isGuaranteeFolded(), "A field that is guaranteed to always be folded is seen as accessed: %s. ", this);
277+
}
278+
267279
public void registerAsFolded(Object reason) {
268280
getDeclaringClass().registerAsReachable(this);
269281

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

277289
public boolean registerAsUnsafeAccessed(Object reason) {
290+
checkGuaranteeFolded();
278291
assert isValidReason(reason) : "Registering a field as unsafe accessed needs to provide a valid reason.";
279292
registerAsAccessed(reason);
280293
/*
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.common.meta;
26+
27+
import java.lang.annotation.ElementType;
28+
import java.lang.annotation.Retention;
29+
import java.lang.annotation.RetentionPolicy;
30+
import java.lang.annotation.Target;
31+
32+
/**
33+
* Verifies that loads of the annotated field are always folded in run time code. This annotation
34+
* doesn't influence the folding logic itself, it just ensures that the annotated fields are not
35+
* present in the image.
36+
*/
37+
@Retention(RetentionPolicy.RUNTIME)
38+
@Target({ElementType.FIELD})
39+
public @interface GuaranteeFolded {
40+
}

substratevm/src/com.oracle.svm.common/src/com/oracle/svm/common/option/CommonOptionParser.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,21 @@
4141

4242
import org.graalvm.collections.EconomicMap;
4343
import org.graalvm.collections.EconomicSet;
44+
import org.graalvm.nativeimage.Platform;
45+
import org.graalvm.nativeimage.Platforms;
46+
47+
import com.oracle.svm.util.ClassUtil;
48+
import com.oracle.svm.util.StringUtil;
49+
4450
import jdk.graal.compiler.options.EnumMultiOptionKey;
4551
import jdk.graal.compiler.options.OptionDescriptor;
4652
import jdk.graal.compiler.options.OptionDescriptors;
4753
import jdk.graal.compiler.options.OptionKey;
4854
import jdk.graal.compiler.options.OptionType;
4955
import jdk.graal.compiler.options.OptionsParser;
5056

51-
import com.oracle.svm.util.ClassUtil;
52-
import com.oracle.svm.util.StringUtil;
53-
5457
public class CommonOptionParser {
58+
@Platforms(Platform.HOSTED_ONLY.class) //
5559
public static final String HOSTED_OPTION_PREFIX = "-H:";
5660
public static final String RUNTIME_OPTION_PREFIX = "-R:";
5761

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.graalvm.word.Pointer;
4747
import org.graalvm.word.UnsignedWord;
4848

49+
import com.oracle.svm.common.meta.GuaranteeFolded;
4950
import com.oracle.svm.core.annotate.Alias;
5051
import com.oracle.svm.core.annotate.RecomputeFieldValue;
5152
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
@@ -69,7 +70,7 @@ public class SubstrateUtil {
6970
/**
7071
* Field that is true during native image generation, but false at run time.
7172
*/
72-
public static final boolean HOSTED;
73+
@GuaranteeFolded public static final boolean HOSTED;
7374

7475
static {
7576
/*

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
public abstract class SystemPropertiesSupport implements RuntimeSystemPropertiesSupport {
6464

6565
/** System properties that are taken from the VM hosting the image generator. */
66+
@Platforms(Platform.HOSTED_ONLY.class) //
6667
private static final String[] HOSTED_PROPERTIES = {
6768
"java.version",
6869
"java.version.date",

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
*/
5555
public class SubstrateOptionsParser {
5656

57+
@Platforms(Platform.HOSTED_ONLY.class) //
5758
public static final String HOSTED_OPTION_PREFIX = CommonOptionParser.HOSTED_OPTION_PREFIX;
5859
public static final String RUNTIME_OPTION_PREFIX = CommonOptionParser.RUNTIME_OPTION_PREFIX;
5960

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,20 @@
3737
import java.util.Enumeration;
3838
import java.util.List;
3939
import java.util.Optional;
40+
import java.util.Set;
4041
import java.util.concurrent.ForkJoinPool;
4142
import java.util.concurrent.TimeUnit;
4243
import java.util.stream.Collectors;
4344

4445
import org.graalvm.collections.EconomicSet;
46+
import org.graalvm.nativeimage.ImageSingletons;
4547
import org.graalvm.nativeimage.Platform;
4648
import org.graalvm.nativeimage.Platforms;
4749

50+
import com.oracle.svm.core.BuildPhaseProvider;
4851
import com.oracle.svm.core.SubstrateOptions;
4952
import com.oracle.svm.core.TypeResult;
53+
import com.oracle.svm.core.util.VMError;
5054
import com.oracle.svm.util.LogUtils;
5155
import com.oracle.svm.util.ReflectionUtil;
5256

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

6672
ImageClassLoader(Platform platform, NativeImageClassLoaderSupport classLoaderSupport) {
6773
this.platform = platform;
@@ -430,4 +436,17 @@ public EconomicSet<String> packages(URI container) {
430436
public boolean noEntryForURI(EconomicSet<String> set) {
431437
return classLoaderSupport.noEntryForURI(set);
432438
}
439+
440+
public Set<Module> getBuilderModules() {
441+
assert builderModules != null : "Builder modules not yet initialized.";
442+
return builderModules;
443+
}
444+
445+
public void initBuilderModules() {
446+
VMError.guarantee(BuildPhaseProvider.isFeatureRegistrationFinished() && ImageSingletons.contains(VMFeature.class),
447+
"Querying builder modules is only possible after feature registration is finished.");
448+
Module m0 = ImageSingletons.lookup(VMFeature.class).getClass().getModule();
449+
Module m1 = SVMHost.class.getModule();
450+
builderModules = m0.equals(m1) ? Set.of(m0) : Set.of(m0, m1);
451+
}
433452
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import java.io.IOException;
3434
import java.lang.annotation.Annotation;
35+
import java.lang.reflect.Field;
3536
import java.lang.reflect.Method;
3637
import java.lang.reflect.Modifier;
3738
import java.nio.file.FileSystems;
@@ -941,6 +942,8 @@ protected void setupNativeImage(String imageName, OptionValues options, Map<Meth
941942
featureHandler.registerFeatures(loader, debug);
942943
BuildPhaseProvider.markFeatureRegistrationFinished();
943944

945+
loader.initBuilderModules();
946+
944947
AfterRegistrationAccessImpl access = new AfterRegistrationAccessImpl(featureHandler, loader, originalMetaAccess, mainEntryPoint, debug);
945948
featureHandler.forEachFeature(feature -> feature.afterRegistration(access));
946949
setDefaultLibCIfMissing();
@@ -1656,17 +1659,17 @@ private void checkUniverse() {
16561659
if (SubstrateOptions.VerifyNamingConventions.getValue()) {
16571660
for (AnalysisMethod method : aUniverse.getMethods()) {
16581661
if ((method.isInvoked() || method.isReachable()) && method.getAnnotation(Fold.class) == null) {
1659-
checkName(method.format("%H.%n(%p)"), method, bb);
1662+
checkName(bb, method);
16601663
}
16611664
}
16621665
for (AnalysisField field : aUniverse.getFields()) {
16631666
if (field.isAccessed()) {
1664-
checkName(field.format("%H.%n"), null, bb);
1667+
checkName(bb, field);
16651668
}
16661669
}
16671670
for (AnalysisType type : aUniverse.getTypes()) {
16681671
if (type.isReachable()) {
1669-
checkName(type.toJavaName(true), null, bb);
1672+
checkName(bb, type);
16701673
}
16711674
}
16721675
}
@@ -1698,32 +1701,47 @@ protected void checkForInvalidCallsToEntryPoints() {
16981701
// the unsupported features are reported after checkUniverse is invoked
16991702
}
17001703

1701-
public static void checkName(String name, AnalysisMethod method, BigBang bb) {
1704+
public static void checkName(BigBang bb, AnalysisMethod method) {
1705+
String format = method.format("%H.%n(%p)");
1706+
checkName(bb, method, format);
1707+
}
1708+
1709+
public static void checkName(BigBang bb, AnalysisField field) {
1710+
String format = field.format("%H.%n");
1711+
checkName(bb, null, format);
1712+
}
1713+
1714+
public static void checkName(BigBang bb, Field field) {
1715+
String format = field.getType().getName() + "." + field.getName();
1716+
checkName(bb, null, format);
1717+
}
1718+
1719+
public static void checkName(BigBang bb, AnalysisType type) {
1720+
String format = type.toJavaName(true);
1721+
checkName(bb, null, format);
1722+
}
1723+
1724+
private static void checkName(BigBang bb, AnalysisMethod method, String format) {
17021725
/*
17031726
* We do not want any parts of the native image generator in the generated image. Therefore,
17041727
* no element whose name contains "hosted" must be seen as reachable by the static analysis.
17051728
* The same holds for "host VM" elements, which come from the hosting VM, unless they are
17061729
* JDK internal types.
17071730
*/
1708-
String message = checkName(name);
1709-
if (message != null) {
1710-
if (bb != null) {
1711-
bb.getUnsupportedFeatures().addMessage(name, method, message);
1712-
} else {
1713-
throw new UnsupportedFeatureException(message);
1714-
}
1731+
String lformat = format.toLowerCase(Locale.ROOT);
1732+
if (lformat.contains("hosted")) {
1733+
report(bb, format, method, "Hosted element used at run time: " + format + ".");
1734+
} else if (!lformat.startsWith("jdk.internal") && lformat.contains("hotspot")) {
1735+
report(bb, format, method, "HotSpot element used at run time: " + format + ".");
17151736
}
17161737
}
17171738

1718-
public static String checkName(String name) {
1719-
String lname = name.toLowerCase(Locale.ROOT);
1720-
String message = null;
1721-
if (lname.contains("hosted")) {
1722-
message = "Hosted element used at run time: " + name;
1723-
} else if (!name.startsWith("jdk.internal") && lname.contains("hotspot")) {
1724-
message = "HotSpot element used at run time: " + name;
1739+
private static void report(BigBang bb, String key, AnalysisMethod method, String message) {
1740+
if (bb != null) {
1741+
bb.getUnsupportedFeatures().addMessage(key, method, message);
1742+
} else {
1743+
throw new UnsupportedFeatureException(message);
17251744
}
1726-
return message;
17271745
}
17281746

17291747
@SuppressWarnings("try")

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,6 @@ public void beforeUniverseBuilding(BeforeUniverseBuildingAccess access) {
6969
}
7070
}
7171

72-
public Set<Module> getBuilderModules() {
73-
Module m0 = ImageSingletons.lookup(VMFeature.class).getClass().getModule();
74-
Module m1 = SVMHost.class.getModule();
75-
return m0.equals(m1) ? Set.of(m0) : Set.of(m0, m1);
76-
}
77-
7872
private final Set<AnalysisType> triggeredTypes = new HashSet<>();
7973
private final Set<AnalysisMethod> triggeredMethods = new HashSet<>();
8074

0 commit comments

Comments
 (0)