Skip to content

Commit

Permalink
Consider all types as serializable
Browse files Browse the repository at this point in the history
  • Loading branch information
loicottet committed Oct 23, 2024
1 parent dea1968 commit 1e15972
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 3 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/reachability-metadata.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# The Universal Permissive License (UPL), Version 1.0
#
#op
# 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
Expand Down Expand Up @@ -71,7 +71,7 @@ jobs:
- name: Build GraalVM JDK
uses: ./.github/actions/build-graalvm
with:
native-images: 'native-image,native-image-configure,lib:native-image-agent'
native-images: 'native-image'
components: 'Native Image,Native Image Configure Tool'
java-version: ${{ env.MINIMUM_METADATA_JAVA_VERSION }}
- name: Tar GraalVM JDK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@

import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.MapCursor;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.impl.ConfigurationCondition;
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition;

import com.oracle.svm.core.TypeResult;
Expand All @@ -58,6 +61,7 @@ public void parseAndRegister(Object json, URI origin) {
}

@Override
@SuppressWarnings("unchecked")
protected void parseClass(EconomicMap<String, Object> data) {
checkAttributes(data, "reflection class descriptor object", List.of(TYPE_KEY), OPTIONAL_REFLECT_METADATA_ATTRS);
RuntimeReflectionSupport.increaseCount(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void parseAndRegister(Object json, URI origin) {
@Override
protected void parseSerializationDescriptorObject(EconomicMap<String, Object> data, boolean lambdaCapturingType) {
checkAttributes(data, "serialization descriptor object", List.of(TYPE_KEY), List.of(CONDITIONAL_KEY, CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY));
RuntimeReflectionSupport.increaseCount(false);
RuntimeReflectionSupport.increaseCount(true);

Optional<ConfigurationTypeDescriptor> targetSerializationClass = parseTypeContents(data.get(TYPE_KEY));
if (targetSerializationClass.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -49,6 +50,15 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

import com.oracle.svm.core.TypeResult;
import com.oracle.svm.core.configure.ConfigurationParser;
import com.oracle.svm.core.configure.ConfigurationTypeDescriptor;
import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor;
import com.oracle.svm.core.configure.ProxyConfigurationTypeDescriptor;
import com.oracle.svm.core.configure.ReflectionConfigurationParser;
import com.oracle.svm.core.configure.ReflectionConfigurationParserDelegate;
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
import com.oracle.svm.hosted.config.ReflectionRegistryAdapter;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
Expand Down Expand Up @@ -109,6 +119,8 @@
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;

import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors;

@AutomaticallyRegisteredFeature
public class SerializationFeature implements InternalFeature {
final Set<Class<?>> capturingClasses = ConcurrentHashMap.newKeySet();
Expand Down Expand Up @@ -136,6 +148,168 @@ public void duringSetup(DuringSetupAccess a) {
strictConfiguration);
loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(parser, imageClassLoader, "serialization");

ReflectionConfigurationParser<ConfigurationCondition, Class<?>> reflectParser = ReflectionConfigurationParser.create(ConfigurationParser.REFLECTION_KEY, true, conditionResolver, new ReflectionConfigurationParserDelegate<ConfigurationCondition, Class<?>>() {
@Override
public TypeResult<Class<?>> resolveType(ConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives) {
switch (typeDescriptor.getDescriptorType()) {
case NAMED -> {
NamedConfigurationTypeDescriptor namedDescriptor = (NamedConfigurationTypeDescriptor) typeDescriptor;
TypeResult<Class<?>> result = resolveNamedType(namedDescriptor, allowPrimitives);
return result;
}
case PROXY -> {
return resolveProxyType((ProxyConfigurationTypeDescriptor) typeDescriptor);
}
default -> {
throw VMError.shouldNotReachHere("Unknown type descriptor kind: %s", typeDescriptor.getDescriptorType());
}
}
}

private TypeResult<Class<?>> resolveNamedType(NamedConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives) {
TypeResult<Class<?>> result = imageClassLoader.findClass(typeDescriptor.name(), allowPrimitives);
if (!result.isPresent() && result.getException() instanceof NoClassDefFoundError) {
/*
* In certain cases when the class name is identical to an existing class name except
* for lettercase, `ClassLoader.findClass` throws a `NoClassDefFoundError` but
* `Class.forName` throws a `ClassNotFoundException`.
*/
try {
Class.forName(typeDescriptor.name());
} catch (ClassNotFoundException notFoundException) {
result = TypeResult.forException(typeDescriptor.name(), notFoundException);
} catch (Throwable t) {
// ignore
}
}
return result;
}

private TypeResult<Class<?>> resolveProxyType(ProxyConfigurationTypeDescriptor typeDescriptor) {
String typeName = typeDescriptor.toString();
List<TypeResult<Class<?>>> interfaceResults = typeDescriptor.interfaceNames().stream().map(name -> resolveNamedType(new NamedConfigurationTypeDescriptor(name), false)).toList();
List<Class<?>> interfaces = new ArrayList<>();
for (TypeResult<Class<?>> intf : interfaceResults) {
if (!intf.isPresent()) {
return TypeResult.forException(typeName, intf.getException());
}
interfaces.add(intf.get());
}
try {
DynamicProxyRegistry proxyRegistry = ImageSingletons.lookup(DynamicProxyRegistry.class);
Class<?> proxyClass = proxyRegistry.getProxyClassHosted(interfaces.toArray(Class<?>[]::new));
return TypeResult.forType(typeName, proxyClass);
} catch (Throwable t) {
return TypeResult.forException(typeName, t);
}
}

@Override
public void registerType(ConfigurationCondition condition, Class<?> type) {
RuntimeSerializationSupport.singleton().register(condition, type);
}

@Override
public void registerPublicClasses(ConfigurationCondition condition, Class<?> type) {

}

@Override
public void registerDeclaredClasses(ConfigurationCondition condition, Class<?> type) {

}

@Override
public void registerRecordComponents(ConfigurationCondition condition, Class<?> type) {

}

@Override
public void registerPermittedSubclasses(ConfigurationCondition condition, Class<?> type) {

}

@Override
public void registerNestMembers(ConfigurationCondition condition, Class<?> type) {

}

@Override
public void registerSigners(ConfigurationCondition condition, Class<?> type) {

}

@Override
public void registerPublicFields(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {

}

@Override
public void registerDeclaredFields(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {

}

@Override
public void registerPublicMethods(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {

}

@Override
public void registerDeclaredMethods(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {

}

@Override
public void registerPublicConstructors(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {

}

@Override
public void registerDeclaredConstructors(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {

}

@Override
public void registerField(ConfigurationCondition condition, Class<?> type, String fieldName, boolean allowWrite) throws NoSuchFieldException {

}

@Override
public boolean registerAllMethodsWithName(ConfigurationCondition condition, boolean queriedOnly, Class<?> type, String methodName) {
return true;
}

@Override
public void registerMethod(ConfigurationCondition condition, boolean queriedOnly, Class<?> type, String methodName, List<Class<?>> methodParameterTypes) throws NoSuchMethodException {

}

@Override
public void registerConstructor(ConfigurationCondition condition, boolean queriedOnly, Class<?> type, List<Class<?>> methodParameterTypes) throws NoSuchMethodException {

}

@Override
public boolean registerAllConstructors(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {
return true;
}

@Override
public void registerUnsafeAllocated(ConfigurationCondition condition, Class<?> clazz) {

}

@Override
public String getTypeName(Class<?> type) {
return type.getTypeName();
}

@Override
public String getSimpleName(Class<?> type) {
return type.getName();
}
}, true, false, false);

SerializationConfigurationParser<ConfigurationCondition> denyCollectorParser = SerializationConfigurationParser.create(false, conditionResolver, serializationDenyRegistry,
strictConfiguration);
ConfigurationParserUtils.parseAndRegisterConfigurations(denyCollectorParser, imageClassLoader, "serialization",
Expand Down

0 comments on commit 1e15972

Please sign in to comment.