Skip to content

Commit a61b9bb

Browse files
committed
[GR-55885] Initialize FileSystemProvider at run time (future defaults)
PullRequest: graal/20640
2 parents a30bf8e + 2fe1d54 commit a61b9bb

File tree

7 files changed

+295
-10
lines changed

7 files changed

+295
-10
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/FutureDefaultsOptions.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public class FutureDefaultsOptions {
5454
private static final String NONE_NAME = "none";
5555
private static final String RUN_TIME_INITIALIZE_JDK_NAME = "run-time-initialized-jdk";
5656

57+
public static final String RUN_TIME_INITIALIZE_JDK_REASON = "Initialize JDK classes at run time (--" + OPTION_NAME + " includes " + RUN_TIME_INITIALIZE_JDK_NAME + ")";
58+
5759
private static final Set<String> ALL_VALUES = Set.of(RUN_TIME_INITIALIZE_JDK_NAME, ALL_NAME, NONE_NAME);
5860

5961
private static String futureDefaultsAllValues() {
@@ -66,7 +68,7 @@ private static String futureDefaultsAllValues() {
6668

6769
@APIOption(name = OPTION_NAME, defaultValue = DEFAULT_NAME) //
6870
@Option(help = "file:doc-files/FutureDefaultsHelp.txt", type = OptionType.User) //
69-
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> FutureDefaults = new HostedOptionKey<>(
71+
static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> FutureDefaults = new HostedOptionKey<>(
7072
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter());
7173

7274
private static EconomicSet<String> futureDefaults;

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 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
@@ -142,7 +142,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
142142
}
143143
}
144144

145-
@TargetClass(java.nio.file.spi.FileSystemProvider.class)
145+
@TargetClass(value = java.nio.file.spi.FileSystemProvider.class, onlyWith = JDKInitializedAtBuildTime.class)
146146
final class Target_java_nio_file_spi_FileSystemProvider {
147147
@Substitute
148148
public static List<FileSystemProvider> installedProviders() {
@@ -171,7 +171,7 @@ public static List<FileSystemProvider> installedProviders() {
171171
* c) Allow UnixFileSystem in the image heap and recompute state at run time on first acccess. This
172172
* approach is implemented here.
173173
*/
174-
@TargetClass(className = "sun.nio.fs.UnixFileSystem")
174+
@TargetClass(className = "sun.nio.fs.UnixFileSystem", onlyWith = JDKInitializedAtBuildTime.class)
175175
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
176176
final class Target_sun_nio_fs_UnixFileSystem {
177177

@@ -224,12 +224,12 @@ final class Target_sun_nio_fs_UnixFileSystem {
224224
native void originalConstructor(Target_sun_nio_fs_UnixFileSystemProvider p, String dir);
225225
}
226226

227-
@TargetClass(className = "sun.nio.fs.UnixFileSystemProvider")
227+
@TargetClass(className = "sun.nio.fs.UnixFileSystemProvider", onlyWith = JDKInitializedAtBuildTime.class)
228228
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
229229
final class Target_sun_nio_fs_UnixFileSystemProvider {
230230
}
231231

232-
@TargetClass(className = "sun.nio.fs.UnixPath")
232+
@TargetClass(className = "sun.nio.fs.UnixPath", onlyWith = JDKInitializedAtBuildTime.class)
233233
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
234234
final class Target_sun_nio_fs_UnixPath {
235235
}
@@ -403,7 +403,7 @@ private static synchronized void reinitialize(Target_sun_nio_fs_WindowsFileSyste
403403
}
404404
}
405405

406-
@TargetClass(className = "java.io.UnixFileSystem")
406+
@TargetClass(className = "java.io.UnixFileSystem", onlyWith = JDKInitializedAtBuildTime.class)
407407
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
408408
final class Target_java_io_UnixFileSystem {
409409

@@ -412,7 +412,7 @@ final class Target_java_io_UnixFileSystem {
412412
private String userDir;
413413
}
414414

415-
@TargetClass(className = "java.io.FileSystem")
415+
@TargetClass(className = "java.io.FileSystem", onlyWith = JDKInitializedAtBuildTime.class)
416416
final class Target_java_io_FileSystem {
417417

418418
@Alias
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.jdk;
26+
27+
import java.util.function.BooleanSupplier;
28+
29+
import com.oracle.svm.core.FutureDefaultsOptions;
30+
31+
public class JDKInitializedAtBuildTime implements BooleanSupplier {
32+
@Override
33+
public boolean getAsBoolean() {
34+
return !FutureDefaultsOptions.isJDKInitializedAtRunTime();
35+
}
36+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.jdk;
26+
27+
import java.util.function.BooleanSupplier;
28+
29+
import com.oracle.svm.core.FutureDefaultsOptions;
30+
31+
public class JDKInitializedAtRunTime implements BooleanSupplier {
32+
@Override
33+
public boolean getAsBoolean() {
34+
return FutureDefaultsOptions.isJDKInitializedAtRunTime();
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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+
26+
package com.oracle.svm.core.jdk.runtimeinit;
27+
28+
import java.nio.file.FileSystem;
29+
import java.nio.file.spi.FileSystemProvider;
30+
31+
import org.graalvm.nativeimage.Platform;
32+
import org.graalvm.nativeimage.Platforms;
33+
34+
import com.oracle.svm.core.SubstrateUtil;
35+
import com.oracle.svm.core.annotate.Alias;
36+
import com.oracle.svm.core.annotate.InjectAccessors;
37+
import com.oracle.svm.core.annotate.TargetClass;
38+
import com.oracle.svm.core.jdk.JDKInitializedAtRunTime;
39+
import com.oracle.svm.core.util.BasedOnJDKFile;
40+
import com.oracle.svm.core.util.VMError;
41+
import com.oracle.svm.util.ReflectionUtil;
42+
43+
/**
44+
* This file contains substitutions that are required for initializing {@link FileSystemProvider} at
45+
* image run time. Other related functionality (general and build time initialization) can be found
46+
* in {@link com.oracle.svm.core.jdk.FileSystemProviderSupport}.
47+
*
48+
* @see JDKInitializedAtRunTime
49+
* @see com.oracle.svm.core.jdk.FileSystemProviderSupport
50+
*/
51+
final class FileSystemProviderRuntimeInitSupport {
52+
}
53+
54+
// java.io
55+
56+
@TargetClass(className = "java.io.FileSystem", onlyWith = JDKInitializedAtRunTime.class)
57+
final class Target_java_io_FileSystem_RunTime {
58+
}
59+
60+
@TargetClass(className = "java.io.File", onlyWith = JDKInitializedAtRunTime.class)
61+
@SuppressWarnings("unused")
62+
final class Target_java_io_File_RunTime {
63+
@Alias //
64+
@InjectAccessors(DefaultFileSystemAccessor.class) //
65+
private static Target_java_io_FileSystem_RunTime FS;
66+
}
67+
68+
@TargetClass(className = "java.io.DefaultFileSystem", onlyWith = JDKInitializedAtRunTime.class)
69+
final class Target_java_io_DefaultFileSystem_RunTime {
70+
@Alias
71+
static native Target_java_io_FileSystem_RunTime getFileSystem();
72+
}
73+
74+
/**
75+
* Holds the default java.io file system. Initialized at run time via
76+
* {@code JDKInitializationFeature}. This cache is needed because
77+
* {@link Target_java_io_DefaultFileSystem_RunTime#getFileSystem()} creates a new instance for every
78+
* time. In the JDK, this method is called only once.
79+
*/
80+
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+25/src/java.base/unix/classes/java/io/DefaultFileSystem.java#L39-L41")
81+
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+25/src/java.base/windows/classes/java/io/DefaultFileSystem.java#L39-L41")
82+
class DefaultFileSystemHolder {
83+
static final Object FS;
84+
static {
85+
if (SubstrateUtil.HOSTED) {
86+
/*
87+
* Should be unused, but layered images might want to initialize it during image build.
88+
* We set it to the real default file system instead of null to guard against
89+
* unintentional usages. In run-time init mode, we don't allow FileSystems in the image
90+
* heap, so we would fail the image build with an exception if it happens, which helps
91+
* to detect problems.
92+
*/
93+
var defaultFileSystem = ReflectionUtil.lookupClass("java.io.DefaultFileSystem");
94+
var getFileSystem = ReflectionUtil.lookupMethod(defaultFileSystem, "getFileSystem");
95+
FS = ReflectionUtil.invokeMethod(getFileSystem, null);
96+
} else {
97+
FS = Target_java_io_DefaultFileSystem_RunTime.getFileSystem();
98+
}
99+
}
100+
}
101+
102+
class DefaultFileSystemAccessor {
103+
@SuppressWarnings("unused")
104+
static Target_java_io_FileSystem_RunTime get() {
105+
VMError.guarantee(DefaultFileSystemHolder.FS != null, "DefaultFileSystemHolder.FS is null");
106+
return SubstrateUtil.cast(DefaultFileSystemHolder.FS, Target_java_io_FileSystem_RunTime.class);
107+
}
108+
}
109+
110+
// sun.nio.fs
111+
112+
@TargetClass(className = "sun.nio.fs.DefaultFileSystemProvider", onlyWith = JDKInitializedAtRunTime.class)
113+
final class Target_sun_nio_fs_DefaultFileSystemProvider_RunTime {
114+
@Alias
115+
static native FileSystem theFileSystem();
116+
}
117+
118+
@TargetClass(className = "sun.nio.fs.UnixFileSystem", onlyWith = JDKInitializedAtRunTime.class)
119+
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
120+
final class Target_sun_nio_fs_UnixFileSystem_RunTime {
121+
}
122+
123+
@TargetClass(className = "sun.nio.fs.UnixPath", onlyWith = JDKInitializedAtRunTime.class)
124+
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
125+
final class Target_sun_nio_fs_UnixPath_RunTime {
126+
@Alias //
127+
@InjectAccessors(UnixFileSystemAccessor.class) //
128+
private Target_sun_nio_fs_UnixFileSystem_RunTime fs;
129+
}
130+
131+
@SuppressWarnings("unused")
132+
class UnixFileSystemAccessor {
133+
static Target_sun_nio_fs_UnixFileSystem_RunTime get(Target_sun_nio_fs_UnixPath_RunTime that) {
134+
FileSystem theFileSystem = Target_sun_nio_fs_DefaultFileSystemProvider_RunTime.theFileSystem();
135+
VMError.guarantee(theFileSystem != null, "DefaultFileSystemProvider.theFileSystem() is null");
136+
return SubstrateUtil.cast(theFileSystem, Target_sun_nio_fs_UnixFileSystem_RunTime.class);
137+
}
138+
139+
static void set(Target_sun_nio_fs_UnixPath_RunTime that, Target_sun_nio_fs_UnixFileSystem_RunTime value) {
140+
/*
141+
* `value` should always be DefaultFileSystemProvider.INSTANCE.theFileSystem() but we cannot
142+
* check that here because it would introduce a class initialization cycle.
143+
*/
144+
}
145+
}
146+
147+
@TargetClass(className = "sun.nio.fs.WindowsFileSystem", onlyWith = JDKInitializedAtRunTime.class)
148+
@Platforms(Platform.WINDOWS.class)
149+
final class Target_sun_nio_fs_WindowsFileSystem_RunTime {
150+
}
151+
152+
@TargetClass(className = "sun.nio.fs.WindowsPath", onlyWith = JDKInitializedAtRunTime.class)
153+
@Platforms(Platform.WINDOWS.class)
154+
final class Target_sun_nio_fs_WindowsPath_RunTime {
155+
@Alias //
156+
@InjectAccessors(WindowsFileSystemAccessor.class) //
157+
private Target_sun_nio_fs_WindowsFileSystem_RunTime fs;
158+
}
159+
160+
@SuppressWarnings("unused")
161+
class WindowsFileSystemAccessor {
162+
static Target_sun_nio_fs_WindowsFileSystem_RunTime get(Target_sun_nio_fs_WindowsPath_RunTime that) {
163+
FileSystem theFileSystem = Target_sun_nio_fs_DefaultFileSystemProvider_RunTime.theFileSystem();
164+
VMError.guarantee(theFileSystem != null, "DefaultFileSystemProvider.theFileSystem() is null");
165+
return SubstrateUtil.cast(theFileSystem, Target_sun_nio_fs_WindowsFileSystem_RunTime.class);
166+
}
167+
168+
static void set(Target_sun_nio_fs_WindowsPath_RunTime that, Target_sun_nio_fs_WindowsFileSystem_RunTime value) {
169+
/*
170+
* `value` should always be DefaultFileSystemProvider.INSTANCE.theFileSystem() but we cannot
171+
* check that here because it would introduce a class initialization cycle.
172+
*/
173+
}
174+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JDKInitializationFeature.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 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
@@ -142,6 +142,42 @@ public void afterRegistration(AfterRegistrationAccess access) {
142142
rci.initializeAtBuildTime("java.awt.font.NumericShaper", "Required for sun.text.bidi.BidiBase.NumericShapings");
143143
rci.initializeAtBuildTime("java.awt.font.JavaAWTFontAccessImpl", "Required for sun.text.bidi.BidiBase.NumericShapings");
144144

145+
/* FileSystemProviders related */
146+
if (FutureDefaultsOptions.isJDKInitializedAtRunTime()) {
147+
rci.initializeAtRunTime("java.nio.file.spi", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
148+
rci.initializeAtRunTime("sun.nio.fs", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
149+
150+
rci.initializeAtRunTime("java.nio.file.FileSystems", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
151+
rci.initializeAtRunTime("java.nio.file.FileSystems$DefaultFileSystemHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
152+
153+
rci.initializeAtRunTime("java.util.zip.ZipFile$Source", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
154+
rci.initializeAtRunTime("java.util.zip.ZipFile$Source", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
155+
156+
rci.initializeAtRunTime("java.io.FileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
157+
rci.initializeAtRunTime("java.io.FileSystem$CurrentWorkingDirectoryHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
158+
rci.initializeAtRunTime("java.io.UnixFileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
159+
rci.initializeAtRunTime("java.io.WindowsFileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
160+
161+
/* Holder for the default file system. */
162+
rci.initializeAtRunTime("com.oracle.svm.core.jdk.runtimeinit.DefaultFileSystemHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
163+
164+
/*
165+
* The following need to be build-time initialized because they can end up in the image
166+
* heap. There are substitutions to patch the links to run-time initialized classes like
167+
* FileSystem or FileSystemProvider.
168+
*/
169+
170+
/*
171+
* Require explicit initializeAtBuildTime because the sun.nio.fs is registered for
172+
* run-time initialization.
173+
*/
174+
rci.initializeAtBuildTime("sun.nio.fs.UnixPath", "Allow UnixPath objects in the image heap (" + FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON + ")");
175+
rci.initializeAtBuildTime("sun.nio.fs.WindowsPath", "Allow WindowsPath objects in the image heap (" + FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON + ")");
176+
177+
/* JrtFS support. */
178+
rci.initializeAtBuildTime("jdk.internal.jrtfs.SystemImage", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
179+
}
180+
145181
/* XML-related */
146182
if (FutureDefaultsOptions.isJDKInitializedAtRunTime()) {
147183
// GR-50683 should remove this part

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JDKRegistrations.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 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
@@ -65,6 +65,7 @@ public void duringSetup(DuringSetupAccess a) {
6565
* a `Random` object and the temporary directory in a static final field.
6666
*/
6767
initializeAtRunTime(a, "sun.nio.ch.UnixDomainSockets");
68+
initializeAtRunTime(a, "sun.nio.ch.UnixDomainSockets$UnnamedHolder");
6869

6970
initializeAtRunTime(a, "java.util.concurrent.ThreadLocalRandom$ThreadLocalRandomProxy");
7071

0 commit comments

Comments
 (0)