Skip to content

[GR-58477] Simplify unsafe allocation, field registration and serialization reachability metadata #10178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/src/org.graalvm.nativeimage/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -1143,6 +1143,7 @@ meth public !varargs static void registerProxyClass(java.lang.Class<?>[])
meth public static void registerIncludingAssociatedClasses(java.lang.Class<?>)
meth public static void registerLambdaCapturingClass(java.lang.Class<?>)
meth public static void registerWithTargetConstructorClass(java.lang.Class<?>,java.lang.Class<?>)
anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="")
supr java.lang.Object

CLSS public final org.graalvm.nativeimage.hosted.RuntimeSystemProperties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ public static void registerIncludingAssociatedClasses(Class<?> clazz) {
* @since 21.3
*/
public static void register(Class<?>... classes) {
RuntimeSerializationSupport.singleton().register(ConfigurationCondition.alwaysTrue(), classes);
for (Class<?> clazz : classes) {
RuntimeSerializationSupport.singleton().register(ConfigurationCondition.alwaysTrue(), clazz);
}
}

/**
Expand All @@ -95,8 +97,10 @@ public static void register(Class<?>... classes) {
*
* @since 21.3
*/
@Deprecated
@SuppressWarnings("unused")
public static void registerWithTargetConstructorClass(Class<?> clazz, Class<?> customTargetConstructorClazz) {
RuntimeSerializationSupport.singleton().registerWithTargetConstructorClass(ConfigurationCondition.alwaysTrue(), clazz, customTargetConstructorClazz);
RuntimeSerializationSupport.singleton().register(ConfigurationCondition.alwaysTrue(), clazz);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@

public interface ReflectionRegistry {
default void register(ConfigurationCondition condition, Class<?>... classes) {
Arrays.stream(classes).forEach(clazz -> register(condition, false, clazz));
Arrays.stream(classes).forEach(clazz -> register(condition, clazz));
}

void register(ConfigurationCondition condition, boolean unsafeAllocated, Class<?> clazz);
void register(ConfigurationCondition condition, Class<?> clazz);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,9 @@ static RuntimeSerializationSupport<ConfigurationCondition> singleton() {

void registerIncludingAssociatedClasses(C condition, Class<?> clazz);

void register(C condition, Class<?>... classes);
void register(C condition, Class<?> clazz);

void registerWithTargetConstructorClass(C condition, Class<?> clazz, Class<?> customTargetConstructorClazz);

void registerWithTargetConstructorClass(C condition, String className, String customTargetConstructorClassName);
void register(C condition, String clazz);

void registerLambdaCapturingClass(C condition, String lambdaCapturingClassName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,7 @@ private static boolean readClassDescriptor(JNIEnvironment jni, JNIObjectHandle t
name = nullHandle();
}
var className = fromJniString(jni, name);
traceSerializeBreakpoint(jni, "ObjectInputStream.readClassDescriptor", true, state.getFullStackTraceOrNull(), className, null);
traceSerializeBreakpoint(jni, "ObjectInputStream.readClassDescriptor", true, state.getFullStackTraceOrNull(), className);
return true;
}

Expand Down Expand Up @@ -1158,7 +1158,7 @@ private static boolean objectStreamClassConstructor(JNIEnvironment jni, JNIObjec
Object interfaceNames = getClassArrayNames(jni, interfaces);
traceSerializeBreakpoint(jni, "ProxyClassSerialization", validObjectStreamClassInstance, state.getFullStackTraceOrNull(), interfaceNames);
} else {
traceSerializeBreakpoint(jni, "ObjectStreamClass.<init>", validObjectStreamClassInstance, state.getFullStackTraceOrNull(), className, null);
traceSerializeBreakpoint(jni, "ObjectStreamClass.<init>", validObjectStreamClassInstance, state.getFullStackTraceOrNull(), className);
}
}
}
Expand All @@ -1177,13 +1177,7 @@ private static boolean customTargetConstructorSerialization(JNIEnvironment jni,
JNIObjectHandle serializeTargetClass = getObjectArgument(thread, 1);
if (Support.isSerializable(jni, serializeTargetClass)) {
String serializeTargetClassName = getClassNameOrNull(jni, serializeTargetClass);

JNIObjectHandle customConstructorObj = getObjectArgument(thread, 2);
JNIObjectHandle customConstructorClass = jniFunctions().getGetObjectClass().invoke(jni, customConstructorObj);
JNIMethodId getDeclaringClassNameMethodID = agent.handles().getJavaLangReflectConstructorDeclaringClassName(jni, customConstructorClass);
JNIObjectHandle declaredClassNameObj = Support.callObjectMethod(jni, customConstructorObj, getDeclaringClassNameMethodID);
String customConstructorClassName = fromJniString(jni, declaredClassNameObj);
traceSerializeBreakpoint(jni, "ObjectStreamClass.<init>", true, state.getFullStackTraceOrNull(), serializeTargetClassName, customConstructorClassName);
traceSerializeBreakpoint(jni, "ObjectStreamClass.<init>", true, state.getFullStackTraceOrNull(), serializeTargetClassName);
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ private static void doTestResourceConfig(ResourceConfiguration resourceConfig) {

private static void doTestSerializationConfig(SerializationConfiguration serializationConfig) {
UnresolvedConfigurationCondition condition = UnresolvedConfigurationCondition.alwaysTrue();
Assert.assertFalse(serializationConfig.contains(condition, "seenType", null));
Assert.assertTrue(serializationConfig.contains(condition, "unseenType", null));
Assert.assertFalse(serializationConfig.contains(condition, "seenType"));
Assert.assertTrue(serializationConfig.contains(condition, "unseenType"));
}

private static ConfigurationType getConfigTypeOrFail(TypeConfiguration typeConfig, String typeName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType
private boolean allPublicClasses;
private ConfigurationMemberAccessibility allDeclaredFieldsAccess = ConfigurationMemberAccessibility.NONE;
private ConfigurationMemberAccessibility allPublicFieldsAccess = ConfigurationMemberAccessibility.NONE;
private boolean unsafeAllocated;
private ConfigurationMemberAccessibility allDeclaredMethodsAccess = ConfigurationMemberAccessibility.NONE;
private ConfigurationMemberAccessibility allPublicMethodsAccess = ConfigurationMemberAccessibility.NONE;
private ConfigurationMemberAccessibility allDeclaredConstructorsAccess = ConfigurationMemberAccessibility.NONE;
Expand Down Expand Up @@ -281,7 +280,6 @@ private void setFlagsFromOther(ConfigurationType other, BiPredicate<Boolean, Boo
allPublicClasses = flagPredicate.test(allPublicClasses, other.allPublicClasses);
allDeclaredFieldsAccess = accessCombiner.apply(allDeclaredFieldsAccess, other.allDeclaredFieldsAccess);
allPublicFieldsAccess = accessCombiner.apply(allPublicFieldsAccess, other.allPublicFieldsAccess);
unsafeAllocated = flagPredicate.test(unsafeAllocated, other.unsafeAllocated);
allDeclaredMethodsAccess = accessCombiner.apply(allDeclaredMethodsAccess, other.allDeclaredMethodsAccess);
allPublicMethodsAccess = accessCombiner.apply(allPublicMethodsAccess, other.allPublicMethodsAccess);
allDeclaredConstructorsAccess = accessCombiner.apply(allDeclaredConstructorsAccess, other.allDeclaredConstructorsAccess);
Expand Down Expand Up @@ -411,10 +409,6 @@ public synchronized void setAllPublicClasses() {
allPublicClasses = true;
}

public void setUnsafeAllocated() {
this.unsafeAllocated = true;
}

public synchronized void setAllDeclaredFields(ConfigurationMemberAccessibility accessibility) {
if (!allDeclaredFieldsAccess.includes(accessibility)) {
allDeclaredFieldsAccess = accessibility;
Expand Down Expand Up @@ -470,7 +464,6 @@ public synchronized void printJson(JsonWriter writer) throws IOException {
printJsonBooleanIfSet(writer, allPublicMethodsAccess == ConfigurationMemberAccessibility.ACCESSED, "allPublicMethods");
printJsonBooleanIfSet(writer, allDeclaredConstructorsAccess == ConfigurationMemberAccessibility.ACCESSED, "allDeclaredConstructors");
printJsonBooleanIfSet(writer, allPublicConstructorsAccess == ConfigurationMemberAccessibility.ACCESSED, "allPublicConstructors");
printJsonBooleanIfSet(writer, unsafeAllocated, "unsafeAllocated");

if (fields != null) {
writer.appendSeparator().quote("fields").appendFieldSeparator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,6 @@ public boolean registerAllConstructors(UnresolvedConfigurationCondition conditio
return true;
}

@Override
public void registerUnsafeAllocated(UnresolvedConfigurationCondition condition, ConfigurationType type) {
VMError.guarantee(condition.equals(type.getCondition()), "condition is here part of the type");
type.setUnsafeAllocated();
}

@Override
public void registerMethod(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type, String methodName, List<ConfigurationType> methodParameterTypes) {
VMError.guarantee(condition.equals(type.getCondition()), "condition is already a part of the type");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ protected void removeIf(Predicate predicate) {
@Override
public void mergeConditional(UnresolvedConfigurationCondition condition, SerializationConfiguration other) {
for (SerializationConfigurationType type : other.serializations) {
serializations.add(new SerializationConfigurationType(condition, type.getQualifiedJavaName(), type.getQualifiedCustomTargetConstructorJavaName()));
serializations.add(new SerializationConfigurationType(condition, type.getQualifiedJavaName()));
}
}

public boolean contains(UnresolvedConfigurationCondition condition, String serializationTargetClass, String customTargetConstructorClass) {
return serializations.contains(createConfigurationType(condition, serializationTargetClass, customTargetConstructorClass)) ||
public boolean contains(UnresolvedConfigurationCondition condition, String serializationTargetClass) {
return serializations.contains(createConfigurationType(condition, serializationTargetClass)) ||
lambdaSerializationCapturingTypes.contains(createLambdaCapturingClassConfigurationType(condition, serializationTargetClass));
}

Expand Down Expand Up @@ -150,20 +150,13 @@ public void registerIncludingAssociatedClasses(UnresolvedConfigurationCondition
}

@Override
public void register(UnresolvedConfigurationCondition condition, Class<?>... classes) {
for (Class<?> clazz : classes) {
registerWithTargetConstructorClass(condition, clazz, null);
}
}

@Override
public void registerWithTargetConstructorClass(UnresolvedConfigurationCondition condition, Class<?> clazz, Class<?> customTargetConstructorClazz) {
registerWithTargetConstructorClass(condition, clazz.getName(), customTargetConstructorClazz == null ? null : customTargetConstructorClazz.getName());
public void register(UnresolvedConfigurationCondition condition, Class<?> clazz) {
register(condition, clazz.getName());
}

@Override
public void registerWithTargetConstructorClass(UnresolvedConfigurationCondition condition, String className, String customTargetConstructorClassName) {
serializations.add(createConfigurationType(condition, className, customTargetConstructorClassName));
public void register(UnresolvedConfigurationCondition condition, String className) {
serializations.add(createConfigurationType(condition, className));
}

@Override
Expand All @@ -181,10 +174,9 @@ public boolean isEmpty() {
return serializations.isEmpty() && lambdaSerializationCapturingTypes.isEmpty() && interfaceListsSerializableProxies.isEmpty();
}

private static SerializationConfigurationType createConfigurationType(UnresolvedConfigurationCondition condition, String className, String customTargetConstructorClassName) {
private static SerializationConfigurationType createConfigurationType(UnresolvedConfigurationCondition condition, String className) {
String convertedClassName = SignatureUtil.toInternalClassName(className);
String convertedCustomTargetConstructorClassName = customTargetConstructorClassName == null ? null : SignatureUtil.toInternalClassName(customTargetConstructorClassName);
return new SerializationConfigurationType(condition, convertedClassName, convertedCustomTargetConstructorClassName);
return new SerializationConfigurationType(condition, convertedClassName);
}

private static SerializationConfigurationLambdaCapturingType createLambdaCapturingClassConfigurationType(UnresolvedConfigurationCondition condition, String className) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,34 @@
*/
package com.oracle.svm.configure.config;

import static com.oracle.svm.core.configure.ConfigurationParser.NAME_KEY;
import static com.oracle.svm.core.configure.ConfigurationParser.TYPE_KEY;

import java.io.IOException;
import java.util.Comparator;
import java.util.Objects;

import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition;

import com.oracle.svm.core.configure.SerializationConfigurationParser;

import jdk.graal.compiler.util.json.JsonPrintable;
import jdk.graal.compiler.util.json.JsonWriter;

import static com.oracle.svm.core.configure.ConfigurationParser.NAME_KEY;
import static com.oracle.svm.core.configure.ConfigurationParser.TYPE_KEY;

public class SerializationConfigurationType implements JsonPrintable, Comparable<SerializationConfigurationType> {
private final UnresolvedConfigurationCondition condition;
private final String qualifiedJavaName;
private final String qualifiedCustomTargetConstructorJavaName;

public SerializationConfigurationType(UnresolvedConfigurationCondition condition, String qualifiedJavaName, String qualifiedCustomTargetConstructorJavaName) {
public SerializationConfigurationType(UnresolvedConfigurationCondition condition, String qualifiedJavaName) {
assert qualifiedJavaName.indexOf('/') == -1 : "Requires qualified Java name, not the internal representation";
assert !qualifiedJavaName.startsWith("[") : "Requires Java source array syntax, for example java.lang.String[]";
assert qualifiedCustomTargetConstructorJavaName == null || qualifiedCustomTargetConstructorJavaName.indexOf('/') == -1 : "Requires qualified Java name, not internal representation";
assert qualifiedCustomTargetConstructorJavaName == null || !qualifiedCustomTargetConstructorJavaName.startsWith("[") : "Requires Java source array syntax, for example java.lang.String[]";
Objects.requireNonNull(condition);
this.condition = condition;
Objects.requireNonNull(qualifiedJavaName);
this.qualifiedJavaName = qualifiedJavaName;
this.qualifiedCustomTargetConstructorJavaName = qualifiedCustomTargetConstructorJavaName;
}

public String getQualifiedJavaName() {
return qualifiedJavaName;
}

public String getQualifiedCustomTargetConstructorJavaName() {
return qualifiedCustomTargetConstructorJavaName;
}

public UnresolvedConfigurationCondition getCondition() {
return condition;
}
Expand All @@ -80,11 +69,6 @@ private void printJson(JsonWriter writer, boolean combinedFile) throws IOExcepti
writer.appendObjectStart();
ConfigurationConditionPrintable.printConditionAttribute(condition, writer, combinedFile);
writer.quote(combinedFile ? TYPE_KEY : NAME_KEY).appendFieldSeparator().quote(qualifiedJavaName);
if (qualifiedCustomTargetConstructorJavaName != null) {
writer.appendSeparator();
writer.quote(SerializationConfigurationParser.CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY).appendFieldSeparator()
.quote(qualifiedCustomTargetConstructorJavaName);
}
writer.appendObjectEnd();
}

Expand All @@ -98,13 +82,12 @@ public boolean equals(Object o) {
}
SerializationConfigurationType that = (SerializationConfigurationType) o;
return condition.equals(that.condition) &&
qualifiedJavaName.equals(that.qualifiedJavaName) &&
Objects.equals(qualifiedCustomTargetConstructorJavaName, that.qualifiedCustomTargetConstructorJavaName);
qualifiedJavaName.equals(that.qualifiedJavaName);
}

@Override
public int hashCode() {
return Objects.hash(condition, qualifiedJavaName, qualifiedCustomTargetConstructorJavaName);
return Objects.hash(condition, qualifiedJavaName);
}

@Override
Expand All @@ -113,11 +96,6 @@ public int compareTo(SerializationConfigurationType other) {
if (compareName != 0) {
return compareName;
}
int compareCondition = condition.compareTo(other.condition);
if (compareCondition != 0) {
return compareCondition;
}
Comparator<String> nullsFirstCompare = Comparator.nullsFirst(Comparator.naturalOrder());
return nullsFirstCompare.compare(qualifiedCustomTargetConstructorJavaName, other.qualifiedCustomTargetConstructorJavaName);
return condition.compareTo(other.condition);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
case "AllocObject":
expectSize(args, 0);
/*
* AllocObject is implemented via Unsafe.allocateInstance, so we need to set the
* "unsafe allocated" flag in the reflection configuration file.
* AllocObject is implemented via Unsafe.allocateInstance, so we need to add the
* type to the reflection configuration file.
*/
configurationSet.getReflectionConfiguration().getOrCreateType(condition, clazz).setUnsafeAllocated();
configurationSet.getReflectionConfiguration().getOrCreateType(condition, clazz);
break;
case "GetStaticMethodID":
case "GetMethodID": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ public void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configur
break;
}
case "allocateInstance": {
configuration.getOrCreateType(condition, clazz).setUnsafeAllocated();
/* Reflectively-accessed types are unsafe-allocatable by default */
configuration.getOrCreateType(condition, clazz);
break;
}
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
SerializationConfiguration serializationConfiguration = configurationSet.getSerializationConfiguration();

if ("ObjectStreamClass.<init>".equals(function) || "ObjectInputStream.readClassDescriptor".equals(function)) {
expectSize(args, 2);
expectSize(args, 1);

if (advisor.shouldIgnore(LazyValueUtils.lazyValue((String) args.get(0)), LazyValueUtils.lazyValue(null), false)) {
return;
Expand All @@ -68,7 +68,7 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
if (className.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) {
serializationConfiguration.registerLambdaCapturingClass(condition, className);
} else {
serializationConfiguration.registerWithTargetConstructorClass(condition, className, (String) args.get(1));
serializationConfiguration.register(condition, className);
}
} else if ("SerializedLambda.readResolve".equals(function)) {
expectSize(args, 1);
Expand Down
Loading