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]

Reply via email to