Skip to content

Commit

Permalink
[GR-30595] Unsafe static fields access support.
Browse files Browse the repository at this point in the history
PullRequest: graal/8691
  • Loading branch information
cstancu committed Apr 7, 2021
2 parents 8926902 + f97bdb5 commit 7df6884
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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<HostedField> invokeReplacer) {
if (targetField == null) {
/* A NullPointerException will be thrown at run time for this call. */
return false;
Expand All @@ -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 {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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.");
}

}

0 comments on commit 7df6884

Please sign in to comment.