Skip to content
Closed
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 @@ -43,7 +43,7 @@
*/
public class CGlobalDataNonConstantRegistry {

private final EconomicMap<CGlobalDataImpl<?>, CGlobalDataInfo> cGlobalDataInfos = ImageHeapMap.create(Equivalence.IDENTITY);
private final EconomicMap<CGlobalDataImpl<?>, CGlobalDataInfo> cGlobalDataInfos = ImageHeapMap.create(Equivalence.IDENTITY, "cGlobalDataInfos");

@Platforms(Platform.HOSTED_ONLY.class) //
private final Lock lock = new ReentrantLock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ public PodInfo(Class<?> podClass, Constructor<?> factoryCtor) {
}
}

private final EconomicMap<PodSpec, PodInfo> pods = ImageHeapMap.create();
private final EconomicMap<PodSpec, PodInfo> pods = ImageHeapMap.create("pods");

@Platforms(Platform.HOSTED_ONLY.class)
public RuntimeSupport() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ private static ClassForNameSupport[] layeredSingletons() {
/**
* The map used to collect registered classes.
*/
private final EconomicMap<String, ConditionalRuntimeValue<Object>> knownClasses = ImageHeapMap.create();
private final EconomicMap<String, ConditionalRuntimeValue<Object>> knownClasses = ImageHeapMap.createNonLayeredMap();
/**
* The map used to collect unsafe allocated classes.
*/
private final EconomicMap<Class<?>, RuntimeConditionSet> unsafeInstantiatedClasses = ImageHeapMap.create();
private final EconomicMap<Class<?>, RuntimeConditionSet> unsafeInstantiatedClasses = ImageHeapMap.createNonLayeredMap();

private static final Object NEGATIVE_QUERY = new Object();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ static PredefinedClassesSupport singleton() {
private final ReentrantLock lock = new ReentrantLock();

/** Predefined classes by hash. */
private final EconomicMap<String, Class<?>> predefinedClassesByHash = ImageHeapMap.create();
private final EconomicMap<String, Class<?>> predefinedClassesByHash = ImageHeapMap.create("predefinedClassesByHash");

/** Predefined classes which have already been loaded, by name. */
private final EconomicMap<String, Class<?>> loadedClassesByName = EconomicMap.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ public static Resources[] layeredSingletons() {
* ends up in the image heap is computed after the runtime module instances have been computed
* {see com.oracle.svm.hosted.ModuleLayerFeature}.
*/
private final EconomicMap<ModuleResourceKey, ConditionalRuntimeValue<ResourceStorageEntryBase>> resources = ImageHeapMap.create();
private final EconomicMap<RequestedPattern, RuntimeConditionSet> requestedPatterns = ImageHeapMap.create();
private final EconomicMap<ModuleResourceKey, ConditionalRuntimeValue<ResourceStorageEntryBase>> resources = ImageHeapMap.createNonLayeredMap();
private final EconomicMap<RequestedPattern, RuntimeConditionSet> requestedPatterns = ImageHeapMap.createNonLayeredMap();

public record RequestedPattern(String module, String resource) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public class LocalizationSupport {

public final Charset defaultCharset;

private final EconomicMap<String, RuntimeConditionSet> registeredBundles = ImageHeapMap.create();
private final EconomicMap<String, RuntimeConditionSet> registeredBundles = ImageHeapMap.create("registeredBundles");

public LocalizationSupport(Set<Locale> locales, Charset defaultCharset) {
this.allLocales = locales.toArray(new Locale[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import java.util.Collection;
import java.util.List;

import jdk.graal.compiler.word.Word;
import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.Equivalence;
import org.graalvm.nativeimage.Platform;
Expand All @@ -47,14 +46,16 @@
import com.oracle.svm.core.jni.headers.JNIVersion;
import com.oracle.svm.core.util.ImageHeapMap;

import jdk.graal.compiler.word.Word;

interface JNIOnLoadFunctionPointer extends CFunctionPointer {
@InvokeCFunctionPointer
int invoke(JNIJavaVM vm, VoidPointer reserved);
}

public class JNILibraryInitializer implements NativeLibrarySupport.LibraryInitializer {

private final EconomicMap<String, CGlobalData<PointerBase>> onLoadCGlobalDataMap = ImageHeapMap.create(Equivalence.IDENTITY);
private final EconomicMap<String, CGlobalData<PointerBase>> onLoadCGlobalDataMap = ImageHeapMap.create(Equivalence.IDENTITY, "onLoadCGlobalDataMap");

private static String getOnLoadName(String libName, boolean isBuiltIn) {
String name = "JNI_OnLoad";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public JNIAccessibleField getField(CharSequence name) {
@Platforms(HOSTED_ONLY.class)
public void addFieldIfAbsent(String name, Function<String, JNIAccessibleField> mappingFunction) {
if (fields == null) {
fields = ImageHeapMap.create(JNIReflectionDictionary.WRAPPED_CSTRING_EQUIVALENCE);
fields = ImageHeapMap.createNonLayeredMap(JNIReflectionDictionary.WRAPPED_CSTRING_EQUIVALENCE);
}
if (!fields.containsKey(name)) {
fields.put(name, mappingFunction.apply(name));
Expand All @@ -86,7 +86,7 @@ public void addFieldIfAbsent(String name, Function<String, JNIAccessibleField> m
@Platforms(HOSTED_ONLY.class)
public void addMethodIfAbsent(JNIAccessibleMethodDescriptor descriptor, Function<JNIAccessibleMethodDescriptor, JNIAccessibleMethod> mappingFunction) {
if (methods == null) {
methods = ImageHeapMap.create();
methods = ImageHeapMap.createNonLayeredMap();
}
if (!methods.containsKey(descriptor)) {
methods.put(descriptor, mappingFunction.apply(descriptor));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ private static JNIReflectionDictionary[] layeredSingletons() {
return MultiLayeredImageSingleton.getAllLayers(JNIReflectionDictionary.class);
}

private final EconomicMap<CharSequence, JNIAccessibleClass> classesByName = ImageHeapMap.create(WRAPPED_CSTRING_EQUIVALENCE);
private final EconomicMap<Class<?>, JNIAccessibleClass> classesByClassObject = ImageHeapMap.create();
private final EconomicMap<JNINativeLinkage, JNINativeLinkage> nativeLinkages = ImageHeapMap.create();
private final EconomicMap<CharSequence, JNIAccessibleClass> classesByName = ImageHeapMap.createNonLayeredMap(WRAPPED_CSTRING_EQUIVALENCE);
private final EconomicMap<Class<?>, JNIAccessibleClass> classesByClassObject = ImageHeapMap.createNonLayeredMap();
private final EconomicMap<JNINativeLinkage, JNINativeLinkage> nativeLinkages = ImageHeapMap.createNonLayeredMap();

private JNIReflectionDictionary() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public static String[] parseAndConsumeAllOptions(String[] initialArgs, boolean i
}

/** All reachable options. */
public EconomicMap<String, OptionDescriptor> options = ImageHeapMap.create();
public EconomicMap<String, OptionDescriptor> options = ImageHeapMap.create("options");

@Platforms(Platform.HOSTED_ONLY.class)
public void addDescriptor(OptionDescriptor optionDescriptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.oracle.svm.core.configure.RuntimeConditionSet;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.hub.PredefinedClassesSupport;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
import com.oracle.svm.core.util.ImageHeapMap;
Expand Down Expand Up @@ -73,7 +74,17 @@ public boolean equals(Object obj) {

@Override
public int hashCode() {
return Arrays.hashCode(interfaces);
if (ImageLayerBuildingSupport.buildingImageLayer()) {
/*
* The hash code cannot be computed using the interfaces' hash code in Layered Image
* because the hash code of classes cannot be injected in the application layer.
* This causes the internal structure of the proxyCache to be unusable in the
* application layer.
*/
return Arrays.hashCode(Arrays.stream(interfaces).map(Class::getName).toArray());
} else {
return Arrays.hashCode(interfaces);
}
}

@Override
Expand All @@ -82,7 +93,7 @@ public String toString() {
}
}

private final EconomicMap<ProxyCacheKey, ConditionalRuntimeValue<Object>> proxyCache = ImageHeapMap.create();
private final EconomicMap<ProxyCacheKey, ConditionalRuntimeValue<Object>> proxyCache = ImageHeapMap.create("proxyCache");

@Platforms(Platform.HOSTED_ONLY.class)
public DynamicProxySupport() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public int hashCode() {

@Platforms(Platform.HOSTED_ONLY.class)
public SerializationSupport() {
constructorAccessors = ImageHeapMap.create();
constructorAccessors = ImageHeapMap.create("constructorAccessors");
}

public void setStubConstructor(Constructor<?> stubConstructor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
package com.oracle.svm.core.util;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.graalvm.collections.EconomicMap;
Expand All @@ -33,6 +34,7 @@
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.BuildPhaseProvider;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;

/**
* A thread-safe map implementation that optimizes its run time representation for space efficiency.
Expand All @@ -57,25 +59,80 @@ private ImageHeapMap() {
* {@link EconomicMap} backed by a flat object array.
*/
@Platforms(Platform.HOSTED_ONLY.class) //
public static <K, V> EconomicMap<K, V> create() {
public static <K, V> EconomicMap<K, V> create(String key) {
return create(Equivalence.DEFAULT, key);
}

@Platforms(Platform.HOSTED_ONLY.class) //
public static <K, V> EconomicMap<K, V> createNonLayeredMap() {
return createNonLayeredMap(Equivalence.DEFAULT);
}

@Platforms(Platform.HOSTED_ONLY.class) //
public static <K, V> EconomicMap<K, V> create(Equivalence strategy, String key) {
assert key != null : "The key should not be null if the map needs to be automatically layered";
VMError.guarantee(!BuildPhaseProvider.isAnalysisFinished(), "Trying to create an ImageHeapMap after analysis.");
return new HostedImageHeapMap<>(Equivalence.DEFAULT);
return HostedImageHeapMap.create(strategy, key, true);
}

@Platforms(Platform.HOSTED_ONLY.class) //
public static <K, V> EconomicMap<K, V> create(Equivalence strategy) {
public static <K, V> EconomicMap<K, V> createNonLayeredMap(Equivalence strategy) {
VMError.guarantee(!BuildPhaseProvider.isAnalysisFinished(), "Trying to create an ImageHeapMap after analysis.");
return new HostedImageHeapMap<>(strategy);
return HostedImageHeapMap.create(strategy, null, false);
}

@Platforms(Platform.HOSTED_ONLY.class) //
public static final class HostedImageHeapMap<K, V> extends EconomicMapWrap<K, V> {
private final EconomicMap<Object, Object> currentLayerMap;
private final EconomicMap<Object, Object> runtimeMap;

public final EconomicMap<Object, Object> runtimeMap;
/**
* The {code key} is only used in the Layered Image context, to link the maps across each
* layer. If an {@link ImageHeapMap} is in a singleton that is already layer aware, there is
* no need to use a {@link LayeredImageHeapMap}, as the singleton should already handle the
* link across layers. In this case, {@code isAlreadyLayered} should be true and the
* {@code key} can be {@code null}.
*/
public HostedImageHeapMap(Map<K, V> hostedMap, EconomicMap<Object, Object> currentLayerMap, EconomicMap<Object, Object> runtimeMap) {
super(hostedMap);
this.currentLayerMap = currentLayerMap;
this.runtimeMap = runtimeMap;
}

@Platforms(Platform.HOSTED_ONLY.class) //
public EconomicMap<Object, Object> getCurrentLayerMap() {
return currentLayerMap;
}

/**
* Returns the run time representation of this map. In a non-layered build this is the same
* as the {@link #currentLayerMap} object returned by {@link #getCurrentLayerMap()}, i.e.,
* an {@link EconomicMap}. In a layered build this is a special {@link LayeredImageHeapMap}
* object that retrieves the layered key-value pairs by accessing the
* {@link #currentLayerMap} installed in every layer.
*/
public EconomicMap<Object, Object> getRuntimeMap() {
return runtimeMap;
}

HostedImageHeapMap(Equivalence strategy) {
super((strategy == Equivalence.IDENTITY) ? new ConcurrentIdentityHashMap<>() : new ConcurrentHashMap<>());
this.runtimeMap = EconomicMap.create(strategy);
public static <K, V> HostedImageHeapMap<K, V> create(Equivalence strategy, String key, boolean needLayeredMap) {
Map<K, V> hostedMap = (strategy == Equivalence.IDENTITY) ? new ConcurrentIdentityHashMap<>() : new ConcurrentHashMap<>();
EconomicMap<Object, Object> currentLayerMap = EconomicMap.create(strategy);
if (!needLayeredMap || !ImageLayerBuildingSupport.buildingImageLayer()) {
return new HostedImageHeapMap<>(hostedMap, currentLayerMap, currentLayerMap);
} else {
LayeredImageHeapMap<Object, Object> runtimeMap = new LayeredImageHeapMap<>(strategy, key);
var previousMap = LayeredImageHeapMapStore.currentLayer().getImageHeapMapStore().put(key, currentLayerMap);
if (previousMap != null) {
throw VMError.shouldNotReachHere("The LayeredImageHeapMap with key %s was added twice", key);
}
HostedImageHeapMap<K, V> hostedImageHeapMap = new HostedImageHeapMap<>(hostedMap, currentLayerMap, runtimeMap);
LayeredHostedImageHeapMapCollector singleton = LayeredHostedImageHeapMapCollector.singleton();
if (ImageLayerBuildingSupport.buildingExtensionLayer() && singleton.isMapKeyReachableInPreviousLayer(key)) {
singleton.registerPreviousLayerHostedImageHeapMap(hostedImageHeapMap);
}
return hostedImageHeapMap;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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.util;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.graalvm.nativeimage.ImageSingletons;
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;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
import com.oracle.svm.core.util.ImageHeapMap.HostedImageHeapMap;

/**
* {@link HostedImageHeapMap} are stored in {@code ImageHeapCollectionFeature#allMaps} through an
* object replacer, meaning that only maps reachable at run time are tracked and rescanned. In the
* extension layers, some maps can be extended at build time, but not be reachable from run time
* code of the current layer. So all the maps reachable in the base layer need to be tracked and
* when the map is created in an extension layer, it needs to be manually added to
* {@code ImageHeapCollectionFeature#allMaps} to ensure it is always rescanned and reachable.
*/
@Platforms(Platform.HOSTED_ONLY.class)
public class LayeredHostedImageHeapMapCollector implements LayeredImageSingleton {
/**
* Map keys of maps reachable in the current layer.
*/
private final List<String> currentLayerReachableMapsKeys = new ArrayList<>();
/**
* Map keys of maps reachable in the previous layers.
*/
private final List<String> previousLayerReachableMapKeys;
/**
* Maps reachable in the previous layers.
*/
private final List<HostedImageHeapMap<?, ?>> previousLayerReachableMaps = ImageLayerBuildingSupport.buildingExtensionLayer() ? new ArrayList<>() : null;

public LayeredHostedImageHeapMapCollector() {
this(null);
}

private LayeredHostedImageHeapMapCollector(List<String> previousLayerReachableMapKeys) {
this.previousLayerReachableMapKeys = previousLayerReachableMapKeys;
}

public static LayeredHostedImageHeapMapCollector singleton() {
return ImageSingletons.lookup(LayeredHostedImageHeapMapCollector.class);
}

public void registerReachableHostedImageHeapMap(LayeredImageHeapMap<Object, Object> layeredImageHeapMap) {
currentLayerReachableMapsKeys.add(layeredImageHeapMap.getMapKey());
}

public boolean isMapKeyReachableInPreviousLayer(String mapKey) {
return previousLayerReachableMapKeys.contains(mapKey);
}

public void registerPreviousLayerHostedImageHeapMap(HostedImageHeapMap<?, ?> hostedImageHeapMap) {
previousLayerReachableMaps.add(hostedImageHeapMap);
}

public List<HostedImageHeapMap<?, ?>> getPreviousLayerReachableMaps() {
return previousLayerReachableMaps;
}

@Override
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY;
}

@Override
public PersistFlags preparePersist(ImageSingletonWriter writer) {
Set<String> reachableMapKeys = new HashSet<>(currentLayerReachableMapsKeys);
if (previousLayerReachableMapKeys != null) {
reachableMapKeys.addAll(previousLayerReachableMapKeys);
}
writer.writeStringList("reachableMapKeys", reachableMapKeys.stream().toList());
return PersistFlags.CREATE;
}

@SuppressWarnings("unused")
public static Object createFromLoader(ImageSingletonLoader loader) {
List<String> previousLayerReachableMapKeys = loader.readStringList("reachableMapKeys");
return new LayeredHostedImageHeapMapCollector(previousLayerReachableMapKeys);
}
}
Loading
Loading