Skip to content

Commit 7fa6385

Browse files
author
Christian Wimmer
committed
Add new class initialization strategy that allows all classes to be used at image build time
1 parent 72f73fd commit 7fa6385

18 files changed

+683
-434
lines changed

substratevm/mx.substratevm/suite.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,7 @@
11741174
"com.oracle.truffle.api.instrumentation.TruffleInstrument.Provider",
11751175
"com.oracle.svm.hosted.agent.NativeImageBytecodeInstrumentationAgentExtension",
11761176
"com.oracle.svm.hosted.NativeImageClassLoaderPostProcessing",
1177+
"java.util.spi.ResourceBundleControlProvider",
11771178
],
11781179
"requiresConcealed": {
11791180
"jdk.internal.vm.ci": [

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
9595
* certificate files while generating X509Certificates.
9696
*/
9797
rci.initializeAtBuildTime("org.jcp.xml.dsig.internal.dom.XMLDSigRI", "Required for TrustStoreManager");
98+
rci.initializeAtBuildTime("org.jcp.xml.dsig.internal.dom.XMLDSigRI$ProviderService", "Required for TrustStoreManager");
9899
}
99100
}
100101

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementFeature.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ public void duringSetup(DuringSetupAccess access) {
8585
access.registerObjectReplacer(this::replaceHostedPlatformManagedObject);
8686

8787
RuntimeClassInitialization.initializeAtBuildTime("com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory");
88+
RuntimeClassInitialization.initializeAtBuildTime("com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory$Mappings");
89+
RuntimeClassInitialization.initializeAtBuildTime("com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory$IdentityMapping");
90+
RuntimeClassInitialization.initializeAtBuildTime("com.sun.jmx.mbeanserver.DescriptorCache");
91+
RuntimeClassInitialization.initializeAtBuildTime("com.sun.jmx.remote.util.ClassLogger");
8892
}
8993

9094
/**

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

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
*/
2525
package com.oracle.svm.hosted;
2626

27+
import static com.oracle.graal.pointsto.api.PointstoOptions.UseExperimentalReachabilityAnalysis;
2728
import static com.oracle.svm.hosted.NativeImageOptions.DiagnosticsDir;
2829
import static com.oracle.svm.hosted.NativeImageOptions.DiagnosticsMode;
2930
import static org.graalvm.compiler.hotspot.JVMCIVersionCheck.OPEN_LABSJDK_RELEASE_URL_PATTERN;
30-
import static com.oracle.graal.pointsto.api.PointstoOptions.UseExperimentalReachabilityAnalysis;
3131
import static org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.registerInvocationPlugins;
3232

3333
import java.io.IOException;
@@ -56,18 +56,6 @@
5656
import java.util.function.BooleanSupplier;
5757
import java.util.stream.Collectors;
5858

