Skip to content

[GR-55885] Initialize FileSystemProvider at run time (future defaults) #11205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public class FutureDefaultsOptions {
private static final String NONE_NAME = "none";
private static final String RUN_TIME_INITIALIZE_JDK_NAME = "run-time-initialized-jdk";

public static final String RUN_TIME_INITIALIZE_JDK_REASON = "Initialize JDK classes at run time (--" + OPTION_NAME + " includes " + RUN_TIME_INITIALIZE_JDK_NAME + ")";

private static final Set<String> ALL_VALUES = Set.of(RUN_TIME_INITIALIZE_JDK_NAME, ALL_NAME, NONE_NAME);

private static String futureDefaultsAllValues() {
Expand All @@ -66,7 +68,7 @@ private static String futureDefaultsAllValues() {

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

private static EconomicSet<String> futureDefaults;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -142,7 +142,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
}
}

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

Expand Down Expand Up @@ -224,12 +224,12 @@ final class Target_sun_nio_fs_UnixFileSystem {
native void originalConstructor(Target_sun_nio_fs_UnixFileSystemProvider p, String dir);
}

@TargetClass(className = "sun.nio.fs.UnixFileSystemProvider")
@TargetClass(className = "sun.nio.fs.UnixFileSystemProvider", onlyWith = JDKInitializedAtBuildTime.class)
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
final class Target_sun_nio_fs_UnixFileSystemProvider {
}

@TargetClass(className = "sun.nio.fs.UnixPath")
@TargetClass(className = "sun.nio.fs.UnixPath", onlyWith = JDKInitializedAtBuildTime.class)
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
final class Target_sun_nio_fs_UnixPath {
}
Expand Down Expand Up @@ -403,7 +403,7 @@ private static synchronized void reinitialize(Target_sun_nio_fs_WindowsFileSyste
}
}

