Skip to content

Commit 601b657

Browse files
committed
Add dynamic access detection phase
1 parent ae016b5 commit 601b657

18 files changed

+955
-54
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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,4 +1471,24 @@ private static void validateRelativeCodePointers(HostedOptionKey<Boolean> option
14711471
UserError.guarantee(!PLTGOTConfiguration.isEnabled(), "%s cannot be used together with PLT/GOT.", enabledOption);
14721472
}
14731473
}
1474+
1475+
@Option(help = "file:doc-files/TrackDynamicAccessHelp.txt")//
1476+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> TrackDynamicAccess = new HostedOptionKey<>(
1477+
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter());
1478+
1479+
@Option(help = "Output all method calls requiring metadata for dynamic access found by -H:TrackDynamicAccess to the console.")//
1480+
public static final HostedOptionKey<Boolean> ReportDynamicAccessToConsole = new HostedOptionKey<>(false);
1481+
1482+
@Option(help = "Track System.getProperty(\\\"java.home\\\") usage in reachable parts of the project.")//
1483+
public static final HostedOptionKey<Boolean> TrackJavaHomeAccess = new HostedOptionKey<>(false);
1484+
1485+
@Option(help = "Output all System.getProperty(\\\"java.home\\\") calls in reachable parts of the project.")//
1486+
public static final HostedOptionKey<Boolean> TrackJavaHomeAccessDetailed = new HostedOptionKey<>(false) {
1487+
@Override
1488+
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
1489+
if (newValue) {
1490+
TrackJavaHomeAccess.update(values, true);
1491+
}
1492+
}
1493+
};
14741494
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 TrackDynamicAccessEnabled implements BooleanSupplier {
30+
31+
private static boolean trackDynamicAccessEnabled = false;
32+
33+
@Override
34+
public boolean getAsBoolean() {
35+
return isTrackDynamicAccessEnabled();
36+
}
37+
38+
public static void trackDynamicAccess(boolean value) {
39+
trackDynamicAccessEnabled = value;
40+
}
41+
42+
public static boolean isTrackDynamicAccessEnabled() {
43+
return trackDynamicAccessEnabled;
44+
}
45+
}
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
@@ -83,6 +83,7 @@
8383
import java.util.function.BiFunction;
8484
import java.util.function.IntFunction;
8585

86+
import com.oracle.svm.core.TrackDynamicAccessEnabled;
8687
import org.graalvm.nativeimage.AnnotationAccess;
8788
import org.graalvm.nativeimage.ImageSingletons;
8889
import org.graalvm.nativeimage.Platform;
@@ -92,6 +93,7 @@
9293
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
9394
import com.oracle.svm.core.BuildPhaseProvider.CompileQueueFinished;
9495
import com.oracle.svm.core.NeverInline;
96+
import com.oracle.svm.core.NeverInlineTrivial;
9597
import com.oracle.svm.core.RuntimeAssertionsSupport;
9698
import com.oracle.svm.core.SubstrateUtil;
9799
import com.oracle.svm.core.Uninterruptible;
@@ -1585,18 +1587,21 @@ private static Constructor<?>[] copyConstructors(Constructor<?>[] original) {
15851587
private native Constructor<?> getEnclosingConstructor();
15861588

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

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

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

16181623
@Substitute
1624+
@NeverInlineTrivial(reason = "Used in dynamic access call usage analysis: DynamicAccessDetectionPhase", onlyWith = TrackDynamicAccessEnabled.class)
16191625
@CallerSensitive
16201626
private static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws Throwable {
16211627
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 & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,4 @@ public void printJavaHomeUsageLocations() {
7474
public void beforeCompilation(BeforeCompilationAccess access) {
7575
AnalyzeJavaHomeAccessFeature.instance().printJavaHomeUsageLocations();
7676
}
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-
}
9277
}

0 commit comments

Comments
 (0)