Skip to content

Commit 5f82ac4

Browse files
committed
Add dynamic access detection phase
1 parent f734b47 commit 5f82ac4

17 files changed

+924
-52
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: 25 additions & 1 deletion
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
@@ -45,4 +46,27 @@
4546
* Documents the reason why the annotated code must not be inlined.
4647
*/
4748
String value();
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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,4 +1435,33 @@ public static boolean printClosedArenaUponThrow() {
14351435
return PrintClosedArenaUponThrow.getValue();
14361436
}
14371437

1438+
@Option(help = "file:doc-files/TrackDynamicAccessHelp.txt")//
1439+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> TrackDynamicAccess = new HostedOptionKey<>(
1440+
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()) {
1441+
@Override
1442+
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, AccumulatingLocatableMultiOptionValue.Strings oldValue, AccumulatingLocatableMultiOptionValue.Strings newValue) {
1443+
NeverInline.update(values, "java.lang.invoke.MethodHandles$Lookup.unreflectGetter");
1444+
NeverInline.update(values, "java.lang.invoke.MethodHandles$Lookup.unreflectSetter");
1445+
NeverInline.update(values, "java.io.ObjectInputStream.readObject");
1446+
NeverInline.update(values, "java.io.ObjectStreamClass.lookup");
1447+
NeverInline.update(values, "java.lang.reflect.Array.newInstance");
1448+
NeverInline.update(values, "java.lang.ClassLoader.loadClass");
1449+
}
1450+
};
1451+
1452+
@Option(help = "Output all method calls requiring metadata for dynamic access found by -H:TrackDynamicAccess to the console.")//
1453+
public static final HostedOptionKey<Boolean> ReportDynamicAccessToConsole = new HostedOptionKey<>(false);
1454+
1455+
@Option(help = "Track System.getProperty(\\\"java.home\\\") usage in reachable parts of the project.")//
1456+
public static final HostedOptionKey<Boolean> TrackJavaHomeAccess = new HostedOptionKey<>(false);
1457+
1458+
@Option(help = "Output all System.getProperty(\\\"java.home\\\") calls in reachable parts of the project.")//
1459+
public static final HostedOptionKey<Boolean> TrackJavaHomeAccessDetailed = new HostedOptionKey<>(false) {
1460+
@Override
1461+
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
1462+
if (newValue) {
1463+
TrackJavaHomeAccess.update(values, true);
1464+
}
1465+
}
1466+
};
14381467
}
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 TrackDynamicAccessEnabled implements BooleanSupplier {
30+
31+
@Override
32+
public boolean getAsBoolean() {
33+
return SubstrateOptions.TrackDynamicAccess.hasBeenSet();
34+
}
35+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Report all method calls requiring metadata for dynamic access in the reachable parts of the project.
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|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+
14+
To output the detected calls to the console as well, use -H:+ReportDynamicAccessToConsole in conjunction with -H:TrackDynamicAccess.
15+
16+
Example of the option usage:
17+
18+
native-image -cp lib/app.jar:lib/util.jar:lib/tck.jar -H:TrackDynamicAccess=path=lib/app.jar,path=lib/util.jar HelloWorld
19+
20+
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;
@@ -1587,18 +1589,21 @@ private static Constructor<?>[] copyConstructors(Constructor<?>[] original) {
15871589
private native Constructor<?> getEnclosingConstructor();
15881590

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

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

16011605
@Substitute
1606+
@NeverInlineTrivial(value = "Used in dynamic access call usage analysis: DynamicAccessDetectionPhase", onlyWith = TrackDynamicAccessEnabled.class)
16021607
@CallerSensitive
16031608
private static Class<?> forName(Module module, String className) throws Throwable {
16041609
return forName(module, className, Reflection.getCallerClass());
@@ -1618,6 +1623,7 @@ private static Class<?> forName(@SuppressWarnings("unused") Module module, Strin
16181623
}
16191624

16201625
@Substitute
1626+
@NeverInlineTrivial(value = "Used in dynamic access call usage analysis: DynamicAccessDetectionPhase", onlyWith = TrackDynamicAccessEnabled.class)
16211627
@CallerSensitive
16221628
private static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws Throwable {
16231629
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(value = "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(value = "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)