Skip to content

Commit

Permalink
Serialization registration from inside features
Browse files Browse the repository at this point in the history
  • Loading branch information
kkriske committed Aug 4, 2021
1 parent 90be384 commit 1b212eb
Show file tree
Hide file tree
Showing 17 changed files with 630 additions and 164 deletions.
4 changes: 4 additions & 0 deletions sdk/src/org.graalvm.nativeimage/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,10 @@ meth public !varargs static void register(java.lang.reflect.Field[])
meth public !varargs static void registerForReflectiveInstantiation(java.lang.Class<?>[])
supr java.lang.Object

CLSS public final org.graalvm.nativeimage.hosted.RuntimeSerialization
meth public !varargs static void register(java.lang.Class<?>[])
supr java.lang.Object

CLSS public abstract interface org.graalvm.nativeimage.impl.InternalPlatform
innr public abstract interface static PLATFORM_JNI

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* 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
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.nativeimage.hosted;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;

/**
* This class provides methods that can be called before and during analysis,
* to register classes for serialization at image runtime.
*
* @since 21.3
*/
@Platforms(Platform.HOSTED_ONLY.class)
public final class RuntimeSerialization {

/**
* Makes the provided classes available for serialization at runtime.
*
* @since 21.3
*/
public static void register(Class<?>... classes) {
ImageSingletons.lookup(RuntimeSerializationSupport.class).register(classes);
}

private RuntimeSerialization() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* 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
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.nativeimage.impl;

public interface RuntimeSerializationSupport {

void register(Class<?>... classes);

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

void registerWithTargetConstructorClass(String className, String customTargetConstructorClassName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public ResourceConfiguration loadResourceConfig(Function<IOException, Exception>

public SerializationConfiguration loadSerializationConfig(Function<IOException, Exception> exceptionHandler) throws Exception {
SerializationConfiguration serializationConfiguration = new SerializationConfiguration();
loadConfig(serializationConfigPaths, new SerializationConfigurationParser(serializationConfiguration::add), exceptionHandler);
loadConfig(serializationConfigPaths, new SerializationConfigurationParser(serializationConfiguration), exceptionHandler);
return serializationConfiguration;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@
package com.oracle.svm.configure.config;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.oracle.svm.configure.ConfigurationBase;
import com.oracle.svm.configure.json.JsonWriter;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.configure.SerializationConfigurationParser;
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;

public class SerializationConfiguration implements ConfigurationBase {
public class SerializationConfiguration implements ConfigurationBase, RuntimeSerializationSupport {

private static final String KEY_SEPARATOR = "|";

private final Set<String> serializations = ConcurrentHashMap.newKeySet();
private final Set<SerializationConfigurationType> serializations = ConcurrentHashMap.newKeySet();

public SerializationConfiguration() {
}
Expand All @@ -51,42 +51,51 @@ public void removeAll(SerializationConfiguration other) {
serializations.removeAll(other.serializations);
}

public void add(String serializationTargetClass, String customTargetConstructorClass) {
serializations.add(mapNameAndConstructor(serializationTargetClass, customTargetConstructorClass));
}

public boolean contains(String serializationTargetClass, String customTargetConstructorClass) {
return serializations.contains(mapNameAndConstructor(serializationTargetClass, customTargetConstructorClass));
}

private static String mapNameAndConstructor(String serializationTargetClass, String customTargetConstructorClass) {
return serializationTargetClass + (customTargetConstructorClass != null ? KEY_SEPARATOR + customTargetConstructorClass : "");
return serializations.contains(createConfigurationType(serializationTargetClass, customTargetConstructorClass));
}

@Override
public void printJson(JsonWriter writer) throws IOException {
writer.append('[').indent();
String prefix = "";
for (String entry : serializations) {
writer.append(prefix);
writer.newline().append('{').newline();
String[] serializationKeyValues = SubstrateUtil.split(entry, KEY_SEPARATOR, 2);
String className = serializationKeyValues[0];
writer.quote(SerializationConfigurationParser.NAME_KEY).append(":").quote(className);
if (serializationKeyValues.length > 1) {
writer.append(",").newline();
writer.quote(SerializationConfigurationParser.CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY).append(":").quote(serializationKeyValues[1]);
}
writer.newline().append('}');
List<SerializationConfigurationType> list = new ArrayList<>(serializations);
list.sort(Comparator.comparing(SerializationConfigurationType::getQualifiedJavaName)
.thenComparing(SerializationConfigurationType::getQualifiedCustomTargetConstructorJavaName));
for (SerializationConfigurationType type : list) {
writer.append(prefix).newline();
type.printJson(writer);
prefix = ",";
}
writer.unindent().newline();
writer.append(']');
}

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

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

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

@Override
public boolean isEmpty() {
return serializations.isEmpty();
}

private static SerializationConfigurationType createConfigurationType(String className, String customTargetConstructorClassName) {
String convertedClassName = SignatureUtil.toInternalClassName(className);
String convertedCustomTargetConstructorClassName = customTargetConstructorClassName == null ? null : SignatureUtil.toInternalClassName(customTargetConstructorClassName);
return new SerializationConfigurationType(convertedClassName, convertedCustomTargetConstructorClassName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.configure.config;

import com.oracle.svm.configure.json.JsonPrintable;
import com.oracle.svm.configure.json.JsonWriter;
import com.oracle.svm.core.configure.SerializationConfigurationParser;

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

public class SerializationConfigurationType implements JsonPrintable {

private final String qualifiedJavaName;
private final String qualifiedCustomTargetConstructorJavaName;

public SerializationConfigurationType(String qualifiedJavaName, String qualifiedCustomTargetConstructorJavaName) {
assert qualifiedJavaName.indexOf('/') == -1 : "Requires qualified Java name, not 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[]";
this.qualifiedJavaName = qualifiedJavaName;
this.qualifiedCustomTargetConstructorJavaName = qualifiedCustomTargetConstructorJavaName;
}

public String getQualifiedJavaName() {
return qualifiedJavaName;
}

public String getQualifiedCustomTargetConstructorJavaName() {
return qualifiedCustomTargetConstructorJavaName;
}

@Override
public void printJson(JsonWriter writer) throws IOException {
writer.append('{').indent().newline();
writer.quote(SerializationConfigurationParser.NAME_KEY).append(':').quote(qualifiedJavaName);
if (qualifiedCustomTargetConstructorJavaName != null) {
writer.append(',').newline();
writer.quote(SerializationConfigurationParser.CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY).append(':')
.quote(qualifiedCustomTargetConstructorJavaName);
}
writer.unindent().newline().append('}');
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SerializationConfigurationType that = (SerializationConfigurationType) o;
return Objects.equals(qualifiedJavaName, that.qualifiedJavaName) &&
Objects.equals(qualifiedCustomTargetConstructorJavaName, that.qualifiedCustomTargetConstructorJavaName);
}

@Override
public int hashCode() {
return Objects.hash(qualifiedJavaName, qualifiedCustomTargetConstructorJavaName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,29 @@ public static String toInternalSignature(List<?> parameterTypes) {
}
return sb.append(')').toString();
}

public static String toInternalClassName(String qualifiedForNameString) {
assert qualifiedForNameString.indexOf('/') == -1 : "Requires qualified Java name, not internal representation";
assert !qualifiedForNameString.endsWith("[]") : "Requires Class.forName syntax, for example '[Ljava.lang.String;'";
String s = qualifiedForNameString;
int n = 0;
while (n < s.length() && s.charAt(n) == '[') {
n++;
}
if (n > 0) { // transform to Java source syntax
StringBuilder sb = new StringBuilder(s.length() + n);
if (s.charAt(n) == 'L' && s.charAt(s.length() - 1) == ';') {
sb.append(s, n + 1, s.length() - 1); // cut off leading '[' and 'L' and trailing ';'
} else if (n == s.length() - 1) {
sb.append(JavaKind.fromPrimitiveOrVoidTypeChar(s.charAt(n)).getJavaName());
} else {
throw new IllegalArgumentException();
}
for (int i = 0; i < n; i++) {
sb.append("[]");
}
s = sb.toString();
}
return s;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@
import com.oracle.svm.configure.json.JsonWriter;
import com.oracle.svm.core.util.UserError;

import jdk.vm.ci.meta.JavaKind;

public class TypeConfiguration implements ConfigurationBase {
private final ConcurrentMap<String, ConfigurationType> types = new ConcurrentHashMap<>();

Expand Down Expand Up @@ -73,28 +71,7 @@ public void add(ConfigurationType type) {
}

public ConfigurationType getOrCreateType(String qualifiedForNameString) {
assert qualifiedForNameString.indexOf('/') == -1 : "Requires qualified Java name, not internal representation";
assert !qualifiedForNameString.endsWith("[]") : "Requires Class.forName syntax, for example '[Ljava.lang.String;'";
String s = qualifiedForNameString;
int n = 0;
while (n < s.length() && s.charAt(n) == '[') {
n++;
}
if (n > 0) { // transform to Java source syntax
StringBuilder sb = new StringBuilder(s.length() + n);
if (s.charAt(n) == 'L' && s.charAt(s.length() - 1) == ';') {
sb.append(s, n + 1, s.length() - 1); // cut off leading '[' and 'L' and trailing ';'
} else if (n == s.length() - 1) {
sb.append(JavaKind.fromPrimitiveOrVoidTypeChar(s.charAt(n)).getJavaName());
} else {
throw new IllegalArgumentException();
}
for (int i = 0; i < n; i++) {
sb.append("[]");
}
s = sb.toString();
}
return types.computeIfAbsent(s, ConfigurationType::new);
return types.computeIfAbsent(SignatureUtil.toInternalClassName(qualifiedForNameString), ConfigurationType::new);
}

@Override
Expand Down
Loading

0 comments on commit 1b212eb

Please sign in to comment.