This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-fury.git
The following commit(s) were added to refs/heads/main by this push:
new 18a8c7a7 feat(java): support jdk proxy serialization for graalvm
(#1379)
18a8c7a7 is described below
commit 18a8c7a783339ff73d08572cb0728a382eed1ec4
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Feb 20 14:59:44 2024 +0800
feat(java): support jdk proxy serialization for graalvm (#1379)
support jdk proxy serialization for graalvm, see more in #1364
Closes #1378
---
integration_tests/graalvm_tests/pom.xml | 2 +
.../main/java/org/apache/fury/graalvm/Main.java | 1 +
.../java/org/apache/fury/graalvm/ProxyExample.java | 56 +++++++++++++++++
.../graalvm_tests/native-image.properties | 1 +
.../META-INF/native-image/proxy-config.json | 7 +++
.../org/apache/fury/resolver/ClassResolver.java | 5 +-
.../apache/fury/serializer/ArraySerializers.java | 1 +
.../apache/fury/serializer/JdkProxySerializer.java | 17 ++---
.../org/apache/fury/serializer/Serializers.java | 14 -----
.../java/org/apache/fury/util/ReflectionUtils.java | 72 ++++++++++++----------
10 files changed, 114 insertions(+), 62 deletions(-)
diff --git a/integration_tests/graalvm_tests/pom.xml
b/integration_tests/graalvm_tests/pom.xml
index c9dbc524..92f7dc2a 100644
--- a/integration_tests/graalvm_tests/pom.xml
+++ b/integration_tests/graalvm_tests/pom.xml
@@ -172,6 +172,8 @@
<buildArgs>
<!-- for fast build -->
<!-- <buildArg>-Ob</buildArg> -->
+ <buildArg>-H:+UnlockExperimentalVMOptions</buildArg>
+
<buildArg>-H:DynamicProxyConfigurationFiles=src/main/resources/META-INF/native-image/proxy-config.json</buildArg>
</buildArgs>
</configuration>
</plugin>
diff --git
a/integration_tests/graalvm_tests/src/main/java/org/apache/fury/graalvm/Main.java
b/integration_tests/graalvm_tests/src/main/java/org/apache/fury/graalvm/Main.java
index 5325f283..d5b403ae 100644
---
a/integration_tests/graalvm_tests/src/main/java/org/apache/fury/graalvm/Main.java
+++
b/integration_tests/graalvm_tests/src/main/java/org/apache/fury/graalvm/Main.java
@@ -28,6 +28,7 @@ public class Main {
RecordExample.main(args);
RecordExample2.main(args);
ThreadSafeExample.main(args);
+ ProxyExample.main(args);
Benchmark.main(args);
}
}
diff --git
a/integration_tests/graalvm_tests/src/main/java/org/apache/fury/graalvm/ProxyExample.java
b/integration_tests/graalvm_tests/src/main/java/org/apache/fury/graalvm/ProxyExample.java
new file mode 100644
index 00000000..43afc8bc
--- /dev/null
+++
b/integration_tests/graalvm_tests/src/main/java/org/apache/fury/graalvm/ProxyExample.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fury.graalvm;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.function.Function;
+import org.apache.fury.Fury;
+import org.apache.fury.util.Preconditions;
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class ProxyExample {
+ private static class TestInvocationHandler implements InvocationHandler,
Serializable {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
+ return 1;
+ }
+ }
+
+ static Fury fury;
+
+ static {
+ fury = Fury.builder().requireClassRegistration(true).build();
+ // register and generate serializer code.
+ fury.register(TestInvocationHandler.class, true);
+ }
+
+ public static void main(String[] args) {
+ Function function =
+ (Function)
+ Proxy.newProxyInstance(
+ fury.getClassLoader(), new Class[] {Function.class}, new
TestInvocationHandler());
+ Function deserializedFunction = (Function)
fury.deserialize(fury.serialize(function));
+ Preconditions.checkArgument(deserializedFunction.apply(null).equals(1));
+ System.out.println("Proxy tests pass");
+ }
+}
diff --git
a/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fury/graalvm_tests/native-image.properties
b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fury/graalvm_tests/native-image.properties
index 631ca2e6..c4f4dc89 100644
---
a/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fury/graalvm_tests/native-image.properties
+++
b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fury/graalvm_tests/native-image.properties
@@ -22,4 +22,5 @@ Args = -H:+ReportExceptionStackTraces \
org.apache.fury.graalvm.record.RecordExample,\
org.apache.fury.graalvm.record.RecordExample2,\
org.apache.fury.graalvm.ThreadSafeExample,\
+ org.apache.fury.graalvm.ProxyExample,\
org.apache.fury.graalvm.Benchmark
diff --git
a/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/proxy-config.json
b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/proxy-config.json
new file mode 100644
index 00000000..305ed56f
--- /dev/null
+++
b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/proxy-config.json
@@ -0,0 +1,7 @@
+[
+ {
+ "interfaces": [
+ "java.util.function.Function"
+ ]
+ }
+]
diff --git
a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java
b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java
index 1d376dfd..a65291f3 100644
--- a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java
+++ b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java
@@ -307,8 +307,8 @@ public class ClassResolver {
// primitive types will be boxed.
addDefaultSerializer(String.class, new StringSerializer(fury));
PrimitiveSerializers.registerDefaultSerializers(fury);
- ArraySerializers.registerDefaultSerializers(fury);
Serializers.registerDefaultSerializers(fury);
+ ArraySerializers.registerDefaultSerializers(fury);
TimeSerializers.registerDefaultSerializers(fury);
OptionalSerializers.registerDefaultSerializers(fury);
CollectionSerializers.registerDefaultSerializers(fury);
@@ -1820,6 +1820,9 @@ public class ClassResolver {
}
}
if (GraalvmSupport.isGraalRuntime()) {
+ if (Functions.isLambda(cls) || ReflectionUtils.isJdkProxy(cls)) {
+ return null;
+ }
throw new RuntimeException(String.format("Class %s is not registered",
cls));
}
return null;
diff --git
a/java/fury-core/src/main/java/org/apache/fury/serializer/ArraySerializers.java
b/java/fury-core/src/main/java/org/apache/fury/serializer/ArraySerializers.java
index 27f33fff..5696fec5 100644
---
a/java/fury-core/src/main/java/org/apache/fury/serializer/ArraySerializers.java
+++
b/java/fury-core/src/main/java/org/apache/fury/serializer/ArraySerializers.java
@@ -661,6 +661,7 @@ public class ArraySerializers {
public static void registerDefaultSerializers(Fury fury) {
fury.registerSerializer(Object[].class, new ObjectArraySerializer<>(fury,
Object[].class));
+ fury.registerSerializer(Class[].class, new ObjectArraySerializer<>(fury,
Class[].class));
fury.registerSerializer(byte[].class, new ByteArraySerializer(fury));
fury.registerSerializer(char[].class, new CharArraySerializer(fury));
fury.registerSerializer(short[].class, new ShortArraySerializer(fury));
diff --git
a/java/fury-core/src/main/java/org/apache/fury/serializer/JdkProxySerializer.java
b/java/fury-core/src/main/java/org/apache/fury/serializer/JdkProxySerializer.java
index 914d11b5..5befb551 100644
---
a/java/fury-core/src/main/java/org/apache/fury/serializer/JdkProxySerializer.java
+++
b/java/fury-core/src/main/java/org/apache/fury/serializer/JdkProxySerializer.java
@@ -19,12 +19,12 @@
package org.apache.fury.serializer;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import org.apache.fury.Fury;
import org.apache.fury.memory.MemoryBuffer;
import org.apache.fury.resolver.RefResolver;
-import org.apache.fury.util.GraalvmSupport;
import org.apache.fury.util.Platform;
import org.apache.fury.util.Preconditions;
import org.apache.fury.util.ReflectionUtils;
@@ -34,21 +34,12 @@ import org.apache.fury.util.ReflectionUtils;
public class JdkProxySerializer extends Serializer {
// Make offset compatible with graalvm native image.
+ private static final Field FIELD;
private static final long PROXY_HANDLER_FIELD_OFFSET;
static {
- if (GraalvmSupport.isGraalBuildtime()) {
- try {
- // Make offset compatible with graalvm native image.
- PROXY_HANDLER_FIELD_OFFSET =
Platform.objectFieldOffset(Proxy.class.getDeclaredField("h"));
- } catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
- }
- } else {
- // not all JVM implementations use 'h' as internal InvocationHandler name
- PROXY_HANDLER_FIELD_OFFSET =
- ReflectionUtils.getFieldOffset(Proxy.class, InvocationHandler.class);
- }
+ FIELD = ReflectionUtils.getField(Proxy.class, InvocationHandler.class);
+ PROXY_HANDLER_FIELD_OFFSET = Platform.objectFieldOffset(FIELD);
}
private static final InvocationHandler STUB_HANDLER =
diff --git
a/java/fury-core/src/main/java/org/apache/fury/serializer/Serializers.java
b/java/fury-core/src/main/java/org/apache/fury/serializer/Serializers.java
index 9a0020fe..19829c97 100644
--- a/java/fury-core/src/main/java/org/apache/fury/serializer/Serializers.java
+++ b/java/fury-core/src/main/java/org/apache/fury/serializer/Serializers.java
@@ -33,7 +33,6 @@ import java.math.BigInteger;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Currency;
-import java.util.IdentityHashMap;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -47,7 +46,6 @@ import org.apache.fury.collection.Tuple2;
import org.apache.fury.memory.MemoryBuffer;
import org.apache.fury.resolver.ClassResolver;
import org.apache.fury.type.Type;
-import org.apache.fury.type.TypeUtils;
import org.apache.fury.util.GraalvmSupport;
import org.apache.fury.util.Platform;
import org.apache.fury.util.Preconditions;
@@ -532,20 +530,8 @@ public class Serializers {
}
public static final class ClassSerializer extends Serializer<Class> {
- private static final byte USE_CLASS_ID = 0;
- private static final byte USE_CLASSNAME = 1;
- private static final byte PRIMITIVE_FLAG = 2;
- private final IdentityHashMap<Class<?>, Byte> primitivesMap = new
IdentityHashMap<>();
- private final Class<?>[] id2PrimitiveClasses = new Class[9];
-
public ClassSerializer(Fury fury) {
super(fury, Class.class);
- byte count = 0;
- for (Class<?> primitiveType : TypeUtils.getSortedPrimitiveClasses()) {
- primitivesMap.put(primitiveType, count);
- id2PrimitiveClasses[count] = primitiveType;
- count++;
- }
}
@Override
diff --git
a/java/fury-core/src/main/java/org/apache/fury/util/ReflectionUtils.java
b/java/fury-core/src/main/java/org/apache/fury/util/ReflectionUtils.java
index f45fef4b..8144a0ba 100644
--- a/java/fury-core/src/main/java/org/apache/fury/util/ReflectionUtils.java
+++ b/java/fury-core/src/main/java/org/apache/fury/util/ReflectionUtils.java
@@ -281,6 +281,44 @@ public class ReflectionUtils {
return field;
}
+ /**
+ * Gets the only field in the class matching the required type.
+ *
+ * @param clazz the class in which the field should be declared
+ * @param fieldType the required type of the field
+ * @return the field with specified type
+ * @throws IllegalStateException if there are multiple fields of the
required type
+ * @throws IllegalArgumentException if there are no fields of the required
type
+ */
+ public static Field getField(Class<?> clazz, Class<?> fieldType) {
+ Field f = null;
+ Class<?> cls = clazz;
+ while (cls != null) {
+ for (Field fi : cls.getDeclaredFields()) {
+ if (fieldType.equals(fi.getType())) {
+ if (f != null) {
+ throw new IllegalStateException(
+ "Found multiple field s matching "
+ + fieldType
+ + " in "
+ + cls
+ + ": "
+ + f
+ + " and "
+ + fi);
+ }
+ f = fi;
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ if (f == null) {
+ throw new IllegalArgumentException(
+ "Found no field matching " + fieldType.getName() + " in " + clazz +
"!");
+ }
+ return f;
+ }
+
public static Field getFieldNullable(Class<?> cls, String fieldName) {
Class<?> clazz = cls;
do {
@@ -348,40 +386,6 @@ public class ReflectionUtils {
return getFieldOffset(field);
}
- /**
- * Gets the offset of the only field in the class matching the required type.
- *
- * @param clazz the class in which the field should be declared
- * @param fieldType the required type of the field
- * @return offset of the field
- * @throws IllegalStateException if there are multiple fields of the
required type
- * @throws IllegalArgumentException if there are no fields of the required
type
- */
- public static long getFieldOffset(Class<?> clazz, Class<?> fieldType) {
- Field f = null;
- for (Field fi : clazz.getDeclaredFields()) {
- if (fieldType.equals(fi.getType())) {
- if (f != null) {
- throw new IllegalStateException(
- "Found multiple field s matching "
- + fieldType
- + " in "
- + clazz
- + ": "
- + f
- + " and "
- + fi);
- }
- f = fi;
- }
- }
- if (f == null) {
- throw new IllegalArgumentException(
- "Found no field matching " + fieldType.getName() + " in " + clazz +
"!");
- }
- return Platform.objectFieldOffset(f);
- }
-
public static long getFieldOffsetChecked(Class<?> cls, String fieldName) {
long offset = getFieldOffset(cls, fieldName);
Preconditions.checkArgument(offset != -1);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]