Skip to content

Commit 4563696

Browse files
committed
Add dynamic access detection phase
1 parent 35647e1 commit 4563696

18 files changed

+952
-58
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ public final void applyResults(AnalysisMethod method) {
252252
return;
253253
}
254254

255+
preStrengthenGraphs(graph, method);
256+
255257
graph.resetDebug(debug);
256258
if (beforeCounters != null) {
257259
beforeCounters.collect(graph);
@@ -279,6 +281,8 @@ public final void applyResults(AnalysisMethod method) {
279281
}
280282
}
281283

284+
protected abstract void preStrengthenGraphs(StructuredGraph graph, AnalysisMethod method);
285+
282286
protected abstract void postStrengthenGraphs(StructuredGraph graph, AnalysisMethod method);
283287

284288
protected abstract void persistStrengthenGraph(AnalysisMethod method);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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.core;
26+
27+
import java.util.function.BooleanSupplier;
28+
29+
public class AnalyzeJavaHomeAccessEnabled implements BooleanSupplier {
30+
31+
@Override
32+
public boolean getAsBoolean() {
33+
return SubstrateOptions.TrackJavaHomeAccess.getValue();
34+
}
35+
}

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
3131
import java.lang.annotation.Retention;
3232
import java.lang.annotation.RetentionPolicy;
3333
import java.lang.annotation.Target;
34+
import java.util.function.BooleanSupplier;
3435

3536
/**
3637
* Every thus annotated method is never trivially inlined by the compiler. Specific inlining to
@@ -44,5 +45,28 @@
4445
/**
4546
* Documents the reason why the annotated code must not be inlined.
4647
*/
47-
String value();
48+
String reason();
49+
50+
/**
51+
* Prevent inlining only if all of the provided predicates are true (default: a predicate that
52+
* always prevents inlining).
53+
*
54+
* The classes must implement {@link BooleanSupplier}.
55+
*/
56+
Class<?>[] onlyWith() default NeverInlined.class;
57+
58+
/**
59+
* A BooleanSupplier that always returns true, used to indicate methods should never be inlined.
60+
* This serves as the default value for the {@link NeverInlineTrivial#onlyWith()} attribute,
61+
* preventing inlining by default.
62+
*/
63+
final class NeverInlined implements BooleanSupplier {
64+
NeverInlined() {
65+
}
66+
67+
@Override
68+
public boolean getAsBoolean() {
69+
return true;
70+
}
71+
}
4872
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,4 +1484,23 @@ public static boolean hasDumpRuntimeCompiledMethodsSupport() {
14841484
return !Platform.includedIn(Platform.WINDOWS.class) && ConcealedOptions.DumpRuntimeCompiledMethods.getValue();
14851485
}
14861486

