Skip to content

Commit efdd5ab

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

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
@@ -1471,4 +1471,33 @@ private static void validateRelativeCodePointers(HostedOptionKey<Boolean> option
14711471
UserError.guarantee(!PLTGOTConfiguration.isEnabled(), "%s cannot be used together with PLT/GOT.", enabledOption);
14721472
}
14731473
}
1474+
@Option(help = "file:doc-files/TrackDynamicAccessHelp.txt")//
1475+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> TrackDynamicAccess = new HostedOptionKey<>(
1476+
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()) {
1477+
@Override
1478+
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, AccumulatingLocatableMultiOptionValue.Strings oldValue, AccumulatingLocatableMultiOptionValue.Strings newValue) {
1479+
NeverInline.update(values, "java.lang.invoke.MethodHandles$Lookup.unreflectGetter");
1480+
NeverInline.update(values, "java.lang.invoke.MethodHandles$Lookup.unreflectSetter");
1481+
NeverInline.update(values, "java.io.ObjectInputStream.readObject");
1482+
NeverInline.update(values, "java.io.ObjectStreamClass.lookup");
1483+
NeverInline.update(values, "java.lang.reflect.Array.newInstance");
1484+
NeverInline.update(values, "java.lang.ClassLoader.loadClass");
1485+
}
1486+
};
1487+
1488+
@Option(help = "Output all method calls requiring metadata for dynamic access found by -H:TrackDynamicAccess to the console.")//
1489+
public static final HostedOptionKey<Boolean> ReportDynamicAccessToConsole = new HostedOptionKey<>(false);
1490+
1491+
@Option(help = "Track System.getProperty(\\\"java.home\\\") usage in reachable parts of the project.")//
1492+
public static final HostedOptionKey<Boolean> TrackJavaHomeAccess = new HostedOptionKey<>(false);
1493+
1494+
@Option(help = "Output all System.getProperty(\\\"java.home\\\") calls in reachable parts of the project.")//
1495+
public static final HostedOptionKey<Boolean> TrackJavaHomeAccessDetailed = new HostedOptionKey<>(false) {
1496+
@Override
1497+
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
1498+
if (newValue) {
1499+
TrackJavaHomeAccess.update(values, true);
1500+
}
1501+
}
1502+
};
14741503
}
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;
@@ -1585,18 +1587,21 @@ private static Constructor<?>[] copyConstructors(Constructor<?>[] original) {
15851587
private native Constructor<?> getEnclosingConstructor();
15861588

15871589
@Substitute
1590+
@NeverInlineTrivial(value = "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(value = "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(value = "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(value = "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(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)