diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index 372ef8e22c70..f6d196ffccd3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -271,7 +271,7 @@ protected final void scanArray(JavaConstant array, ScanReason prevReason) { scanningObserver.forNullArrayElement(array, arrayType, idx, reason); } else { try { - JavaConstant element = bb.getUniverse().getHostedValuesProvider().forObject(bb.getUniverse().replaceObject(e)); + JavaConstant element = bb.getUniverse().replaceObjectWithConstant(e); scanArrayElement(array, arrayType, reason, idx, element); } catch (UnsupportedFeatureException | AnalysisError.TypeNotFoundError ex) { unsupportedFeatureDuringConstantScan(bb, bb.getUniverse().getHostedValuesProvider().forObject(e), ex, reason); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HostedValuesProvider.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HostedValuesProvider.java index b6de24ef84d5..4118c3e25a7c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HostedValuesProvider.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HostedValuesProvider.java @@ -76,9 +76,9 @@ public JavaConstant replaceObject(JavaConstant value) { } if (value.getJavaKind() == JavaKind.Object) { Object oldObject = asObject(Object.class, value); - Object newObject = universe.replaceObject(oldObject); - if (newObject != oldObject) { - return validateReplacedConstant(forObject(newObject)); + JavaConstant replacedConstant = universe.replaceObjectWithConstant(oldObject); + if (!replacedConstant.equals(value)) { + return validateReplacedConstant(replacedConstant); } } return value; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index 04e43cbba1bc..8761aa5f41c7 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -413,9 +413,9 @@ private Optional maybeReplace(JavaConstant constant, ScanReason re /* Run all registered object replacers. */ if (constant.getJavaKind() == JavaKind.Object) { try { - Object replaced = universe.replaceObject(unwrapped); - if (replaced != unwrapped) { - return Optional.of(hostedValuesProvider.validateReplacedConstant(universe.getHostedValuesProvider().forObject(replaced))); + JavaConstant replaced = universe.replaceObjectWithConstant(unwrapped); + if (!replaced.equals(constant)) { + return Optional.of(hostedValuesProvider.validateReplacedConstant(replaced)); } } catch (UnsupportedFeatureException e) { /* Enhance the unsupported feature message with the object trace and rethrow. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index 3027be4e6b90..e2e1e0615be5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -47,6 +47,7 @@ import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier; import com.oracle.graal.pointsto.heap.HostedValuesProvider; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.heap.ImageLayerLoader; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; @@ -103,6 +104,7 @@ public class AnalysisUniverse implements Universe { protected final SubstitutionProcessor substitutions; private Function[] objectReplacers; + private Function[] objectToConstantReplacers; private SubstitutionProcessor[] featureSubstitutions; private SubstitutionProcessor[] featureNativeSubstitutions; @@ -142,6 +144,7 @@ public AnalysisUniverse(HostVM hostVM, JavaKind wordKind, AnalysisPolicy analysi sealed = false; objectReplacers = (Function[]) new Function[0]; + objectToConstantReplacers = (Function[]) new Function[0]; featureSubstitutions = new SubstitutionProcessor[0]; featureNativeSubstitutions = new SubstitutionProcessor[0]; } @@ -565,6 +568,12 @@ public void registerObjectReplacer(Function replacer) { objectReplacers[objectReplacers.length - 1] = replacer; } + public void registerObjectToConstantReplacer(Function replacer) { + assert replacer != null; + objectToConstantReplacers = Arrays.copyOf(objectToConstantReplacers, objectToConstantReplacers.length + 1); + objectToConstantReplacers[objectToConstantReplacers.length - 1] = replacer; + } + public void registerFeatureSubstitution(SubstitutionProcessor substitution) { SubstitutionProcessor[] subs = featureSubstitutions; subs = Arrays.copyOf(subs, subs.length + 1); @@ -587,22 +596,56 @@ public SubstitutionProcessor[] getFeatureNativeSubstitutions() { return featureNativeSubstitutions; } + public Object replaceObject(Object source) { + return replaceObject0(source, false); + } + + public JavaConstant replaceObjectWithConstant(Object source) { + assert !(source instanceof ImageHeapConstant) : source; + + var replacedObject = replaceObject0(source, true); + if (replacedObject instanceof ImageHeapConstant constant) { + return constant; + } + + return getHostedValuesProvider().forObject(replacedObject); + } + /** - * Invokes all registered object replacers for an object. + * Invokes all registered object replacers and "object to constant" replacers for an object.> + * + *

+ * The "object to constant" replacer is allowed to successfully complete only when + * {@code allowObjectToConstantReplacement} is true. When + * {@code allowObjectToConstantReplacement} is false, if any "object to constant" replacer is + * triggered we throw an error. * * @param source The source object + * @param allowObjectToConstantReplacement whether object to constant replacement is supported * @return The replaced object or the original source, if the source is not replaced by any * registered replacer. */ - public Object replaceObject(Object source) { + private Object replaceObject0(Object source, boolean allowObjectToConstantReplacement) { if (source == null) { return null; } + Object destination = source; for (Function replacer : objectReplacers) { destination = replacer.apply(destination); } - return destination; + + ImageHeapConstant ihc = null; + for (Function replacer : objectToConstantReplacers) { + var result = replacer.apply(destination); + if (result != null) { + AnalysisError.guarantee(allowObjectToConstantReplacement, "Object to constant replacement has been triggered from an unsupported location"); + AnalysisError.guarantee(ihc == null, "Multiple object to constant replacers have been trigger on a single object %s %s %s", destination, ihc, result); + ihc = result; + } + } + + return ihc == null ? destination : ihc; } public void registerOverrideReachabilityNotification(AnalysisMethod declaredMethod, MethodOverrideReachableNotification notification) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ForkJoinPoolFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ForkJoinPoolFeature.java deleted file mode 100644 index 0b13d3ac036c..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ForkJoinPoolFeature.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.core.jdk; - -import java.util.concurrent.ForkJoinPool; - -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - -import com.oracle.svm.core.feature.InternalFeature; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; - -@AutomaticallyRegisteredFeature -@Platforms(Platform.HOSTED_ONLY.class) -class ForkJoinPoolFeature implements InternalFeature { - - private final DeferredCommonPool commonPool = new DeferredCommonPool(); - - @Override - public void duringSetup(DuringSetupAccess access) { - access.registerObjectReplacer(this::replaceCommonPool); - } - - private Object replaceCommonPool(Object original) { - if (original == ForkJoinPool.commonPool()) { - return this.commonPool; - } - return original; - } -} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemInOutErrSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemInOutErrSupport.java index a578d6272322..65006c0bbbaf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemInOutErrSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemInOutErrSupport.java @@ -32,11 +32,22 @@ import java.io.InputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; +import java.util.EnumSet; import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; +import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; +import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; +import com.oracle.svm.core.util.VMError; + +import jdk.graal.compiler.api.replacements.Fold; + /** * This class provides replacement values for the {@link System#in}, {@link System#out}, and * {@link System#err} streams at run time. We want a fresh set of objects, so that any buffers @@ -47,11 +58,14 @@ * This can be customized by calling {@link #setIn}, {@link #setOut}, and {@link #setErr} before the * static analysis starts, i.e., in a {@link Feature#beforeAnalysis} method. */ -public final class SystemInOutErrSupport { +public final class SystemInOutErrSupport implements InitialLayerOnlyImageSingleton { private InputStream in = new BufferedInputStream(new FileInputStream(FileDescriptor.in)); private PrintStream out = newPrintStream(new FileOutputStream(FileDescriptor.out), System.getProperty("sun.stdout.encoding")); private PrintStream err = newPrintStream(new FileOutputStream(FileDescriptor.err), System.getProperty("sun.stderr.encoding")); + @Platforms(Platform.HOSTED_ONLY.class) // + final AtomicBoolean isSealed = new AtomicBoolean(false); + /* Create `PrintStream` in the same way as `System.newPrintStream`. */ private static PrintStream newPrintStream(FileOutputStream fos, String enc) { if (enc != null) { @@ -63,32 +77,66 @@ private static PrintStream newPrintStream(FileOutputStream fos, String enc) { return new PrintStream(new BufferedOutputStream(fos, 128), true); } + public void seal() { + if (!isSealed.getPlain()) { + isSealed.set(true); + } + } + + public void checkSealed() { + VMError.guarantee(!isSealed.get(), "SystemInOurErrorSupport is already sealed"); + } + + private static SystemInOutErrSupport singleton() { + return ImageSingletons.lookup(SystemInOutErrSupport.class); + } + + @Fold public InputStream in() { + seal(); return in; } + @Platforms(Platform.HOSTED_ONLY.class) public static void setIn(InputStream in) { - ImageSingletons.lookup(SystemInOutErrSupport.class).in = Objects.requireNonNull(in); + var support = singleton(); + support.checkSealed(); + support.in = Objects.requireNonNull(in); } + @Fold public PrintStream out() { + seal(); return out; } + @Platforms(Platform.HOSTED_ONLY.class) public static void setOut(PrintStream out) { - ImageSingletons.lookup(SystemInOutErrSupport.class).out = Objects.requireNonNull(out); + var support = singleton(); + support.checkSealed(); + support.out = Objects.requireNonNull(out); } + @Fold public PrintStream err() { + seal(); return err; } + @Platforms(Platform.HOSTED_ONLY.class) public static void setErr(PrintStream err) { - ImageSingletons.lookup(SystemInOutErrSupport.class).err = Objects.requireNonNull(err); + var support = singleton(); + support.checkSealed(); + support.err = Objects.requireNonNull(err); + } + + @Override + public EnumSet getImageBuilderFlags() { + return LayeredImageSingletonBuilderFlags.ALL_ACCESS; } } @SuppressWarnings("unused") -class SystemInOutErrFeature implements Feature { +class SystemInOutErrFeature implements Feature, FeatureSingleton { /* Dummy for backward compatibility. */ } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index 588f29030843..b08c554f9874 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -54,6 +54,7 @@ import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; @@ -293,6 +294,15 @@ public void registerObjectReplacer(Function replacer) { getUniverse().registerObjectReplacer(replacer); } + /** + * Register an object replacer which may return an ImageHeapConstant. Note only one replacer + * can be triggered for a given object; otherwise an error will be thrown. Too, if the + * object should not be replaced then {@code null} should be returned. + */ + public void registerObjectToConstantReplacer(Function replacer) { + getUniverse().registerObjectToConstantReplacer(replacer); + } + /** * Register a callback that is executed when an object of the specified type or any of its * subtypes is marked as reachable. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SystemInOutErrFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SystemInOutErrFeature.java index 0127c6623ca6..95dc963840fa 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SystemInOutErrFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SystemInOutErrFeature.java @@ -31,10 +31,15 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.jdk.SystemInOutErrSupport; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.imagelayer.CrossLayerConstantRegistry; /** * We use an {@link Feature.DuringSetupAccess#registerObjectReplacer object replacer} because the @@ -43,7 +48,7 @@ * {@link RecomputeFieldValue} annotations. */ @AutomaticallyRegisteredFeature -public class SystemInOutErrFeature implements InternalFeature { +public class SystemInOutErrFeature implements InternalFeature, FeatureSingleton { private final InputStream hostedIn; private final PrintStream hostedOut; private final PrintStream hostedErr; @@ -57,16 +62,36 @@ public SystemInOutErrFeature() { private SystemInOutErrSupport runtime; + private static final String SYSTEM_IN_KEY_NAME = "System#in"; + private static final String SYSTEM_ERR_KEY_NAME = "System#err"; + private static final String SYSTEM_OUT_KEY_NAME = "System#out"; + @Override public void afterRegistration(AfterRegistrationAccess access) { - runtime = new SystemInOutErrSupport(); - ImageSingletons.add(SystemInOutErrSupport.class, runtime); + if (ImageLayerBuildingSupport.firstImageBuild()) { + runtime = new SystemInOutErrSupport(); + ImageSingletons.add(SystemInOutErrSupport.class, runtime); + } } @Override public void duringSetup(DuringSetupAccess access) { NativeImageSystemIOWrappers.singleton().verifySystemOutErrReplacement(); - access.registerObjectReplacer(this::replaceStreams); + if (ImageLayerBuildingSupport.firstImageBuild()) { + if (ImageLayerBuildingSupport.buildingInitialLayer()) { + var registry = CrossLayerConstantRegistry.singletonOrNull(); + registry.registerConstantCandidate(SYSTEM_IN_KEY_NAME, runtime.in()); + registry.registerConstantCandidate(SYSTEM_OUT_KEY_NAME, runtime.out()); + registry.registerConstantCandidate(SYSTEM_ERR_KEY_NAME, runtime.err()); + } + access.registerObjectReplacer(this::replaceStreamsWithRuntimeObject); + } else { + var registry = CrossLayerConstantRegistry.singletonOrNull(); + VMError.guarantee(registry.constantExists(SYSTEM_IN_KEY_NAME), "Missing Constant %s", SYSTEM_IN_KEY_NAME); + VMError.guarantee(registry.constantExists(SYSTEM_OUT_KEY_NAME), "Missing Constant %s", SYSTEM_OUT_KEY_NAME); + VMError.guarantee(registry.constantExists(SYSTEM_ERR_KEY_NAME), "Missing Constant %s", SYSTEM_ERR_KEY_NAME); + ((FeatureImpl.DuringSetupAccessImpl) access).registerObjectToConstantReplacer(obj -> replaceStreamsWithLayerConstant(registry, obj)); + } } @Override @@ -74,7 +99,7 @@ public void cleanup() { NativeImageSystemIOWrappers.singleton().verifySystemOutErrReplacement(); } - Object replaceStreams(Object object) { + Object replaceStreamsWithRuntimeObject(Object object) { if (object == hostedIn) { return runtime.in(); } else if (object == hostedOut) { @@ -85,4 +110,16 @@ Object replaceStreams(Object object) { return object; } } + + ImageHeapConstant replaceStreamsWithLayerConstant(CrossLayerConstantRegistry idTracking, Object object) { + if (object == hostedIn) { + return idTracking.getConstant(SYSTEM_IN_KEY_NAME); + } else if (object == hostedOut) { + return idTracking.getConstant(SYSTEM_OUT_KEY_NAME); + } else if (object == hostedErr) { + return idTracking.getConstant(SYSTEM_ERR_KEY_NAME); + } else { + return null; + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index 787c710236d3..beb5056c6e6c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -219,7 +219,11 @@ private void writeConstant(RelocatableBuffer buffer, int index, JavaKind kind, J * can only happen late, during compilation. */ private RelocatedPointer prepareRelocatable(ObjectInfo info, JavaConstant value) { - return (RelocatedPointer) maybeReplace(snippetReflection().asObject(RelocatedPointer.class, value), info); + try { + return (RelocatedPointer) heap.aUniverse.replaceObject(snippetReflection().asObject(RelocatedPointer.class, value)); + } catch (AnalysisError.TypeNotFoundError ex) { + throw NativeImageHeap.reportIllegalType(ex.getType(), info); + } } private void writeConstant(RelocatableBuffer buffer, int index, JavaKind kind, Object value, ObjectInfo info) { @@ -467,14 +471,6 @@ private void writePrimitiveArray(ObjectInfo info, RelocatableBuffer buffer, Obje Unsafe.ARRAY_BYTE_BASE_OFFSET + elementIndex, length * elementTypeSize); } - private Object maybeReplace(Object object, Object reason) { - try { - return heap.aUniverse.replaceObject(object); - } catch (AnalysisError.TypeNotFoundError ex) { - throw NativeImageHeap.reportIllegalType(ex.getType(), reason); - } - } - private SnippetReflectionProvider snippetReflection() { return heap.hUniverse.getSnippetReflection(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ObjectGroupHistogram.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ObjectGroupHistogram.java index f558f8228c18..85c2ab1f5fab 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ObjectGroupHistogram.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ObjectGroupHistogram.java @@ -31,6 +31,9 @@ import org.graalvm.nativeimage.ImageSingletons; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; +import com.oracle.graal.pointsto.heap.ImageHeapInstance; +import com.oracle.graal.pointsto.heap.ImageHeapObjectArray; import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.code.CodeInfoTable; @@ -181,7 +184,8 @@ public void processObject(Object object, String group, boolean addObject, Object private void processObject(ObjectInfo info, String group, boolean addObject, int recursionLevel, ObjectFilter objectFilter, FieldFilter fieldFilter) { assert info != null; - if (info.getConstant().isInBaseLayer()) { + ImageHeapConstant ihc = info.getConstant(); + if (ihc.isInBaseLayer()) { /* Base layer objects don't count towards current layer's statistics. */ return; } @@ -193,27 +197,27 @@ private void processObject(ObjectInfo info, String group, boolean addObject, int return; } } - if (info.getClazz().isInstanceClass()) { - JavaConstant con = heap.hUniverse.getSnippetReflection().forObject(info.getObject()); + + if (ihc instanceof ImageHeapInstance) { for (HostedField field : info.getClazz().getInstanceFields(true)) { if (field.getType().getStorageKind() == JavaKind.Object && !HostedConfiguration.isInlinedField(field) && field.isAccessed()) { if (fieldFilter == null || fieldFilter.test(info, field)) { - JavaConstant fieldValue = heap.hConstantReflection.readFieldValue(field, con); + JavaConstant fieldValue = heap.hConstantReflection.readFieldValue(field, ihc); if (fieldValue.isNonNull()) { processObject(heap.getConstantInfo(fieldValue), group, true, recursionLevel + 1, objectFilter, fieldFilter); } } } } - } else if (info.getObject() instanceof Object[]) { - for (Object element : (Object[]) info.getObject()) { - if (element != null) { - ObjectInfo elementInfo = heap.getObjectInfo(heap.aUniverse.replaceObject(element)); + } else if (ihc instanceof ImageHeapObjectArray) { + heap.hConstantReflection.forEachArrayElement(ihc, (element, idx) -> { + if (element.isNonNull()) { + ObjectInfo elementInfo = heap.getConstantInfo(element); if (elementInfo != null) { processObject(elementInfo, group, true, recursionLevel + 1, objectFilter, fieldFilter); } } - } + }); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistry.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistry.java new file mode 100644 index 000000000000..2ca5d971a337 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistry.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, 2024, 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.hosted.imagelayer; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.heap.ImageHeapConstant; + +/** + * Registry to manage cross-layer constant references. + */ +public interface CrossLayerConstantRegistry { + + static CrossLayerConstantRegistry singletonOrNull() { + if (ImageSingletons.contains(CrossLayerConstantRegistry.class)) { + return ImageSingletons.lookup(CrossLayerConstantRegistry.class); + } + + return null; + } + + /** + * Retrieves the constant associated with {@code keyName}. If a constant does not exist then an + * error is thrown. + */ + ImageHeapConstant getConstant(String keyName); + + /** + * Checks whether a constant for {@code keyName} was stored in a prior layer. + */ + boolean constantExists(String keyName); + + /** + * Registers a value which may be stored in this layer's heap. If the value is stored in the + * heap, later layers can access it via {@link #getConstant} and check whether it exists via + * {@link #constantExists}. + */ + void registerConstantCandidate(String keyName, Object obj); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java new file mode 100644 index 000000000000..1d98b5137a4f --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2024, 2024, 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.hosted.imagelayer; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.heap.ImageHeapConstant; +import com.oracle.graal.pointsto.heap.ImageLayerLoader; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; +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.VMError; +import com.oracle.svm.hosted.FeatureImpl; + +@AutomaticallyRegisteredFeature +public class CrossLayerConstantRegistryFeature implements InternalFeature, FeatureSingleton, CrossLayerConstantRegistry { + + ImageLayerIdTrackingSingleton tracker; + boolean sealed = false; + ImageLayerLoader loader; + + Map constantCandidates; + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return ImageLayerBuildingSupport.buildingImageLayer(); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + if (ImageLayerBuildingSupport.buildingInitialLayer()) { + tracker = new ImageLayerIdTrackingSingleton(); + ImageSingletons.add(ImageLayerIdTrackingSingleton.class, tracker); + } else { + tracker = ImageSingletons.lookup(ImageLayerIdTrackingSingleton.class); + } + + if (ImageLayerBuildingSupport.buildingSharedLayer()) { + constantCandidates = new ConcurrentHashMap<>(); + } + ImageSingletons.add(CrossLayerConstantRegistry.class, this); + } + + @Override + public void duringSetup(DuringSetupAccess access) { + loader = ((FeatureImpl.DuringSetupAccessImpl) access).getUniverse().getImageLayerLoader(); + } + + @Override + public void beforeImageWrite(BeforeImageWriteAccess access) { + sealed = true; + if (ImageLayerBuildingSupport.buildingSharedLayer()) { + /* + * Register constant candidates installed in this layer. + */ + + var config = (FeatureImpl.BeforeImageWriteAccessImpl) access; + var snippetReflection = config.getHostedUniverse().getSnippetReflection(); + var heap = config.getImage().getHeap(); + + for (var entry : constantCandidates.entrySet()) { + + var optional = config.getHostedMetaAccess().optionalLookupJavaType(entry.getValue().getClass()); + if (optional.isPresent()) { + var constant = (ImageHeapConstant) snippetReflection.forObject(entry.getValue()); + var objectInfo = heap.getConstantInfo(constant); + if (objectInfo != null && objectInfo.getOffset() >= 0) { + int id = ImageHeapConstant.getConstantID(constant); + tracker.registerKey(entry.getKey(), id); + } + } + } + } + } + + private void checkSealed() { + VMError.guarantee(!sealed, "Id tracking is sealed"); + } + + @Override + public void registerConstantCandidate(String keyName, Object obj) { + checkSealed(); + VMError.guarantee(ImageLayerBuildingSupport.buildingSharedLayer(), "This only applies to shared layers"); + var previous = constantCandidates.putIfAbsent(keyName, obj); + VMError.guarantee(previous == null && !constantExists(keyName), "This key has been registered before: %s", keyName); + } + + @Override + public ImageHeapConstant getConstant(String keyName) { + checkSealed(); + Integer id = tracker.getId(keyName); + VMError.guarantee(id != null, "Missing key: %s", keyName); + return loader.getOrCreateConstant(id); + } + + @Override + public boolean constantExists(String keyName) { + checkSealed(); + return tracker.getId(keyName) != null; + } +} + +class ImageLayerIdTrackingSingleton implements LayeredImageSingleton { + private final Map keyToIdMap = new ConcurrentHashMap<>(); + + Integer getId(String key) { + return keyToIdMap.get(key); + } + + void registerKey(String key, int id) { + var previous = keyToIdMap.putIfAbsent(key, id); + VMError.guarantee(previous == null, "Two values are registered for this key %s", key); + } + + @Override + public EnumSet getImageBuilderFlags() { + return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY; + } + + @Override + public PersistFlags preparePersist(ImageSingletonWriter writer) { + ArrayList keys = new ArrayList<>(); + ArrayList ids = new ArrayList<>(); + for (var entry : keyToIdMap.entrySet()) { + keys.add(entry.getKey()); + ids.add(entry.getValue()); + } + + writer.writeStringList("keys", keys); + writer.writeIntList("ids", ids); + return PersistFlags.CREATE; + } + + @SuppressWarnings("unused") + public static Object createFromLoader(ImageSingletonLoader loader) { + var tracker = new ImageLayerIdTrackingSingleton(); + + Iterator keys = loader.readStringList("keys").iterator(); + Iterator ids = loader.readIntList("ids").iterator(); + + while (keys.hasNext()) { + tracker.registerKey(keys.next(), ids.next()); + } + return tracker; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ForkJoinPoolFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ForkJoinPoolFeature.java new file mode 100644 index 000000000000..3ee3bcaa1f79 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/ForkJoinPoolFeature.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017, 2024, 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.hosted.jdk; + +import java.util.concurrent.ForkJoinPool; + +import com.oracle.graal.pointsto.heap.ImageHeapConstant; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.jdk.DeferredCommonPool; +import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; +import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.imagelayer.CrossLayerConstantRegistry; + +@AutomaticallyRegisteredFeature +class ForkJoinPoolFeature implements InternalFeature, FeatureSingleton { + + private static final String KEY_NAME = "ForkJoinPool#commonPool"; + + @Override + public void duringSetup(DuringSetupAccess access) { + CrossLayerConstantRegistry registry = CrossLayerConstantRegistry.singletonOrNull(); + if (ImageLayerBuildingSupport.buildingExtensionLayer() && registry.constantExists(KEY_NAME)) { + ((FeatureImpl.DuringSetupAccessImpl) access).registerObjectToConstantReplacer(obj -> replaceCommonPoolWithLayerConstant(registry, obj)); + } else { + var commonPool = new DeferredCommonPool(); + access.registerObjectReplacer(obj -> replaceCommonPoolWithRuntimeObject(obj, commonPool)); + + if (ImageLayerBuildingSupport.buildingSharedLayer()) { + registry.registerConstantCandidate(KEY_NAME, commonPool); + } + } + } + + private static Object replaceCommonPoolWithRuntimeObject(Object original, DeferredCommonPool commonPool) { + if (original == ForkJoinPool.commonPool()) { + return commonPool; + } + return original; + } + + private static ImageHeapConstant replaceCommonPoolWithLayerConstant(CrossLayerConstantRegistry idTracking, Object original) { + if (original == ForkJoinPool.commonPool()) { + return idTracking.getConstant(KEY_NAME); + } + return null; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index 22b4433e9bac..09ca74350909 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -1151,7 +1151,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec * to ensure certain singleton are not installed into the image. */ if (!RuntimeCompilation.isEnabled()) { - throw b.bailout("Layered image singleton without runtime access is in runtime graph: " + singleton); + throw VMError.shouldNotReachHere("Layered image singleton without runtime access is in runtime graph: " + singleton); } } b.addPush(JavaKind.Object, ConstantNode.forConstant(b.getSnippetReflection().forObject(singleton), b.getMetaAccess()));