1487+
@Option(help = "file:doc-files/TrackDynamicAccessHelp.txt")//
1488+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> TrackDynamicAccess = new HostedOptionKey<>(
1489+
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter());
1490+
1491+
@Option(help = "Output all method calls requiring metadata for dynamic access found by -H:TrackDynamicAccess to the console.")//
1492+
public static final HostedOptionKey<Boolean> ReportDynamicAccessToConsole = new HostedOptionKey<>(false);
1493+
1494+
@Option(help = "Track System.getProperty(\\\"java.home\\\") usage in reachable parts of the project.")//
1495+
public static final HostedOptionKey<Boolean> TrackJavaHomeAccess = new HostedOptionKey<>(false);
1496+
1497+
@Option(help = "Output all System.getProperty(\\\"java.home\\\") calls in reachable parts of the project.")//
1498+
public static final HostedOptionKey<Boolean> TrackJavaHomeAccessDetailed = new HostedOptionKey<>(false) {
1499+
@Override
1500+
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
1501+
if (newValue) {
1502+
TrackJavaHomeAccess.update(values, true);
1503+
}
1504+
}
1505+
};
14871506
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.core;
26+
27+
import org.graalvm.nativeimage.ImageSingletons;
28+
29+
import java.util.function.BooleanSupplier;
30+
31+
public class TrackDynamicAccessEnabled implements BooleanSupplier {
32+
33+
@Override
34+
public boolean getAsBoolean() {
35+
return isTrackDynamicAccessEnabled();
36+
}
37+
38+
public static boolean isTrackDynamicAccessEnabled() {
39+
return ImageSingletons.contains(TrackDynamicAccessEnabledSingleton.class);
40+
}
41+
42+
public interface TrackDynamicAccessEnabledSingleton {
43+
}
44+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Reports all reflection (e.g., Class#forName(String)) and resource (e.g., Class#getResource(String)) calls in reachable code that require metadata for dynamic access.
2+
The analysis is limited to the provided comma-separated list of class-path entries, and module or package names.
3+
If any dynamic access calls are found, a "dynamic-access" directory is created in the native image output,
4+
and the calls are serialized in "dynamic-access/<entry-name>/[reflection-calls.json][resource-calls.json]".
5+
6+
Usage: -H:TrackDynamicAccess=[all|none|path=<cp-entry>|module=<module>|package=<package>][,...]
7+
8+
The flag can be used in following ways:
9+
1. -H:TrackDynamicAccess=all reports all dynamic access calls made across the entire project
10+
2. -H:TrackDynamicAccess=path=<cp-entry> reports all dynamic access calls made from the specified class-path entry
11+
3. -H:TrackDynamicAccess=module=<module> reports all dynamic access calls made from the specified module
12+
4. -H:TrackDynamicAccess=package=<package> reports all dynamic access calls made from the specified package
13+
5. -H:TrackDynamicAccess=none disables all previous selections for dynamic access detection
14+
15+
To output the detected calls to the console as well, use -H:+ReportDynamicAccessToConsole in conjunction with -H:TrackDynamicAccess.
16+
17+
Example of the option usage:
18+
19+
native-image -cp lib/app.jar:lib/util.jar:lib/tck.jar -H:TrackDynamicAccess=path=lib/app.jar,path=lib/util.jar HelloWorld
20+
21+
In this example, the phase will look for dynamic access calls in lib/app.jar and lib/util.jar, and will not detect any calls in lib/tck.jar.

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import java.util.function.BiFunction;
8585
import java.util.function.IntFunction;
8686

87+
import com.oracle.svm.core.TrackDynamicAccessEnabled;
8788
import org.graalvm.nativeimage.AnnotationAccess;
8889
import org.graalvm.nativeimage.ImageSingletons;
8990
import org.graalvm.nativeimage.Platform;
@@ -93,6 +94,7 @@
9394
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
9495
import com.oracle.svm.core.BuildPhaseProvider.CompileQueueFinished;
9596
import com.oracle.svm.core.NeverInline;
97+
import com.oracle.svm.core.NeverInlineTrivial;
9698
import com.oracle.svm.core.RuntimeAssertionsSupport;
9799
import com.oracle.svm.core.SubstrateUtil;
98100
import com.oracle.svm.core.Uninterruptible;
@@ -1586,18 +1588,21 @@ private static Constructor<?>[] copyConstructors(Constructor<?>[] original) {
15861588
private native Constructor<?> getEnclosingConstructor();
15871589

15881590
@Substitute
1591+
@NeverInlineTrivial(reason = "Used in dynamic access call usage analysis: DynamicAccessDetectionPhase", onlyWith = TrackDynamicAccessEnabled.class)
15891592
@CallerSensitive
15901593
private static Class<?> forName(String className) throws Throwable {
15911594
return forName(className, Reflection.getCallerClass());
15921595
}
15931596

15941597
@Substitute
1598+
@NeverInlineTrivial(reason = "Used in dynamic access call usage analysis: DynamicAccessDetectionPhase", onlyWith = TrackDynamicAccessEnabled.class)
15951599
@CallerSensitiveAdapter
15961600
private static Class<?> forName(String className, Class<?> caller) throws Throwable {
15971601
return forName(className, true, caller == null ? ClassLoader.getSystemClassLoader() : caller.getClassLoader(), caller);
15981602
}
15991603

16001604
@Substitute
1605+
@NeverInlineTrivial(reason = "Used in dynamic access call usage analysis: DynamicAccessDetectionPhase", onlyWith = TrackDynamicAccessEnabled.class)
16011606
@CallerSensitive
16021607
private static Class<?> forName(Module module, String className) throws Throwable {
16031608
return forName(module, className, Reflection.getCallerClass());
@@ -1617,6 +1622,7 @@ private static Class<?> forName(@SuppressWarnings("unused") Module module, Strin
16171622
}
16181623

16191624
@Substitute
1625+
@NeverInlineTrivial(reason = "Used in dynamic access call usage analysis: DynamicAccessDetectionPhase", onlyWith = TrackDynamicAccessEnabled.class)
16201626
@CallerSensitive
16211627
private static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws Throwable {
16221628
return forName(name, initialize, loader, Reflection.getCallerClass());

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.util.function.BooleanSupplier;
4141
import java.util.stream.Stream;
4242

43+
import com.oracle.svm.core.AnalyzeJavaHomeAccessEnabled;
4344
import org.graalvm.nativeimage.Platform;
4445
import org.graalvm.nativeimage.Platforms;
4546
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
@@ -403,7 +404,7 @@ public static String setProperty(String key, String value) {
403404
}
404405

405406
@Substitute
406-
@NeverInlineTrivial("Used in 'java.home' access analysis: AnalyzeJavaHomeAccessPhase")
407+
@NeverInlineTrivial(reason = "Used in 'java.home' access analysis: AnalyzeJavaHomeAccessPhase", onlyWith = AnalyzeJavaHomeAccessEnabled.class)
407408
private static String getProperty(String key) {
408409
checkKey(key);
409410
return SystemPropertiesSupport.singleton().getCurrentProperty(key);
@@ -416,7 +417,7 @@ public static String clearProperty(String key) {
416417
}
417418

418419
@Substitute
419-
@NeverInlineTrivial("Used in 'java.home' access analysis: AnalyzeJavaHomeAccessPhase")
420+
@NeverInlineTrivial(reason = "Used in 'java.home' access analysis: AnalyzeJavaHomeAccessPhase", onlyWith = AnalyzeJavaHomeAccessEnabled.class)
420421
private static String getProperty(String key, String def) {
421422
checkKey(key);
422423
return SystemPropertiesSupport.singleton().getCurrentProperty(key, def);

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

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@
2626

2727
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
2828
import com.oracle.svm.core.feature.InternalFeature;
29-
import com.oracle.svm.core.option.HostedOptionKey;
3029
import com.oracle.svm.util.LogUtils;
31-
import jdk.graal.compiler.options.Option;
32-
import jdk.graal.compiler.options.OptionKey;
33-
import org.graalvm.collections.EconomicMap;
3430
import org.graalvm.nativeimage.ImageSingletons;
3531

3632
import java.util.Collections;
@@ -74,19 +70,4 @@ public void printJavaHomeUsageLocations() {
7470
public void beforeCompilation(BeforeCompilationAccess access) {
7571
AnalyzeJavaHomeAccessFeature.instance().printJavaHomeUsageLocations();
7672
}
77-
78-
public static class Options {
79-
@Option(help = "Track System.getProperty(\\\"java.home\\\") usage in reachable parts of the project.")//
80-
public static final HostedOptionKey<Boolean> TrackJavaHomeAccess = new HostedOptionKey<>(false);
81-
82-
@Option(help = "Output all System.getProperty(\\\"java.home\\\") calls in reachable parts of the project.")//
83-
public static final HostedOptionKey<Boolean> TrackJavaHomeAccessDetailed = new HostedOptionKey<>(false) {
84-
@Override
85-
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
86-
if (newValue) {
87-
TrackJavaHomeAccess.update(values, true);
88-
}
89-
}
90-
};
91-
}
9273
}

0 commit comments

Comments
 (0)