Skip to content

Commit caea050

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

File tree

13 files changed

+829
-30
lines changed

13 files changed

+829
-30
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);

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+
* Avoid inlining only if all of the provided predicates are true (default: class that is never
52+
* inlined).
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+
* enforcing non-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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,4 +1435,21 @@ public static boolean printClosedArenaUponThrow() {
14351435
return PrintClosedArenaUponThrow.getValue();
14361436
}
14371437

1438+
1439+
@Option(help = "file:doc-files/TrackDynamicAccessHelp.txt")//
1440+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> TrackDynamicAccess = new HostedOptionKey<>(
1441+
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()) {
1442+
@Override
1443+
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, AccumulatingLocatableMultiOptionValue.Strings oldValue, AccumulatingLocatableMultiOptionValue.Strings newValue) {
1444+
NeverInline.update(values, "java.lang.invoke.MethodHandles$Lookup.unreflectGetter");
1445+
NeverInline.update(values, "java.lang.invoke.MethodHandles$Lookup.unreflectSetter");
1446+
NeverInline.update(values, "java.io.ObjectInputStream.readObject");
1447+
NeverInline.update(values, "java.io.ObjectStreamClass.lookup");
1448+
NeverInline.update(values, "java.lang.reflect.Array.newInstance");
1449+
NeverInline.update(values, "java.lang.ClassLoader.loadClass");
1450+
}
1451+
};
1452+
1453+
@Option(help = "Output all method calls requiring metadata for dynamic access found by -H:TrackDynamicAccess to the console.")//
1454+
public static final HostedOptionKey<Boolean> ReportDynamicAccessToConsole = new HostedOptionKey<>(false);
14381455
}
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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Serialize 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 classpath 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=[path=<classpath-entry>][,module=<module-name>][,package=<package-name>]
7+
8+
To output the detected calls to the console as well, use -H:+ReportDynamicAccessToConsole in conjunction with -H:TrackDynamicAccess.
9+
10+
Example of the option usage:
11+
12+
native-image -cp lib/app.jar:lib/util.jar:lib/tck.jar -H:TrackDynamicAccess=path=lib/app.jar,path=lib/util.jar HelloWorld
13+
14+
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());

0 commit comments

Comments
 (0)