59-
import com.oracle.graal.pointsto.ObjectScanningObserver;
60-
import com.oracle.graal.pointsto.PointsToAnalysis;
61-
import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder;
62-
import com.oracle.graal.pointsto.meta.AnalysisFactory;
63-
import com.oracle.graal.pointsto.meta.PointsToAnalysisFactory;
64-
import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod;
65-
import com.oracle.graal.reachability.MethodSummaryProvider;
66-
import com.oracle.graal.reachability.ReachabilityAnalysisFactory;
67-
import com.oracle.graal.reachability.ReachabilityObjectScanner;
68-
import com.oracle.svm.hosted.analysis.NativeImageReachabilityAnalysisEngine;
69-
import com.oracle.graal.pointsto.util.TimerCollection;
70-
import com.oracle.svm.util.AnnotationExtracter;
7159
import org.graalvm.collections.EconomicSet;
7260
import org.graalvm.collections.Pair;
7361
import org.graalvm.compiler.api.replacements.Fold;
@@ -143,28 +131,38 @@
143131
import com.oracle.graal.pointsto.AnalysisObjectScanningObserver;
144132
import com.oracle.graal.pointsto.AnalysisPolicy;
145133
import com.oracle.graal.pointsto.BigBang;
146-
import com.oracle.graal.pointsto.typestate.DefaultAnalysisPolicy;
134+
import com.oracle.graal.pointsto.ObjectScanningObserver;
135+
import com.oracle.graal.pointsto.PointsToAnalysis;
147136
import com.oracle.graal.pointsto.api.PointstoOptions;
148137
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
149138
import com.oracle.graal.pointsto.flow.FormalParamTypeFlow;
139+
import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder;
150140
import com.oracle.graal.pointsto.flow.TypeFlow;
151141
import com.oracle.graal.pointsto.flow.context.bytecode.BytecodeSensitiveAnalysisPolicy;
152142
import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier;
153143
import com.oracle.graal.pointsto.heap.ImageHeap;
154144
import com.oracle.graal.pointsto.heap.ImageHeapScanner;
155145
import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor;
156146
import com.oracle.graal.pointsto.infrastructure.WrappedJavaMethod;
147+
import com.oracle.graal.pointsto.meta.AnalysisFactory;
157148
import com.oracle.graal.pointsto.meta.AnalysisField;
158149
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
159150
import com.oracle.graal.pointsto.meta.AnalysisMethod;
160151
import com.oracle.graal.pointsto.meta.AnalysisType;
161152
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
162153
import com.oracle.graal.pointsto.meta.HostedProviders;
154+
import com.oracle.graal.pointsto.meta.PointsToAnalysisFactory;
155+
import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod;
163156
import com.oracle.graal.pointsto.reports.AnalysisReporter;
157+
import com.oracle.graal.pointsto.typestate.DefaultAnalysisPolicy;
164158
import com.oracle.graal.pointsto.typestate.TypeState;
165159
import com.oracle.graal.pointsto.util.AnalysisError;
166160
import com.oracle.graal.pointsto.util.GraalAccess;
167161
import com.oracle.graal.pointsto.util.Timer.StopTimer;
162+
import com.oracle.graal.pointsto.util.TimerCollection;
163+
import com.oracle.graal.reachability.MethodSummaryProvider;
164+
import com.oracle.graal.reachability.ReachabilityAnalysisFactory;
165+
import com.oracle.graal.reachability.ReachabilityObjectScanner;
168166
import com.oracle.svm.core.BuildArtifacts;
169167
import com.oracle.svm.core.BuildArtifacts.ArtifactType;
170168
import com.oracle.svm.core.BuildPhaseProvider;
@@ -241,6 +239,7 @@
241239
import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider;
242240
import com.oracle.svm.hosted.analysis.Inflation;
243241
import com.oracle.svm.hosted.analysis.NativeImagePointsToAnalysis;
242+
import com.oracle.svm.hosted.analysis.NativeImageReachabilityAnalysisEngine;
244243
import com.oracle.svm.hosted.analysis.SVMAnalysisMetaAccess;
245244
import com.oracle.svm.hosted.analysis.SubstrateUnsupportedFeatures;
246245
import com.oracle.svm.hosted.annotation.AnnotationSupport;
@@ -253,7 +252,6 @@
253252
import com.oracle.svm.hosted.cenum.CEnumCallWrapperSubstitutionProcessor;
254253
import com.oracle.svm.hosted.classinitialization.ClassInitializationFeature;
255254
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
256-
import com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization;
257255
import com.oracle.svm.hosted.code.CEntryPointCallStubSupport;
258256
import com.oracle.svm.hosted.code.CEntryPointData;
259257
import com.oracle.svm.hosted.code.CFunctionSubstitutionProcessor;
@@ -293,6 +291,7 @@
293291
import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor;
294292
import com.oracle.svm.hosted.substitute.DeletedFieldsPlugin;
295293
import com.oracle.svm.hosted.substitute.UnsafeAutomaticSubstitutionProcessor;
294+
import com.oracle.svm.util.AnnotationExtracter;
296295
import com.oracle.svm.util.ImageBuildStatistics;
297296
import com.oracle.svm.util.ReflectionUtil;
298297
import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError;
@@ -828,7 +827,7 @@ private void setupNativeImage(OptionValues options, Map<Method, CEntryPointData>
828827
ImageSingletons.add(ClassLoaderSupport.class, classLoaderSupport);
829828
ImageSingletons.add(LinkAtBuildTimeSupport.class, new LinkAtBuildTimeSupport(loader, classLoaderSupport));
830829

831-
ClassInitializationSupport classInitializationSupport = new ConfigurableClassInitialization(originalMetaAccess, loader);
830+
ClassInitializationSupport classInitializationSupport = ClassInitializationSupport.create(originalMetaAccess, loader);
832831
ImageSingletons.add(RuntimeClassInitializationSupport.class, classInitializationSupport);
833832
ClassInitializationFeature.processClassInitializationOptions(classInitializationSupport);
834833

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
* Copyright (c) 2022, 2022, 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.hosted.classinitialization;
26+
27+
import java.lang.reflect.Proxy;
28+
29+
import org.graalvm.compiler.java.LambdaUtils;
30+
31+
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
32+
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
33+
import com.oracle.svm.core.util.UserError;
34+
import com.oracle.svm.core.util.VMError;
35+
import com.oracle.svm.hosted.ImageClassLoader;
36+
37+
import jdk.internal.misc.Unsafe;
38+
import jdk.vm.ci.meta.MetaAccessProvider;
39+
40+
class AllowAllHostedUsagesClassInitializationSupport extends ClassInitializationSupport {
41+
42+
AllowAllHostedUsagesClassInitializationSupport(MetaAccessProvider metaAccess, ImageClassLoader loader) {
43+
super(metaAccess, loader);
44+
}
45+
46+
@Override
47+
public void initializeAtRunTime(Class<?> clazz, String reason) {
48+
UserError.guarantee(!configurationSealed, "The class initialization configuration can be changed only before the phase analysis.");
49+
classInitializationConfiguration.insert(clazz.getTypeName(), InitKind.RUN_TIME, reason, true);
50+
setSubclassesAsRunTime(clazz);
51+
}
52+
53+
@Override
54+
public void rerunInitialization(Class<?> clazz, String reason) {
55+
UserError.guarantee(!configurationSealed, "The class initialization configuration can be changed only before the phase analysis.");
56+
classInitializationConfiguration.insert(clazz.getTypeName(), InitKind.RERUN, reason, true);
57+
58+
try {
59+
Unsafe.getUnsafe().ensureClassInitialized(clazz);
60+
} catch (Throwable ex) {
61+
throw UserError.abort(ex, "Class initialization failed for %s. The class is requested for re-running (reason: %s)", clazz.getTypeName(), reason);
62+
}
63+
}
64+
65+
@Override
66+
String reasonForClass(Class<?> clazz) {
67+
InitKind initKind = classInitKinds.get(clazz);
68+
String reason = classInitializationConfiguration.lookupReason(clazz.getTypeName());
69+
if (initKind.isRunTime()) {
70+
return "classes are initialized at run time by default";
71+
} else if (reason != null) {
72+
return reason;
73+
} else {
74+
throw VMError.shouldNotReachHere("Must be either proven or specified");
75+
}
76+
}
77+
78+
private void setSubclassesAsRunTime(Class<?> clazz) {
79+
if (clazz.isInterface() && !metaAccess.lookupJavaType(clazz).declaresDefaultMethods()) {
80+
/*
81+
* An interface that does not declare a default method is independent from a class
82+
* initialization point of view, i.e., it is not initialized when a class implementing
83+
* that interface is initialized.
84+
*/
85+
return;
86+
}
87+
loader.findSubclasses(clazz, false).stream()
88+
.filter(c -> !c.equals(clazz))
89+
.filter(c -> !(c.isInterface() && !metaAccess.lookupJavaType(c).declaresDefaultMethods()))
90+
.forEach(c -> classInitializationConfiguration.insert(c.getTypeName(), InitKind.RUN_TIME, "subtype of " + clazz.getTypeName(), true));
91+
}
92+
93+
@Override
94+
public void forceInitializeHosted(Class<?> clazz, String reason, boolean allowInitializationErrors) {
95+
if (clazz == null) {
96+
return;
97+
}
98+
classInitializationConfiguration.insert(clazz.getTypeName(), InitKind.BUILD_TIME, reason, true);
99+
InitKind initKind = ensureClassInitialized(clazz, allowInitializationErrors);
100+
classInitKinds.put(clazz, initKind);
101+
102+
forceInitializeHosted(clazz.getSuperclass(), "super type of " + clazz.getTypeName(), allowInitializationErrors);
103+
forceInitializeInterfaces(clazz.getInterfaces(), "super type of " + clazz.getTypeName());
104+
}
105+
106+
private void forceInitializeInterfaces(Class<?>[] interfaces, String reason) {
107+
for (Class<?> iface : interfaces) {
108+
if (metaAccess.lookupJavaType(iface).declaresDefaultMethods()) {
109+
classInitializationConfiguration.insert(iface.getTypeName(), InitKind.BUILD_TIME, reason, true);
110+
111+
ensureClassInitialized(iface, false);
112+
classInitKinds.put(iface, InitKind.BUILD_TIME);
113+
}
114+
forceInitializeInterfaces(iface.getInterfaces(), "super type of " + iface.getTypeName());
115+
}
116+
}
117+
118+
@Override
119+
boolean checkDelayedInitialization() {
120+
/* Nothing to check, all classes are allowed to be initialized in the image builder VM. */
121+
return true;
122+
}
123+
124+
@Override
125+
InitKind computeInitKindAndMaybeInitializeClass(Class<?> clazz) {
126+
return computeInitKindAndMaybeInitializeClass(clazz, true);
127+
}
128+
129+
/**
130+
* Computes the class initialization kind of the provided class, all superclasses, and all
131+
* interfaces that the provided class depends on (i.e., interfaces implemented by the provided
132+
* class that declare default methods).
133+
*
134+
* Also defines class initialization based on a policy of the subclass.
135+
*/
136+
InitKind computeInitKindAndMaybeInitializeClass(Class<?> clazz, boolean memoize) {
137+
InitKind existing = classInitKinds.get(clazz);
138+
if (existing != null) {
139+
return existing;
140+
}
141+
142+
if (clazz.isPrimitive()) {
143+
forceInitializeHosted(clazz, "primitive types are initialized at build time", false);
144+
return InitKind.BUILD_TIME;
145+
}
146+
147+
if (clazz.isArray()) {
148+
forceInitializeHosted(clazz, "arrays are initialized at build time", false);
149+
return InitKind.BUILD_TIME;
150+
}
151+
152+
if (clazz.getTypeName().contains("$$StringConcat")) {
153+
forceInitializeHosted(clazz, "string concatenation classes are initialized at build time", false);
154+
return InitKind.BUILD_TIME;
155+
}
156+
157+
InitKind specifiedInitKind = specifiedInitKindFor(clazz);
158+
InitKind clazzResult = specifiedInitKind != null ? specifiedInitKind : InitKind.RUN_TIME;
159+
160+
InitKind superResult = InitKind.BUILD_TIME;
161+
if (clazz.getSuperclass() != null) {
162+
superResult = superResult.max(computeInitKindAndMaybeInitializeClass(clazz.getSuperclass(), memoize));
163+
}
164+
superResult = superResult.max(processInterfaces(clazz, memoize));
165+
166+
if (superResult == InitKind.BUILD_TIME && (Proxy.isProxyClass(clazz) || LambdaUtils.isLambdaType(metaAccess.lookupJavaType(clazz)))) {
167+
forceInitializeHosted(clazz, "proxy/lambda classes with interfaces initialized at build time are also initialized at build time", false);
168+
return InitKind.BUILD_TIME;
169+
}
170+
171+
InitKind result = superResult.max(clazzResult);
172+
173+
if (memoize) {
174+
if (!result.isRunTime()) {
175+
result = result.max(ensureClassInitialized(clazz, false));
176+
}
177+
178+
/*
179+
* Unfortunately, the computation of canInitializeWithoutSideEffects is not completely
180+
* deterministic: Consider a class A whose class initializer depends on class B. Assume
181+
* class B has no other dependencies and can therefore be initialized at build time.
182+
* When class A is analyzed after class B has been initialized, it can also be
183+
* initialized at build time. But when class A is analyzed before class B has been
184+
* initialized, it cannot. Since two threads can analyze class A at the same time (there
185+
* is no per-class locking) and another thread can initialize B at the same time, we can
186+
* have a conflicting initialization status. In that case, BUILD_TIME must win over
187+
* RUN_TIME because one thread has already initialized class A.
188+
*/
189+
result = classInitKinds.merge(clazz, result, InitKind::min);
190+
}
191+
return result;
192+
}
193+
194+
private InitKind processInterfaces(Class<?> clazz, boolean memoizeEager) {
195+
/*
196+
* Note that we do not call computeInitKindForClass(clazz) on purpose: if clazz is the root
197+
* class or an interface declaring default methods, then
198+
* computeInitKindAndMaybeInitializeClass() already calls computeInitKindForClass. If the
199+
* interface does not declare default methods, than we must not take the InitKind of that
200+
* interface into account, because interfaces without default methods are independent from a
201+
* class initialization point of view.
202+
*/
203+
InitKind result = InitKind.BUILD_TIME;
204+
205+
for (Class<?> iface : clazz.getInterfaces()) {
206+
if (metaAccess.lookupJavaType(iface).declaresDefaultMethods()) {
207+
/*
208+
* An interface that declares default methods is initialized when a class
209+
* implementing it is initialized. So we need to inherit the InitKind from such an
210+
* interface.
211+
*/
212+
result = result.max(computeInitKindAndMaybeInitializeClass(iface, memoizeEager));
213+
} else {
214+
/*
215+
* An interface that does not declare default methods is independent from a class
216+
* that implements it, i.e., the interface can still be uninitialized even when the
217+
* class is initialized.
218+
*/
219+
result = result.max(processInterfaces(iface, memoizeEager));
220+
}
221+
}
222+
return result;
223+
}
224+
225+
@Override
226+
void doLateInitialization(AnalysisUniverse aUniverse, AnalysisMetaAccess aMetaAccess) {
227+
/* Nothing for now. */
228+
}
229+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
* Every node tracks a list of reasons for the set configuration. This list helps the users debug
5454
* conflicts in the configuration.
5555
*/
56-
public class ClassInitializationConfiguration {
56+
final class ClassInitializationConfiguration {
5757
private static final String ROOT_QUALIFIER = "";
5858
private static final int MAX_NUMBER_OF_REASONS = 10;
5959

0 commit comments

Comments
 (0)