From 837030bdc171a2a9efd8ea04da7e2dcc6a635891 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Wed, 17 Aug 2022 14:14:03 -0700 Subject: [PATCH] Provide FieldValueTransformer API --- sdk/CHANGELOG.md | 3 + .../org.graalvm.nativeimage/snapshot.sigtest | 3 + .../graalvm/nativeimage/hosted/Feature.java | 8 + .../hosted/FieldValueTransformer.java | 86 +++++++++++ .../oracle/svm/core/windows/WindowsUtils.java | 14 +- .../com/oracle/svm/core/annotate/Alias.java | 16 +- .../svm/core/annotate/AutomaticFeature.java | 10 +- .../com/oracle/svm/core/annotate/Inject.java | 12 +- .../svm/core/annotate/KeepOriginal.java | 18 ++- .../core/annotate/RecomputeFieldValue.java | 112 +------------- .../oracle/svm/core/annotate/Substitute.java | 12 +- .../oracle/svm/core/annotate/TargetClass.java | 43 +++--- ...FieldValueTransformerWithAvailability.java | 63 ++++++++ .../NewEmptyArrayFieldValueTransformer.java | 43 ++++++ .../NewInstanceFieldValueTransformer.java | 42 +++++ .../heap/Target_java_lang_ref_Reference.java | 23 +-- .../core/jdk/FileSystemProviderSupport.java | 13 +- .../svm/core/jdk/JavaIOSubstitutions.java | 24 +-- .../svm/core/jdk/JavaLangSubstitutions.java | 14 +- .../svm/core/jdk/SecuritySubstitutions.java | 31 +--- .../jdk/Target_java_lang_ClassLoader.java | 14 +- .../oracle/svm/core/jdk/VarHandleFeature.java | 31 ++-- .../substitutions/DefaultLocaleComputer.java | 14 +- .../Target_jdk_jfr_internal_LogTag.java | 15 +- ...et_java_lang_invoke_BoundMethodHandle.java | 3 +- ...arget_java_lang_invoke_MethodTypeForm.java | 6 +- .../svm/core/reflect/SubstrateAccessor.java | 4 + .../core/reflect/SubstrateMethodAccessor.java | 52 +------ .../target/ExecutableAccessorComputer.java | 13 +- .../reflect/target/FieldOffsetComputer.java | 21 +-- .../target/ReflectionMetadataComputer.java | 19 +-- .../target/ReflectionSubstitutionSupport.java | 9 +- ...et_java_lang_reflect_AccessibleObject.java | 5 +- .../Target_java_lang_reflect_Constructor.java | 7 +- .../Target_java_lang_reflect_Executable.java | 5 +- .../Target_java_lang_reflect_Field.java | 21 +-- .../Target_java_lang_reflect_Method.java | 9 +- .../JavaLangThreadGroupSubstitutions.java | 76 ++------- .../hotspot/libgraal/LibGraalFeature.java | 12 +- .../oracle/svm/graal/GraalSubstitutions.java | 21 +-- .../graal/hosted/FieldsOffsetsFeature.java | 17 +- .../oracle/svm/graal/hosted/GraalFeature.java | 2 +- .../com/oracle/svm/hosted/FeatureImpl.java | 8 +- .../svm/hosted/NativeImageGenerator.java | 8 +- .../analysis/CustomTypeFieldHandler.java | 6 +- .../phases/ConstantFoldLoadFieldPlugin.java | 15 +- .../svm/hosted/reflect/ReflectionFeature.java | 58 +++++-- .../AnnotationSubstitutionProcessor.java | 36 ++++- .../hosted/substitute/ComputedValueField.java | 146 ++++++------------ ...get_org_junit_runners_model_TestClass.java | 14 +- ...ffle_nfi_backend_libffi_LibFFIContext.java | 8 +- .../svm/truffle/TruffleBaseFeature.java | 115 +++++--------- 52 files changed, 627 insertions(+), 753 deletions(-) create mode 100644 sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/FieldValueTransformer.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/FieldValueTransformerWithAvailability.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/NewEmptyArrayFieldValueTransformer.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/NewInstanceFieldValueTransformer.java diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 238d7298e8d4..2cbd3eadde4e 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -2,6 +2,9 @@ This changelog summarizes major changes between GraalVM SDK versions. The main focus is on APIs exported by GraalVM SDK. +## Version 22.3.0 +* (GR-39852) Native Image API: Added FieldValueTransformer API + ## Version 22.2.0 * (GR-38925) Added `Value.hasMetaParents() and Value.getMetaParents()` that allow lookup of the hierarchy of parents for meta objects (e.g. super class or implemented interface of Java classes). * (GR-38351) Added [FileSystem#allowLanguageHomeAccess](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/io/FileSystem.html#allowLanguageHomeAccess-org.graalvm.polyglot.io.FileSystem-) returning a `FileSystem` that forwards access to files in the language home to the default file system. diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest index 6fa9c0c59501..0a23936dbf16 100644 --- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest +++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest @@ -922,6 +922,7 @@ meth public abstract void registerAsUsed(java.lang.Class) meth public abstract void registerClassInitializerReachabilityHandler(java.util.function.Consumer,java.lang.Class) meth public abstract void registerMethodOverrideReachabilityHandler(java.util.function.BiConsumer,java.lang.reflect.Executable) meth public abstract void registerSubtypeReachabilityHandler(java.util.function.BiConsumer>,java.lang.Class) +meth public abstract void registerFieldValueTransformer(java.lang.reflect.Field,org.graalvm.nativeimage.hosted.FieldValueTransformer) CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$BeforeCompilationAccess outer org.graalvm.nativeimage.hosted.Feature @@ -1024,3 +1025,5 @@ meth public abstract boolean equals(java.lang.Object) anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="") meth public abstract long rawValue() +CLSS public abstract interface org.graalvm.nativeimage.hosted.FieldValueTransformer +meth public abstract java.lang.Object transform(java.lang.Object,java.lang.Object) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java index 60ec0ca00227..dc5efc882d09 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java @@ -296,6 +296,14 @@ interface BeforeAnalysisAccess extends FeatureAccess { * @since 21.0 */ void registerClassInitializerReachabilityHandler(Consumer callback, Class clazz); + + /** + * Registers a field value transformer for the provided field. See the JavaDoc of + * {@link FieldValueTransformer} for details. + * + * @since 22.3 + */ + void registerFieldValueTransformer(Field field, FieldValueTransformer transformer); } /** diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/FieldValueTransformer.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/FieldValueTransformer.java new file mode 100644 index 000000000000..04afbd762e9e --- /dev/null +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/FieldValueTransformer.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.nativeimage.hosted; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature.BeforeAnalysisAccess; + +/** + * A transformer for a field value that can be registered using + * {@link BeforeAnalysisAccess#registerFieldValueTransformer}. + *

+ * At image build time, the field value transformer provides the value of the field for the image + * heap. Without a transformer, the value of the field in the image heap is the same as the hosted + * value in the image generator. A field value transformer allows to intercept the value. A field + * value transformer can, for example, reset a field to the default null/0 value, replace a filled + * collection with a new empty collection, or provide any kind of new value for a field. + *

+ * Only one field value transformer can be registered for each field. + *

+ * A field value transformer can be registered for fields of classes that are initialized at image + * run time. That allows constant folding of final fields even though the declaring class is not + * initialized at image build time. + *

