Skip to content

Commit 42e5b32

Browse files
committed
[GR-37224] Support for Unsafe.allocateInstance in NI
PullRequest: graal/11331
2 parents 7922824 + f14432b commit 42e5b32

File tree

13 files changed

+250
-26
lines changed

13 files changed

+250
-26
lines changed

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -42,9 +42,14 @@
4242

4343
import java.lang.reflect.Executable;
4444
import java.lang.reflect.Field;
45+
import java.util.Arrays;
4546

4647
public interface ReflectionRegistry {
47-
void register(ConfigurationCondition condition, Class<?>... classes);
48+
default void register(ConfigurationCondition condition, Class<?>... classes) {
49+
Arrays.stream(classes).forEach(clazz -> register(condition, false, clazz));
50+
}
51+
52+
void register(ConfigurationCondition condition, boolean unsafeAllocated, Class<?> clazz);
4853

4954
void register(ConfigurationCondition condition, boolean queriedOnly, Executable... methods);
5055

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java

Lines changed: 187 additions & 5 deletions
Large diffs are not rendered by default.

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgent.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
127127
List<String> accessFilterFiles = new ArrayList<>();
128128
boolean experimentalClassLoaderSupport = true;
129129
boolean experimentalClassDefineSupport = false;
130+
boolean experimentalUnsafeAllocationSupport = false;
130131
boolean experimentalOmitClasspathConfig = false;
131132
boolean build = false;
132133
boolean configurationWithOrigins = false;
@@ -187,6 +188,10 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
187188
experimentalClassDefineSupport = true;
188189
} else if (token.startsWith("experimental-class-define-support=")) {
189190
experimentalClassDefineSupport = Boolean.parseBoolean(getTokenValue(token));
191+
} else if (token.equals("experimental-unsafe-allocation-support")) {
192+
experimentalUnsafeAllocationSupport = Boolean.parseBoolean(getTokenValue(token));
193+
} else if (token.startsWith("experimental-unsafe-allocation-support=")) {
194+
experimentalUnsafeAllocationSupport = Boolean.parseBoolean(getTokenValue(token));
190195
} else if (token.startsWith("config-write-period-secs=")) {
191196
configWritePeriod = parseIntegerOrNegative(getTokenValue(token));
192197
if (configWritePeriod <= 0) {
@@ -370,7 +375,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
370375

371376
try {
372377
BreakpointInterceptor.onLoad(jvmti, callbacks, tracer, this, interceptedStateSupplier,
373-
experimentalClassLoaderSupport, experimentalClassDefineSupport, trackReflectionMetadata);
378+
experimentalClassLoaderSupport, experimentalClassDefineSupport, experimentalUnsafeAllocationSupport, trackReflectionMetadata);
374379
} catch (Throwable t) {
375380
return error(3, t.toString());
376381
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType
9696
private boolean allPublicClasses;
9797
private boolean allDeclaredFields;
9898
private boolean allPublicFields;
99+
private boolean unsafeAllocated;
99100
private ConfigurationMemberAccessibility allDeclaredMethodsAccess = ConfigurationMemberAccessibility.NONE;
100101
private ConfigurationMemberAccessibility allPublicMethodsAccess = ConfigurationMemberAccessibility.NONE;
101102
private ConfigurationMemberAccessibility allDeclaredConstructorsAccess = ConfigurationMemberAccessibility.NONE;
@@ -269,6 +270,7 @@ private void setFlagsFromOther(ConfigurationType other, BiPredicate<Boolean, Boo
269270
allPublicClasses = flagPredicate.test(allPublicClasses, other.allPublicClasses);
270271
allDeclaredFields = flagPredicate.test(allDeclaredFields, other.allDeclaredFields);
271272
allPublicFields = flagPredicate.test(allPublicFields, other.allPublicFields);
273+
unsafeAllocated = flagPredicate.test(unsafeAllocated, other.unsafeAllocated);
272274
allDeclaredMethodsAccess = accessCombiner.apply(allDeclaredMethodsAccess, other.allDeclaredMethodsAccess);
273275
allPublicMethodsAccess = accessCombiner.apply(allPublicMethodsAccess, other.allPublicMethodsAccess);
274276
allDeclaredConstructorsAccess = accessCombiner.apply(allDeclaredConstructorsAccess, other.allDeclaredConstructorsAccess);
@@ -373,6 +375,10 @@ public synchronized void setAllPublicClasses() {
373375
allPublicClasses = true;
374376
}
375377

378+
public void setUnsafeAllocated() {
379+
this.unsafeAllocated = true;
380+
}
381+
376382
public synchronized void setAllDeclaredFields() {
377383
allDeclaredFields = true;
378384
removeFields(ConfigurationMemberDeclaration.DECLARED);
@@ -430,6 +436,8 @@ public synchronized void printJson(JsonWriter writer) throws IOException {
430436
optionallyPrintJsonBoolean(writer, allPublicMethodsAccess == ConfigurationMemberAccessibility.QUERIED, "queryAllPublicMethods");
431437
optionallyPrintJsonBoolean(writer, allDeclaredConstructorsAccess == ConfigurationMemberAccessibility.QUERIED, "queryAllDeclaredConstructors");
432438
optionallyPrintJsonBoolean(writer, allPublicConstructorsAccess == ConfigurationMemberAccessibility.QUERIED, "queryAllPublicConstructors");
439+
optionallyPrintJsonBoolean(writer, unsafeAllocated, "unsafeAllocated");
440+
433441
if (fields != null) {
434442
writer.append(',').newline().quote("fields").append(':');
435443
JsonPrinter.printCollection(writer, fields.entrySet(), Map.Entry.comparingByKey(), ConfigurationType::printField);

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ParserConfigurationAdapter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ public boolean registerAllConstructors(boolean queriedOnly, ConfigurationType ty
7676
return true;
7777
}
7878

79+
@Override
80+
public void registerUnsafeAllocated(ConfigurationType type) {
81+
type.setUnsafeAllocated();
82+
}
83+
7984
@Override
8085
public void registerMethod(boolean queriedOnly, ConfigurationType type, String methodName, List<ConfigurationType> methodParameterTypes) {
8186
type.addMethod(methodName, ConfigurationMethod.toInternalParamsSignature(methodParameterTypes), ConfigurationMemberDeclaration.PRESENT,

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ public void processEntry(Map<String, ?> entry, ConfigurationSet configurationSet
256256
resourceConfiguration.addBundle(condition, classNames, locales, baseName);
257257
break;
258258
}
259+
case "allocateInstance": {
260+
configuration.getOrCreateType(condition, clazz).setUnsafeAllocated();
261+
break;
262+
}
259263
default:
260264
System.err.println("Unsupported reflection method: " + function);
261265
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public final class ReflectionConfigurationParser<T> extends ConfigurationParser
5050
private static final List<String> OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS = Arrays.asList("allDeclaredConstructors", "allPublicConstructors",
5151
"allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields",
5252
"allDeclaredClasses", "allPermittedSubclasses", "allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY,
53-
"queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods");
53+
"queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods", "unsafeAllocated");
5454

5555
public ReflectionConfigurationParser(ReflectionConfigurationParserDelegate<T> delegate) {
5656
this(delegate, true);
@@ -165,6 +165,11 @@ private void parseClass(Map<String, Object> data) {
165165
delegate.registerPublicMethods(true, clazz);
166166
}
167167
break;
168+
case "unsafeAllocated":
169+
if (asBoolean(value, "unsafeAllocated")) {
170+
delegate.registerUnsafeAllocated(clazz);
171+
}
172+
break;
168173
case "methods":
169174
parseMethods(false, asList(value, "Attribute 'methods' must be an array of method descriptors"), clazz);
170175
break;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ public interface ReflectionConfigurationParserDelegate<T> {
6666

6767
boolean registerAllConstructors(boolean queriedOnly, T type);
6868

69+
void registerUnsafeAllocated(T clazz);
70+
6971
String getTypeName(T type);
7072

7173
String getSimpleName(T type);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import java.util.Map;
3434

35+
import com.oracle.svm.core.configure.ConfigurationFile;
3536
import org.graalvm.compiler.api.replacements.Fold;
3637
import org.graalvm.compiler.api.replacements.Snippet;
3738
import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
@@ -69,7 +70,6 @@
6970
import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo;
7071
import org.graalvm.compiler.word.BarrieredAccess;
7172
import org.graalvm.compiler.word.Word;
72-
import org.graalvm.nativeimage.hosted.RuntimeReflection;
7373
import org.graalvm.word.LocationIdentity;
7474
import org.graalvm.word.UnsignedWord;
7575
import org.graalvm.word.WordFactory;
@@ -105,8 +105,6 @@ public abstract class SubstrateAllocationSnippets extends AllocationSnippets {
105105
private static final SubstrateForeignCallDescriptor ARRAY_HUB_ERROR = SnippetRuntime.findForeignCall(SubstrateAllocationSnippets.class, "arrayHubErrorStub", true);
106106
private static final SubstrateForeignCallDescriptor[] FOREIGN_CALLS = new SubstrateForeignCallDescriptor[]{NEW_MULTI_ARRAY, HUB_ERROR, ARRAY_HUB_ERROR};
107107

108-
private static final String RUNTIME_REFLECTION_TYPE_NAME = RuntimeReflection.class.getTypeName();
109-
110108
public static void registerForeignCalls(SubstrateForeignCallsProvider foreignCalls) {
111109
foreignCalls.register(FOREIGN_CALLS);
112110
}
@@ -216,8 +214,8 @@ private static void hubErrorStub(DynamicHub hub) throws InstantiationException {
216214
} else if (!LayoutEncoding.isInstance(hub.getLayoutEncoding())) {
217215
throw new InstantiationException("Cannot allocate instance.");
218216
} else if (!hub.isInstantiated()) {
219-
throw new IllegalArgumentException("Class " + DynamicHub.toClass(hub).getTypeName() +
220-
" is instantiated reflectively but was never registered. Register the class by using " + RUNTIME_REFLECTION_TYPE_NAME);
217+
throw new IllegalArgumentException("Type " + DynamicHub.toClass(hub).getTypeName() + " is instantiated reflectively but was never registered." +
218+
" Register the type by adding \"unsafeAllocated\" for the type in " + ConfigurationFile.REFLECTION.getFileName() + ".");
221219
} else {
222220
throw VMError.shouldNotReachHere();
223221
}
@@ -250,8 +248,8 @@ private static void arrayHubErrorStub(DynamicHub elementType) {
250248
} else if (elementType == DynamicHub.fromClass(void.class)) {
251249
throw new IllegalArgumentException("Cannot allocate void array.");
252250
} else if (elementType.getArrayHub() == null || !elementType.getArrayHub().isInstantiated()) {
253-
throw new IllegalArgumentException("Class " + DynamicHub.toClass(elementType).getTypeName() + "[]" +
254-
" is instantiated reflectively but was never registered. Register the class by using " + RUNTIME_REFLECTION_TYPE_NAME);
251+
throw new IllegalArgumentException("Class " + DynamicHub.toClass(elementType).getTypeName() + "[] is instantiated reflectively but was never registered." +
252+
"Register the class by adding \"unsafeAllocated\" for the class in " + ConfigurationFile.REFLECTION.getFileName() + ".");
255253
} else {
256254
VMError.shouldNotReachHere();
257255
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ public boolean registerAllConstructors(boolean queriedOnly, ConditionalElement<C
151151
return methods.length > 0;
152152
}
153153

154+
@Override
155+
public void registerUnsafeAllocated(ConditionalElement<Class<?>> clazz) {
156+
registry.register(clazz.getCondition(), true, clazz.getElement());
157+
}
158+
154159
@Override
155160
public void registerMethod(boolean queriedOnly, ConditionalElement<Class<?>> type, String methodName, List<ConditionalElement<Class<?>>> methodParameterTypes) throws NoSuchMethodException {
156161
Class<?>[] parameterTypesArray = getParameterTypes(methodParameterTypes);

0 commit comments

Comments
 (0)