From f97bdb527dc2c272b5cb17438f5691e20ac3b012 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 6 Apr 2021 16:28:25 -0700 Subject: [PATCH] Unsafe static fields access support. --- .../svm/core/jdk/SunMiscSubstitutions.java | 10 ---- .../SubstrateGraphBuilderPlugins.java | 53 ++++++++++++++++--- ...t_jdk_internal_misc_Unsafe_Reflection.java | 46 ++++++++++++---- 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java index d4d428bc7558..2ec7f93aedca 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java @@ -206,16 +206,6 @@ public void ensureClassInitialized(Class c) { DynamicHub.fromClass(c).ensureInitialized(); } - @Substitute - private long staticFieldOffset(Field f) { - throw VMError.unsupportedFeature("Unsupported method of Unsafe"); - } - - @Substitute - private Object staticFieldBase(Field f) { - throw VMError.unsupportedFeature("Unsupported method of Unsafe"); - } - @Substitute private Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain) { throw VMError.unsupportedFeature("Defining new classes at run time is not supported. All classes need to be known at image build time."); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index a0209093bf4f..6ebfb81b0ebb 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -36,6 +36,7 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.function.Consumer; import java.util.stream.Stream; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -108,6 +109,7 @@ import com.oracle.svm.core.OS; import com.oracle.svm.core.ParsingReason; import com.oracle.svm.core.RuntimeAssertionsSupport; +import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; @@ -534,7 +536,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec String fieldName = snippetReflection.asObject(String.class, nameNode.asJavaConstant()); try { Field targetField = clazz.getDeclaredField(fieldName); - return processObjectFieldOffset(b, targetField, reason, metaAccess); + return processFieldOffset(b, targetField, reason, metaAccess); } catch (NoSuchFieldException | LinkageError e) { return false; } @@ -568,13 +570,34 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec } } - private static void registerUnsafePlugins(MetaAccessProvider metaAccess, Registration r, SnippetReflectionProvider snippetReflection, ParsingReason analysis) { + private static void registerUnsafePlugins(MetaAccessProvider metaAccess, Registration r, SnippetReflectionProvider snippetReflection, ParsingReason reason) { + r.register2("staticFieldOffset", Receiver.class, Field.class, new InvocationPlugin() { + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode fieldNode) { + if (fieldNode.isConstant()) { + Field targetField = snippetReflection.asObject(Field.class, fieldNode.asJavaConstant()); + return processFieldOffset(b, targetField, reason, metaAccess); + } + return false; + } + }); + r.register2("staticFieldBase", Receiver.class, Field.class, new InvocationPlugin() { + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode fieldNode) { + if (fieldNode.isConstant()) { + Field targetField = snippetReflection.asObject(Field.class, fieldNode.asJavaConstant()); + return processStaticFieldBase(b, targetField, reason, metaAccess); + } + return false; + + } + }); r.register2("objectFieldOffset", Receiver.class, Field.class, new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode fieldNode) { if (fieldNode.isConstant()) { Field targetField = snippetReflection.asObject(Field.class, fieldNode.asJavaConstant()); - return processObjectFieldOffset(b, targetField, analysis, metaAccess); + return processFieldOffset(b, targetField, reason, metaAccess); } return false; } @@ -597,7 +620,26 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec }); } - private static boolean processObjectFieldOffset(GraphBuilderContext b, Field targetField, ParsingReason reason, MetaAccessProvider metaAccess) { + private static boolean processFieldOffset(GraphBuilderContext b, Field targetField, ParsingReason reason, MetaAccessProvider metaAccess) { + return processUnsafeAccessedField(targetField, reason, metaAccess, hostedField -> { + assert hostedField.wrapped.isUnsafeAccessed(); + JavaConstant offsetValue = JavaConstant.forLong(hostedField.getLocation()); + b.addPush(JavaKind.Long, ConstantNode.forConstant(offsetValue, b.getMetaAccess(), b.getGraph())); + }); + } + + private static boolean processStaticFieldBase(GraphBuilderContext b, Field targetField, ParsingReason reason, MetaAccessProvider metaAccess) { + return processUnsafeAccessedField(targetField, reason, metaAccess, hostedField -> { + assert hostedField.wrapped.isUnsafeAccessed(); + Object staticFields = hostedField.getType().isPrimitive() + ? StaticFieldsSupport.getStaticPrimitiveFields() + : StaticFieldsSupport.getStaticObjectFields(); + JavaConstant baseValue = SubstrateObjectConstant.forObject(staticFields); + b.addPush(JavaKind.Object, ConstantNode.forConstant(baseValue, b.getMetaAccess(), b.getGraph())); + }); + } + + private static boolean processUnsafeAccessedField(Field targetField, ParsingReason reason, MetaAccessProvider metaAccess, Consumer invokeReplacer) { if (targetField == null) { /* A NullPointerException will be thrown at run time for this call. */ return false; @@ -613,8 +655,7 @@ private static boolean processObjectFieldOffset(GraphBuilderContext b, Field tar HostedMetaAccess hostedMetaAccess = (HostedMetaAccess) metaAccess; HostedField hostedField = hostedMetaAccess.lookupJavaField(targetField); if (hostedField.wrapped.isUnsafeAccessed()) { - JavaConstant offsetValue = JavaConstant.forLong(hostedField.getLocation()); - b.addPush(JavaKind.Long, ConstantNode.forConstant(offsetValue, b.getMetaAccess(), b.getGraph())); + invokeReplacer.accept(hostedField); return true; } else { /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_misc_Unsafe_Reflection.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_misc_Unsafe_Reflection.java index 0be5575908a4..74687636fae4 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_misc_Unsafe_Reflection.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_misc_Unsafe_Reflection.java @@ -27,6 +27,9 @@ // Checkstyle: allow reflection +import java.lang.reflect.Field; + +import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; @@ -35,22 +38,30 @@ import com.oracle.svm.core.jdk.Package_jdk_internal_misc; import com.oracle.svm.core.util.VMError; -import java.lang.reflect.Field; - @TargetClass(classNameProvider = Package_jdk_internal_misc.class, className = "Unsafe") @SuppressWarnings({"static-method"}) public final class Target_jdk_internal_misc_Unsafe_Reflection { @Substitute public long objectFieldOffset(Target_java_lang_reflect_Field field) { - int offset = field.root == null ? field.offset : field.root.offset; - if (offset > 0) { - return offset; + return UnsafeUtil.getFieldOffset(field); + } + + @Substitute + public long staticFieldOffset(Target_java_lang_reflect_Field field) { + return UnsafeUtil.getFieldOffset(field); + } + + @Substitute + public Object staticFieldBase(Target_java_lang_reflect_Field field) { + if (field == null) { + throw new NullPointerException(); + } + if (SubstrateUtil.cast(field, Field.class).getType().isPrimitive()) { + return StaticFieldsSupport.getStaticPrimitiveFields(); + } else { + return StaticFieldsSupport.getStaticObjectFields(); } - throw VMError.unsupportedFeature("The offset of " + field + " is accessed without the field being first registered as unsafe accessed. " + - "Please register the field as unsafe accessed. You can do so with a reflection configuration that " + - "contains an entry for the field with the attribute \"allowUnsafeAccess\": true. Such a configuration " + - "file can be generated for you. Read BuildConfiguration.md and Reflection.md for details."); } @Substitute @@ -68,3 +79,20 @@ public long objectFieldOffset(Class c, String name) { } } } + +class UnsafeUtil { + static long getFieldOffset(Target_java_lang_reflect_Field field) { + if (field == null) { + throw new NullPointerException(); + } + int offset = field.root == null ? field.offset : field.root.offset; + if (offset > 0) { + return offset; + } + throw VMError.unsupportedFeature("The offset of " + field + " is accessed without the field being first registered as unsafe accessed. " + + "Please register the field as unsafe accessed. You can do so with a reflection configuration that " + + "contains an entry for the field with the attribute \"allowUnsafeAccess\": true. Such a configuration " + + "file can be generated for you. Read BuildConfiguration.md and Reflection.md for details."); + } + +}