+ * A field value transformer must be registered before the field is seen as reachable by the static + * analysis. It is generally safe to register a transformer in {@link Feature#beforeAnalysis} before + * the static analysis is started, in a {@link BeforeAnalysisAccess#registerReachabilityHandler type + * reachability handler} for the declaring class of the field, or a + * {@link BeforeAnalysisAccess#registerSubtypeReachabilityHandler subtype reachability handler} for + * a super-type of the declaring class of the field. + * + * @since 22.3 + */ +@Platforms(Platform.HOSTED_ONLY.class) +public interface FieldValueTransformer { + + /** + * Transforms the field value for the provided receiver. The receiver is null for static fields. + * The original value of the field, i.e., the hosted value of the field in the image generator, + * is also provided as an argument. + *

+ * The type of the returned object must be assignable to the declared type of the field. If the + * field has a primitive type, the return value must be a boxed value, and must not be null. + * + * @since 22.3 + */ + Object transform(Object receiver, Object originalValue); +} diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java index 4886cd6ce5c9..627fc3e979cb 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java @@ -36,6 +36,7 @@ import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.CLongPointer; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -43,7 +44,6 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.function.CEntryPointActions; @@ -52,9 +52,6 @@ import com.oracle.svm.core.windows.headers.WinBase; import com.oracle.svm.core.windows.headers.WinBase.HMODULE; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - public class WindowsUtils { @TargetClass(className = "java.lang.ProcessImpl") @@ -70,14 +67,9 @@ public static int getpid(java.lang.Process process) { @TargetClass(java.io.FileDescriptor.class) private static final class Target_java_io_FileDescriptor { /** Invalidates the standard FileDescriptors, which are allowed in the image heap. */ - static class InvalidHandleValueComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - + static class InvalidHandleValueComputer implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return -1L; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Alias.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Alias.java index 0932acadb3a1..dc10febad85d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Alias.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Alias.java @@ -24,14 +24,14 @@ */ package com.oracle.svm.core.annotate; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - 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; + /** * Mechanism for referring to fields and methods otherwise inaccessible due to Java language access * control rules. This enables VM code to directly access a private field or invoke a private method @@ -40,10 +40,12 @@ *

* The idiom for using {@link Alias} is somewhat related to the {@link Substitute} annotation, but * reversed; both are often used in combination. In both cases a separate class is used to declare - * the aliased or substituted methods. In the substitution case occurrences of {@code this} actually - * refer to the instance of the class being substituted. In the aliased case we pretend that the - * class declaring the aliased method is an instance of the aliasee in order to access its fields or - * invoke its methods. + * the aliased and/or substituted methods. In the substitution case occurrences of {@code this} + * actually refer to the instance of the class being substituted. In the aliased case we pretend + * that the class declaring the aliased method or field is an instance of the aliasee in order to + * access its fields or invoke its methods. An alias is always called (method alias) or accessed + * (field alias), whereas a substitution method is only implemented (usually not called directly). + * In the body of a substitution method, aliases are often called or accessed. *

* The element can also be annotated with {@link TargetElement} to specify additional properties. * See {@link TargetClass} for an overview of the annotation system. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/AutomaticFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/AutomaticFeature.java index 05d56460ec8b..18fddf07cae2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/AutomaticFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/AutomaticFeature.java @@ -24,15 +24,19 @@ */ package com.oracle.svm.core.annotate; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - 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; + /** + * Supported API is available to replace this non-API annotation: Instead of using this annotation, + * use {@code "--features "} in the {@code Args} of a + * {@code native-image.properties} file to ensure a user-provided feature gets processed. + * * Feature classes can use this annotation are unconditionally added when they are reachable on the * class path. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Inject.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Inject.java index 85f82ee56ca1..16ca4bc68e9f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Inject.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Inject.java @@ -24,20 +24,20 @@ */ package com.oracle.svm.core.annotate; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - 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; + /** * Injects the annotated field into the {@link TargetClass}. * - * The field must not be declared static. If instances of the target class are in the native image - * heap, the field also needs to be annotated with {@link RecomputeFieldValue} to provide a value - * for the native image objects. + * The field must not be declared static. If instances of the target class are in the image heap, + * the field also needs to be annotated with {@link RecomputeFieldValue} to provide a value for the + * injected field. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/KeepOriginal.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/KeepOriginal.java index 8768c98b277f..65e67244bd16 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/KeepOriginal.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/KeepOriginal.java @@ -24,23 +24,25 @@ */ package com.oracle.svm.core.annotate; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - 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; + /** - * In a {@link Substitute substituted} type, keep the original definition of this method. The - * default behavior is that all non-substituted methods are implicitly treated as {@link Delete - * deleted}. Unless this annotation is applied to the {@link Substitute substituted} type itself, - * then the original definition of all methods and fields in the target type are kept by default. + * If a class annotated with {@link TargetClass} is also annotated with {@link Substitute}, all + * non-substituted methods in that class are by default treated as {@link Delete deleted}. This + * annotation changes the behavior: A method annotated with {@link KeepOriginal} keeps the original + * definition of the method. + *

+ * If this annotation is used to the {@link Substitute * substituted} type itself, then the original + * definition of all methods and fields in the target type are kept by default. *

* The element can also be annotated with {@link TargetElement} to specify additional properties. * See {@link TargetClass} for an overview of the annotation system. - *

*/ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java index 9c6b8574ac12..f835bbed1c0a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java @@ -28,16 +28,16 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.lang.reflect.Array; -import com.oracle.svm.core.util.VMError; - -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature.BeforeAnalysisAccess; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; /** + * Supported API is available to replace this non-API annotation: Use + * {@link BeforeAnalysisAccess#registerFieldValueTransformer}. + * * Mechanism to change the value of a field. Normally, field values in the native image heap of the * Substrate VM are just taken from the host VM. This annotation allows the field value to be * intercepted and recomputed. @@ -112,111 +112,11 @@ enum Kind { */ Manual, /** - * Use a {@link CustomFieldValueComputer} or {@link CustomFieldValueTransformer}, which is - * specified as the target class. + * Use a {@link FieldValueTransformer}, which is specified as the target class. */ Custom, } - enum ValueAvailability { - /** Value is independent of analysis/compilation results. */ - BeforeAnalysis, - /** Value depends on data computed by the analysis. */ - AfterAnalysis, - /** Value depends on data computed during compilation. */ - AfterCompilation - } - - interface CustomFieldValueProvider { - - /** - * When is the value for this custom computation available? By default, it is assumed that - * the value is available {@link ValueAvailability#BeforeAnalysis before analysis}, i.e., it - * doesn't depend on analysis results. - */ - ValueAvailability valueAvailability(); - - /** - * Specify types that this field can take if the value is not available for analysis. The - * concrete type of the computed value can be a subtype of one of the specified types as - * specified by {@link Class#isAssignableFrom(Class)}. If the array contains `null` then the - * field value can also be null. - */ - default Class[] types() { - if (valueAvailability() != ValueAvailability.BeforeAnalysis) { - throw VMError.shouldNotReachHere("Custom value field whose value is not available during analysis " + - "must override CustomFieldValueProvider.types() and specify types for analysis."); - } - return null; - } - } - - /** - * Custom recomputation of field values. A class implementing this interface must have a - * no-argument constructor, which is used to instantiate it before invoking {@link #compute}. - */ - interface CustomFieldValueComputer extends CustomFieldValueProvider { - /** - * Computes the new field value. This method can already be invoked during the analysis, - * especially when it computes the value of an object field that needs to be visited. - * - * @param metaAccess The {@code AnalysisMetaAccess} instance during the analysis or - * {@code HostedMetaAccess} instance after the analysis. - * @param original The original field (if {@link RecomputeFieldValue} is used for an - * {@link Alias} field). - * @param annotated The field annotated with {@link RecomputeFieldValue}. - * @param receiver The original object for instance fields, or {@code null} for static - * fields. - * @return The new field value. - */ - Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver); - } - - /** - * Custom recomputation of field values. A class implementing this interface must have a - * no-argument constructor, which is used to instantiate it before invoking {@link #transform}. - * - * In contrast to {@link CustomFieldValueComputer}, the {@link #transform} method also has the - * original field value as a parameter. This is convenient if the new value depends on the - * original value, but also requires the original field to be present, e.g., it cannot be use - * for {@link Inject injected fields}. - */ - interface CustomFieldValueTransformer extends CustomFieldValueProvider { - /** - * Computes the new field value. This method can already be invoked during the analysis, - * especially when it computes the value of an object field that needs to be visited. - * - * @param metaAccess The {@code AnalysisMetaAccess} instance during the analysis or - * {@code HostedMetaAccess} instance after the analysis. - * @param original The original field. - * @param annotated The field annotated with {@link RecomputeFieldValue}. - * @param receiver The original object for instance fields, or {@code null} for static - * fields. - * @return The new field value. - */ - Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue); - } - - /** - * Reset an array field to a new empty array of the same type and length. - */ - final class NewEmptyArrayTransformer implements CustomFieldValueTransformer { - @Override - public ValueAvailability valueAvailability() { - return ValueAvailability.BeforeAnalysis; - } - - @Override - public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) { - if (originalValue == null) { - return null; - } else { - int originalLength = Array.getLength(originalValue); - return Array.newInstance(originalValue.getClass().getComponentType(), originalLength); - } - } - } - /** * The kind of the recomputation performed. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Substitute.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Substitute.java index ad28b219339f..709ef1b24e99 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Substitute.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Substitute.java @@ -24,14 +24,14 @@ */ package com.oracle.svm.core.annotate; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - 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; + /** * When used to annotate a method, it indicates that a method declaration is intended to be a * substitute for a method declaration in another class. A substitute method must be declared in a @@ -47,9 +47,9 @@ * There must never be an explicit call to a non-static method annotated with {@link Substitute} * unless it is from another non-static method in the same class. *

- * When used to annotate a class, it indicates that the class is intended to be a substitute for the - * class specified via {@link TargetClass}. All methods in the target class that are not substituted - * in the annotated class are implicitly treated as {@link Delete}d. + * When used to annotate a class, it indicates that the class is intended to be a full substitute + * for the class specified via {@link TargetClass}. All methods in the target class that are not + * substituted in the annotated class are implicitly treated as {@link Delete}d. *

* See {@link TargetClass} for an overview of the annotation system. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/TargetClass.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/TargetClass.java index a4365a8bceda..c55808923338 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/TargetClass.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/TargetClass.java @@ -24,9 +24,6 @@ */ package com.oracle.svm.core.annotate; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -35,13 +32,18 @@ import java.util.function.Function; import java.util.function.Predicate; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + /** * A class annotated with this annotation denotes a class that modifies methods of fields of another - * the class, called the "original" class. The original class is specified using the properties: + * class, called the "original" class. The original class is specified using annotation parameters: * {@link #value} or {@link #className} specify the original class either as a class literal or by - * name. Optionally, inner classes can be specified using the {@link #innerClass} property. + * name, while {@link #classNameProvider} is the most flexible approach where the class name is + * computed by user code. Optionally, inner classes can be specified using the {@link #innerClass} + * property. * - * Distinguished using additional annotations, the original class is modified in different ways: + * Based on additional annotations, the original class is modified in different ways: *

    *
  • None of {@link Delete} or {@link Substitute}: the annotated class is an alias for the * original class. This is the most frequently used case. All methods and fields of the annotated @@ -78,31 +80,32 @@ public @interface TargetClass { /** - * Specifies the substitutee class. + * Specifies the substitutee class using a class literal. * - * If the default value is specified for this element, then a non-default value must be given - * for the {@link #className()} or {@link #classNameProvider()} element. + * Either {@link #value()}, {@link #className()} or {@link #classNameProvider()} element can be + * used to specify the substitutee class. */ Class value() default TargetClass.class; /** - * Specifies the substitutee class. This method is provided for cases where the substitutee - * class is not accessible (according to Java language access control rules). + * Specifies the substitutee class using a class-name string. This method is provided for cases + * where the substitutee class is not accessible (according to Java language access control + * rules). * - * If the default value is specified for this element, then a non-default value must be given - * for the {@link #value()} or {@link #classNameProvider()} element. + * Either {@link #value()}, {@link #className()} or {@link #classNameProvider()} element can be + * used to specify the substitutee class. */ String className() default ""; /** - * Specifies the substitutee class. This is the most flexible version to provide the class name. - * The {@link Function#apply} method of the provided class can compute the class name based on - * system properties (like the JDK version). This annotation is the argument of the function, so - * the function can, e.g., build a class name that incorporates the {@link #className()} - * property. + * Specifies the substitutee class. This is the most flexible version to provide the class name + * to specify which class should be substituted. The {@link Function#apply} method of the + * provided class can compute the class name based on system properties (like the JDK version). + * This annotation is the argument of the function, so the function can, e.g., build a class + * name that incorporates the {@link #className()} property. * - * If the default value is specified for this element, then a non-default value must be given - * for the {@link #value()} or {@link #className()} element. + * Either {@link #value()}, {@link #className()} or {@link #classNameProvider()} element can be + * used to specify the substitutee class. */ Class> classNameProvider() default NoClassNameProvider.class; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/FieldValueTransformerWithAvailability.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/FieldValueTransformerWithAvailability.java new file mode 100644 index 000000000000..ab2fba048d23 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/FieldValueTransformerWithAvailability.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022, 2022, 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.fieldvaluetransformer; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; + +@Platforms(Platform.HOSTED_ONLY.class) +public interface FieldValueTransformerWithAvailability extends FieldValueTransformer { + + /** + * Controls when the transformed value is available at image build time. + */ + enum ValueAvailability { + /** + * The value is available without time constraints, i.e., it is independent of static + * analysis or compilation. + */ + BeforeAnalysis, + + /** + * The value depends on data computed by the static analysis and is therefore not yet + * available to the static analysis. The value still might be constant folded during + * compilation. + */ + AfterAnalysis, + + /** + * Value depends on data computed during compilation and is therefore available only when + * writing out the image heap into the native image. Such a value is never available for + * constant folding. + */ + AfterCompilation + } + + /** + * Returns information about when the value for this custom computation is available. + */ + ValueAvailability valueAvailability(); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/NewEmptyArrayFieldValueTransformer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/NewEmptyArrayFieldValueTransformer.java new file mode 100644 index 000000000000..87425dce77d3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/NewEmptyArrayFieldValueTransformer.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022, 2022, 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.fieldvaluetransformer; + +import java.lang.reflect.Array; + +import org.graalvm.nativeimage.hosted.FieldValueTransformer; + +/** + * Reset an array field to a new empty array of the same type and length. + */ +public final class NewEmptyArrayFieldValueTransformer implements FieldValueTransformer { + @Override + public Object transform(Object receiver, Object originalValue) { + if (originalValue == null) { + return null; + } + int originalLength = Array.getLength(originalValue); + return Array.newInstance(originalValue.getClass().getComponentType(), originalLength); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/NewInstanceFieldValueTransformer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/NewInstanceFieldValueTransformer.java new file mode 100644 index 000000000000..68599505207d --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/fieldvaluetransformer/NewInstanceFieldValueTransformer.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022, 2022, 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.fieldvaluetransformer; + +import org.graalvm.nativeimage.hosted.FieldValueTransformer; + +import com.oracle.svm.util.ReflectionUtil; + +/** + * Creates a new instance by calling the no-args constructor of the original value's class. + */ +public final class NewInstanceFieldValueTransformer implements FieldValueTransformer { + @Override + public Object transform(Object receiver, Object originalValue) { + if (originalValue == null) { + return null; + } + return ReflectionUtil.newInstance(originalValue.getClass()); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Target_java_lang_ref_Reference.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Target_java_lang_ref_Reference.java index 9bce5e7b54c7..a093090338d6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Target_java_lang_ref_Reference.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Target_java_lang_ref_Reference.java @@ -32,6 +32,7 @@ import org.graalvm.compiler.nodes.java.ReachabilityFenceNode; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; @@ -41,7 +42,6 @@ import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.KeepOriginal; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; @@ -52,9 +52,6 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - /** * Substitution of {@link Reference}, which is the abstract base class of all non-strong reference * classes, the basis of the cleaner mechanism, and subject to special treatment by the garbage @@ -192,17 +189,12 @@ final class Target_java_lang_ref_Reference_ReferenceHandler { } @Platforms(Platform.HOSTED_ONLY.class) -class ComputeReferenceValue implements CustomFieldValueComputer { +class ComputeReferenceValue implements FieldValueTransformer { private static final Field REFERENT_FIELD = ReflectionUtil.lookupField(Reference.class, "referent"); @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { if (receiver instanceof PhantomReference) { /* * PhantomReference does not allow access to its object, so it is mostly useless to have @@ -224,17 +216,12 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, } @Platforms(Platform.HOSTED_ONLY.class) -class ComputeQueueValue implements CustomFieldValueComputer { +class ComputeQueueValue implements FieldValueTransformer { private static final Field QUEUE_FIELD = ReflectionUtil.lookupField(Reference.class, "queue"); @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { try { return QUEUE_FIELD.get(receiver); } catch (ReflectiveOperationException ex) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderSupport.java index 2544bd3c4f7e..0bcc855c4cc5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderSupport.java @@ -35,6 +35,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.AutomaticFeature; @@ -48,9 +49,6 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.option.HostedOptionKey; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - public final class FileSystemProviderSupport { public static class Options { @@ -226,7 +224,7 @@ final class Target_sun_nio_fs_UnixFileSystemProvider { final class Target_sun_nio_fs_UnixPath { } -class NeedsReinitializationProvider implements RecomputeFieldValue.CustomFieldValueComputer { +class NeedsReinitializationProvider implements FieldValueTransformer { static final int STATUS_NEEDS_REINITIALIZATION = 2; static final int STATUS_IN_REINITIALIZATION = 1; /* @@ -236,12 +234,7 @@ class NeedsReinitializationProvider implements RecomputeFieldValue.CustomFieldVa static final int STATUS_REINITIALIZED = 0; @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return STATUS_NEEDS_REINITIALIZATION; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java index 4afbe0579da5..358bb5c18a51 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java @@ -36,11 +36,8 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.fieldvaluetransformer.NewInstanceFieldValueTransformer; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.util.ReflectionUtil; - -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; @TargetClass(java.io.FileDescriptor.class) final class Target_java_io_FileDescriptor { @@ -61,9 +58,9 @@ private static boolean hasStaticInitializer(Class cl) { @TargetClass(value = java.io.ObjectStreamClass.class, innerClass = "Caches") final class Target_java_io_ObjectStreamClass_Caches { - @TargetElement(onlyWith = JavaIOClassCachePresent.class, name = "localDescs") @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = ConstructCopy.class) static Target_java_io_ClassCache localDescs0; + @TargetElement(onlyWith = JavaIOClassCachePresent.class, name = "localDescs") @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewInstanceFieldValueTransformer.class) static Target_java_io_ClassCache localDescs0; - @TargetElement(onlyWith = JavaIOClassCachePresent.class, name = "reflectors") @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = ConstructCopy.class) static Target_java_io_ClassCache reflectors0; + @TargetElement(onlyWith = JavaIOClassCachePresent.class, name = "reflectors") @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewInstanceFieldValueTransformer.class) static Target_java_io_ClassCache reflectors0; @TargetElement(onlyWith = JavaIOClassCacheAbsent.class) @Alias @RecomputeFieldValue(kind = Kind.NewInstance, declClass = ConcurrentHashMap.class) static ConcurrentMap localDescs; @@ -78,21 +75,6 @@ final class Target_java_io_ObjectStreamClass_Caches { final class Target_java_io_ClassCache { } -/** - * Creates a new instance by calling the no-args constructor of the original value's class. - */ -final class ConstructCopy implements RecomputeFieldValue.CustomFieldValueTransformer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) { - return ReflectionUtil.newInstance(originalValue.getClass()); - } -} - /** Dummy class to have a class with the file's name. */ public final class JavaIOSubstitutions { } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java index 828d0a366ce7..ef5f7aded70e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java @@ -47,6 +47,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.function.CLibrary; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import org.graalvm.nativeimage.impl.InternalPlatform; import org.graalvm.word.WordBase; import org.graalvm.word.WordFactory; @@ -60,7 +61,6 @@ import com.oracle.svm.core.annotate.KeepOriginal; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; @@ -73,9 +73,6 @@ import com.oracle.svm.core.thread.JavaThreads; import com.oracle.svm.core.util.VMError; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - @TargetClass(java.lang.Object.class) @SuppressWarnings("static-method") final class Target_java_lang_Object { @@ -750,14 +747,9 @@ public ClassValueSupport(Map, Map, Object>> map) { } } - static class ClassValueInitializer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - + static class ClassValueInitializer implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { ClassValueSupport support = ImageSingletons.lookup(ClassValueSupport.class); ClassValue v = (ClassValue) receiver; Map, Object> map = support.values.get(v); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java index f0a4ca3a062d..12ea40aa8b64 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java @@ -48,6 +48,7 @@ import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import org.graalvm.word.Pointer; import com.oracle.svm.core.SubstrateUtil; @@ -64,8 +65,6 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; import sun.security.jca.ProviderList; import sun.security.util.SecurityConstants; @@ -240,15 +239,9 @@ final class Target_java_security_Provider { } @Platforms(Platform.HOSTED_ONLY.class) -class ServiceKeyComputer implements RecomputeFieldValue.CustomFieldValueComputer { - +class ServiceKeyComputer implements FieldValueTransformer { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { try { Class serviceKey = Class.forName("java.security.Provider$ServiceKey"); Constructor constructor = ReflectionUtil.lookupConstructor(serviceKey, String.class, String.class, boolean.class); @@ -388,14 +381,9 @@ static Exception getVerificationResult(Provider p) { "All providers must be registered and verified in the Native Image builder. "); } - private static class VerificationCacheTransformer implements RecomputeFieldValue.CustomFieldValueTransformer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - + private static class VerificationCacheTransformer implements FieldValueTransformer { @Override - public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) { + public Object transform(Object receiver, Object originalValue) { return SecurityProvidersFilter.instance().cleanVerificationCache(originalValue); } } @@ -664,14 +652,9 @@ final class Target_sun_security_jca_Providers { @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ProviderListTransformer.class, disableCaching = true)// private static ProviderList providerList; - private static class ProviderListTransformer implements RecomputeFieldValue.CustomFieldValueTransformer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - + private static class ProviderListTransformer implements FieldValueTransformer { @Override - public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) { + public Object transform(Object receiver, Object originalValue) { ProviderList originalProviderList = (ProviderList) originalValue; return SecurityProvidersFilter.instance().cleanUnregisteredProviders(originalProviderList); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java index f48c93500442..c825c2f27301 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java @@ -36,6 +36,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; + import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Delete; @@ -49,9 +51,6 @@ import com.oracle.svm.core.util.LazyFinalReference; import com.oracle.svm.core.util.VMError; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - @TargetClass(className = "jdk.internal.loader.Resource") @SuppressWarnings("unused") final class Target_jdk_internal_loader_Resource { @@ -422,14 +421,9 @@ final class Target_java_lang_ClassLoader_NativeLibrary { final class Target_java_lang_AssertionStatusDirectives { } -class PackageFieldTransformer implements RecomputeFieldValue.CustomFieldValueTransformer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +class PackageFieldTransformer implements FieldValueTransformer { @Override - public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) { + public Object transform(Object receiver, Object originalValue) { assert receiver instanceof ClassLoader; /* JDK9+ stores packages in a ConcurrentHashMap, while 8 and before use a HashMap. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java index f8cc8624eefe..195dc659bcab 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.core.jdk; -import static com.oracle.svm.core.util.VMError.guarantee; - import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; @@ -48,13 +46,12 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; -import com.oracle.svm.core.meta.SharedField; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; +import com.oracle.svm.core.reflect.target.ReflectionSubstitutionSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; import jdk.internal.misc.Unsafe; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; /** * This file contains most of the code necessary for supporting VarHandle in native images. The @@ -209,24 +206,20 @@ class VarHandleInfo { } } -class VarHandleFieldOffsetComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.AfterAnalysis; - } - +class VarHandleFieldOffsetComputer implements FieldValueTransformerWithAvailability { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object varHandle) { - Field field = ImageSingletons.lookup(VarHandleFeature.class).findVarHandleField(varHandle); - SharedField sField = (SharedField) metaAccess.lookupJavaField(field); - - guarantee(sField.isAccessed() && sField.getLocation() > 0, "Field not marked as accessed"); - return Long.valueOf(sField.getLocation()); + public ValueAvailability valueAvailability() { + return ValueAvailability.AfterAnalysis; } @Override - public Class[] types() { - return new Class[]{long.class}; + public Object transform(Object receiver, Object originalValue) { + Field field = ImageSingletons.lookup(VarHandleFeature.class).findVarHandleField(receiver); + int offset = ImageSingletons.lookup(ReflectionSubstitutionSupport.class).getFieldOffset(field, true); + if (offset <= 0) { + throw VMError.shouldNotReachHere("Field is not marked as unsafe accessed: " + field); + } + return Long.valueOf(offset); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/substitutions/DefaultLocaleComputer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/substitutions/DefaultLocaleComputer.java index 3de12eb38ceb..db412334bacb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/substitutions/DefaultLocaleComputer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/substitutions/DefaultLocaleComputer.java @@ -25,21 +25,13 @@ package com.oracle.svm.core.jdk.localization.substitutions; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; -import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.jdk.localization.LocalizationSupport; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - -final class DefaultLocaleComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +final class DefaultLocaleComputer implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(LocalizationSupport.class).defaultLocale; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/logging/Target_jdk_jfr_internal_LogTag.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/logging/Target_jdk_jfr_internal_LogTag.java index a8965542fca8..35c793a0626d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/logging/Target_jdk_jfr_internal_LogTag.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/logging/Target_jdk_jfr_internal_LogTag.java @@ -25,16 +25,14 @@ */ package com.oracle.svm.core.jfr.logging; -import com.oracle.svm.core.jfr.HasJfrSupport; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.TargetClass; - -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; +import com.oracle.svm.core.jfr.HasJfrSupport; @TargetClass(value = jdk.jfr.internal.LogTag.class, onlyWith = HasJfrSupport.class) final class Target_jdk_jfr_internal_LogTag { @@ -45,14 +43,9 @@ final class Target_jdk_jfr_internal_LogTag { } @Platforms(Platform.HOSTED_ONLY.class) -class ComputeTagSetLevel implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +class ComputeTagSetLevel implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { // Reset the value as it gets set during the image build. return JfrLogConfiguration.JfrLogLevel.OFF.level; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_BoundMethodHandle.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_BoundMethodHandle.java index 382c6c4e8020..abe25c22b7ab 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_BoundMethodHandle.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_BoundMethodHandle.java @@ -37,6 +37,7 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.fieldvaluetransformer.NewEmptyArrayFieldValueTransformer; /** * In the JDK implementation of method handles, each bound method handle is an instance of a @@ -138,7 +139,7 @@ static Target_java_lang_invoke_BoundMethodHandle make(MethodType mt, Target_java @TargetClass(className = "java.lang.invoke.BoundMethodHandle", innerClass = "SpeciesData") final class Target_java_lang_invoke_BoundMethodHandle_SpeciesData { - @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = RecomputeFieldValue.NewEmptyArrayTransformer.class) // + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = NewEmptyArrayFieldValueTransformer.class) // private Target_java_lang_invoke_BoundMethodHandle_SpeciesData[] extensions; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodTypeForm.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodTypeForm.java index 15aaf5283cd3..083b86a8f715 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodTypeForm.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodTypeForm.java @@ -29,8 +29,8 @@ import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; -import com.oracle.svm.core.annotate.RecomputeFieldValue.NewEmptyArrayTransformer; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.fieldvaluetransformer.NewEmptyArrayFieldValueTransformer; @TargetClass(className = "java.lang.invoke.MethodTypeForm") final class Target_java_lang_invoke_MethodTypeForm { @@ -40,8 +40,8 @@ final class Target_java_lang_invoke_MethodTypeForm { * consistent state, to avoid problems when the lazily initialization happens during image heap * writing. */ - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewEmptyArrayTransformer.class) // + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewEmptyArrayFieldValueTransformer.class) // private SoftReference[] methodHandles; - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewEmptyArrayTransformer.class) // + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewEmptyArrayFieldValueTransformer.class) // private SoftReference[] lambdaForms; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java index 2bd339c97194..241e3605e4bd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java @@ -68,4 +68,8 @@ public abstract class SubstrateAccessor { this.directTarget = directTarget; this.initializeBeforeInvoke = initializeBeforeInvoke; } + + public Executable getMember() { + return member; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java index 57842bd83644..38398e1276f0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java @@ -32,22 +32,13 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunctionPointer; -import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; -import com.oracle.svm.core.annotate.RecomputeFieldValue.ValueAvailability; -import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; -import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.InternalVMMethod; -import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointer; import com.oracle.svm.core.util.VMError; import jdk.internal.reflect.MethodAccessor; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; interface MethodAccessorJDK19 { Object invoke(Object obj, Object[] args, Class caller); @@ -64,8 +55,8 @@ public final class SubstrateMethodAccessor extends SubstrateAccessor implements * method, or null for static methods. */ private final Class receiverType; - /** The actual value is computed after static analysis by {@link ComputeVTableOffset}. */ - int vtableOffset; + /** The actual value is computed after static analysis using a field value transformer. */ + private int vtableOffset; @Platforms(Platform.HOSTED_ONLY.class) public SubstrateMethodAccessor(Executable member, Class receiverType, CFunctionPointer expandSignature, CFunctionPointer directTarget, int vtableOffset, DynamicHub initializeBeforeInvoke) { @@ -74,6 +65,10 @@ public SubstrateMethodAccessor(Executable member, Class receiverType, CFuncti this.vtableOffset = vtableOffset; } + public int getVTableOffset() { + return vtableOffset; + } + private void preInvoke(Object obj) { if (initializeBeforeInvoke != null) { EnsureClassInitializedNode.ensureClassInitialized(DynamicHub.toClass(initializeBeforeInvoke)); @@ -126,38 +121,3 @@ public Object invokeSpecial(Object obj, Object[] args) { return ((MethodInvokeFunctionPointer) expandSignature).invoke(obj, args, target); } } - -/** - * The actual vtable offset is not available at the time the accessor is created, but only after - * static analysis when the layout of objects is known. Registering a field recomputation using an - * alias field is currently the only way to register a custom field value computer. - */ -@TargetClass(SubstrateMethodAccessor.class) -final class Target_com_oracle_svm_core_reflect_SubstrateMethodAccessor { - @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ComputeVTableOffset.class) // - private int vtableOffset; -} - -final class ComputeVTableOffset implements CustomFieldValueComputer { - @Override - public ValueAvailability valueAvailability() { - return ValueAvailability.AfterAnalysis; - } - - @Override - public Class[] types() { - return new Class[]{int.class}; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - SubstrateMethodAccessor accessor = (SubstrateMethodAccessor) receiver; - if (accessor.vtableOffset == SubstrateMethodAccessor.OFFSET_NOT_YET_COMPUTED) { - SharedMethod member = (SharedMethod) metaAccess.lookupJavaMethod(accessor.member); - return KnownOffsets.singleton().getVTableOffset(member.getVTableIndex()); - } else { - VMError.guarantee(accessor.vtableOffset == SubstrateMethodAccessor.STATICALLY_BOUND); - return accessor.vtableOffset; - } - } -} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ExecutableAccessorComputer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ExecutableAccessorComputer.java index 24840dfe7c28..c4093cf3ec9e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ExecutableAccessorComputer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ExecutableAccessorComputer.java @@ -27,26 +27,19 @@ import java.lang.reflect.Executable; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - /** * Computes new values for the accessor fields of {@link Executable} subclasses, to be used instead * of the value from the host VM. The new values are the ones that will be in the Native Image heap. * * @see RecomputeFieldValue */ -public final class ExecutableAccessorComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +public final class ExecutableAccessorComputer implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(ReflectionSubstitutionSupport.class).getOrCreateAccessor((Executable) receiver); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/FieldOffsetComputer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/FieldOffsetComputer.java index be3bb8822e01..25a921c6d472 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/FieldOffsetComputer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/FieldOffsetComputer.java @@ -28,26 +28,17 @@ import org.graalvm.nativeimage.ImageSingletons; -import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - -public class FieldOffsetComputer implements CustomFieldValueComputer { - - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.AfterAnalysis; - } +public class FieldOffsetComputer implements FieldValueTransformerWithAvailability { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(ReflectionSubstitutionSupport.class).getFieldOffset((Field) receiver); + public ValueAvailability valueAvailability() { + return ValueAvailability.AfterAnalysis; } @Override - public Class[] types() { - return new Class[]{int.class}; + public Object transform(Object receiver, Object originalValue) { + return ImageSingletons.lookup(ReflectionSubstitutionSupport.class).getFieldOffset((Field) receiver, true); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ReflectionMetadataComputer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ReflectionMetadataComputer.java index 3d31c04a9915..cab8d248ebec 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ReflectionMetadataComputer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ReflectionMetadataComputer.java @@ -24,23 +24,12 @@ */ package com.oracle.svm.core.reflect.target; -import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - -public abstract class ReflectionMetadataComputer implements RecomputeFieldValue.CustomFieldValueComputer { - - @Override - public final RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.AfterCompilation; - } - - @Override - public abstract Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver); +public abstract class ReflectionMetadataComputer implements FieldValueTransformerWithAvailability { @Override - public final Class[] types() { - return new Class[]{byte[].class, null}; + public final ValueAvailability valueAvailability() { + return ValueAvailability.AfterCompilation; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ReflectionSubstitutionSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ReflectionSubstitutionSupport.java index 0069389c83f4..ca9719de5884 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ReflectionSubstitutionSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/ReflectionSubstitutionSupport.java @@ -27,11 +27,18 @@ import java.lang.reflect.Executable; import java.lang.reflect.Field; +import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.reflect.SubstrateAccessor; public interface ReflectionSubstitutionSupport { SubstrateAccessor getOrCreateAccessor(Executable member); /** Offset of the field or -1 if the field was not registered for unsafe access. */ - int getFieldOffset(Field field); + int getFieldOffset(Field field, boolean checkUnsafeAccessed); + + /** + * Returns the {@link Delete#value reason} why a field was deleted, or null if the field is not + * deleted. + */ + String getDeletionReason(Field field); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_AccessibleObject.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_AccessibleObject.java index 1afc085f39ba..27bf48e0e0a1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_AccessibleObject.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_AccessibleObject.java @@ -36,9 +36,6 @@ import com.oracle.svm.core.jdk.JDK11OrEarlier; import com.oracle.svm.core.jdk.JDK17OrLater; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - @TargetClass(value = AccessibleObject.class) public final class Target_java_lang_reflect_AccessibleObject { @Alias // @@ -60,7 +57,7 @@ public final class Target_java_lang_reflect_AccessibleObject { static class TypeAnnotationsComputer extends ReflectionMetadataComputer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(EncodedReflectionMetadataSupplier.class).getTypeAnnotationsEncoding((AccessibleObject) receiver); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java index 6b67a24d3313..020c5f7a71e9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java @@ -40,9 +40,6 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.util.VMError; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - @TargetClass(value = Constructor.class) public final class Target_java_lang_reflect_Constructor { @@ -75,14 +72,14 @@ Target_jdk_internal_reflect_ConstructorAccessor acquireConstructorAccessor() { static class AnnotationsComputer extends ReflectionMetadataComputer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(EncodedReflectionMetadataSupplier.class).getAnnotationsEncoding((AccessibleObject) receiver); } } static class ParameterAnnotationsComputer extends ReflectionMetadataComputer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(EncodedReflectionMetadataSupplier.class).getParameterAnnotationsEncoding((Executable) receiver); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Executable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Executable.java index 684d005bf83e..bf85ffa0d74f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Executable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Executable.java @@ -40,9 +40,6 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - @TargetClass(value = Executable.class) public final class Target_java_lang_reflect_Executable { @@ -70,7 +67,7 @@ byte[] getTypeAnnotationBytes0() { static class RawParametersComputer extends ReflectionMetadataComputer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(EncodedReflectionMetadataSupplier.class).getReflectParametersEncoding((Executable) receiver); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Field.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Field.java index c41aa4e80ce4..cd667ba7ccef 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Field.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Field.java @@ -35,23 +35,18 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; import com.oracle.svm.core.jdk.JDK11OrEarlier; import com.oracle.svm.core.jdk.JDK17OrEarlier; import com.oracle.svm.core.jdk.JDK17OrLater; import com.oracle.svm.core.jdk.JDK19OrLater; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.util.GuardedAnnotationAccess; - -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; @TargetClass(value = Field.class) public final class Target_java_lang_reflect_Field { @@ -158,23 +153,21 @@ private byte[] getTypeAnnotationBytes0() { return SubstrateUtil.cast(this, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations; } - public static final class FieldDeletionReasonComputer implements CustomFieldValueComputer { + public static final class FieldDeletionReasonComputer implements FieldValueTransformerWithAvailability { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; + public ValueAvailability valueAvailability() { + return ValueAvailability.AfterAnalysis; } @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - ResolvedJavaField field = metaAccess.lookupJavaField((Field) receiver); - Delete annotation = GuardedAnnotationAccess.getAnnotation(field, Delete.class); - return (annotation != null) ? annotation.value() : null; + public Object transform(Object receiver, Object originalValue) { + return ImageSingletons.lookup(ReflectionSubstitutionSupport.class).getDeletionReason((Field) receiver); } } static class AnnotationsComputer extends ReflectionMetadataComputer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(EncodedReflectionMetadataSupplier.class).getAnnotationsEncoding((AccessibleObject) receiver); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java index 73d5c526c2e2..d5325b8d979f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java @@ -40,9 +40,6 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.util.VMError; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - @TargetClass(value = Method.class) public final class Target_java_lang_reflect_Method { @@ -78,21 +75,21 @@ public Target_jdk_internal_reflect_MethodAccessor acquireMethodAccessor() { static class AnnotationsComputer extends ReflectionMetadataComputer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(EncodedReflectionMetadataSupplier.class).getAnnotationsEncoding((AccessibleObject) receiver); } } static class ParameterAnnotationsComputer extends ReflectionMetadataComputer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(EncodedReflectionMetadataSupplier.class).getParameterAnnotationsEncoding((Executable) receiver); } } static class AnnotationDefaultComputer extends ReflectionMetadataComputer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return ImageSingletons.lookup(EncodedReflectionMetadataSupplier.class).getAnnotationDefaultEncoding((Method) receiver); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaLangThreadGroupSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaLangThreadGroupSubstitutions.java index 8d5111062f95..871604256ec5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaLangThreadGroupSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaLangThreadGroupSubstitutions.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; @@ -46,9 +47,6 @@ import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.util.ReflectionUtil; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - @TargetClass(ThreadGroup.class) final class Target_java_lang_ThreadGroup { @@ -127,28 +125,18 @@ static long getId(Target_java_lang_ThreadGroup that) { } @Platforms(Platform.HOSTED_ONLY.class) -class ThreadIdRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +class ThreadIdRecomputation implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { Thread thread = (Thread) receiver; return JavaThreadsFeature.threadId(thread); } } @Platforms(Platform.HOSTED_ONLY.class) -class ThreadStatusRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +class ThreadStatusRecomputation implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { Thread thread = (Thread) receiver; if (thread.getState() == Thread.State.TERMINATED) { return ThreadStatus.TERMINATED; @@ -165,14 +153,9 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, } @Platforms(Platform.HOSTED_ONLY.class) -class ThreadHolderRecomputation implements RecomputeFieldValue.CustomFieldValueTransformer { +class ThreadHolderRecomputation implements FieldValueTransformer { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) { + public Object transform(Object receiver, Object originalValue) { assert JavaVersionUtil.JAVA_SPEC >= 19 : "ThreadHolder only exists on JDK 19+"; int threadStatus = ReflectionUtil.readField(ReflectionUtil.lookupClass(false, "java.lang.Thread$FieldHolder"), "threadStatus", receiver); if (threadStatus == ThreadStatus.TERMINATED) { @@ -190,14 +173,9 @@ public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField origina } @Platforms(Platform.HOSTED_ONLY.class) -class ThreadGroupNUnstartedThreadsRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +class ThreadGroupNUnstartedThreadsRecomputation implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { ThreadGroup group = (ThreadGroup) receiver; int result = 0; for (Thread thread : JavaThreadsFeature.singleton().reachableThreads.keySet()) { @@ -211,14 +189,9 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, } @Platforms(Platform.HOSTED_ONLY.class) -class ThreadGroupNThreadsRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { +class ThreadGroupNThreadsRecomputation implements FieldValueTransformer { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { ThreadGroup group = (ThreadGroup) receiver; if (group == PlatformThreads.singleton().mainGroup) { @@ -232,14 +205,9 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, } @Platforms(Platform.HOSTED_ONLY.class) -class ThreadGroupThreadsRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +class ThreadGroupThreadsRecomputation implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { ThreadGroup group = (ThreadGroup) receiver; if (group == PlatformThreads.singleton().mainGroup) { @@ -253,28 +221,18 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, } @Platforms(Platform.HOSTED_ONLY.class) -class ThreadGroupNGroupsRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { +class ThreadGroupNGroupsRecomputation implements FieldValueTransformer { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { ThreadGroup group = (ThreadGroup) receiver; return JavaThreadsFeature.singleton().reachableThreadGroups.get(group).ngroups; } } @Platforms(Platform.HOSTED_ONLY.class) -class ThreadGroupGroupsRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - +class ThreadGroupGroupsRecomputation implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { ThreadGroup group = (ThreadGroup) receiver; return JavaThreadsFeature.singleton().reachableThreadGroups.get(group).groups; } diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java index 621aedb18437..fbacf24404fd 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java @@ -102,6 +102,7 @@ import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.VMRuntime; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.word.LocationIdentity; @@ -153,8 +154,6 @@ import jdk.vm.ci.hotspot.HotSpotSignature; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaType; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.services.Services; @@ -690,14 +689,9 @@ private static void startupLibGraal(HotSpotGraalRuntime runtime) { VMRuntime.initialize(); } - private static final class InjectedManagementComputer implements RecomputeFieldValue.CustomFieldValueComputer { + private static final class InjectedManagementComputer implements FieldValueTransformer { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { try { Class clazz = Thread.currentThread().getContextClassLoader().loadClass("org.graalvm.compiler.hotspot.management.libgraal.LibGraalHotSpotGraalManagement$Factory"); Constructor constructor = clazz.getDeclaredConstructor(); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSubstitutions.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSubstitutions.java index f49efec2678d..7bda19ba2d06 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSubstitutions.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSubstitutions.java @@ -62,6 +62,7 @@ import org.graalvm.compiler.printer.NoDeadCodeVerifyHandler; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.svm.core.SubstrateTargetDescription; import com.oracle.svm.core.annotate.Alias; @@ -81,8 +82,6 @@ import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; @TargetClass(value = org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.class, onlyWith = GraalFeature.IsEnabledAndNotLibgraal.class) @@ -137,14 +136,9 @@ final class Target_org_graalvm_compiler_debug_DebugContext_Invariants { @TargetClass(value = DebugContext.class, innerClass = "Immutable", onlyWith = GraalFeature.IsEnabled.class) final class Target_org_graalvm_compiler_debug_DebugContext_Immutable { - static class ClearImmutableCache implements RecomputeFieldValue.CustomFieldValueComputer { + static class ClearImmutableCache implements FieldValueTransformer { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { for (Class c : DebugContext.class.getDeclaredClasses()) { // Checkstyle: allow Class.getSimpleName if (c.getSimpleName().equals("Immutable")) { @@ -171,14 +165,9 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, @TargetClass(value = DebugHandlersFactory.class, onlyWith = GraalFeature.IsEnabled.class) final class Target_org_graalvm_compiler_debug_DebugHandlersFactory { - static class CachedFactories implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - + static class CachedFactories implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { return GraalSupport.get().debugHandlersFactories; } } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java index d245b453f343..916bbc44f035 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java @@ -46,7 +46,7 @@ import com.oracle.graal.pointsto.api.DefaultUnsafePartition; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.svm.core.BuildPhaseProvider; -import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; import com.oracle.svm.core.graal.GraalEdgeUnsafePartition; import com.oracle.svm.core.util.VMError; import com.oracle.svm.graal.GraalSupport; @@ -58,8 +58,6 @@ import com.oracle.svm.util.UnsafePartitionKind; import jdk.internal.misc.Unsafe; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; /** * Graal uses unsafe memory accesses to access {@link Node}s and {@link LIRInstruction}s. The @@ -70,14 +68,14 @@ */ public class FieldsOffsetsFeature implements Feature { - abstract static class IterationMaskRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { + abstract static class IterationMaskRecomputation implements FieldValueTransformerWithAvailability { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.AfterAnalysis; + public ValueAvailability valueAvailability() { + return ValueAvailability.AfterAnalysis; } @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { Edges edges = getEdges((NodeClass) receiver); FieldsOffsetsReplacement replacement = FieldsOffsetsFeature.getReplacements().get(edges.getOffsets()); assert replacement.fields == edges; @@ -85,11 +83,6 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, return replacement.newIterationInitMask; } - @Override - public Class[] types() { - return new Class[]{long.class}; - } - protected abstract Edges getEdges(NodeClass nodeClass); } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java index 0bf7565b3d92..17cab5d9d6bc 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java @@ -384,7 +384,7 @@ public void beforeAnalysis(BeforeAnalysisAccess c) { FeatureHandler featureHandler = config.getFeatureHandler(); NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, runtimeConfig, hostedProviders, config.getMetaAccess(), config.getUniverse(), null, null, config.getNativeLibraries(), config.getImageClassLoader(), ParsingReason.JITCompilation, ((Inflation) config.getBigBang()).getAnnotationSubstitutionProcessor(), - new SubstrateClassInitializationPlugin(config.getHostVM()), classInitializationSupport, ConfigurationValues.getTarget()); + new SubstrateClassInitializationPlugin(config.getHostVM()), ConfigurationValues.getTarget()); NativeImageGenerator.registerReplacements(debug, featureHandler, runtimeConfig, runtimeConfig.getProviders(), false, true); featureHandler.forEachGraalFeature(feature -> feature.registerCodeObserver(runtimeConfig)); 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 c6c89f322aa9..baf4f13cc5b0 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 @@ -52,8 +52,8 @@ import org.graalvm.compiler.phases.util.Providers; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import org.graalvm.nativeimage.hosted.RuntimeReflection; -import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.DefaultUnsafePartition; @@ -88,6 +88,7 @@ import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.option.HostedOptionProvider; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.UnsafePartitionKind; @@ -448,6 +449,11 @@ public void registerClassInitializerReachabilityHandler(Consumer entryPoints, hMetaAccess, hUniverse, nativeLibraries, loader, ParsingReason.AOTCompilation, bb.getAnnotationSubstitutionProcessor(), new SubstrateClassInitializationPlugin((SVMHost) aUniverse.hostVM()), - classInitializationSupport, ConfigurationValues.getTarget()); + ConfigurationValues.getTarget()); if (NativeImageOptions.PrintUniverse.getValue()) { printTypes(); @@ -1049,7 +1049,7 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature bb.getMetaAccess().lookupJavaType(com.oracle.svm.core.allocationprofile.AllocationCounter.class).registerAsReachable(); NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, null, aProviders, aMetaAccess, aUniverse, null, null, nativeLibraries, loader, ParsingReason.PointsToAnalysis, - bb.getAnnotationSubstitutionProcessor(), classInitializationPlugin, bb.getHostVM().getClassInitializationSupport(), ConfigurationValues.getTarget()); + bb.getAnnotationSubstitutionProcessor(), classInitializationPlugin, ConfigurationValues.getTarget()); registerReplacements(debug, featureHandler, null, aProviders, true, initForeignCalls); Collection snippetGraphs = aReplacements.getSnippetGraphs(GraalOptions.TrackNodeSourcePosition.getValue(options), options); @@ -1178,7 +1178,7 @@ protected void register(Type declaringClass, InvocationPlugin plugin, boolean al public static void registerGraphBuilderPlugins(FeatureHandler featureHandler, RuntimeConfiguration runtimeConfig, HostedProviders providers, AnalysisMetaAccess aMetaAccess, AnalysisUniverse aUniverse, HostedMetaAccess hMetaAccess, HostedUniverse hUniverse, NativeLibraries nativeLibs, ImageClassLoader loader, ParsingReason reason, - AnnotationSubstitutionProcessor annotationSubstitutionProcessor, ClassInitializationPlugin classInitializationPlugin, ClassInitializationSupport classInitializationSupport, + AnnotationSubstitutionProcessor annotationSubstitutionProcessor, ClassInitializationPlugin classInitializationPlugin, TargetDescription target) { GraphBuilderConfiguration.Plugins plugins = new GraphBuilderConfiguration.Plugins(new SubstitutionInvocationPlugins(annotationSubstitutionProcessor)); @@ -1197,7 +1197,7 @@ public static void registerGraphBuilderPlugins(FeatureHandler featureHandler, Ru ((AnalysisField) field).registerAsAccessed(); } plugins.appendNodePlugin(new EarlyConstantFoldLoadFieldPlugin(providers.getMetaAccess(), providers.getSnippetReflection())); - plugins.appendNodePlugin(new ConstantFoldLoadFieldPlugin(classInitializationSupport)); + plugins.appendNodePlugin(new ConstantFoldLoadFieldPlugin()); plugins.appendNodePlugin(new CInterfaceInvocationPlugin(providers.getMetaAccess(), providers.getWordTypes(), nativeLibs)); plugins.appendNodePlugin(new LocalizationFeature.CharsetNodePlugin()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/CustomTypeFieldHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/CustomTypeFieldHandler.java index 153921b01cc1..0963f5953979 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/CustomTypeFieldHandler.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/CustomTypeFieldHandler.java @@ -66,10 +66,8 @@ public void handleField(AnalysisField field) { assert field.isAccessed(); if (field.wrapped instanceof ComputedValueField) { ComputedValueField computedField = ((ComputedValueField) field.wrapped); - Class[] customTypes = computedField.getCustomTypes(); - if (customTypes != null) { - injectFieldTypes(field, transformTypes(field, customTypes)); - field.setCanBeNull(computedField.getComputedValueCanBeNull()); + if (!computedField.isValueAvailableBeforeAnalysis()) { + injectFieldTypes(field, field.getType()); } } else { UnknownObjectField unknownObjectField = field.getAnnotation(UnknownObjectField.class); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/ConstantFoldLoadFieldPlugin.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/ConstantFoldLoadFieldPlugin.java index c12529696d81..e4fe8a2e2732 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/ConstantFoldLoadFieldPlugin.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/ConstantFoldLoadFieldPlugin.java @@ -30,19 +30,11 @@ import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin; import org.graalvm.compiler.nodes.util.ConstantFoldUtil; -import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; - import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaField; public final class ConstantFoldLoadFieldPlugin implements NodePlugin { - private ClassInitializationSupport classInitializationSupport; - - public ConstantFoldLoadFieldPlugin(ClassInitializationSupport classInitializationSupport) { - this.classInitializationSupport = classInitializationSupport; - } - @Override public boolean handleLoadField(GraphBuilderContext b, ValueNode receiver, ResolvedJavaField field) { if (receiver.isConstant()) { @@ -57,16 +49,11 @@ public boolean handleLoadStaticField(GraphBuilderContext b, ResolvedJavaField st return tryConstantFold(b, staticField, null); } - private boolean tryConstantFold(GraphBuilderContext b, ResolvedJavaField field, JavaConstant receiver) { + private static boolean tryConstantFold(GraphBuilderContext b, ResolvedJavaField field, JavaConstant receiver) { ConstantNode result = ConstantFoldUtil.tryConstantFold(b.getConstantFieldProvider(), b.getConstantReflection(), b.getMetaAccess(), field, receiver, b.getOptions()); if (result != null) { assert result.asJavaConstant() != null; - JavaConstant value = result.asJavaConstant(); - assert !classInitializationSupport.shouldInitializeAtRuntime(field.getDeclaringClass()) || - value.isDefaultForKind() : "Fields in classes that are marked for initialization at run time must not be constant folded, unless they are not written in the static initializer, i.e., have the default value: " + - field.format("%H.%n"); - result = b.getGraph().unique(result); b.push(field.getJavaKind(), result); return true; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index f80b825660bb..6ff06777dc8c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -48,13 +48,17 @@ import com.oracle.svm.core.ParsingReason; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.configure.ConditionalElement; import com.oracle.svm.core.configure.ConfigurationFile; import com.oracle.svm.core.configure.ConfigurationFiles; import com.oracle.svm.core.configure.ReflectionConfigurationParser; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; import com.oracle.svm.core.graal.InternalFeature; +import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.reflect.ReflectionAccessorHolder; import com.oracle.svm.core.reflect.SubstrateAccessor; import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; @@ -75,10 +79,12 @@ import com.oracle.svm.hosted.snippets.ReflectionPlugins; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.util.AnnotationExtracter; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; @AutomaticFeature @@ -90,7 +96,7 @@ public class ReflectionFeature implements InternalFeature, ReflectionSubstitutio private ImageClassLoader loader; private AnalysisUniverse aUniverse; private int loadedConfigurations; - private HostedMetaAccess hMetaAccess; + HostedMetaAccess hMetaAccess; final Map accessors = new ConcurrentHashMap<>(); private final Map expandSignatureMethods = new ConcurrentHashMap<>(); @@ -238,6 +244,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { reflectionData.flushConditionalConfiguration(access); /* Make sure array classes don't need to be registered for reflection. */ RuntimeReflection.register(Object[].class.getMethods()); + + access.registerFieldValueTransformer(ReflectionUtil.lookupField(SubstrateMethodAccessor.class, "vtableOffset"), new ComputeVTableOffset()); } @Override @@ -254,18 +262,18 @@ public void afterAnalysis(AfterAnalysisAccess access) { @Override public void beforeCompilation(BeforeCompilationAccess access) { - if (!ImageSingletons.contains(FallbackFeature.class)) { - return; - } - FallbackFeature.FallbackImageRequest reflectionFallback = ImageSingletons.lookup(FallbackFeature.class).reflectionFallback; - if (reflectionFallback != null && loadedConfigurations == 0) { - throw reflectionFallback; - } hMetaAccess = ((BeforeCompilationAccessImpl) access).getMetaAccess(); + + if (ImageSingletons.contains(FallbackFeature.class)) { + FallbackFeature.FallbackImageRequest reflectionFallback = ImageSingletons.lookup(FallbackFeature.class).reflectionFallback; + if (reflectionFallback != null && loadedConfigurations == 0) { + throw reflectionFallback; + } + } } @Override - public int getFieldOffset(Field field) { + public int getFieldOffset(Field field, boolean checkUnsafeAccessed) { VMError.guarantee(hMetaAccess != null, "Field offsets are available only for compilation and afterwards."); /* @@ -273,12 +281,17 @@ public int getFieldOffset(Field field) { * reflective access in an image. */ HostedField hostedField = hMetaAccess.optionalLookupJavaField(field); - if (hostedField == null || !hostedField.wrapped.isUnsafeAccessed()) { + if (hostedField == null || (checkUnsafeAccessed && !hostedField.wrapped.isUnsafeAccessed())) { return -1; } - int location = hostedField.getLocation(); - VMError.guarantee(location > 0, "Incorrect field location: " + location + " for " + hostedField.format("%H.%n")); - return location; + return hostedField.getLocation(); + } + + @Override + public String getDeletionReason(Field reflectionField) { + ResolvedJavaField field = hMetaAccess.lookupJavaField(reflectionField); + Delete annotation = GuardedAnnotationAccess.getAnnotation(field, Delete.class); + return annotation != null ? annotation.value() : null; } @Override @@ -337,3 +350,22 @@ String uniqueShortName() { return SubstrateUtil.digest(toString()); } } + +final class ComputeVTableOffset implements FieldValueTransformerWithAvailability { + @Override + public ValueAvailability valueAvailability() { + return ValueAvailability.AfterAnalysis; + } + + @Override + public Object transform(Object receiver, Object originalValue) { + SubstrateMethodAccessor accessor = (SubstrateMethodAccessor) receiver; + if (accessor.getVTableOffset() == SubstrateMethodAccessor.OFFSET_NOT_YET_COMPUTED) { + SharedMethod member = ImageSingletons.lookup(ReflectionFeature.class).hMetaAccess.lookupJavaMethod(accessor.getMember()); + return KnownOffsets.singleton().getVTableOffset(member.getVTableIndex()); + } else { + VMError.guarantee(accessor.getVTableOffset() == SubstrateMethodAccessor.STATICALLY_BOUND); + return accessor.getVTableOffset(); + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index 8a3aae68a486..fb28a3c04240 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -47,7 +47,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import com.oracle.svm.util.GuardedAnnotationAccess; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; @@ -75,6 +75,7 @@ import com.oracle.svm.hosted.annotation.AnnotationSubstitutionType; import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; @@ -115,7 +116,21 @@ public AnnotationSubstitutionProcessor(ImageClassLoader imageClassLoader, MetaAc typeSubstitutions = new ConcurrentHashMap<>(); methodSubstitutions = new ConcurrentHashMap<>(); polymorphicMethodSubstitutions = new HashMap<>(); - fieldSubstitutions = new HashMap<>(); + fieldSubstitutions = new ConcurrentHashMap<>(); + } + + public void registerFieldValueTransformer(Field reflectionField, FieldValueTransformer transformer) { + ResolvedJavaField field = metaAccess.lookupJavaField(reflectionField); + boolean isFinal = field.isFinal(); + ComputedValueField computedValueField = new ComputedValueField(field, field, Kind.Custom, reflectionField.getType(), transformer, null, null, isFinal, false); + ResolvedJavaField existingSubstitution = fieldSubstitutions.put(field, computedValueField); + + if (existingSubstitution != null) { + String reason = existingSubstitution.equals(field) + ? "The field was already accessed by the static analysis. The transformer must be registered earlier, before the static analysis sees a reference to the field for the first time." + : "A field value transformer is already registered for this field."; + throw UserError.abort("Cannot register a field value transformer for field %s: %s", field, reason); + } } @Override @@ -184,11 +199,14 @@ public ResolvedJavaField lookup(ResolvedJavaField field) { if (deleteAnnotation != null) { throw new DeletedElementException(deleteErrorMessage(field, deleteAnnotation, true)); } - ResolvedJavaField substitution = fieldSubstitutions.get(field); - if (substitution != null) { - return substitution; - } - return field; + + /* + * If there is no substitution registered yet, put in the field itself as a marker that the + * field was used in a lookup. Registering a substitution after a lookup was done is not + * allowed because that means the substitution was missed in the prior lookup. + */ + ResolvedJavaField existing = fieldSubstitutions.putIfAbsent(field, field); + return existing != null ? existing : field; } public boolean isDeleted(Field field) { @@ -930,7 +948,9 @@ private ResolvedJavaField fieldValueRecomputation(Class originalClass, Resolv targetClass = imageClassLoader.findClassOrFail(recomputeAnnotation.declClassName()); } } - return new ComputedValueField(original, annotated, kind, targetClass, targetName, isFinal, disableCaching); + Class transformedValueAllowedType = getTargetClass(annotatedField.getType()); + + return new ComputedValueField(original, annotated, kind, transformedValueAllowedType, null, targetClass, targetName, isFinal, disableCaching); } private void reinitializeField(Field annotatedField) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java index 11832cb737a5..e13b27f4023f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java @@ -31,14 +31,9 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; import java.util.EnumSet; -import java.util.List; import java.util.Objects; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; @@ -46,6 +41,7 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -53,11 +49,9 @@ import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueProvider; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueTransformer; -import com.oracle.svm.core.annotate.RecomputeFieldValue.ValueAvailability; import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability.ValueAvailability; import com.oracle.svm.core.meta.ReadableJavaField; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; @@ -91,7 +85,8 @@ public class ComputedValueField implements ReadableJavaField, OriginalFieldProvi private final RecomputeFieldValue.Kind kind; private final Class targetClass; private final Field targetField; - private final CustomFieldValueProvider customValueProvider; + private final Class transformedValueAllowedType; + private final FieldValueTransformer fieldValueTransformer; private final boolean isFinal; private final boolean disableCaching; /** True if the value doesn't depend on any analysis results. */ @@ -100,10 +95,6 @@ public class ComputedValueField implements ReadableJavaField, OriginalFieldProvi private final boolean isValueAvailableOnlyAfterAnalysis; /** True if the value depends on compilation results. */ private final boolean isValueAvailableOnlyAfterCompilation; - /** Types that this field can take, if its value is not available during analysis. */ - private final Class[] customTypes; - /** True if the computer can return `null`. */ - private final boolean computedValueCanBeNull; private JavaConstant constantValue; @@ -116,17 +107,18 @@ public class ComputedValueField implements ReadableJavaField, OriginalFieldProvi private final ReentrantReadWriteLock valueCacheLock = new ReentrantReadWriteLock(); public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotated, RecomputeFieldValue.Kind kind, Class targetClass, String targetName, boolean isFinal) { - this(original, annotated, kind, targetClass, targetName, isFinal, false); + this(original, annotated, kind, null, null, targetClass, targetName, isFinal, false); } - public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotated, RecomputeFieldValue.Kind kind, Class targetClass, String targetName, boolean isFinal, - boolean disableCaching) { + public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotated, RecomputeFieldValue.Kind kind, Class transformedValueAllowedType, FieldValueTransformer initialTransformer, + Class targetClass, String targetName, boolean isFinal, boolean disableCaching) { assert original != null; - assert targetClass != null; + assert initialTransformer != null || targetClass != null; this.original = original; this.annotated = annotated; this.kind = kind; + this.transformedValueAllowedType = transformedValueAllowedType; this.targetClass = targetClass; this.isFinal = isFinal; this.disableCaching = disableCaching; @@ -134,9 +126,7 @@ public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotate boolean customValueAvailableBeforeAnalysis = true; boolean customValueAvailableOnlyAfterAnalysis = false; boolean customValueAvailableOnlyAfterCompilation = false; - boolean canBeNull = false; - Class[] customProviderTypes = null; - CustomFieldValueProvider customProvider = null; + FieldValueTransformer transformer = null; Field f = null; switch (kind) { case Reset: @@ -150,37 +140,17 @@ public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotate } break; case Custom: - try { - Constructor[] constructors = targetClass.getDeclaredConstructors(); - if (constructors.length != 1) { - throw UserError.abort("The custom field value computer class %s has more than one constructor", targetClass.getName()); - } - Constructor constructor = constructors[0]; - - Object[] constructorArgs = new Object[constructor.getParameterCount()]; - for (int i = 0; i < constructorArgs.length; i++) { - constructorArgs[i] = configurationValue(constructor.getParameterTypes()[i]); - } - constructor.setAccessible(true); - customProvider = (CustomFieldValueProvider) constructor.newInstance(constructorArgs); - ValueAvailability valueAvailability = customProvider.valueAvailability(); + if (initialTransformer != null) { + transformer = initialTransformer; + } else { + transformer = (FieldValueTransformer) ReflectionUtil.newInstance(targetClass); + } + + if (transformer instanceof FieldValueTransformerWithAvailability) { + ValueAvailability valueAvailability = ((FieldValueTransformerWithAvailability) transformer).valueAvailability(); customValueAvailableBeforeAnalysis = valueAvailability == ValueAvailability.BeforeAnalysis; customValueAvailableOnlyAfterAnalysis = valueAvailability == ValueAvailability.AfterAnalysis; customValueAvailableOnlyAfterCompilation = valueAvailability == ValueAvailability.AfterCompilation; - Class[] types = customProvider.types(); - if (types != null) { - List> nonNullTypes = new ArrayList<>(); - for (Class clazz : types) { - if (clazz == null) { - canBeNull = true; - } else { - nonNullTypes.add(clazz); - } - } - customProviderTypes = nonNullTypes.toArray(new Class[0]); - } - } catch (InvocationTargetException | InstantiationException | IllegalAccessException ex) { - throw shouldNotReachHere("Error creating custom field value computer for alias " + annotated.format("%H.%n"), ex); } } boolean isOffsetField = isOffsetRecomputation(kind); @@ -188,10 +158,8 @@ public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotate this.isValueAvailableBeforeAnalysis = customValueAvailableBeforeAnalysis && !isOffsetField; this.isValueAvailableOnlyAfterAnalysis = customValueAvailableOnlyAfterAnalysis || isOffsetField; this.isValueAvailableOnlyAfterCompilation = customValueAvailableOnlyAfterCompilation; - this.customTypes = customProviderTypes; - this.computedValueCanBeNull = canBeNull; this.targetField = f; - this.customValueProvider = customProvider; + this.fieldValueTransformer = transformer; this.valueCache = EconomicMap.create(); } @@ -215,14 +183,6 @@ public ResolvedJavaField getAnnotated() { return annotated; } - public Class[] getCustomTypes() { - return customTypes; - } - - public boolean getComputedValueCanBeNull() { - return computedValueCanBeNull; - } - @Override public Field getTargetField() { return targetField; @@ -379,16 +339,8 @@ private JavaConstant computeValue(MetaAccessProvider metaAccess, JavaConstant re break; case Custom: Object receiverValue = receiver == null ? null : originalSnippetReflection.asObject(Object.class, receiver); - Object newValue; - if (customValueProvider instanceof CustomFieldValueComputer) { - newValue = ((CustomFieldValueComputer) customValueProvider).compute(metaAccess, original, annotated, receiverValue); - } else if (customValueProvider instanceof CustomFieldValueTransformer) { - originalValue = fetchOriginalValue(metaAccess, receiver, originalSnippetReflection); - newValue = ((CustomFieldValueTransformer) customValueProvider).transform(metaAccess, original, annotated, receiverValue, originalValue); - } else { - throw UserError.abort("The custom field value computer class %s does not implement %s or %s", targetClass.getName(), - CustomFieldValueComputer.class.getSimpleName(), CustomFieldValueTransformer.class.getSimpleName()); - } + originalValue = fetchOriginalValue(metaAccess, receiver, originalSnippetReflection); + Object newValue = fieldValueTransformer.transform(receiverValue, originalValue); checkValue(newValue); result = originalSnippetReflection.forBoxed(annotated.getJavaKind(), newValue); @@ -401,27 +353,24 @@ private JavaConstant computeValue(MetaAccessProvider metaAccess, JavaConstant re } private void checkValue(Object newValue) { - if (customTypes != null) { - /* Only check the value if custom types are specified. */ - if (newValue == null) { - if (!computedValueCanBeNull) { - throw shouldNotReachHere("Computed value for " + fieldFormat() + " should not be null."); - } + boolean primitive = transformedValueAllowedType.isPrimitive(); + if (newValue == null) { + if (primitive) { + throw UserError.abort("Field value transformer returned null for primitive %s", fieldFormat()); } else { - /* - * The compute/transform methods autobox primitive values. We unbox them here, but - * only if the original field is primitive. - */ - boolean primitive = ((ResolvedJavaType) original.getType()).isPrimitive(); - Class actualType = primitive ? toUnboxedClass(newValue.getClass()) : newValue.getClass(); - for (Class customType : customTypes) { - if (customType.isAssignableFrom(actualType)) { - return; - } - } - VMError.shouldNotReachHere("Unexpected class " + actualType + " for " + fieldFormat() + ". Expected types :" + Arrays.toString(customTypes) + "."); + /* Null is always allowed for reference fields. */ + return; } } + /* + * The compute/transform methods autobox primitive values. We unbox them here, but only if + * the original field is primitive. + */ + Class actualType = primitive ? toUnboxedClass(newValue.getClass()) : newValue.getClass(); + if (!transformedValueAllowedType.isAssignableFrom(actualType)) { + throw UserError.abort("Field value transformer returned value of type `%s` that is not assignable to declared type `%s` of %s", + actualType.getTypeName(), transformedValueAllowedType.getTypeName(), fieldFormat()); + } } private static Class toUnboxedClass(Class clazz) { @@ -462,13 +411,18 @@ private JavaConstant createNewInstance(SnippetReflectionProvider originalSnippet private Object fetchOriginalValue(MetaAccessProvider metaAccess, JavaConstant receiver, SnippetReflectionProvider originalSnippetReflection) { JavaConstant originalValueConstant = ReadableJavaField.readFieldValue(metaAccess, GraalAccess.getOriginalProviders().getConstantReflection(), original, receiver); - Object originalValue; - if (originalValueConstant.getJavaKind().isPrimitive()) { - originalValue = originalValueConstant.asBoxedPrimitive(); + if (originalValueConstant == null) { + /* + * The class is still uninitialized, so static fields cannot be read. Or it is an + * instance field in a substitution class, i.e., a field that does not exist in the + * hosted object. + */ + return null; + } else if (originalValueConstant.getJavaKind().isPrimitive()) { + return originalValueConstant.asBoxedPrimitive(); } else { - originalValue = originalSnippetReflection.asObject(Object.class, originalValueConstant); + return originalSnippetReflection.asObject(Object.class, originalValueConstant); } - return originalValue; } private void putCached(JavaConstant receiver, JavaConstant result) { @@ -492,7 +446,7 @@ private JavaConstant getCached(JavaConstant receiver) { @Override public boolean allowConstantFolding() { - return getDeclaringClass().isInitialized() && isFinal; + return isFinal; } @Override @@ -508,10 +462,6 @@ public boolean injectFinalForRuntimeCompilation() { return ReadableJavaField.injectFinalForRuntimeCompilation(original); } - private static Object configurationValue(Class clazz) { - throw shouldNotReachHere("Parameter type not supported yet: " + clazz.getName()); - } - private JavaConstant translateFieldOffset(MetaAccessProvider metaAccess, JavaConstant receiver, Class tclass) { long searchOffset = ReadableJavaField.readFieldValue(metaAccess, GraalAccess.getOriginalProviders().getConstantReflection(), original, receiver).asLong(); // search the declared fields for a field with a matching offset diff --git a/substratevm/src/com.oracle.svm.junit/src/com/oracle/svm/junit/Target_org_junit_runners_model_TestClass.java b/substratevm/src/com.oracle.svm.junit/src/com/oracle/svm/junit/Target_org_junit_runners_model_TestClass.java index 74f67f797f3c..ba8f9e36b8de 100644 --- a/substratevm/src/com.oracle.svm.junit/src/com/oracle/svm/junit/Target_org_junit_runners_model_TestClass.java +++ b/substratevm/src/com.oracle.svm.junit/src/com/oracle/svm/junit/Target_org_junit_runners_model_TestClass.java @@ -26,6 +26,7 @@ import java.lang.reflect.Constructor; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.junit.Assert; import org.junit.runners.model.TestClass; @@ -33,25 +34,16 @@ import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; - @TargetClass(className = "org.junit.runners.model.TestClass", onlyWith = JUnitFeature.IsEnabled.class) public final class Target_org_junit_runners_model_TestClass { - public static final class OnlyConstructorComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - + public static final class OnlyConstructorComputer implements FieldValueTransformer { @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { TestClass testClass = (TestClass) receiver; if (testClass.getJavaClass() != null) { /* Make sure Class.forName works because Description.getTestClass can use it. */ diff --git a/substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/Target_com_oracle_truffle_nfi_backend_libffi_LibFFIContext.java b/substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/Target_com_oracle_truffle_nfi_backend_libffi_LibFFIContext.java index 279ccac442ba..cc5e0013e9d3 100644 --- a/substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/Target_com_oracle_truffle_nfi_backend_libffi_LibFFIContext.java +++ b/substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/Target_com_oracle_truffle_nfi_backend_libffi_LibFFIContext.java @@ -38,9 +38,9 @@ import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; -import com.oracle.svm.core.annotate.RecomputeFieldValue.NewEmptyArrayTransformer; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.fieldvaluetransformer.NewEmptyArrayFieldValueTransformer; import com.oracle.svm.truffle.nfi.NativeAPI.NativeTruffleContext; import com.oracle.svm.truffle.nfi.NativeAPI.NativeTruffleEnv; import com.oracle.svm.truffle.nfi.NativeSignature.CifData; @@ -48,16 +48,16 @@ import com.oracle.svm.truffle.nfi.libffi.LibFFI; import com.oracle.svm.truffle.nfi.libffi.LibFFI.ffi_cif; import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.interop.TruffleObject; @TargetClass(className = "com.oracle.truffle.nfi.backend.libffi.LibFFIContext", onlyWith = TruffleNFIFeature.IsEnabled.class) final class Target_com_oracle_truffle_nfi_backend_libffi_LibFFIContext { // clear these fields, they will be re-filled by patchContext @Alias @RecomputeFieldValue(kind = Kind.Reset) private long nativeContext; - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewEmptyArrayTransformer.class) Target_com_oracle_truffle_nfi_backend_libffi_LibFFIType[] simpleTypeMap; - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewEmptyArrayTransformer.class) Target_com_oracle_truffle_nfi_backend_libffi_LibFFIType[] arrayTypeMap; + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewEmptyArrayFieldValueTransformer.class) Target_com_oracle_truffle_nfi_backend_libffi_LibFFIType[] simpleTypeMap; + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewEmptyArrayFieldValueTransformer.class) Target_com_oracle_truffle_nfi_backend_libffi_LibFFIType[] arrayTypeMap; @Alias @RecomputeFieldValue(kind = Kind.Reset) Target_com_oracle_truffle_nfi_backend_libffi_LibFFIType cachedEnvType; @Alias diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index d42f17087426..4e52e8e93150 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -47,8 +47,6 @@ import java.util.function.BiConsumer; import java.util.function.BooleanSupplier; -import com.oracle.svm.core.heap.Pod; -import com.oracle.svm.hosted.heap.PodSupport; import org.graalvm.collections.Pair; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.nodes.ConstantNode; @@ -62,10 +60,10 @@ import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.ConfigurationCondition; -import com.oracle.svm.util.DirectAnnotationAccess; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; @@ -82,6 +80,9 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.configure.ResourcesRegistry; +import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; +import com.oracle.svm.core.heap.Pod; +import com.oracle.svm.core.reflect.target.ReflectionSubstitutionSupport; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.graal.hosted.GraalObjectReplacer; @@ -92,9 +93,10 @@ import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; -import com.oracle.svm.hosted.meta.HostedMetaAccess; +import com.oracle.svm.hosted.heap.PodSupport; import com.oracle.svm.hosted.snippets.SubstrateGraphBuilderPlugins; import com.oracle.svm.truffle.api.SubstrateTruffleRuntime; +import com.oracle.svm.util.DirectAnnotationAccess; import com.oracle.svm.util.ReflectionUtil; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -121,8 +123,6 @@ import jdk.internal.misc.Unsafe; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; /** @@ -346,6 +346,13 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { if (needsAllEncodings) { ImageSingletons.lookup(ResourcesRegistry.class).addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$"); } + + access.registerFieldValueTransformer(ReflectionUtil.lookupField(ArrayBasedShapeGeneratorOffsetTransformer.SHAPE_GENERATOR, "byteArrayOffset"), + new ArrayBasedShapeGeneratorOffsetTransformer("primitive")); + access.registerFieldValueTransformer(ReflectionUtil.lookupField(ArrayBasedShapeGeneratorOffsetTransformer.SHAPE_GENERATOR, "objectArrayOffset"), + new ArrayBasedShapeGeneratorOffsetTransformer("object")); + access.registerFieldValueTransformer(ReflectionUtil.lookupField(ArrayBasedShapeGeneratorOffsetTransformer.SHAPE_GENERATOR, "shapeOffset"), + new ArrayBasedShapeGeneratorOffsetTransformer("shape")); } public static void preInitializeEngine() { @@ -816,7 +823,7 @@ final class Target_com_oracle_truffle_api_staticobject_StaticProperty { @Alias native void initOffset(int o); - public static final class OffsetTransformer implements RecomputeFieldValue.CustomFieldValueTransformer { + public static final class OffsetTransformer implements FieldValueTransformer { /* * We have to use reflection to access private members instead of aliasing them in the * substitution class since substitutions are present only at runtime @@ -828,13 +835,7 @@ public static final class OffsetTransformer implements RecomputeFieldValue.Custo } @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, - Object receiver, Object originalValue) { + public Object transform(Object receiver, Object originalValue) { int offset = (int) originalValue; if (offset == 0) { /* @@ -887,61 +888,30 @@ public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField origina } } -@TargetClass(className = "com.oracle.truffle.api.staticobject.ArrayBasedShapeGenerator", onlyWith = TruffleBaseFeature.IsEnabled.class) -final class Target_com_oracle_truffle_api_staticobject_ArrayBasedShapeGenerator { +final class ArrayBasedShapeGeneratorOffsetTransformer implements FieldValueTransformerWithAvailability { + static final Class SHAPE_GENERATOR = ReflectionUtil.lookupClass(false, "com.oracle.truffle.api.staticobject.ArrayBasedShapeGenerator"); - public static final class OffsetTransformer implements RecomputeFieldValue.CustomFieldValueTransformer { - private static final Class SHAPE_GENERATOR; - - static { - try { - SHAPE_GENERATOR = Class.forName("com.oracle.truffle.api.staticobject.ArrayBasedShapeGenerator"); - } catch (ClassNotFoundException e) { - throw VMError.shouldNotReachHere(e); - } - } + private final String storageClassFieldName; - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.AfterAnalysis; - } + ArrayBasedShapeGeneratorOffsetTransformer(String storageClassFieldName) { + this.storageClassFieldName = storageClassFieldName; + } - @Override - public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, - Object receiver, Object originalValue) { - Class generatedStorageClass = ReflectionUtil.readField(SHAPE_GENERATOR, "generatedStorageClass", - receiver); - String name; - switch (original.getName()) { - case "byteArrayOffset": - name = "primitive"; - break; - case "objectArrayOffset": - name = "object"; - break; - case "shapeOffset": - name = "shape"; - break; - default: - throw VMError.shouldNotReachHere(); - } - Field f = ReflectionUtil.lookupField(generatedStorageClass, name); - assert metaAccess instanceof HostedMetaAccess; - return ((HostedMetaAccess) metaAccess).lookupJavaField(f).getLocation(); - } + @Override + public ValueAvailability valueAvailability() { + return ValueAvailability.AfterAnalysis; + } - @Override - public Class[] types() { - return new Class[]{int.class}; + @Override + public Object transform(Object receiver, Object originalValue) { + Class generatedStorageClass = ReflectionUtil.readField(SHAPE_GENERATOR, "generatedStorageClass", receiver); + Field field = ReflectionUtil.lookupField(generatedStorageClass, storageClassFieldName); + int offset = ImageSingletons.lookup(ReflectionSubstitutionSupport.class).getFieldOffset(field, false); + if (offset <= 0) { + throw VMError.shouldNotReachHere("Field is not marked as accessed: " + field); } + return Integer.valueOf(offset); } - - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetTransformer.class) // - int byteArrayOffset; - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetTransformer.class) // - int objectArrayOffset; - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetTransformer.class) // - int shapeOffset; } /* @@ -1031,23 +1001,22 @@ final class Target_com_oracle_truffle_api_nodes_NodeClassImpl_NodeFieldData { @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class) // private long offset; - private static class OffsetComputer implements RecomputeFieldValue.CustomFieldValueComputer { + private static class OffsetComputer implements FieldValueTransformerWithAvailability { @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.AfterAnalysis; + public ValueAvailability valueAvailability() { + return ValueAvailability.AfterAnalysis; } @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + public Object transform(Object receiver, Object originalValue) { Class declaringClass = ReflectionUtil.readField(receiver.getClass(), "declaringClass", receiver); String name = ReflectionUtil.readField(receiver.getClass(), "name", receiver); Field field = ReflectionUtil.lookupField(declaringClass, name); - return (long) metaAccess.lookupJavaField(field).getOffset(); - } - - @Override - public Class[] types() { - return new Class[]{long.class}; + int offset = ImageSingletons.lookup(ReflectionSubstitutionSupport.class).getFieldOffset(field, false); + if (offset <= 0) { + throw VMError.shouldNotReachHere("Field is not marked as accessed: " + field); + } + return Long.valueOf(offset); } } }