diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMFeature.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMFeature.java index c0bc6e828411..5048269c51e7 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMFeature.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMFeature.java @@ -30,7 +30,6 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.hosted.Feature; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.meta.AnalysisMethod; @@ -52,9 +51,11 @@ import com.oracle.svm.core.graal.llvm.util.LLVMOptions; import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; -import com.oracle.svm.core.layeredimagesingleton.UnsupportedLayeredSingleton; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.snippets.ExceptionUnwind; +import com.oracle.svm.core.traits.BuiltinTraits; +import com.oracle.svm.core.traits.SingletonLayeredInstallationKind; +import com.oracle.svm.core.traits.SingletonTraits; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.c.codegen.CCompilerInvoker; @@ -81,7 +82,8 @@ */ @AutomaticallyRegisteredFeature @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) -public class LLVMFeature implements Feature, InternalFeature, UnsupportedLayeredSingleton { +@SingletonTraits(access = BuiltinTraits.BuildtimeAccessOnly.class, layeredCallbacks = BuiltinTraits.NoLayeredCallbacks.class, layeredInstallationKind = SingletonLayeredInstallationKind.Disallowed.class) +public class LLVMFeature implements InternalFeature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LayeredImageSingletonBuilderFlags.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LayeredImageSingletonBuilderFlags.java index a9e88321bb2b..38b82a228083 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LayeredImageSingletonBuilderFlags.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LayeredImageSingletonBuilderFlags.java @@ -41,11 +41,7 @@ public enum LayeredImageSingletonBuilderFlags { /** * This singleton can be accessed from the buildtime. */ - BUILDTIME_ACCESS, - /** - * This singleton should not have been created. Throw error if it is created. - */ - UNSUPPORTED; + BUILDTIME_ACCESS; /* * Below are some common flag patterns. @@ -57,15 +53,9 @@ public enum LayeredImageSingletonBuilderFlags { public static final EnumSet ALL_ACCESS = EnumSet.of(RUNTIME_ACCESS, BUILDTIME_ACCESS); - public static boolean verifyImageBuilderFlags(LayeredImageSingleton singleton) { - EnumSet flags = singleton.getImageBuilderFlags(); - - if (!(flags.contains(UNSUPPORTED) || flags.contains(BUILDTIME_ACCESS) || flags.contains(RUNTIME_ACCESS))) { - assert false : String.format("At least one of the following flags must be set: %s, %s, %s", UNSUPPORTED, BUILDTIME_ACCESS, RUNTIME_ACCESS); - } - - if (flags.contains(UNSUPPORTED)) { - assert flags.equals(EnumSet.of(UNSUPPORTED)) : "Unsupported should be the only flag set " + flags; + public static boolean verifyImageBuilderFlags(Object singleton, EnumSet flags) { + if (!(flags.contains(BUILDTIME_ACCESS) || flags.contains(RUNTIME_ACCESS))) { + assert false : String.format("At least one of the following flags must be set: %s, %s", BUILDTIME_ACCESS, RUNTIME_ACCESS); } if (singleton instanceof MultiLayeredImageSingleton || ApplicationLayerOnlyImageSingleton.isSingletonInstanceOf(singleton)) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/BuiltinTraits.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/BuiltinTraits.java new file mode 100644 index 000000000000..6e98e334d488 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/BuiltinTraits.java @@ -0,0 +1,107 @@ +/* + * 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.traits; + +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; + +/** + * Commonly used {@link SingletonTrait}s. + */ +public class BuiltinTraits { + + /** + * Trait indicating this singleton should only be accessed from code executed at runtime. + */ + public static final SingletonTrait RUNTIME_ONLY = new SingletonTrait(SingletonTraitKind.ACCESS, + (SingletonAccess.Supplier) () -> LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS_ONLY); + + public static final class RuntimeAccessOnly extends SingletonAccessSupplier { + @Override + public SingletonTrait getAccessTrait() { + return RUNTIME_ONLY; + } + } + + /** + * Trait indicating this singleton should only be accessed from the native image generator + * process and not at runtime. + */ + public static final SingletonTrait BUILDTIME_ONLY = new SingletonTrait(SingletonTraitKind.ACCESS, + (SingletonAccess.Supplier) () -> LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY); + + public static final class BuildtimeAccessOnly extends SingletonAccessSupplier { + @Override + public SingletonTrait getAccessTrait() { + return BUILDTIME_ONLY; + } + } + + /** + * Trait indicating this singleton can be freely accessed both from the native image generator + * process and at runtime. + */ + public static final SingletonTrait ALL_ACCESS = new SingletonTrait(SingletonTraitKind.ACCESS, + (SingletonAccess.Supplier) () -> LayeredImageSingletonBuilderFlags.ALL_ACCESS); + + public static final class AllAccess extends SingletonAccessSupplier { + @Override + public SingletonTrait getAccessTrait() { + return ALL_ACCESS; + } + } + + /** + * Trait indicating this singleton has no special callbacks needed during layered builds. + */ + public static final SingletonTrait NO_LAYERED_CALLBACKS = new SingletonTrait(SingletonTraitKind.LAYERED_CALLBACKS, new SingletonLayeredCallbacks() { + @Override + public LayeredImageSingleton.PersistFlags doPersist(ImageSingletonWriter writer, Object singleton) { + return LayeredImageSingleton.PersistFlags.NOTHING; + } + }); + + public static class NoLayeredCallbacks extends SingletonLayeredCallbacksSupplier { + @Override + public SingletonTrait getLayeredCallbacksTrait() { + return NO_LAYERED_CALLBACKS; + } + } + + /** + * Trait indicating this singleton can be made more layer-aware in the future. See + * {@link SingletonTraitKind#DUPLICABLE} for more information. + */ + public static final SingletonTrait DUPLICABLE_TRAIT = new SingletonTrait(SingletonTraitKind.DUPLICABLE, EmptyMetadata.EMPTY); + + @SuppressWarnings("unused") + public static class Duplicable extends SingletonTraitsSupplier { + @Override + public SingletonTrait getTrait() { + return DUPLICABLE_TRAIT; + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/RuntimeOnlyWrapper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/EmptyMetadata.java similarity index 76% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/RuntimeOnlyWrapper.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/EmptyMetadata.java index 6660b2ba8525..72be1be1926d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/RuntimeOnlyWrapper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/EmptyMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,11 +22,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.layeredimagesingleton; +package com.oracle.svm.core.traits; /** - * This is used to wrap singletons which are only allowed to be accessed at runtime. When a - * singleton wrapped with this is called during image build time an error is thrown. + * This metadata class is used for {@link SingletonTrait}s which need to store no additional + * content. */ -public record RuntimeOnlyWrapper(LayeredImageSingleton wrappedObject) { +final class EmptyMetadata { + static final EmptyMetadata EMPTY = new EmptyMetadata(); + + private EmptyMetadata() { + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/InjectedSingletonLayeredCallbacks.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/InjectedSingletonLayeredCallbacks.java new file mode 100644 index 000000000000..68e7e1fb8ad6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/InjectedSingletonLayeredCallbacks.java @@ -0,0 +1,51 @@ +/* + * 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.traits; + +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; + +// GR-66792 remove once no custom persist actions exist + +/** + * Temporarily used to convert {@link LayeredImageSingleton} callbacks into {@link SingletonTrait} + * information. + */ +public final class InjectedSingletonLayeredCallbacks extends SingletonLayeredCallbacks { + final LayeredImageSingleton singleton; + + public InjectedSingletonLayeredCallbacks(LayeredImageSingleton singleton) { + this.singleton = singleton; + } + + @Override + public LayeredImageSingleton.PersistFlags doPersist(ImageSingletonWriter writer, Object obj) { + return singleton.preparePersist(writer); + } + + public Class getSingletonClass() { + return singleton.getClass(); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonAccess.java new file mode 100644 index 000000000000..d4e29c7e48a8 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonAccess.java @@ -0,0 +1,46 @@ +/* + * 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.traits; + +import java.util.EnumSet; + +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; +import com.oracle.svm.core.util.VMError; + +/** + * Metadata associated with the {@link SingletonTraitKind#ACCESS} trait. Describes when this + * singleton can be accessed (e.g., during the native image generator process and/or from within the + * generated code at runtime). + */ +public class SingletonAccess { + interface Supplier { + EnumSet getAccessFlags(); + } + + public static EnumSet getAccess(SingletonTrait trait) { + VMError.guarantee(trait.kind() == SingletonTraitKind.ACCESS); + return ((Supplier) trait.metadata()).getAccessFlags(); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonAccessSupplier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonAccessSupplier.java new file mode 100644 index 000000000000..f6e9ab600948 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonAccessSupplier.java @@ -0,0 +1,33 @@ +/* + * 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.traits; + +/** + * Represents a supplier of the {@link SingletonTraitKind#ACCESS} {@link SingletonTrait}. See + * {@link SingletonTraits} and {@link SingletonTraitKind#ACCESS} for more information. + */ +public abstract sealed class SingletonAccessSupplier permits BuiltinTraits.BuildtimeAccessOnly, BuiltinTraits.RuntimeAccessOnly, BuiltinTraits.AllAccess { + public abstract SingletonTrait getAccessTrait(); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredCallbacks.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredCallbacks.java new file mode 100644 index 000000000000..14ed40e78132 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredCallbacks.java @@ -0,0 +1,76 @@ +/* + * 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.traits; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton.PersistFlags; +import com.oracle.svm.core.util.VMError; + +/** + * This class contains actions which can be called on singletons during a layered image build. + */ +@Platforms(Platform.HOSTED_ONLY.class) +public abstract class SingletonLayeredCallbacks { + + /** + * Used to recreate a singleton across layers. + * + *

+ * To explain in more detail, consider a two-layered configuration, where "I" is the initial + * layer and "A" is the application layer. If, while building I, singleton "S" specifies that it + * wants to be created during startup of the next layer (via returning + * {@link PersistFlags#CREATE} from its {@link #doPersist} callback), then it will also provide + * a {@link LayeredSingletonInstantiator} "LI" to use via {@link #getSingletonInstantiator}. + * + *

+ * Now while building layer A, in order to create S in this layer, the native image generator + * will initialize LI and will then call {@link #createFromLoader} to attain the S to be used in + * layer A. + */ + public interface LayeredSingletonInstantiator { + Object createFromLoader(ImageSingletonLoader loader); + } + + /** + * When {@link ImageLayerBuildingSupport#buildingSharedLayer()} is true, this method is called + * at the end of native image generation to perform any needed final actions. The method's + * return value also specifies what actions should be taken at the startup of the next layer. + */ + public abstract PersistFlags doPersist(ImageSingletonWriter writer, Object singleton); + + /** + * If {@link #doPersist} returns {@link PersistFlags#CREATE}, then this method is called to + * determine how to instantiate the singleton in the next layer. + */ + public Class getSingletonInstantiator() { + throw VMError.shouldNotReachHere("getSingletonInstantiator is not implemented. This method must only be implemented if doPersist returns PersistFlag.CREATE"); + } + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredCallbacksSupplier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredCallbacksSupplier.java new file mode 100644 index 000000000000..710149b2afb9 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredCallbacksSupplier.java @@ -0,0 +1,34 @@ +/* + * 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.traits; + +/** + * Represents a supplier of the {@link SingletonTraitKind#LAYERED_CALLBACKS} {@link SingletonTrait}. + * See {@link SingletonTraits} and {@link SingletonTraitKind#LAYERED_CALLBACKS} for more + * information. + */ +public abstract class SingletonLayeredCallbacksSupplier { + public abstract SingletonTrait getLayeredCallbacksTrait(); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredInstallationKind.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredInstallationKind.java new file mode 100644 index 000000000000..bd9c98e0c1d9 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredInstallationKind.java @@ -0,0 +1,134 @@ +/* + * 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.traits; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredAllowNullEntries; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; +import com.oracle.svm.core.util.VMError; + +/** + * {@link SingletonTrait} which describes how this singleton acts in layered builds. + */ +public record SingletonLayeredInstallationKind(InstallationKind kind, Object metadata) { + public enum InstallationKind { + /** + * This singleton cannot be installed in a layered build. + */ + DISALLOWED(EmptyMetadata.class), + + /** + * A different version of this singleton can be installed in each layer. If a given layer X + * generates compiled code referring to an independent singleton with key K, then it will + * refer to the singleton installed in the layer X and the singleton will be installed in + * layer X's image heap. If a later layer Y generates compiled code referring to the same + * singleton key K, it will not be linked to the same singleton as installed in layer X, but + * instead will refer to the singleton installed in layer Y that will be installed in layer + * Y's image heap. + */ + INDEPENDENT(EmptyMetadata.class), + + /* + * GR-66793, GR-66794, GR-66795, : switch from using interfaces to traits for implementing + * the following behaviors. + */ + + /** + * This singleton can only be installed in the initial layer. All references to this + * singleton in all code compiled across all layers refer to the singleton installed in the + * base layer. + */ + INITIAL_LAYER_ONLY(EmptyMetadata.class), + + /** + * This singleton can only be installed in the app layer. All references to this singleton + * in all code compiled across all layers refer to the singleton installed in the app layer. + */ + APP_LAYER_ONLY(EmptyMetadata.class), + + /** + * A different version of this singleton can be installed in each layer. For these + * singletons, {@link ImageSingletons#lookup} should no longer be used. Instead, there is + * the method {@link MultiLayeredImageSingleton#getAllLayers} which returns an array with + * the image singletons corresponding to this key for all layers. The length of this array + * will always be the total number of layers. If a singleton corresponding to this key was + * not installed in a given layer (and this is allowed), then the array will contain null + * for the given index. See {@link MultiLayeredAllowNullEntries} for more details. Within + * the array, the singletons will be arranged so that index [0] corresponds to the singleton + * originating from the initial layer and index [length - 1] holds the singleton from the + * application layer. See {@link ImageLayerBuildingSupport} for a description of the + * different layer names. + * + *

+ * Calling {@link MultiLayeredImageSingleton#getAllLayers} during a traditional build + * requires the singleton to be installed in the build and will return an array of length 1 + * * containing that singleton. + */ + MULTI_LAYER(EmptyMetadata.class); + + InstallationKind(Class metadataClass) { + this.metadataClass = metadataClass; + } + + private final Class metadataClass; + } + + public SingletonLayeredInstallationKind(InstallationKind kind, Object metadata) { + this.kind = kind; + this.metadata = metadata; + VMError.guarantee(kind.metadataClass.isInstance(metadata)); + } + + public static void validate(SingletonTrait trait) { + VMError.guarantee(trait.kind() == SingletonTraitKind.LAYERED_INSTALLATION_KIND); + SingletonLayeredInstallationKind behavior = (SingletonLayeredInstallationKind) trait.metadata(); + if (behavior.kind() == InstallationKind.DISALLOWED) { + throw VMError.shouldNotReachHere("This singleton cannot be added to this layer"); + + } + } + + static final SingletonTrait DISALLOWED_TRAIT = new SingletonTrait(SingletonTraitKind.LAYERED_INSTALLATION_KIND, + new SingletonLayeredInstallationKind(InstallationKind.DISALLOWED, EmptyMetadata.EMPTY)); + + public static final class Disallowed extends SingletonLayeredInstallationKindSupplier { + @Override + public SingletonTrait getLayeredInstallationKindTrait() { + return DISALLOWED_TRAIT; + } + } + + static final SingletonTrait INDEPENDENT_TRAIT = new SingletonTrait(SingletonTraitKind.LAYERED_INSTALLATION_KIND, + new SingletonLayeredInstallationKind(InstallationKind.INDEPENDENT, EmptyMetadata.EMPTY)); + + public static final class Independent extends SingletonLayeredInstallationKindSupplier { + @Override + public SingletonTrait getLayeredInstallationKindTrait() { + return INDEPENDENT_TRAIT; + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredInstallationKindSupplier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredInstallationKindSupplier.java new file mode 100644 index 000000000000..93f1b9698e1f --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonLayeredInstallationKindSupplier.java @@ -0,0 +1,34 @@ +/* + * 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.traits; + +/** + * Represents a supplier of the {@link SingletonTraitKind#LAYERED_INSTALLATION_KIND} + * {@link SingletonTrait}. See {@link SingletonTraits} and + * {@link SingletonTraitKind#LAYERED_INSTALLATION_KIND} for more information. + */ +public abstract sealed class SingletonLayeredInstallationKindSupplier permits SingletonLayeredInstallationKind.Disallowed, SingletonLayeredInstallationKind.Independent { + public abstract SingletonTrait getLayeredInstallationKindTrait(); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/UnsupportedLayeredSingleton.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTrait.java similarity index 64% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/UnsupportedLayeredSingleton.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTrait.java index 142a1d23c73b..f0d7ed5392a7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/UnsupportedLayeredSingleton.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTrait.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,24 +22,19 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.layeredimagesingleton; - -import java.util.EnumSet; +package com.oracle.svm.core.traits; import com.oracle.svm.core.util.VMError; /** - * Marker for singletons which currently cannot be part of layered images. + * Describes a facet of a singleton's behavior. See {@link SingletonTraits} and + * {@link SingletonTraitKind} for more details. */ -public interface UnsupportedLayeredSingleton extends LayeredImageSingleton { - - @Override - default EnumSet getImageBuilderFlags() { - return EnumSet.of(LayeredImageSingletonBuilderFlags.UNSUPPORTED); - } - - @Override - default PersistFlags preparePersist(ImageSingletonWriter writer) { - throw VMError.shouldNotReachHere("Unsupported feature singleton cannot be added to image"); +public record SingletonTrait(SingletonTraitKind kind, Object metadata) { + public SingletonTrait { + /* + * Guarantee the metadata for this trait is of the expected kind. + */ + VMError.guarantee(kind.getMetadataClass().isInstance(metadata)); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraitKind.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraitKind.java new file mode 100644 index 000000000000..b4332a4369c2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraitKind.java @@ -0,0 +1,106 @@ +/* + * 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.traits; + +/** + * Enumerates the different kinds of traits that can be associated with a singleton via + * {@link SingletonTraits}. See {@link SingletonTraits} for more details. + */ +public enum SingletonTraitKind { + + /** + * Describes when this singleton can be accessed (e.g., during the native image generator + * process and/or from within the generated code at runtime). + * + *

+ * In cases where this SingletonTrait is not defined the singleton will have the equivalent + * behavior as {@link BuiltinTraits#ALL_ACCESS}. + */ + ACCESS(SingletonAccess.Supplier.class), + + /** + * Links to layered-build specific callbacks for this singleton. + * + *

+ * In cases where this SingletonTrait is not defined then no layered-build specific callbacks + * will be triggered for this singleton. + */ + LAYERED_CALLBACKS(SingletonLayeredCallbacks.class), + + /** + * Describes any special layered-specific behavior associated with this singleton. See + * {@link SingletonLayeredInstallationKind} for more details about the various behaviors. + * + *

+ * In cases where this SingletonTrait is not defined the singleton will have the equivalent + * behavior as {@link SingletonLayeredInstallationKind.Independent}. + */ + LAYERED_INSTALLATION_KIND(SingletonLayeredInstallationKind.class), + + /** + * Used as a marker to indicate this singleton's behavior should be made layer-aware in the + * future. + * + *

+ * In detail, a duplicable singleton can have multiple instances of the object installed in the + * Image Heap (at most one per a layer). The specific instance referred to from a given piece of + * code is dependent on the layer in which the code was installed in. + * + *

+ * It is expected that either the installed objects (1) have no instance fields or (2) have + * instance fields which have been made layer-aware through other means (e.g. using a layered + * ImageHeapMap). + * + *

+ * Note this is a temporary marker and eventually all instances of this trait should be removed. + * This trait should only be used when there is not a correctness issue with installing multiple + * instances of the singleton. Instead, the trait indicates there is merely a performance/memory + * overhead due to having multiple copies of this singleton installed (via different layers) + * within the image heap. + */ + DUPLICABLE(EmptyMetadata.class); + + private final Class metadataClass; + + public Class getMetadataClass() { + return metadataClass; + } + + SingletonTraitKind(Class metadataClass) { + this.metadataClass = metadataClass; + } + + /** + * Determines whether {@link SingletonTraits} of a given kind should be linked during this + * build. Presently many traits are specific to layered build. + */ + public boolean isInConfiguration(boolean layeredBuild) { + return switch (this) { + case ACCESS -> true; + case LAYERED_CALLBACKS, DUPLICABLE, LAYERED_INSTALLATION_KIND -> layeredBuild; + }; + } + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraits.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraits.java new file mode 100644 index 000000000000..5fbec17ae180 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraits.java @@ -0,0 +1,86 @@ +/* + * 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.traits; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +/** + * Denotes behaviors associated with a singleton. Each singleton can have multiple + * {@link SingletonTrait}s installed to describe different facets of its behavior. A given + * {@link SingletonTrait} will be of a specific {@link SingletonTraitKind} and also may have + * metadata associated with it. The type of metadata associated with each {@link SingletonTraitKind} + * is specified in the enum. + * + *

+ * Note at most one {@link SingletonTrait} of each {@link SingletonTraitKind} can be associated with + * a singleton. If a singleton does not have a given {@link SingletonTraitKind} defined, then it + * will assume the default behavior for the trait as described in {@link SingletonTraitKind}. + * + *

+ * Note also that these traits are associated with a singleton object based on the singleton's + * runtime class, not the singleton's key class. In other words, + *

    + *
  • If a singleton's key class is annotated with {@link SingletonTraits}, but the singleton's + * runtime class does not, then no {@link SingletonTrait}s will be installed.
  • + *
  • Even if a singleton whose runtime class implements {@link SingletonTraits} is installed under + * multiple keys, it will not cause multiple instances of the {@link SingletonTrait}s to be + * created.
  • + *
+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@Platforms(Platform.HOSTED_ONLY.class) +public @interface SingletonTraits { + + /** + * Access kind allowed by this singleton. See {@link SingletonTraitKind#ACCESS} for more + * details. + */ + Class access(); + + /** + * Layered callback hooks linked to this singleton. See + * {@link SingletonTraitKind#LAYERED_CALLBACKS} for more details. + */ + Class layeredCallbacks(); + + /** + * Special layered-specific behavior associated with this singleton. + * {@link SingletonTraitKind#LAYERED_INSTALLATION_KIND} for more details. + */ + Class layeredInstallationKind(); + + /** + * Other {@link SingletonTraitsSupplier} classes used to create {@link SingletonTrait}s + * associated with singletons installed of this type. + */ + Class[] other() default {}; +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraitsSupplier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraitsSupplier.java new file mode 100644 index 000000000000..48e79939542b --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/traits/SingletonTraitsSupplier.java @@ -0,0 +1,34 @@ +/* + * 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.traits; + +/** + * Represents a supplier of {@link SingletonTrait}s. See {@link SingletonTraits} for more + * information. + */ +public abstract class SingletonTraitsSupplier { + + public abstract SingletonTrait getTrait(); +} diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/stubs/AMD64StubForeignCallsFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/stubs/AMD64StubForeignCallsFeature.java index bc2a1d5b543a..96349cab9258 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/stubs/AMD64StubForeignCallsFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/stubs/AMD64StubForeignCallsFeature.java @@ -38,6 +38,9 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; +import com.oracle.svm.core.traits.BuiltinTraits; +import com.oracle.svm.core.traits.SingletonLayeredInstallationKind; +import com.oracle.svm.core.traits.SingletonTraits; import jdk.graal.compiler.replacements.StringLatin1InflateNode; import jdk.graal.compiler.replacements.StringUTF16CompressNode; @@ -71,6 +74,7 @@ @AutomaticallyRegisteredFeature @Platforms(AMD64.class) +@SingletonTraits(access = BuiltinTraits.BuildtimeAccessOnly.class, layeredCallbacks = BuiltinTraits.NoLayeredCallbacks.class, layeredInstallationKind = SingletonLayeredInstallationKind.Independent.class) public class AMD64StubForeignCallsFeature extends StubForeignCallsFeatureBase implements FeatureSingleton, UnsavedSingleton { private static final EnumSet BASELINE = EnumSet.of(SSE2); diff --git a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp index 8b8c10e1a3b8..46b0acc6e3d5 100644 --- a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp +++ b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp @@ -239,6 +239,9 @@ struct ImageSingletonObject { id @0 :SingletonObjId; className @1 :Text; store @2 :List(KeyStoreEntry); + recreateClass @3 :Text; + # GR-66792 remove once no custom persist actions exist + recreateMethod @4 :Text; } struct Annotation { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageSingletonsSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageSingletonsSupportImpl.java index 337c3b565c85..b87cba89d2f8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageSingletonsSupportImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageSingletonsSupportImpl.java @@ -26,12 +26,15 @@ import java.util.Collection; import java.util.Comparator; +import java.util.EnumMap; import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.AnnotationExtractor; import org.graalvm.nativeimage.impl.ImageSingletonsSupport; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; @@ -43,11 +46,26 @@ import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport; import com.oracle.svm.core.layeredimagesingleton.LoadedLayeredImageSingletonInfo; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; -import com.oracle.svm.core.layeredimagesingleton.RuntimeOnlyWrapper; +import com.oracle.svm.core.traits.BuiltinTraits; +import com.oracle.svm.core.traits.InjectedSingletonLayeredCallbacks; +import com.oracle.svm.core.traits.SingletonAccess; +import com.oracle.svm.core.traits.SingletonAccessSupplier; +import com.oracle.svm.core.traits.SingletonLayeredCallbacks; +import com.oracle.svm.core.traits.SingletonLayeredCallbacksSupplier; +import com.oracle.svm.core.traits.SingletonLayeredInstallationKind; +import com.oracle.svm.core.traits.SingletonLayeredInstallationKindSupplier; +import com.oracle.svm.core.traits.SingletonTrait; +import com.oracle.svm.core.traits.SingletonTraitKind; +import com.oracle.svm.core.traits.SingletonTraits; +import com.oracle.svm.core.traits.SingletonTraitsSupplier; +import com.oracle.svm.core.util.ConcurrentIdentityHashMap; import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; import com.oracle.svm.hosted.imagelayer.SVMImageLayerSingletonLoader; +import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.debug.Assertions; import jdk.vm.ci.meta.JavaConstant; public final class ImageSingletonsSupportImpl extends ImageSingletonsSupport implements LayeredImageSingletonSupport { @@ -59,12 +77,12 @@ public void add(Class key, T value) { @Override public T lookup(Class key) { - return HostedManagement.getAndAssertExists().doLookup(key, false, false); + return HostedManagement.getAndAssertExists().doLookup(key, true, false); } @Override public T lookup(Class key, boolean accessRuntimeOnly, boolean accessMultiLayer) { - return HostedManagement.getAndAssertExists().doLookup(key, accessRuntimeOnly, accessMultiLayer); + return HostedManagement.getAndAssertExists().doLookup(key, !accessRuntimeOnly, accessMultiLayer); } @Override @@ -104,6 +122,114 @@ public boolean contains(Class key) { } } + /** + * Value linked to each singleton key class from {@link HostedManagement#configObjects}. Beyond + * the singleton value itself, this container also references the singleton's trait map and + * provides fast lookups on if the singleton has builtime & runtime access permissions. + */ + public static final class SingletonInfo { + Object singleton; + final SingletonTraitMap traitMap; + final boolean buildtimeAccessAllowed; + final boolean runtimeAccessAllowed; + + SingletonInfo(Object singleton, SingletonTraitMap traitMap) { + this.singleton = singleton; + this.traitMap = traitMap; + + var accessTrait = traitMap.getTrait(SingletonTraitKind.ACCESS); + buildtimeAccessAllowed = accessTrait.map(singletonTrait -> SingletonAccess.getAccess(singletonTrait).contains(LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS)).orElse(true); + runtimeAccessAllowed = accessTrait.map(singletonTrait -> SingletonAccess.getAccess(singletonTrait).contains(LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS)).orElse(true); + } + + public Object singleton() { + return singleton; + } + + public SingletonTraitMap traitMap() { + return traitMap; + } + } + + /** + * Stores the traits associated with a given singleton. + */ + public static final class SingletonTraitMap { + private final EnumMap traitMap; + + private SingletonTraitMap(EnumMap traitMap) { + this.traitMap = traitMap; + } + + void addTrait(SingletonTrait value) { + SingletonTraitKind key = value.kind(); + var prev = traitMap.put(key, value); + assert prev == null; + } + + public Optional getTrait(SingletonTraitKind key) { + return Optional.ofNullable(traitMap.get(key)); + } + + /** + * Creates a new {@link SingletonTraitMap} based on the {@link SingletonTraits} assigned to + * a given singleton. + */ + static SingletonTraitMap getAnnotatedTraits(Class singletonClass, AnnotationExtractor extractor, boolean layeredBuild) { + if (extractor != null) { + SingletonTraits annotation = extractor.extractAnnotation(singletonClass, SingletonTraits.class, false); + + if (annotation != null) { + EnumMap traitMap = new EnumMap<>(SingletonTraitKind.class); + + if (annotation.access() != null) { + var accessSupplierClass = annotation.access(); + SingletonAccessSupplier accessSupplier = ReflectionUtil.newInstance(accessSupplierClass); + SingletonTrait accessTrait = accessSupplier.getAccessTrait(); + assert accessTrait.kind() == SingletonTraitKind.ACCESS && accessTrait.kind().isInConfiguration(layeredBuild) : accessTrait; + var prev = traitMap.put(accessTrait.kind(), accessTrait); + assert prev == null : Assertions.errorMessage("Added multiple access traits", accessTrait, prev); + } + + if (SingletonTraitKind.LAYERED_CALLBACKS.isInConfiguration(layeredBuild) && annotation.layeredCallbacks() != null) { + var callbacksSupplierClass = annotation.layeredCallbacks(); + SingletonLayeredCallbacksSupplier callbacksSupplier = ReflectionUtil.newInstance(callbacksSupplierClass); + SingletonTrait callbacksTrait = callbacksSupplier.getLayeredCallbacksTrait(); + assert callbacksTrait.kind() == SingletonTraitKind.LAYERED_CALLBACKS : callbacksTrait; + var prev = traitMap.put(callbacksTrait.kind(), callbacksTrait); + assert prev == null : Assertions.errorMessage("Added multiple layered callbacks traits", callbacksTrait, prev); + } + + if (SingletonTraitKind.LAYERED_INSTALLATION_KIND.isInConfiguration(layeredBuild) && annotation.layeredInstallationKind() != null) { + var installationKindSupplierClass = annotation.layeredInstallationKind(); + SingletonLayeredInstallationKindSupplier installationKindSupplier = ReflectionUtil.newInstance(installationKindSupplierClass); + SingletonTrait installationTrait = installationKindSupplier.getLayeredInstallationKindTrait(); + assert installationTrait.kind() == SingletonTraitKind.LAYERED_INSTALLATION_KIND : installationTrait; + var prev = traitMap.put(installationTrait.kind(), installationTrait); + assert prev == null : Assertions.errorMessage("Added multiple layered installation kind traits", installationTrait, prev); + } + + if (annotation.other() != null) { + for (var traitSupplierClass : annotation.other()) { + SingletonTraitsSupplier traitSupplier = ReflectionUtil.newInstance(traitSupplierClass); + SingletonTrait traitInfo = traitSupplier.getTrait(); + if (traitInfo.kind().isInConfiguration(layeredBuild)) { + var prev = traitMap.put(traitInfo.kind(), traitInfo); + assert prev == null : Assertions.errorMessage("Added multiple traits for singleton kind", traitInfo, prev); + } + } + } + + if (!traitMap.isEmpty()) { + return new SingletonTraitMap(traitMap); + } + } + } + + return new SingletonTraitMap(new EnumMap<>(SingletonTraitKind.class)); + } + } + /** * Management of the {@link ImageSingletons} registry during image generation. */ @@ -152,13 +278,13 @@ public static void install(HostedManagement vmConfig, HostedImageLayerBuildingSu * on it. We also intentionally do not mark this singleton as a LayerImageSingleton * to prevent circular dependency complications. */ - singletonDuringImageBuild.doAddInternal(ImageLayerBuildingSupport.class, support); + singletonDuringImageBuild.addSingleton(ImageLayerBuildingSupport.class, support); } else { /* * Create a placeholder ImageLayerBuilding support to indicate this is not a layered * build. */ - singletonDuringImageBuild.doAddInternal(ImageLayerBuildingSupport.class, new ImageLayerBuildingSupport(false, false, false) { + singletonDuringImageBuild.addSingleton(ImageLayerBuildingSupport.class, new ImageLayerBuildingSupport(false, false, false) { }); } if (support != null && support.getSingletonLoader() != null) { @@ -168,7 +294,7 @@ public static void install(HostedManagement vmConfig, HostedImageLayerBuildingSu */ singletonDuringImageBuild.installPriorSingletonInfo(support.getSingletonLoader()); } else { - singletonDuringImageBuild.doAddInternal(LoadedLayeredImageSingletonInfo.class, new LoadedLayeredImageSingletonInfo(Set.of())); + singletonDuringImageBuild.addSingleton(LoadedLayeredImageSingletonInfo.class, new LoadedLayeredImageSingletonInfo(Set.of())); } } @@ -178,67 +304,109 @@ private void installPriorSingletonInfo(SVMImageLayerSingletonLoader info) { for (var entry : result.entrySet()) { Object singletonToInstall = entry.getKey(); for (Class key : entry.getValue()) { - doAddInternal(key, singletonToInstall); + addSingleton(key, singletonToInstall); installedKeys.add(key); } } // document what was installed during loading - doAddInternal(LoadedLayeredImageSingletonInfo.class, new LoadedLayeredImageSingletonInfo(Set.copyOf(installedKeys))); + addSingleton(LoadedLayeredImageSingletonInfo.class, new LoadedLayeredImageSingletonInfo(Set.copyOf(installedKeys))); } public static void clear() { singletonDuringImageBuild = null; } - private static Object stripRuntimeWrapper(Object singleton) { - if (singleton instanceof RuntimeOnlyWrapper r) { - return r.wrappedObject(); - } - return singleton; - } - public static void persist() { - var list = singletonDuringImageBuild.configObjects.entrySet().stream().filter(e -> stripRuntimeWrapper(e.getValue()) instanceof LayeredImageSingleton) + var list = singletonDuringImageBuild.configObjects.entrySet().stream().filter(e -> e.getValue().traitMap.getTrait(SingletonTraitKind.LAYERED_CALLBACKS).isPresent()) .sorted(Comparator.comparing(e -> e.getKey().getName())) .toList(); HostedImageLayerBuildingSupport.singleton().getWriter().writeImageSingletonInfo(list); } - private final Map, Object> configObjects; - private final boolean checkUnsupported; + private final Map, SingletonInfo> configObjects; + private final Map singletonToTraitMap; private Set> multiLayeredImageSingletonKeys; private Set> futureLayerAccessibleImageSingletonKeys; + private final boolean layeredBuild; + private final AnnotationExtractor extractor; public HostedManagement() { - this(false); + this(false, null); } - public HostedManagement(boolean checkUnsupported) { + public HostedManagement(boolean layeredBuild, AnnotationExtractor extractor) { this.configObjects = new ConcurrentHashMap<>(); + this.singletonToTraitMap = new ConcurrentIdentityHashMap<>(); this.multiLayeredImageSingletonKeys = ConcurrentHashMap.newKeySet(); this.futureLayerAccessibleImageSingletonKeys = ConcurrentHashMap.newKeySet(); - this.checkUnsupported = checkUnsupported; + this.layeredBuild = layeredBuild; + this.extractor = extractor; } void doAdd(Class key, T value) { - doAddInternal(key, value); + addSingleton(key, value); } - private void doAddInternal(Class key, Object value) { + /** + * GR-66797 remove all reference to layered image singletons. + *

+ * Temporary feature to convert the legacy {@link LayeredImageSingleton} interface into + * singleton traits. This will be removed once all singletons are converted to use the new + * {@link SingletonTraits} API. + */ + private void injectLayeredInformation(Object singleton, SingletonTraitMap traitMap) { + if (singleton instanceof LayeredImageSingleton layeredImageSingleton) { + if (traitMap.getTrait(SingletonTraitKind.ACCESS).isEmpty()) { + var flags = layeredImageSingleton.getImageBuilderFlags(); + SingletonTrait accessTrait; + if (flags.equals(LayeredImageSingletonBuilderFlags.ALL_ACCESS)) { + accessTrait = BuiltinTraits.ALL_ACCESS; + } else if (flags.equals(LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY)) { + accessTrait = BuiltinTraits.BUILDTIME_ONLY; + } else { + VMError.guarantee(flags.equals(LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS_ONLY)); + accessTrait = BuiltinTraits.RUNTIME_ONLY; + } + traitMap.addTrait(accessTrait); + } + if (layeredBuild) { + if (traitMap.getTrait(SingletonTraitKind.LAYERED_CALLBACKS).isEmpty()) { + SingletonLayeredCallbacks action = new InjectedSingletonLayeredCallbacks(layeredImageSingleton); + traitMap.addTrait(new SingletonTrait(SingletonTraitKind.LAYERED_CALLBACKS, action)); + } + } + } + } + + /** + * Creates or collects the {@link SingletonTraitMap} associated with this singleton before + * adding the singleton to the internal map. + */ + private void addSingleton(Class key, Object value) { + SingletonTraitMap traitMap = singletonToTraitMap.get(value); + if (traitMap == null) { + traitMap = SingletonTraitMap.getAnnotatedTraits(value.getClass(), extractor, layeredBuild); + injectLayeredInformation(value, traitMap); + traitMap.getTrait(SingletonTraitKind.ACCESS).ifPresent(trait -> { + LayeredImageSingletonBuilderFlags.verifyImageBuilderFlags(value, SingletonAccess.getAccess(trait)); + }); + if (layeredBuild) { + traitMap.getTrait(SingletonTraitKind.LAYERED_INSTALLATION_KIND).ifPresent(SingletonLayeredInstallationKind::validate); + } + } + SingletonTraitMap candidateTraitMap = traitMap; + traitMap = singletonToTraitMap.computeIfAbsent(value, k -> candidateTraitMap); + addSingletonToMap(key, value, traitMap); + } + + private void addSingletonToMap(Class key, Object value, SingletonTraitMap traitMap) { checkKey(key); if (value == null) { throw UserError.abort("ImageSingletons do not allow null value for key %s", key.getTypeName()); } - Object storedValue = value; if (value instanceof LayeredImageSingleton singleton) { - assert LayeredImageSingletonBuilderFlags.verifyImageBuilderFlags(singleton); - - if (checkUnsupported && singleton.getImageBuilderFlags().contains(LayeredImageSingletonBuilderFlags.UNSUPPORTED)) { - throw UserError.abort("Unsupported image singleton is being installed %s %s", key.getTypeName(), singleton); - } - if (singleton instanceof MultiLayeredImageSingleton || ApplicationLayerOnlyImageSingleton.isSingletonInstanceOf(singleton)) { if (singleton instanceof MultiLayeredImageSingleton && ApplicationLayerOnlyImageSingleton.isSingletonInstanceOf(singleton)) { @@ -257,13 +425,9 @@ private void doAddInternal(Class key, Object value) { if (singleton instanceof InitialLayerOnlyImageSingleton initial && initial.accessibleInFutureLayers()) { futureLayerAccessibleImageSingletonKeys.add(key); } - - if (!singleton.getImageBuilderFlags().contains(LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS)) { - storedValue = new RuntimeOnlyWrapper(singleton); - } } - Object prevValue = configObjects.putIfAbsent(key, storedValue); + Object prevValue = configObjects.putIfAbsent(key, new SingletonInfo(value, traitMap)); if (prevValue != null) { throw UserError.abort("ImageSingletons.add must not overwrite existing key %s%nExisting value: %s%nNew value: %s", key.getTypeName(), prevValue, value); @@ -283,10 +447,15 @@ void freezeLayeredImageSingletonMetadata() { futureLayerAccessibleImageSingletonKeys = Set.copyOf(futureLayerAccessibleImageSingletonKeys); } - T doLookup(Class key, boolean stripRuntimeOnly, boolean allowMultiLayered) { + /** + * @param buildtimeAccess If true, only allow lookup of singletons with buildtime access + * permissions. If false, only allow lookup of singletons within runtime access + * permissions. + */ + T doLookup(Class key, boolean buildtimeAccess, boolean allowMultiLayered) { checkKey(key); - Object result = configObjects.get(key); - if (result == null) { + SingletonInfo info = configObjects.get(key); + if (info == null) { var others = configObjects.keySet().stream()// .filter(c -> c.getName().equals(key.getName()))// .map(c -> c.getClassLoader().getName() + "/" + c.getTypeName())// @@ -297,26 +466,24 @@ T doLookup(Class key, boolean stripRuntimeOnly, boolean allowMultiLayered throw UserError.abort("ImageSingletons do not contain key %s/%s but does contain the following key(s): %s", key.getClassLoader().getName(), key.getTypeName(), String.join(", ", others)); - } else if (result == SINGLETON_INSTALLATION_FORBIDDEN) { - throw UserError.abort("A LayeredImageSingleton was installed in a prior layer which forbids creating the singleton in a subsequent layer. Key %s", key.getTypeName()); - } else if (result instanceof RuntimeOnlyWrapper wrapper) { - if (!stripRuntimeOnly) { - throw UserError.abort("A LayeredImageSingleton was accessed during image building which does not have %s access. Key: %s, object %s", - LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS, key, wrapper.wrappedObject()); - } - result = wrapper.wrappedObject(); - } - if (!allowMultiLayered && result instanceof MultiLayeredImageSingleton) { - throw UserError.abort("Forbidden lookup of MultiLayeredImageSingleton. Use LayeredImageSingletonSupport.lookup if really necessary. Key: %s, object %s", key, result); + boolean allowedAccess = buildtimeAccess ? info.buildtimeAccessAllowed : info.runtimeAccessAllowed; + if (!allowedAccess) { + throw UserError.abort("Singleton cannot be accessed. Key: %s, Access type: %s", key.getTypeName(), buildtimeAccess ? "BUILD_TIME" : "RUN_TIME"); + } + + VMError.guarantee(info.singleton() != null); + Object singleton = info.singleton(); + if (!allowMultiLayered && singleton instanceof MultiLayeredImageSingleton) { + throw UserError.abort("Forbidden lookup of MultiLayeredImageSingleton. Use LayeredImageSingletonSupport.lookup if really necessary. Key: %s, object %s", key, singleton); } - return key.cast(result); + return key.cast(singleton); } boolean doContains(Class key) { checkKey(key); var value = configObjects.get(key); - return value != null && value != SINGLETON_INSTALLATION_FORBIDDEN; + return value != null && value.singleton() != SINGLETON_INSTALLATION_FORBIDDEN; } private static void checkKey(Class key) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 32e85f41a044..df6f12842a1c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -519,7 +519,8 @@ public void run(Map entryPoints, try (TemporaryBuildDirectoryProviderImpl tempDirectoryProvider = new TemporaryBuildDirectoryProviderImpl(tempDirectoryOptionValue)) { var builderTempDir = tempDirectoryProvider.getTemporaryBuildDirectory(); HostedImageLayerBuildingSupport imageLayerSupport = HostedImageLayerBuildingSupport.initialize(hostedOptionValues, loader, builderTempDir); - ImageSingletonsSupportImpl.HostedManagement.install(new ImageSingletonsSupportImpl.HostedManagement(imageLayerSupport.buildingImageLayer), imageLayerSupport); + ImageSingletonsSupportImpl.HostedManagement.install(new ImageSingletonsSupportImpl.HostedManagement(imageLayerSupport.buildingImageLayer, loader.classLoaderSupport.annotationExtractor), + imageLayerSupport); ImageSingletons.add(LayeredImageSingletonSupport.class, (LayeredImageSingletonSupport) ImageSingletonsSupportImpl.get()); ImageSingletons.add(ProgressReporter.class, reporter); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java index 0447a199fb38..ee104e237178 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java @@ -26,7 +26,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -46,8 +45,14 @@ import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; -import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.traits.BuiltinTraits; +import com.oracle.svm.core.traits.SingletonLayeredCallbacks; +import com.oracle.svm.core.traits.SingletonLayeredCallbacksSupplier; +import com.oracle.svm.core.traits.SingletonLayeredInstallationKind; +import com.oracle.svm.core.traits.SingletonTrait; +import com.oracle.svm.core.traits.SingletonTraitKind; +import com.oracle.svm.core.traits.SingletonTraits; import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; import com.oracle.svm.hosted.imagelayer.SVMImageLayerLoader; import com.oracle.svm.hosted.meta.HostedMethod; @@ -259,7 +264,8 @@ static TypeCheckInfo getTypecheckInfo(SVMImageLayerLoader loader, HostedType hTy record TypeCheckInfo(boolean installed, int typeID, int numClassTypes, int numInterfaceTypes, int[] typecheckSlots) { } - private static final class LayerTypeCheckInfo implements LayeredImageSingleton { + @SingletonTraits(access = BuiltinTraits.BuildtimeAccessOnly.class, layeredCallbacks = LayeredCallbacks.class, layeredInstallationKind = SingletonLayeredInstallationKind.Independent.class) + private static final class LayerTypeCheckInfo { final int maxTypeID; LayerTypeCheckInfo(int maxTypeID) { @@ -277,21 +283,30 @@ public int loadTypeID(Collection types) { return maxTypeID; } + } + static class LayeredCallbacks extends SingletonLayeredCallbacksSupplier { @Override - public EnumSet getImageBuilderFlags() { - return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY; - } + public SingletonTrait getLayeredCallbacksTrait() { + return new SingletonTrait(SingletonTraitKind.LAYERED_CALLBACKS, new SingletonLayeredCallbacks() { + @Override + public LayeredImageSingleton.PersistFlags doPersist(ImageSingletonWriter writer, Object singleton) { + writer.writeInt("maxTypeID", DynamicHubSupport.currentLayer().getMaxTypeId()); - @Override - public PersistFlags preparePersist(ImageSingletonWriter writer) { - writer.writeInt("maxTypeID", DynamicHubSupport.currentLayer().getMaxTypeId()); + return LayeredImageSingleton.PersistFlags.CREATE; + } - return PersistFlags.CREATE; + @Override + public Class getSingletonInstantiator() { + return SingletonInstantiator.class; + } + }); } + } - @SuppressWarnings("unused") - public static Object createFromLoader(ImageSingletonLoader loader) { + static class SingletonInstantiator implements SingletonLayeredCallbacks.LayeredSingletonInstantiator { + @Override + public Object createFromLoader(ImageSingletonLoader loader) { return new LayerTypeCheckInfo(loader.readInt("maxTypeID")); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index 2cba61d77c84..34bf77cbfe0b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -74,8 +74,6 @@ import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.hub.ClassForNameSupport; import com.oracle.svm.core.jdk.Resources; -import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; -import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.HostedOptionValues; @@ -84,6 +82,9 @@ import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.traits.BuiltinTraits; +import com.oracle.svm.core.traits.SingletonLayeredInstallationKind; +import com.oracle.svm.core.traits.SingletonTraits; import com.oracle.svm.core.util.TimeUtils; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ProgressReporterFeature.UserRecommendation; @@ -108,7 +109,8 @@ import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.util.json.JsonWriter; -public class ProgressReporter implements FeatureSingleton, UnsavedSingleton { +@SingletonTraits(access = BuiltinTraits.BuildtimeAccessOnly.class, layeredCallbacks = BuiltinTraits.NoLayeredCallbacks.class, layeredInstallationKind = SingletonLayeredInstallationKind.Independent.class) +public class ProgressReporter { private static final int CHARACTERS_PER_LINE; private static final String HEADLINE_SEPARATOR; private static final String LINE_SEPARATOR; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerSingletonLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerSingletonLoader.java index fefb4b46a5cc..972803710258 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerSingletonLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerSingletonLoader.java @@ -37,6 +37,7 @@ import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton.PersistFlags; +import com.oracle.svm.core.traits.SingletonLayeredCallbacks; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.ImageSingletonKey; @@ -60,8 +61,6 @@ public Map>> loadImageSingletons(Object forbiddenObject) { Map idToObjectMap = new HashMap<>(); Map, Integer> initialLayerKeyToIdMap = new HashMap<>(); for (ImageSingletonObject.Reader obj : snapshot.getSingletonObjects()) { - String className = obj.getClassName().toString(); - EconomicMap keyStore = EconomicMap.create(); for (KeyStoreEntry.Reader entry : obj.getStore()) { KeyStoreEntry.Value.Reader v = entry.getValue(); @@ -80,9 +79,19 @@ public Map>> loadImageSingletons(Object forbiddenObject) { // create singleton object instance Object result; try { - Class clazz = imageLayerBuildingSupport.lookupClass(false, className); - Method createMethod = ReflectionUtil.lookupMethod(clazz, "createFromLoader", ImageSingletonLoader.class); - result = createMethod.invoke(null, new ImageSingletonLoaderImpl(keyStore, snapshot)); + String recreateClass = obj.getRecreateClass().toString(); + Class clazz = imageLayerBuildingSupport.lookupClass(false, recreateClass); + if (SingletonLayeredCallbacks.LayeredSingletonInstantiator.class.isAssignableFrom(clazz)) { + SingletonLayeredCallbacks.LayeredSingletonInstantiator instance = (SingletonLayeredCallbacks.LayeredSingletonInstantiator) ReflectionUtil.newInstance(clazz); + result = instance.createFromLoader(new ImageSingletonLoaderImpl(keyStore, snapshot)); + } else { + // GR-66792 remove once no custom persist actions exist + String recreateMethod = obj.getRecreateMethod().toString(); + Method createMethod = ReflectionUtil.lookupMethod(clazz, recreateMethod, ImageSingletonLoader.class); + result = createMethod.invoke(null, new ImageSingletonLoaderImpl(keyStore, snapshot)); + } + Class instanceClass = imageLayerBuildingSupport.lookupClass(false, obj.getClassName().toString()); + VMError.guarantee(result.getClass().equals(instanceClass)); } catch (Throwable t) { throw VMError.shouldNotReachHere("Failed to recreate image singleton", t); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java index 0d470f5d6924..ce8532796b28 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java @@ -38,6 +38,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.nio.ByteBuffer; import java.nio.channels.Channels; @@ -101,16 +102,20 @@ import com.oracle.svm.core.classinitialization.ClassInitializationInfo; import com.oracle.svm.core.graal.code.CGlobalDataBasePointer; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; -import com.oracle.svm.core.layeredimagesingleton.RuntimeOnlyWrapper; import com.oracle.svm.core.meta.MethodOffset; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.reflect.serialize.SerializationSupport; import com.oracle.svm.core.threadlocal.FastThreadLocal; +import com.oracle.svm.core.traits.InjectedSingletonLayeredCallbacks; +import com.oracle.svm.core.traits.SingletonLayeredCallbacks; +import com.oracle.svm.core.traits.SingletonTraitKind; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.ImageSingletonsSupportImpl; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.FieldValueInterceptionSupport; import com.oracle.svm.hosted.annotation.AnnotationMemberValue; @@ -165,6 +170,7 @@ import com.oracle.svm.shaded.org.capnproto.Void; import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.ModuleSupport; +import com.oracle.svm.util.ReflectionUtil; import jdk.graal.compiler.core.common.NumUtil; import jdk.graal.compiler.debug.Assertions; @@ -1092,30 +1098,55 @@ public void addPolymorphicSignatureCaller(AnalysisMethod polymorphicSignature, A polymorphicSignatureCallers.computeIfAbsent(polymorphicSignature, (m) -> ConcurrentHashMap.newKeySet()).add(caller); } - record SingletonPersistInfo(LayeredImageSingleton.PersistFlags flags, int id, EconomicMap keyStore) { + record SingletonPersistInfo(LayeredImageSingleton.PersistFlags flags, int id, RecreateInfo recreateInfo, EconomicMap keyStore) { } - public void writeImageSingletonInfo(List, Object>> layeredImageSingletons) { + // GR-66792 remove once no custom persist actions exist + record RecreateInfo(String clazz, String method) { + } + + RecreateInfo createRecreateInfo(SingletonLayeredCallbacks action) { + if (action instanceof InjectedSingletonLayeredCallbacks injectAction) { + // GR-66792 remove once no custom persist actions exist + Class singletonClass = injectAction.getSingletonClass(); + String recreateName = "createFromLoader"; + Method loaderMethod = ReflectionUtil.lookupMethod(true, singletonClass, recreateName, ImageSingletonLoader.class); + if (loaderMethod == null) { + throw VMError.shouldNotReachHere("Unable to find createFromLoader for %s", singletonClass); + } + return new RecreateInfo(singletonClass.getName(), recreateName); + + } else { + return new RecreateInfo(action.getSingletonInstantiator().getName(), ""); + } + } + + public void writeImageSingletonInfo(List, ImageSingletonsSupportImpl.SingletonInfo>> layeredImageSingletons) { StructList.Builder singletonsBuilder = snapshotBuilder.initSingletonKeys(layeredImageSingletons.size()); - Map singletonInfoMap = new HashMap<>(); + Map singletonPersistInfoMap = new HashMap<>(); int nextID = 1; for (int i = 0; i < layeredImageSingletons.size(); i++) { - var singletonInfo = layeredImageSingletons.get(i); - LayeredImageSingleton singleton; - if (singletonInfo.getValue() instanceof RuntimeOnlyWrapper wrapper) { - singleton = wrapper.wrappedObject(); - } else { - singleton = (LayeredImageSingleton) singletonInfo.getValue(); - } - String key = singletonInfo.getKey().getName(); - if (!singletonInfoMap.containsKey(singleton)) { + var singletonEntry = layeredImageSingletons.get(i); + String key = singletonEntry.getKey().getName(); + Object singleton = singletonEntry.getValue().singleton(); + if (!singletonPersistInfoMap.containsKey(singleton)) { var writer = new ImageSingletonWriterImpl(snapshotBuilder, hUniverse); - var flags = singleton.preparePersist(writer); + SingletonLayeredCallbacks action = (SingletonLayeredCallbacks) singletonEntry.getValue().traitMap().getTrait(SingletonTraitKind.LAYERED_CALLBACKS).get().metadata(); + var flags = action.doPersist(writer, singleton); boolean persistData = flags == LayeredImageSingleton.PersistFlags.CREATE; - var info = new SingletonPersistInfo(flags, persistData ? nextID++ : -1, persistData ? writer.getKeyValueStore() : null); - singletonInfoMap.put(singleton, info); + int id = -1; + RecreateInfo recreateInfo = null; + EconomicMap keyValueStore = null; + if (persistData) { + id = nextID++; + recreateInfo = createRecreateInfo(action); + keyValueStore = writer.getKeyValueStore(); + } + + var info = new SingletonPersistInfo(flags, id, recreateInfo, keyValueStore); + singletonPersistInfoMap.put(singleton, info); } - var info = singletonInfoMap.get(singleton); + var info = singletonPersistInfoMap.get(singleton); ImageSingletonKey.Builder sb = singletonsBuilder.get(i); sb.setKeyClassName(key); @@ -1129,7 +1160,7 @@ public void writeImageSingletonInfo(List, Object>> layeredIma sb.setIsInitialLayerOnly(singleton instanceof InitialLayerOnlyImageSingleton); } - var sortedByIDs = singletonInfoMap.entrySet().stream() + var sortedByIDs = singletonPersistInfoMap.entrySet().stream() .filter(e -> e.getValue().flags == LayeredImageSingleton.PersistFlags.CREATE) .sorted(Comparator.comparingInt(e -> e.getValue().id)) .toList(); @@ -1141,6 +1172,8 @@ public void writeImageSingletonInfo(List, Object>> layeredIma ImageSingletonObject.Builder ob = objectsBuilder.get(i); ob.setId(info.id); ob.setClassName(entry.getKey().getClass().getName()); + ob.setRecreateClass(info.recreateInfo().clazz()); + ob.setRecreateMethod(info.recreateInfo().method()); writeImageSingletonKeyStore(ob, info.keyStore); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java index a83fe5d68ec3..a9cacd242950 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java @@ -4026,7 +4026,7 @@ public final boolean getIsInitialLayerOnly() { public static class ImageSingletonObject { - public static final com.oracle.svm.shaded.org.capnproto.StructSize STRUCT_SIZE = new com.oracle.svm.shaded.org.capnproto.StructSize((short)1,(short)2); + public static final com.oracle.svm.shaded.org.capnproto.StructSize STRUCT_SIZE = new com.oracle.svm.shaded.org.capnproto.StructSize((short)1,(short)4); public static final class Factory extends com.oracle.svm.shaded.org.capnproto.StructFactory { public Factory() { } @@ -4087,6 +4087,36 @@ public final void setStore(com.oracle.svm.shaded.org.capnproto.StructList.Reader public final com.oracle.svm.shaded.org.capnproto.StructList.Builder initStore(int size) { return _initPointerField(com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.KeyStoreEntry.listFactory, 1, size); } + public final boolean hasRecreateClass() { + return !_pointerFieldIsNull(2); + } + public final com.oracle.svm.shaded.org.capnproto.Text.Builder getRecreateClass() { + return _getPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 2, null, 0, 0); + } + public final void setRecreateClass(com.oracle.svm.shaded.org.capnproto.Text.Reader value) { + _setPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 2, value); + } + public final void setRecreateClass(String value) { + _setPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 2, new com.oracle.svm.shaded.org.capnproto.Text.Reader(value)); + } + public final com.oracle.svm.shaded.org.capnproto.Text.Builder initRecreateClass(int size) { + return _initPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 2, size); + } + public final boolean hasRecreateMethod() { + return !_pointerFieldIsNull(3); + } + public final com.oracle.svm.shaded.org.capnproto.Text.Builder getRecreateMethod() { + return _getPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 3, null, 0, 0); + } + public final void setRecreateMethod(com.oracle.svm.shaded.org.capnproto.Text.Reader value) { + _setPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 3, value); + } + public final void setRecreateMethod(String value) { + _setPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 3, new com.oracle.svm.shaded.org.capnproto.Text.Reader(value)); + } + public final com.oracle.svm.shaded.org.capnproto.Text.Builder initRecreateMethod(int size) { + return _initPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 3, size); + } } public static final class Reader extends com.oracle.svm.shaded.org.capnproto.StructReader { @@ -4112,6 +4142,20 @@ public final com.oracle.svm.shaded.org.capnproto.StructList.Reader key = constantObjectParameter(b, targetMethod, 0, Class.class, classNode); boolean result = ImageSingletons.contains(key); if (!result && ImageLayerBuildingSupport.buildingImageLayer()) { + // GR-66793 add detection for initial layer only image singleton if (ApplicationLayerOnlyImageSingleton.isAssignableFrom(key) || MultiLayeredImageSingleton.class.isAssignableFrom(key)) { /* * ApplicationLayerOnlyImageSingletons and the array representation of a diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/HostedJavaThreadsFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/HostedJavaThreadsFeature.java index 0f1c65a7b368..d04e7f22360c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/HostedJavaThreadsFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/HostedJavaThreadsFeature.java @@ -25,7 +25,6 @@ package com.oracle.svm.hosted.thread; import java.util.Arrays; -import java.util.EnumSet; import java.util.Map; import org.graalvm.nativeimage.ImageSingletons; @@ -39,10 +38,16 @@ import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; -import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; import com.oracle.svm.core.thread.JavaThreads; import com.oracle.svm.core.thread.JavaThreadsFeature; import com.oracle.svm.core.thread.PlatformThreads; +import com.oracle.svm.core.traits.BuiltinTraits; +import com.oracle.svm.core.traits.SingletonLayeredCallbacks; +import com.oracle.svm.core.traits.SingletonLayeredCallbacksSupplier; +import com.oracle.svm.core.traits.SingletonLayeredInstallationKind; +import com.oracle.svm.core.traits.SingletonTrait; +import com.oracle.svm.core.traits.SingletonTraitKind; +import com.oracle.svm.core.traits.SingletonTraits; import com.oracle.svm.core.util.ConcurrentIdentityHashMap; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.FeatureImpl; @@ -241,7 +246,8 @@ synchronized void add(ThreadGroup g) { } @AutomaticallyRegisteredImageSingleton -class HostedJavaThreadsMetadata implements LayeredImageSingleton { +@SingletonTraits(access = BuiltinTraits.BuildtimeAccessOnly.class, layeredCallbacks = HostedJavaThreadsMetadata.LayeredCallbacks.class, layeredInstallationKind = SingletonLayeredInstallationKind.Independent.class) +class HostedJavaThreadsMetadata { long maxThreadId; int maxAutonumber; @@ -259,23 +265,37 @@ private HostedJavaThreadsMetadata(long maxThreadId, int maxAutonumber) { this.maxAutonumber = maxAutonumber; } - @Override - public EnumSet getImageBuilderFlags() { - return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY; - } - - @Override - public PersistFlags preparePersist(ImageSingletonWriter writer) { + public LayeredImageSingleton.PersistFlags preparePersist(ImageSingletonWriter writer) { writer.writeLong("maxThreadId", maxThreadId); writer.writeInt("maxAutonumber", maxAutonumber); - return PersistFlags.CREATE; + return LayeredImageSingleton.PersistFlags.CREATE; } - @SuppressWarnings("unused") - public static Object createFromLoader(ImageSingletonLoader loader) { - long maxThreadId = loader.readLong("maxThreadId"); - int maxAutonumber = loader.readInt("maxAutonumber"); + static class LayeredCallbacks extends SingletonLayeredCallbacksSupplier { + @Override + public SingletonTrait getLayeredCallbacksTrait() { + SingletonLayeredCallbacks action = new SingletonLayeredCallbacks() { + @Override + public LayeredImageSingleton.PersistFlags doPersist(ImageSingletonWriter writer, Object singleton) { + return ((HostedJavaThreadsMetadata) singleton).preparePersist(writer); + } - return new HostedJavaThreadsMetadata(maxThreadId, maxAutonumber); + @Override + public Class getSingletonInstantiator() { + return SingletonInstantiator.class; + } + }; + return new SingletonTrait(SingletonTraitKind.LAYERED_CALLBACKS, action); + } + } + + static class SingletonInstantiator implements SingletonLayeredCallbacks.LayeredSingletonInstantiator { + @Override + public Object createFromLoader(ImageSingletonLoader loader) { + long maxThreadId = loader.readLong("maxThreadId"); + int maxAutonumber = loader.readInt("maxAutonumber"); + + return new HostedJavaThreadsMetadata(maxThreadId, maxAutonumber); + } } } diff --git a/substratevm/src/com.oracle.svm.processor/src/com/oracle/svm/processor/AutomaticallyRegisteredFeatureProcessor.java b/substratevm/src/com.oracle.svm.processor/src/com/oracle/svm/processor/AutomaticallyRegisteredFeatureProcessor.java index 89d58b54cebe..b3e543dfe7ab 100644 --- a/substratevm/src/com.oracle.svm.processor/src/com/oracle/svm/processor/AutomaticallyRegisteredFeatureProcessor.java +++ b/substratevm/src/com.oracle.svm.processor/src/com/oracle/svm/processor/AutomaticallyRegisteredFeatureProcessor.java @@ -39,7 +39,7 @@ // Checkstyle: allow Class.getSimpleName /** - * Annotation processor for the @AutomaticallyRegistredFeature annotation. We need to generate some + * Annotation processor for the @AutomaticallyRegisteredFeature annotation. We need to generate some * textual listing of all annotated feature classes that can be easily loaded in the image builder. * Standard Java ServiceLoader descriptors are the easiest, because mx already has the support to * aggregate service descriptors for multiple projects that end up in the same module.