@TargetClass(className = "java.io.UnixFileSystem")
@TargetClass(className = "java.io.UnixFileSystem", onlyWith = JDKInitializedAtBuildTime.class)
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
final class Target_java_io_UnixFileSystem {

Expand All @@ -412,7 +412,7 @@ final class Target_java_io_UnixFileSystem {
private String userDir;
}

@TargetClass(className = "java.io.FileSystem")
@TargetClass(className = "java.io.FileSystem", onlyWith = JDKInitializedAtBuildTime.class)
final class Target_java_io_FileSystem {

@Alias
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.jdk;

import java.util.function.BooleanSupplier;

import com.oracle.svm.core.FutureDefaultsOptions;

public class JDKInitializedAtBuildTime implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
return !FutureDefaultsOptions.isJDKInitializedAtRunTime();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.jdk;

import java.util.function.BooleanSupplier;

import com.oracle.svm.core.FutureDefaultsOptions;

public class JDKInitializedAtRunTime implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
return FutureDefaultsOptions.isJDKInitializedAtRunTime();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package com.oracle.svm.core.jdk.runtimeinit;

import java.nio.file.FileSystem;
import java.nio.file.spi.FileSystemProvider;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.InjectAccessors;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.jdk.JDKInitializedAtRunTime;
import com.oracle.svm.core.util.BasedOnJDKFile;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;

/**
* This file contains substitutions that are required for initializing {@link FileSystemProvider} at
* image run time. Other related functionality (general and build time initialization) can be found
* in {@link com.oracle.svm.core.jdk.FileSystemProviderSupport}.
*
* @see JDKInitializedAtRunTime
* @see com.oracle.svm.core.jdk.FileSystemProviderSupport
*/
final class FileSystemProviderRuntimeInitSupport {
}

// java.io

@TargetClass(className = "java.io.FileSystem", onlyWith = JDKInitializedAtRunTime.class)
final class Target_java_io_FileSystem_RunTime {
}

@TargetClass(className = "java.io.File", onlyWith = JDKInitializedAtRunTime.class)
@SuppressWarnings("unused")
final class Target_java_io_File_RunTime {
@Alias //
@InjectAccessors(DefaultFileSystemAccessor.class) //
private static Target_java_io_FileSystem_RunTime FS;
}

@TargetClass(className = "java.io.DefaultFileSystem", onlyWith = JDKInitializedAtRunTime.class)
final class Target_java_io_DefaultFileSystem_RunTime {
@Alias
static native Target_java_io_FileSystem_RunTime getFileSystem();
}

/**
* Holds the default java.io file system. Initialized at run time via
* {@code JDKInitializationFeature}. This cache is needed because
* {@link Target_java_io_DefaultFileSystem_RunTime#getFileSystem()} creates a new instance for every
* time. In the JDK, this method is called only once.
*/
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+25/src/java.base/unix/classes/java/io/DefaultFileSystem.java#L39-L41")
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+25/src/java.base/windows/classes/java/io/DefaultFileSystem.java#L39-L41")
class DefaultFileSystemHolder {
static final Object FS;
static {
if (SubstrateUtil.HOSTED) {
/*
* Should be unused, but layered images might want to initialize it during image build.
* We set it to the real default file system instead of null to guard against
* unintentional usages. In run-time init mode, we don't allow FileSystems in the image
* heap, so we would fail the image build with an exception if it happens, which helps
* to detect problems.
*/
var defaultFileSystem = ReflectionUtil.lookupClass("java.io.DefaultFileSystem");
var getFileSystem = ReflectionUtil.lookupMethod(defaultFileSystem, "getFileSystem");
FS = ReflectionUtil.invokeMethod(getFileSystem, null);
} else {
FS = Target_java_io_DefaultFileSystem_RunTime.getFileSystem();
}
}
}

class DefaultFileSystemAccessor {
@SuppressWarnings("unused")
static Target_java_io_FileSystem_RunTime get() {
VMError.guarantee(DefaultFileSystemHolder.FS != null, "DefaultFileSystemHolder.FS is null");
return SubstrateUtil.cast(DefaultFileSystemHolder.FS, Target_java_io_FileSystem_RunTime.class);
}
}

// sun.nio.fs

@TargetClass(className = "sun.nio.fs.DefaultFileSystemProvider", onlyWith = JDKInitializedAtRunTime.class)
final class Target_sun_nio_fs_DefaultFileSystemProvider_RunTime {
@Alias
static native FileSystem theFileSystem();
}

@TargetClass(className = "sun.nio.fs.UnixFileSystem", onlyWith = JDKInitializedAtRunTime.class)
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
final class Target_sun_nio_fs_UnixFileSystem_RunTime {
}

@TargetClass(className = "sun.nio.fs.UnixPath", onlyWith = JDKInitializedAtRunTime.class)
@Platforms({Platform.LINUX.class, Platform.DARWIN.class})
final class Target_sun_nio_fs_UnixPath_RunTime {
@Alias //
@InjectAccessors(UnixFileSystemAccessor.class) //
private Target_sun_nio_fs_UnixFileSystem_RunTime fs;
}

@SuppressWarnings("unused")
class UnixFileSystemAccessor {
static Target_sun_nio_fs_UnixFileSystem_RunTime get(Target_sun_nio_fs_UnixPath_RunTime that) {
FileSystem theFileSystem = Target_sun_nio_fs_DefaultFileSystemProvider_RunTime.theFileSystem();
VMError.guarantee(theFileSystem != null, "DefaultFileSystemProvider.theFileSystem() is null");
return SubstrateUtil.cast(theFileSystem, Target_sun_nio_fs_UnixFileSystem_RunTime.class);
}

static void set(Target_sun_nio_fs_UnixPath_RunTime that, Target_sun_nio_fs_UnixFileSystem_RunTime value) {
/*
* `value` should always be DefaultFileSystemProvider.INSTANCE.theFileSystem() but we cannot
* check that here because it would introduce a class initialization cycle.
*/
}
}

@TargetClass(className = "sun.nio.fs.WindowsFileSystem", onlyWith = JDKInitializedAtRunTime.class)
@Platforms(Platform.WINDOWS.class)
final class Target_sun_nio_fs_WindowsFileSystem_RunTime {
}

@TargetClass(className = "sun.nio.fs.WindowsPath", onlyWith = JDKInitializedAtRunTime.class)
@Platforms(Platform.WINDOWS.class)
final class Target_sun_nio_fs_WindowsPath_RunTime {
@Alias //
@InjectAccessors(WindowsFileSystemAccessor.class) //
private Target_sun_nio_fs_WindowsFileSystem_RunTime fs;
}

@SuppressWarnings("unused")
class WindowsFileSystemAccessor {
static Target_sun_nio_fs_WindowsFileSystem_RunTime get(Target_sun_nio_fs_WindowsPath_RunTime that) {
FileSystem theFileSystem = Target_sun_nio_fs_DefaultFileSystemProvider_RunTime.theFileSystem();
VMError.guarantee(theFileSystem != null, "DefaultFileSystemProvider.theFileSystem() is null");
return SubstrateUtil.cast(theFileSystem, Target_sun_nio_fs_WindowsFileSystem_RunTime.class);
}

static void set(Target_sun_nio_fs_WindowsPath_RunTime that, Target_sun_nio_fs_WindowsFileSystem_RunTime value) {
/*
* `value` should always be DefaultFileSystemProvider.INSTANCE.theFileSystem() but we cannot
* check that here because it would introduce a class initialization cycle.
*/
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -142,6 +142,42 @@ public void afterRegistration(AfterRegistrationAccess access) {
rci.initializeAtBuildTime("java.awt.font.NumericShaper", "Required for sun.text.bidi.BidiBase.NumericShapings");
rci.initializeAtBuildTime("java.awt.font.JavaAWTFontAccessImpl", "Required for sun.text.bidi.BidiBase.NumericShapings");

/* FileSystemProviders related */
if (FutureDefaultsOptions.isJDKInitializedAtRunTime()) {
rci.initializeAtRunTime("java.nio.file.spi", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
rci.initializeAtRunTime("sun.nio.fs", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);

rci.initializeAtRunTime("java.nio.file.FileSystems", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
rci.initializeAtRunTime("java.nio.file.FileSystems$DefaultFileSystemHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);

rci.initializeAtRunTime("java.util.zip.ZipFile$Source", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
rci.initializeAtRunTime("java.util.zip.ZipFile$Source", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);

rci.initializeAtRunTime("java.io.FileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
rci.initializeAtRunTime("java.io.FileSystem$CurrentWorkingDirectoryHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
rci.initializeAtRunTime("java.io.UnixFileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
rci.initializeAtRunTime("java.io.WindowsFileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);

/* Holder for the default file system. */
rci.initializeAtRunTime("com.oracle.svm.core.jdk.runtimeinit.DefaultFileSystemHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);

/*
* The following need to be build-time initialized because they can end up in the image
* heap. There are substitutions to patch the links to run-time initialized classes like
* FileSystem or FileSystemProvider.
*/

/*
* Require explicit initializeAtBuildTime because the sun.nio.fs is registered for
* run-time initialization.
*/
rci.initializeAtBuildTime("sun.nio.fs.UnixPath", "Allow UnixPath objects in the image heap (" + FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON + ")");
rci.initializeAtBuildTime("sun.nio.fs.WindowsPath", "Allow WindowsPath objects in the image heap (" + FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON + ")");

/* JrtFS support. */
rci.initializeAtBuildTime("jdk.internal.jrtfs.SystemImage", FutureDefaultsOptions.RUN_TIME_INITIALIZE_JDK_REASON);
}

/* XML-related */
if (FutureDefaultsOptions.isJDKInitializedAtRunTime()) {
// GR-50683 should remove this part
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -65,6 +65,7 @@ public void duringSetup(DuringSetupAccess a) {
* a `Random` object and the temporary directory in a static final field.
*/
initializeAtRunTime(a, "sun.nio.ch.UnixDomainSockets");
initializeAtRunTime(a, "sun.nio.ch.UnixDomainSockets$UnnamedHolder");

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

Expand Down
Loading