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/fory.git
The following commit(s) were added to refs/heads/main by this push:
new bb430b03e fix(ci): Exit with subprocess return code in run_ci.py
(#2560)
bb430b03e is described below
commit bb430b03e58dbd339a49e78d4bd3bd4d49e11f5c
Author: Emre Şafak <[email protected]>
AuthorDate: Tue Sep 2 10:58:53 2025 -0400
fix(ci): Exit with subprocess return code in run_ci.py (#2560)
## Why?
CI was not reflecting failures in shell script fallbacks (we are
migrating to python)
## What does this PR do?
* Use sys.exit() to propagate the return code from subprocess.call().
* This ensures that the CI pipeline correctly reflects the success or
failure of the executed scripts.
## Related issues
#2555
## Notes
You might want to merge #2561 first, to reduce the number of errors.
---------
Co-authored-by: chaokunyang <[email protected]>
---
.github/pull_request_template.md | 1 +
.github/workflows/ci.yml | 17 ++++--
ci/run_ci.py | 7 +--
ci/run_ci.sh | 4 +-
ci/tasks/java.py | 61 +++++++++++--------
integration_tests/graalvm_tests/pom.xml | 1 -
.../apache/fory/graalvm/ObjectStreamExample.java | 40 ++++++------
.../java/org/apache/fory/graalvm/ProxyExample.java | 1 +
.../graalvm_tests/proxy-config.json | 10 +++
.../META-INF/native-image/proxy-config.json | 7 ---
integration_tests/jdk_compatibility_tests/pom.xml | 15 +++++
.../java/org/apache/fory/builder/CodecBuilder.java | 4 ++
.../java/org/apache/fory/codegen/Expression.java | 3 +-
.../org/apache/fory/resolver/AllowListChecker.java | 2 +-
.../org/apache/fory/resolver/ClassResolver.java | 54 ++++++++++++----
.../apache/fory/serializer/JdkProxySerializer.java | 22 +++++--
.../apache/fory/serializer/LambdaSerializer.java | 71 ++++++++++++++++------
.../fory/serializer/ObjectStreamSerializer.java | 4 +-
.../org/apache/fory/serializer/Serializers.java | 27 ++++++++
.../main/java/org/apache/fory/type/TypeUtils.java | 4 +-
.../java/org/apache/fory/util/GraalvmSupport.java | 17 ++++++
.../fory-core/native-image.properties | 9 +++
.../fory-core/reflection-config.json | 9 +++
.../fory-core/serialization-config.json | 11 ++++
.../fory/format/encoder/RowEncoderBuilder.java | 3 -
.../format/encoder/ImplementInterfaceTest.java | 1 -
python/pyfory/_fory.py | 39 ++++--------
python/pyfory/_registry.py | 11 +++-
python/pyfory/tests/test_function.py | 8 +--
python/pyfory/tests/test_serializer.py | 16 ++++-
python/setup.py | 6 +-
31 files changed, 337 insertions(+), 148 deletions(-)
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 2b4a80c57..10c620545 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -9,6 +9,7 @@ Contribution Checklist
- Fory has a strong focus on performance. If the PR you submit will have
an impact on performance, please benchmark it first and provide the benchmark
result here.
-->
+
## Why?
<!-- Describe the purpose of this PR. -->
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b6f47cf79..eeb6ea48a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -62,6 +62,8 @@ jobs:
run: python ./ci/run_ci.py cpp --install-deps-only
- name: Install python dependencies
run: pip install pyarrow==15.0.0 Cython wheel pytest setuptools -U
+ - name: Install pyfory for xlang tests
+ run: pip install -e python/
- name: Run CI with Maven
run: python ./ci/run_ci.py java --version ${{ matrix.java-version }}
- name: Upload Test Report
@@ -93,6 +95,8 @@ jobs:
python-version: 3.8
- name: Install bazel
run: python ./ci/run_ci.py cpp --install-deps-only
+ - name: Install pyfory for xlang tests
+ run: pip install -e python/
- name: Install python dependencies
run: pip install pyarrow==15.0.0 Cython wheel pytest setuptools -U
- name: Run CI with Maven
@@ -190,10 +194,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- - name: Set up JDK8
+ - name: Set up JDK11
uses: actions/setup-java@v4
with:
- java-version: 8
+ java-version: 11
distribution: "temurin"
- name: Set up Python 3.8
uses: actions/setup-python@v5
@@ -274,9 +278,14 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- - name: Install bazel
+ - name: Install bazel (Unix)
+ if: runner.os != 'Windows'
shell: bash
run: python ./ci/run_ci.py cpp --install-deps-only
+ - name: Install bazel (Windows)
+ if: runner.os == 'Windows'
+ shell: bash
+ run: ./ci/run_ci.sh install_bazel_windows
- name: Run Python CI
shell: bash
run: python ./ci/run_ci.py python
@@ -303,7 +312,7 @@ jobs:
run: python ./ci/run_ci.py cpp --install-deps-only
- name: Install python dependencies
run: pip install pyarrow==15.0.0 cython wheel pytest setuptools -U
- - name: Install pyfory
+ - name: Install pyfory for xlang tests
run: pip install -e python/
- name: Run Golang CI
run: python ./ci/run_ci.py go
diff --git a/ci/run_ci.py b/ci/run_ci.py
index e637cc94f..ed1a3f59b 100644
--- a/ci/run_ci.py
+++ b/ci/run_ci.py
@@ -84,7 +84,7 @@ def run_shell_script(command, *args):
cmd = [bash_path, script_path, command]
cmd.extend(args)
logging.info(f"Falling back to shell script with bash: {'
'.join(cmd)}")
- return subprocess.call(cmd)
+ sys.exit(subprocess.call(cmd))
else:
logging.error(
"Bash is not available on this Windows system. Cannot run
shell script."
@@ -101,7 +101,7 @@ def run_shell_script(command, *args):
cmd = [script_path, command]
cmd.extend(args)
logging.info(f"Falling back to shell script: {' '.join(cmd)}")
- return subprocess.call(cmd)
+ sys.exit(subprocess.call(cmd))
def parse_args():
@@ -237,14 +237,13 @@ def parse_args():
if USE_PYTHON_JAVA:
func(**arg_dict)
else:
-
if not arg_dict.get("version"):
func(**arg_dict)
return
# Map Python version argument to shell script command
version = arg_dict.get("version", "17")
release = arg_dict.get("release", False)
-
+
if release:
logging.info("Release mode requested - using Python
implementation")
func(**arg_dict)
diff --git a/ci/run_ci.sh b/ci/run_ci.sh
index 63a1330d1..789f78cda 100755
--- a/ci/run_ci.sh
+++ b/ci/run_ci.sh
@@ -141,7 +141,7 @@ install_jdks() {
graalvm_test() {
cd "$ROOT"/java
- mvn -T10 -B --no-transfer-progress clean install -DskipTests
+ mvn -T10 -B --no-transfer-progress clean install -DskipTests -pl
'!:fory-format,!:fory-testsuite'
echo "Start to build graalvm native image"
cd "$ROOT"/integration_tests/graalvm_tests
mvn -DskipTests=true --no-transfer-progress -Pnative package
@@ -231,7 +231,7 @@ case $1 in
echo "Executing fory java tests"
cd "$ROOT/java"
set +e
- mvn -T16 --batch-mode --no-transfer-progress test
+ mvn -T16 --batch-mode --no-transfer-progress test -pl
'!:fory-format,!:fory-testsuite'
testcode=$?
if [[ $testcode -ne 0 ]]; then
exit $testcode
diff --git a/ci/tasks/java.py b/ci/tasks/java.py
index 7bb73e50a..6fa0754ed 100644
--- a/ci/tasks/java.py
+++ b/ci/tasks/java.py
@@ -17,7 +17,6 @@
import logging
import os
-import sys
import subprocess
import re
from . import common
@@ -26,7 +25,7 @@ from . import common
def get_jdk_major_version():
try:
# Run the 'java -version' command
- result = subprocess.run(['java', '-version'], capture_output=True,
text=True)
+ result = subprocess.run(["java", "-version"], capture_output=True,
text=True)
output = result.stderr # java -version outputs to stderr
# Use regex to find the version string
@@ -37,8 +36,8 @@ def get_jdk_major_version():
version_string = match.group(1)
# Parse the version string
- version_parts = version_string.split('.')
- if version_parts[0] == '1':
+ version_parts = version_string.split(".")
+ if version_parts[0] == "1":
# Java 8 or earlier
return int(version_parts[1])
else:
@@ -81,54 +80,56 @@ def create_toolchains_xml(jdk_mappings):
import os
import xml.etree.ElementTree as ET
from xml.dom import minidom
-
+
# Create ~/.m2 directory if it doesn't exist
m2_dir = os.path.expanduser("~/.m2")
os.makedirs(m2_dir, exist_ok=True)
-
+
# Create the root element
toolchains = ET.Element("toolchains")
-
+
for version, jdk_name in jdk_mappings.items():
toolchain = ET.SubElement(toolchains, "toolchain")
-
+
# Set type
type_elem = ET.SubElement(toolchain, "type")
type_elem.text = "jdk"
-
+
# Set provides
provides = ET.SubElement(toolchain, "provides")
version_elem = ET.SubElement(provides, "version")
version_elem.text = version
vendor_elem = ET.SubElement(provides, "vendor")
vendor_elem.text = "azul"
-
+
# Set configuration
configuration = ET.SubElement(toolchain, "configuration")
jdk_home = ET.SubElement(configuration, "jdkHome")
jdk_home.text = os.path.abspath(os.path.join(common.PROJECT_ROOT_DIR,
jdk_name))
-
+
# Create pretty XML string
- rough_string = ET.tostring(toolchains, 'unicode')
+ rough_string = ET.tostring(toolchains, "unicode")
reparsed = minidom.parseString(rough_string)
pretty_xml = reparsed.toprettyxml(indent=" ")
-
+
# Add proper XML header with encoding
xml_header = '<?xml version="1.0" encoding="UTF8"?>\n'
- pretty_xml = xml_header + pretty_xml.split('\n', 1)[1] # Remove the
default header and add our custom one
-
+ pretty_xml = (
+ xml_header + pretty_xml.split("\n", 1)[1]
+ ) # Remove the default header and add our custom one
+
# Write to ~/.m2/toolchains.xml
toolchains_path = os.path.join(m2_dir, "toolchains.xml")
- with open(toolchains_path, 'w', encoding='utf-8') as f:
+ with open(toolchains_path, "w", encoding="utf-8") as f:
f.write(pretty_xml)
-
+
logging.info(f"Created toolchains.xml at {toolchains_path}")
logging.info("Toolchains configuration:")
for version, jdk_name in jdk_mappings.items():
jdk_path = os.path.join(common.PROJECT_ROOT_DIR, jdk_name)
logging.info(f" JDK {version}: {jdk_path}")
# print toolchains.xml
- with open(toolchains_path, 'r', encoding='utf-8') as f:
+ with open(toolchains_path, "r", encoding="utf-8") as f:
logging.info(f.read())
@@ -149,7 +150,9 @@ def run_java8():
logging.info("Executing fory java tests with Java 8")
install_jdks()
common.cd_project_subdir("java")
- common.exec_cmd("mvn -T16 --batch-mode --no-transfer-progress test -pl
'!fory-format'")
+ common.exec_cmd(
+ "mvn -T16 --batch-mode --no-transfer-progress test -pl
'!:fory-format,!:fory-testsuite'"
+ )
logging.info("Executing fory java tests succeeds")
@@ -208,7 +211,9 @@ def run_integration_tests():
logging.info("Executing fory integration tests")
common.cd_project_subdir("java")
- common.exec_cmd("mvn -T10 -B --no-transfer-progress clean install
-DskipTests")
+ common.exec_cmd(
+ "mvn -T10 -B --no-transfer-progress clean install -DskipTests -pl
'!:fory-format,!:fory-testsuite'"
+ )
logging.info("benchmark tests")
common.cd_project_subdir("java/benchmark")
@@ -270,7 +275,9 @@ def run_graalvm_test():
logging.info("Start GraalVM tests")
common.cd_project_subdir("java")
- common.exec_cmd("mvn -T10 -B --no-transfer-progress clean install
-DskipTests")
+ common.exec_cmd(
+ "mvn -T10 -B --no-transfer-progress clean install -DskipTests -pl
'!:fory-format,!:fory-testsuite'"
+ )
logging.info("Start to build graalvm native image")
common.cd_project_subdir("integration_tests/graalvm_tests")
@@ -285,17 +292,19 @@ def run_graalvm_test():
def run_release():
"""Release to Maven Central."""
- logging.info(f"Starting release to Maven Central with Java")
+ logging.info("Starting release to Maven Central with Java")
common.cd_project_subdir("java")
-
+
# Clean and install without tests first
logging.info("Cleaning and installing dependencies")
common.exec_cmd("mvn -T10 -B --no-transfer-progress clean install
-DskipTests")
-
+
# Deploy to Maven Central
logging.info("Deploying to Maven Central")
- common.exec_cmd("mvn -T10 -B --no-transfer-progress clean deploy
-Dgpg.skip -DskipTests -Papache-release")
-
+ common.exec_cmd(
+ "mvn -T10 -B --no-transfer-progress clean deploy -Dgpg.skip
-DskipTests -Papache-release"
+ )
+
logging.info("Release to Maven Central completed successfully")
diff --git a/integration_tests/graalvm_tests/pom.xml
b/integration_tests/graalvm_tests/pom.xml
index 84da032e4..94a0d83d3 100644
--- a/integration_tests/graalvm_tests/pom.xml
+++ b/integration_tests/graalvm_tests/pom.xml
@@ -173,7 +173,6 @@
<!-- 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/fory/graalvm/ObjectStreamExample.java
b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java
index a73c90bdf..227ce126d 100644
---
a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java
+++
b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java
@@ -17,42 +17,42 @@
* under the License.
*/
- package org.apache.fory.graalvm;
-
-import org.apache.fory.Fory;
+package org.apache.fory.graalvm;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
+import org.apache.fory.Fory;
public class ObjectStreamExample extends AbstractMap<Integer, Integer> {
- private static final Fory FORY = Fory.builder()
- .withName(ObjectStreamExample.class.getName())
- .registerGuavaTypes(false)
- .build();
+ private static final Fory FORY =
+ Fory.builder()
+ .withName(ObjectStreamExample.class.getName())
+ .registerGuavaTypes(false)
+ .build();
static {
- FORY.register(ObjectStreamExample.class, true);
- FORY.ensureSerializersCompiled();
+ FORY.register(ObjectStreamExample.class, true);
+ FORY.ensureSerializersCompiled();
}
final int[] ints = new int[10];
public static void main(String[] args) {
- FORY.reset();
- byte[] bytes = FORY.serialize(new ObjectStreamExample());
- FORY.reset();
- ObjectStreamExample o = (ObjectStreamExample) FORY.deserialize(bytes);
- System.out.println(Arrays.toString(o.ints));
+ FORY.reset();
+ byte[] bytes = FORY.serialize(new ObjectStreamExample());
+ FORY.reset();
+ ObjectStreamExample o = (ObjectStreamExample) FORY.deserialize(bytes);
+ System.out.println(Arrays.toString(o.ints));
}
@Override
public Set<Entry<Integer, Integer>> entrySet() {
- HashSet<Entry<Integer, Integer>> set = new HashSet<>();
- for (int i = 0; i < ints.length; i++) {
- set.add(new AbstractMap.SimpleEntry<>(i, ints[i]));
- }
- return set;
+ HashSet<Entry<Integer, Integer>> set = new HashSet<>();
+ for (int i = 0; i < ints.length; i++) {
+ set.add(new AbstractMap.SimpleEntry<>(i, ints[i]));
+ }
+ return set;
}
-}
\ No newline at end of file
+}
diff --git
a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java
b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java
index 990faaa2b..2fd195675 100644
---
a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java
+++
b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java
@@ -50,6 +50,7 @@ public class ProxyExample {
.build();
// register and generate serializer code.
fory.register(TestInvocationHandler.class, true);
+ fory.ensureSerializersCompiled();
return fory;
}
diff --git
a/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/proxy-config.json
b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/proxy-config.json
new file mode 100644
index 000000000..549b812dd
--- /dev/null
+++
b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/proxy-config.json
@@ -0,0 +1,10 @@
+[
+ {
+ "interfaces": [
+ "java.util.function.Function"
+ ],
+ "condition": {
+ "typeReachable":
"org.apache.fory.graalvm.ProxyExample$TestInvocationHandler"
+ }
+ }
+]
\ No newline at end of file
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
deleted file mode 100644
index 305ed56fe..000000000
---
a/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/proxy-config.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[
- {
- "interfaces": [
- "java.util.function.Function"
- ]
- }
-]
diff --git a/integration_tests/jdk_compatibility_tests/pom.xml
b/integration_tests/jdk_compatibility_tests/pom.xml
index a2eb0f6b8..2464b080e 100644
--- a/integration_tests/jdk_compatibility_tests/pom.xml
+++ b/integration_tests/jdk_compatibility_tests/pom.xml
@@ -38,11 +38,26 @@
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.apache.fory</groupId>
+ <artifactId>fory-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.fory</groupId>
+ <artifactId>fory-format</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.fory</groupId>
<artifactId>benchmark</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java
index cf37d8ff7..eefb7212b 100644
--- a/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java
@@ -257,6 +257,10 @@ public abstract class CodecBuilder {
if (ref == null) {
Class<?> funcInterface = methodInfo.f0;
TypeRef<?> getterType = TypeRef.of(funcInterface);
+ if (GraalvmSupport.isGraalBuildtime()) {
+ // generate getter ahead at native image build time.
+ Functions.makeGetterFunction(beanClass, fieldName);
+ }
Expression getter =
new StaticInvoke(
Functions.class,
diff --git
a/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java
b/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java
index f5135bcb6..a6a751e8c 100644
--- a/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java
+++ b/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java
@@ -2432,7 +2432,8 @@ public interface Expression {
action.apply(
new Reference(i),
new Reference(leftElemValue, leftElemType, true),
- // elemValue nullability check uses isNullAt inside action, so
elemValueRef's nullable is false.
+ // elemValue nullability check uses isNullAt inside action, so
elemValueRef's nullable
+ // is false.
new Reference(rightElemValue, rightElemType, false));
ExprCode elementExprCode = elemExpr.genCode(ctx);
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java
index b49ded2e4..9b280cce5 100644
---
a/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java
+++
b/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java
@@ -239,7 +239,7 @@ public class AllowListChecker implements ClassChecker {
lock.writeLock().lock();
listeners.put(classResolver, true);
} finally {
- lock.writeLock().unlock();
+ lock.writeLock().unlock();
}
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
index 54895c592..3f2f339cd 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
@@ -37,6 +37,7 @@ import com.google.common.collect.HashBiMap;
import java.io.Externalizable;
import java.io.IOException;
import java.io.Serializable;
+import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Type;
@@ -87,7 +88,8 @@ import org.apache.fory.ForyCopyable;
import org.apache.fory.annotation.CodegenInvoke;
import org.apache.fory.annotation.Internal;
import org.apache.fory.builder.CodecUtils;
-import org.apache.fory.builder.Generated;
+import org.apache.fory.builder.Generated.GeneratedMetaSharedSerializer;
+import org.apache.fory.builder.Generated.GeneratedObjectSerializer;
import org.apache.fory.builder.JITContext;
import org.apache.fory.codegen.CodeGenerator;
import org.apache.fory.codegen.Expression;
@@ -163,6 +165,7 @@ import org.apache.fory.type.ScalaTypes;
import org.apache.fory.type.TypeUtils;
import org.apache.fory.type.Types;
import org.apache.fory.util.GraalvmSupport;
+import org.apache.fory.util.GraalvmSupport.GraalvmSerializerHolder;
import org.apache.fory.util.Preconditions;
import org.apache.fory.util.StringUtils;
import org.apache.fory.util.function.Functions;
@@ -420,6 +423,7 @@ public class ClassResolver implements TypeResolver {
register(AtomicReference.class);
register(EnumSet.allOf(Language.class).getClass());
register(EnumSet.of(Language.JAVA).getClass());
+ register(SerializedLambda.class);
register(Throwable.class, StackTraceElement.class, Exception.class,
RuntimeException.class);
register(NullPointerException.class);
register(IOException.class);
@@ -968,14 +972,14 @@ public class ClassResolver implements TypeResolver {
return CollectionSerializers.EnumSetSerializer.class;
} else if (Charset.class.isAssignableFrom(cls)) {
return Serializers.CharsetSerializer.class;
- } else if (Functions.isLambda(cls)) {
- return LambdaSerializer.class;
} else if (ReflectionUtils.isJdkProxy(cls)) {
if (JavaSerializer.getWriteReplaceMethod(cls) != null) {
return ReplaceResolveSerializer.class;
} else {
return JdkProxySerializer.class;
}
+ } else if (Functions.isLambda(cls)) {
+ return LambdaSerializer.class;
} else if (Calendar.class.isAssignableFrom(cls)) {
return TimeSerializers.CalendarSerializer.class;
} else if (ZoneId.class.isAssignableFrom(cls)) {
@@ -1423,7 +1427,7 @@ public class ClassResolver implements TypeResolver {
if (deserializationClassInfo != null &&
GraalvmSupport.isGraalBuildtime()) {
getGraalvmClassRegistry()
.deserializerClassMap
- .put(classDef.getId(),
deserializationClassInfo.serializer.getClass());
+ .put(classDef.getId(),
getGraalvmSerializerClass(deserializationClassInfo.serializer));
Tuple2<ClassDef, ClassInfo> classDefTuple =
extRegistry.classIdToDef.get(classDef.getId());
// empty serializer for graalvm build time
classDefTuple.f1.serializer = null;
@@ -1432,7 +1436,9 @@ public class ClassResolver implements TypeResolver {
}
if (GraalvmSupport.isGraalBuildtime()) {
// Instance for generated class should be hold at graalvm runtime only.
- getGraalvmClassRegistry().serializerClassMap.put(cls,
classInfo.serializer.getClass());
+ getGraalvmClassRegistry()
+ .serializerClassMap
+ .put(cls, getGraalvmSerializerClass(classInfo.serializer));
classInfo.serializer = null;
}
}
@@ -1560,13 +1566,21 @@ public class ClassResolver implements TypeResolver {
}
boolean needToWriteClassDef(Serializer serializer) {
- return fory.getConfig().getCompatibleMode() == CompatibleMode.COMPATIBLE
- && (serializer instanceof Generated.GeneratedObjectSerializer
- // May already switched to MetaSharedSerializer when update class
info cache.
- || serializer instanceof Generated.GeneratedMetaSharedSerializer
- || serializer instanceof LazyInitBeanSerializer
- || serializer instanceof ObjectSerializer
- || serializer instanceof MetaSharedSerializer);
+ if (fory.getConfig().getCompatibleMode() != CompatibleMode.COMPATIBLE) {
+ return false;
+ }
+ if (GraalvmSupport.isGraalBuildtime() && serializer instanceof
GraalvmSerializerHolder) {
+ Class<? extends Serializer> serializerClass =
+ ((GraalvmSerializerHolder) serializer).getSerializerClass();
+ return GeneratedObjectSerializer.class.isAssignableFrom(serializerClass)
+ ||
GeneratedMetaSharedSerializer.class.isAssignableFrom(serializerClass);
+ }
+ return (serializer instanceof GeneratedObjectSerializer
+ // May already switched to MetaSharedSerializer when update class info
cache.
+ || serializer instanceof GeneratedMetaSharedSerializer
+ || serializer instanceof LazyInitBeanSerializer
+ || serializer instanceof ObjectSerializer
+ || serializer instanceof MetaSharedSerializer);
}
private ClassInfo readClassInfoWithMetaShare(MemoryBuffer buffer,
MetaContext metaContext) {
@@ -2212,6 +2226,10 @@ public class ClassResolver implements TypeResolver {
*/
public void ensureSerializersCompiled() {
try {
+ fory.getJITContext().lock();
+ Serializers.newSerializer(fory, LambdaSerializer.STUB_LAMBDA_CLASS,
LambdaSerializer.class);
+ Serializers.newSerializer(
+ fory, JdkProxySerializer.SUBT_PROXY.getClass(),
JdkProxySerializer.class);
classInfoMap.forEach(
(cls, classInfo) -> {
if (classInfo.serializer == null) {
@@ -2262,6 +2280,13 @@ public class ClassResolver implements TypeResolver {
fory.getConfig().getConfigHash(), k -> new GraalvmClassRegistry());
}
+ private Class<? extends Serializer> getGraalvmSerializerClass(Serializer
serializer) {
+ if (serializer instanceof GraalvmSerializerHolder) {
+ return ((GraalvmSerializerHolder) serializer).getSerializerClass();
+ }
+ return serializer.getClass();
+ }
+
private Class<? extends Serializer>
getSerializerClassFromGraalvmRegistry(Class<?> cls) {
GraalvmClassRegistry registry = getGraalvmClassRegistry();
List<ClassResolver> classResolvers = registry.resolvers;
@@ -2307,7 +2332,10 @@ public class ClassResolver implements TypeResolver {
if (Functions.isLambda(cls) || ReflectionUtils.isJdkProxy(cls)) {
return null;
}
- throw new RuntimeException(String.format("Class %s is not registered",
cls));
+ throw new RuntimeException(
+ String.format(
+ "Class %s is not registered, registered classes: %s",
+ cls, registry.deserializerClassMap));
}
return null;
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java
index ac1dd80c5..9c2bc7418 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java
@@ -21,6 +21,7 @@ package org.apache.fory.serializer;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.fory.Fory;
import org.apache.fory.memory.MemoryBuffer;
@@ -32,7 +33,6 @@ import org.apache.fory.util.Preconditions;
/** Serializer for jdk {@link Proxy}. */
@SuppressWarnings({"rawtypes", "unchecked"})
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;
@@ -42,10 +42,22 @@ public class JdkProxySerializer extends Serializer {
PROXY_HANDLER_FIELD_OFFSET = Platform.objectFieldOffset(FIELD);
}
- private static final InvocationHandler STUB_HANDLER =
- (proxy, method, args) -> {
- throw new IllegalStateException("Deserialization stub handler still
active");
- };
+ private static class StubInvocationHandler implements InvocationHandler {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
+ throw new IllegalStateException("Deserialization stub handler still
active");
+ }
+ }
+
+ private static final InvocationHandler STUB_HANDLER = new
StubInvocationHandler();
+
+ private interface StubInterface {
+ int apply();
+ }
+
+ public static Object SUBT_PROXY =
+ Proxy.newProxyInstance(
+ Serializer.class.getClassLoader(), new Class[]
{StubInterface.class}, STUB_HANDLER);
public JdkProxySerializer(Fory fory, Class cls) {
super(fory, cls);
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java
index 2edfafeac..efa80b772 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java
@@ -21,13 +21,18 @@ package org.apache.fory.serializer;
import java.io.ObjectStreamClass;
import java.io.Serializable;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.invoke.SerializedLambda;
-import java.lang.reflect.Method;
import java.util.Objects;
import org.apache.fory.Fory;
+import org.apache.fory.collection.ClassValueCache;
+import org.apache.fory.exception.ForyException;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.reflect.ReflectionUtils;
import org.apache.fory.util.Preconditions;
+import org.apache.fory.util.function.SerializableFunction;
+import org.apache.fory.util.unsafe._JDKAccess;
/**
* Serializer for java serializable lambda. Use fory to serialize java lambda
instead of JDK
@@ -36,20 +41,34 @@ import org.apache.fory.util.Preconditions;
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class LambdaSerializer extends Serializer {
+ public static Class<?> STUB_LAMBDA_CLASS =
+ ((SerializableFunction<Integer, Integer>) (x -> x * 2)).getClass();
private static final Class<SerializedLambda> SERIALIZED_LAMBDA =
SerializedLambda.class;
- private static final Method READ_RESOLVE_METHOD =
- (Method)
- Objects.requireNonNull(
- ReflectionUtils.getObjectFieldValue(
- ObjectStreamClass.lookup(SERIALIZED_LAMBDA),
"readResolveMethod"));
+ private static final MethodHandle READ_RESOLVE_HANDLE;
private static final boolean SERIALIZED_LAMBDA_HAS_JDK_WRITE =
JavaSerializer.getWriteObjectMethod(SERIALIZED_LAMBDA) != null;
private static final boolean SERIALIZED_LAMBDA_HAS_JDK_READ =
JavaSerializer.getReadObjectMethod(SERIALIZED_LAMBDA) != null;
- private final Method writeReplaceMethod;
+ private static final ClassValueCache<MethodHandle> writeReplaceMethodCache =
+ ClassValueCache.newClassKeySoftCache(32);
+
+ private final MethodHandle writeReplaceHandle;
private Serializer dataSerializer;
- public LambdaSerializer(Fory fory, Class cls) {
+ static {
+ try {
+ // Initialize READ_RESOLVE_HANDLE
+ MethodHandles.Lookup lookup =
_JDKAccess._trustedLookup(SERIALIZED_LAMBDA);
+ Object readResolveMethod =
+ ReflectionUtils.getObjectFieldValue(
+ ObjectStreamClass.lookup(SERIALIZED_LAMBDA),
"readResolveMethod");
+ READ_RESOLVE_HANDLE = lookup.unreflect((java.lang.reflect.Method)
readResolveMethod);
+ } catch (IllegalAccessException e) {
+ throw new ForyException(e);
+ }
+ }
+
+ public LambdaSerializer(Fory fory, Class<?> cls) {
super(fory, cls);
if (cls != ReplaceStub.class) {
if (!Serializable.class.isAssignableFrom(cls)) {
@@ -59,12 +78,28 @@ public class LambdaSerializer extends Serializer {
cls, Serializable.class.getName());
throw new UnsupportedOperationException(msg);
}
- writeReplaceMethod =
- (Method)
+ MethodHandle methodHandle = writeReplaceMethodCache.getIfPresent(cls);
+ if (methodHandle == null) {
+ try {
+ MethodHandles.Lookup lookup = _JDKAccess._trustedLookup(cls);
+ Object writeReplaceMethod =
ReflectionUtils.getObjectFieldValue(
ObjectStreamClass.lookup(cls), "writeReplaceMethod");
+ methodHandle =
+ lookup.unreflect(
+ (java.lang.reflect.Method)
Objects.requireNonNull(writeReplaceMethod));
+ writeReplaceMethodCache.put(cls, methodHandle);
+ } catch (Throwable e) {
+ throw new RuntimeException(
+ String.format("Failed to create writeReplace MethodHandle for
%s", cls), e);
+ }
+ }
+ writeReplaceHandle = methodHandle;
} else {
- writeReplaceMethod = null;
+ writeReplaceHandle = null;
+ }
+ if (cls == STUB_LAMBDA_CLASS) {
+ getDataSerializer();
}
}
@@ -72,10 +107,10 @@ public class LambdaSerializer extends Serializer {
public void write(MemoryBuffer buffer, Object value) {
assert value.getClass() != ReplaceStub.class;
try {
- Object replacement = writeReplaceMethod.invoke(value);
+ Object replacement = writeReplaceHandle.invoke(value);
Preconditions.checkArgument(SERIALIZED_LAMBDA.isInstance(replacement));
getDataSerializer().write(buffer, replacement);
- } catch (Exception e) {
+ } catch (Throwable e) {
throw new RuntimeException("Can't serialize lambda " + value, e);
}
}
@@ -83,10 +118,10 @@ public class LambdaSerializer extends Serializer {
@Override
public Object copy(Object value) {
try {
- Object replacement = writeReplaceMethod.invoke(value);
+ Object replacement = writeReplaceHandle.invoke(value);
Object newReplacement = getDataSerializer().copy(replacement);
- return READ_RESOLVE_METHOD.invoke(newReplacement);
- } catch (Exception e) {
+ return READ_RESOLVE_HANDLE.invoke(newReplacement);
+ } catch (Throwable e) {
throw new RuntimeException("Can't copy lambda " + value, e);
}
}
@@ -95,8 +130,8 @@ public class LambdaSerializer extends Serializer {
public Object read(MemoryBuffer buffer) {
try {
Object replacement = getDataSerializer().read(buffer);
- return READ_RESOLVE_METHOD.invoke(replacement);
- } catch (Exception e) {
+ return READ_RESOLVE_HANDLE.invoke(replacement);
+ } catch (Throwable e) {
throw new RuntimeException("Can't deserialize lambda", e);
}
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java
index b976046fc..ef672d9b7 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java
@@ -61,6 +61,7 @@ import org.apache.fory.resolver.ClassInfo;
import org.apache.fory.resolver.FieldResolver;
import org.apache.fory.resolver.FieldResolver.ClassField;
import org.apache.fory.util.ExceptionUtils;
+import org.apache.fory.util.GraalvmSupport;
import org.apache.fory.util.Preconditions;
import org.apache.fory.util.unsafe._JDKAccess;
@@ -354,7 +355,8 @@ public class ObjectStreamSerializer extends
AbstractObjectSerializer {
this.slotsSerializer =
(CompatibleSerializerBase)
Serializers.newSerializer(fory, type, c));
}
- if (sc == CompatibleSerializer.class) {
+ if (sc == CompatibleSerializer.class ||
GraalvmSupport.isGraalBuildtime()) {
+ // skip init generated serializer at graalvm build time
this.slotsSerializer = new CompatibleSerializer(fory, type,
fieldResolver);
} else {
this.slotsSerializer = (CompatibleSerializerBase)
Serializers.newSerializer(fory, type, sc);
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
index ce936d740..c1719b64b 100644
--- a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
+++ b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
@@ -43,6 +43,7 @@ import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.regex.Pattern;
import org.apache.fory.Fory;
+import org.apache.fory.builder.Generated;
import org.apache.fory.collection.Tuple2;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.memory.Platform;
@@ -50,6 +51,7 @@ import org.apache.fory.reflect.ReflectionUtils;
import org.apache.fory.resolver.ClassResolver;
import org.apache.fory.util.ExceptionUtils;
import org.apache.fory.util.GraalvmSupport;
+import org.apache.fory.util.GraalvmSupport.GraalvmSerializerHolder;
import org.apache.fory.util.StringUtils;
import org.apache.fory.util.unsafe._JDKAccess;
@@ -88,6 +90,11 @@ public class Serializers {
}
Tuple2<MethodType, MethodHandle> ctrInfo =
CTR_MAP.getIfPresent(serializerClass);
if (ctrInfo != null) {
+ if (GraalvmSupport.isGraalBuildtime()) {
+ if (Generated.class.isAssignableFrom(serializerClass)) {
+ return new GraalvmSerializerHolder(fory, type, serializerClass);
+ }
+ }
MethodType sig = ctrInfo.f0;
MethodHandle handle = ctrInfo.f1;
if (sig.equals(SIG1)) {
@@ -125,6 +132,11 @@ public class Serializers {
try {
MethodHandle ctr = lookup.findConstructor(serializerClass, SIG1);
CTR_MAP.put(serializerClass, Tuple2.of(SIG1, ctr));
+ if (GraalvmSupport.isGraalBuildtime()) {
+ if (Generated.class.isAssignableFrom(serializerClass)) {
+ return new GraalvmSerializerHolder(fory, type, serializerClass);
+ }
+ }
return (Serializer<T>) ctr.invoke(fory, type);
} catch (NoSuchMethodException e) {
ExceptionUtils.ignore(e);
@@ -132,6 +144,11 @@ public class Serializers {
try {
MethodHandle ctr = lookup.findConstructor(serializerClass, SIG2);
CTR_MAP.put(serializerClass, Tuple2.of(SIG2, ctr));
+ if (GraalvmSupport.isGraalBuildtime()) {
+ if (Generated.class.isAssignableFrom(serializerClass)) {
+ return new GraalvmSerializerHolder(fory, type, serializerClass);
+ }
+ }
return (Serializer<T>) ctr.invoke(fory);
} catch (NoSuchMethodException e) {
ExceptionUtils.ignore(e);
@@ -139,10 +156,20 @@ public class Serializers {
try {
MethodHandle ctr = lookup.findConstructor(serializerClass, SIG3);
CTR_MAP.put(serializerClass, Tuple2.of(SIG3, ctr));
+ if (GraalvmSupport.isGraalBuildtime()) {
+ if (Generated.class.isAssignableFrom(serializerClass)) {
+ return new GraalvmSerializerHolder(fory, type, serializerClass);
+ }
+ }
return (Serializer<T>) ctr.invoke(type);
} catch (NoSuchMethodException e) {
MethodHandle ctr = ReflectionUtils.getCtrHandle(serializerClass);
CTR_MAP.put(serializerClass, Tuple2.of(SIG4, ctr));
+ if (GraalvmSupport.isGraalBuildtime()) {
+ if (Generated.class.isAssignableFrom(serializerClass)) {
+ return new GraalvmSerializerHolder(fory, type, serializerClass);
+ }
+ }
return (Serializer<T>) ctr.invoke();
}
}
diff --git a/java/fory-core/src/main/java/org/apache/fory/type/TypeUtils.java
b/java/fory-core/src/main/java/org/apache/fory/type/TypeUtils.java
index afee63b3d..dde3fca6a 100644
--- a/java/fory-core/src/main/java/org/apache/fory/type/TypeUtils.java
+++ b/java/fory-core/src/main/java/org/apache/fory/type/TypeUtils.java
@@ -657,8 +657,8 @@ public class TypeUtils {
}
/**
- * Check if a class is one of {@link Optional), {@link OptionalInt},
- * {@link OptionaLong}, or {@link OptionalDouble}.
+ * Check if a class is one of {@link Optional}, {@link OptionalInt}, {@link
OptionalLong}, or
+ * {@link OptionalDouble}.
*/
public static boolean isOptionalType(Class<?> type) {
return type == Optional.class
diff --git
a/java/fory-core/src/main/java/org/apache/fory/util/GraalvmSupport.java
b/java/fory-core/src/main/java/org/apache/fory/util/GraalvmSupport.java
index 55d023981..edf00db99 100644
--- a/java/fory-core/src/main/java/org/apache/fory/util/GraalvmSupport.java
+++ b/java/fory-core/src/main/java/org/apache/fory/util/GraalvmSupport.java
@@ -19,6 +19,10 @@
package org.apache.fory.util;
+import java.util.Objects;
+import org.apache.fory.Fory;
+import org.apache.fory.serializer.Serializer;
+
/** A helper for Graalvm native image support. */
public class GraalvmSupport {
//
https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java
@@ -46,4 +50,17 @@ public class GraalvmSupport {
return IN_GRAALVM_NATIVE_IMAGE
&&
GRAAL_IMAGE_RUNTIME.equals(System.getProperty(GRAAL_IMAGE_CODE_KEY));
}
+
+ public static class GraalvmSerializerHolder extends Serializer {
+ private final Class serializerClass;
+
+ public GraalvmSerializerHolder(Fory fory, Class<?> type, Class<?>
serializerClass) {
+ super(fory, type);
+ this.serializerClass = Objects.requireNonNull(serializerClass);
+ }
+
+ public Class<? extends Serializer> getSerializerClass() {
+ return serializerClass;
+ }
+ }
}
diff --git
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
index 50f599329..c9dc1b45d 100644
---
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
+++
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
@@ -226,6 +226,8 @@
Args=--initialize-at-build-time=org.apache.fory.memory.MemoryBuffer,\
org.apache.fory.logging.LoggerFactory,\
org.apache.fory.memory.BoundsChecking,\
org.apache.fory.memory.MemoryBuffer,\
+ org.apache.fory.memory.MemoryAllocator,\
+ org.apache.fory.memory.MemoryBuffer$DefaultMemoryAllocator,\
org.apache.fory.memory.MemoryUtils,\
org.apache.fory.memory.Platform,\
org.apache.fory.meta.ClassDef$ArrayFieldType,\
@@ -308,6 +310,8 @@
Args=--initialize-at-build-time=org.apache.fory.memory.MemoryBuffer,\
org.apache.fory.serializer.JavaSerializer$4,\
org.apache.fory.serializer.JavaSerializer,\
org.apache.fory.serializer.JdkProxySerializer$ReplaceStub,\
+ org.apache.fory.serializer.JdkProxySerializer$StubInterface,\
+ org.apache.fory.serializer.JdkProxySerializer$StubInvocationHandler,\
org.apache.fory.serializer.JdkProxySerializer,\
org.apache.fory.serializer.LambdaSerializer$ReplaceStub,\
org.apache.fory.serializer.LambdaSerializer,\
@@ -433,6 +437,11 @@
Args=--initialize-at-build-time=org.apache.fory.memory.MemoryBuffer,\
org.apache.fory.serializer.LazySerializer$LazyObjectSerializer,\
org.apache.fory.serializer.shim.ShimDispatcher,\
org.apache.fory.serializer.shim.ProtobufDispatcher,\
+ org.apache.fory.serializer.ObjectStreamSerializer,\
+ org.apache.fory.serializer.ObjectStreamSerializer$1,\
+ org.apache.fory.serializer.ObjectStreamSerializer$StreamClassInfo,\
+
org.apache.fory.serializer.collection.ChildContainerSerializers$ChildCollectionSerializer,\
+
org.apache.fory.serializer.collection.ChildContainerSerializers$ChildMapSerializer,\
org.apache.fory.shaded.org.codehaus.janino.IClass$1,\
org.apache.fory.type.Descriptor$1,\
org.apache.fory.type.Descriptor,\
diff --git
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/reflection-config.json
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/reflection-config.json
new file mode 100644
index 000000000..505934bf9
--- /dev/null
+++
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/reflection-config.json
@@ -0,0 +1,9 @@
+[
+ {
+ "name": "java.lang.reflect.Proxy",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true
+ }
+]
diff --git
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/serialization-config.json
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/serialization-config.json
new file mode 100644
index 000000000..154ba0da2
--- /dev/null
+++
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/serialization-config.json
@@ -0,0 +1,11 @@
+[
+ {
+ "name": "java.lang.reflect.Proxy",
+ "fields": [],
+ "methods": [
+ { "name": "<init>" },
+ { "name": "writeReplace" }
+ ],
+ "customTargetConstructorClass": "java.lang.Object"
+ }
+]
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java
index 49ec2e134..f24da862d 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java
@@ -27,9 +27,6 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
-import java.util.OptionalDouble;
-import java.util.OptionalInt;
-import java.util.OptionalLong;
import java.util.SortedMap;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.Schema;
diff --git
a/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java
b/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java
index 724acb773..c054edeab 100644
---
a/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java
+++
b/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java
@@ -26,7 +26,6 @@ import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.TreeSet;
-
import lombok.Data;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.fory.annotation.ForyField;
diff --git a/python/pyfory/_fory.py b/python/pyfory/_fory.py
index 35ab78d36..6bc87ba1a 100644
--- a/python/pyfory/_fory.py
+++ b/python/pyfory/_fory.py
@@ -133,9 +133,7 @@ class Fory:
"""
self.language = language
self.is_py = language == Language.PYTHON
- self.require_type_registration = (
- _ENABLE_TYPE_REGISTRATION_FORCIBLY or require_type_registration
- )
+ self.require_type_registration = _ENABLE_TYPE_REGISTRATION_FORCIBLY or
require_type_registration
self.ref_tracking = ref_tracking
if self.ref_tracking:
self.ref_resolver = MapRefResolver()
@@ -151,8 +149,7 @@ class Fory:
self.buffer = Buffer.allocate(32)
if not require_type_registration:
warnings.warn(
- "Type registration is disabled, unknown types can be
deserialized "
- "which may be insecure.",
+ "Type registration is disabled, unknown types can be
deserialized which may be insecure.",
RuntimeWarning,
stacklevel=2,
)
@@ -166,7 +163,7 @@ class Fory:
self._unsupported_callback = None
self._unsupported_objects = None
self._peer_language = None
-
+
def register(
self,
cls: Union[type, TypeVar],
@@ -345,7 +342,7 @@ class Fory:
buffers: Iterable = None,
unsupported_objects: Iterable = None,
):
- if type(buffer) == bytes:
+ if isinstance(buffer, bytes):
buffer = Buffer(buffer)
if unsupported_objects is not None:
self._unsupported_objects = iter(unsupported_objects)
@@ -360,10 +357,7 @@ class Fory:
if get_bit(buffer, reader_index, 0):
return None
is_little_endian_ = get_bit(buffer, reader_index, 1)
- assert is_little_endian_, (
- "Big endian is not supported for now, "
- "please ensure peer machine is little endian."
- )
+ assert is_little_endian_, "Big endian is not supported for now, please
ensure peer machine is little endian."
is_target_x_lang = get_bit(buffer, reader_index, 2)
if is_target_x_lang:
self._peer_language = Language(buffer.read_int8())
@@ -371,16 +365,10 @@ class Fory:
self._peer_language = Language.PYTHON
is_out_of_band_serialization_enabled = get_bit(buffer, reader_index, 3)
if is_out_of_band_serialization_enabled:
- assert buffers is not None, (
- "buffers shouldn't be null when the serialized stream is "
- "produced with buffer_callback not null."
- )
+ assert buffers is not None, "buffers shouldn't be null when the
serialized stream is produced with buffer_callback not null."
self._buffers = iter(buffers)
else:
- assert buffers is None, (
- "buffers should be null when the serialized stream is "
- "produced with buffer_callback null."
- )
+ assert buffers is None, "buffers should be null when the
serialized stream is produced with buffer_callback null."
if is_target_x_lang:
obj = self.xdeserialize_ref(buffer)
else:
@@ -532,9 +520,7 @@ class SerializationContext:
self.objects.clear()
-_ENABLE_TYPE_REGISTRATION_FORCIBLY = os.getenv(
- "ENABLE_TYPE_REGISTRATION_FORCIBLY", "0"
-) in {
+_ENABLE_TYPE_REGISTRATION_FORCIBLY =
os.getenv("ENABLE_TYPE_REGISTRATION_FORCIBLY", "0") in {
"1",
"true",
}
@@ -544,8 +530,8 @@ class _PicklerStub:
def dump(self, o):
raise ValueError(
f"Type {type(o)} is not registered, "
- f"pickle is not allowed when type registration enabled, Please
register"
- f"the type or pass unsupported_callback"
+ f"pickle is not allowed when type registration enabled, "
+ f"Please register the type or pass unsupported_callback"
)
def clear_memo(self):
@@ -554,7 +540,4 @@ class _PicklerStub:
class _UnpicklerStub:
def load(self):
- raise ValueError(
- "pickle is not allowed when type registration enabled, Please
register"
- "the type or pass unsupported_callback"
- )
+ raise ValueError("pickle is not allowed when type registration
enabled, Please register the type or pass unsupported_callback")
diff --git a/python/pyfory/_registry.py b/python/pyfory/_registry.py
index 05715a579..2333ff782 100644
--- a/python/pyfory/_registry.py
+++ b/python/pyfory/_registry.py
@@ -379,6 +379,9 @@ class TypeResolver:
serializer: Serializer = None,
internal: bool = False,
):
+ # Set default type_id when None, similar to _register_xtype
+ if type_id is None and typename is not None:
+ type_id = self._next_type_id()
return self.__register_type(
cls,
type_id=type_id,
@@ -398,7 +401,7 @@ class TypeResolver:
serializer: Serializer = None,
internal: bool = False,
):
- dynamic_type = type_id < 0
+ dynamic_type = type_id is not None and type_id < 0
if not internal and serializer is None:
serializer = self._create_serializer(cls)
if typename is None:
@@ -471,6 +474,8 @@ class TypeResolver:
type_id = TypeId.NAMED_ENUM
elif type(serializer) is PickleSerializer:
type_id = PickleSerializer.PICKLE_TYPE_ID
+ elif isinstance(serializer, FunctionSerializer):
+ type_id = TypeId.NAMED_EXT
elif isinstance(serializer, (ObjectSerializer, StatefulSerializer,
ReduceSerializer)):
type_id = TypeId.NAMED_EXT
if not self.require_registration:
@@ -494,8 +499,8 @@ class TypeResolver:
break
else:
if cls is types.FunctionType:
- # Use PickleSerializer for function types (including lambdas)
- serializer = PickleSerializer(self.fory, cls)
+ # Use FunctionSerializer for function types (including lambdas)
+ serializer = FunctionSerializer(self.fory, cls)
elif dataclasses.is_dataclass(cls):
serializer = DataClassSerializer(self.fory, cls)
elif issubclass(cls, enum.Enum):
diff --git a/python/pyfory/tests/test_function.py
b/python/pyfory/tests/test_function.py
index ad2e03c06..738ef69d6 100644
--- a/python/pyfory/tests/test_function.py
+++ b/python/pyfory/tests/test_function.py
@@ -26,7 +26,7 @@ def test_lambda_functions_serialization():
# Register the necessary types
fory.register_type(tuple)
fory.register_type(list)
- fory.register_type(dict)
+ # dict is already registered by default with MapSerializer
# Simple lambda
simple_lambda = lambda x: x * 2 # noqa: E731
@@ -64,7 +64,7 @@ def test_regular_functions_serialization():
# Register the necessary types for complex functions
fory.register_type(tuple)
fory.register_type(list)
- fory.register_type(dict)
+ # dict is already registered by default with MapSerializer
# Test complex function
serialized = fory.serialize(complex_function)
@@ -79,7 +79,7 @@ def test_nested_functions_serialization():
# Register the necessary types
fory.register_type(tuple)
fory.register_type(list)
- fory.register_type(dict)
+ # dict is already registered by default with MapSerializer
def outer_function(x):
def inner_function(y):
@@ -104,7 +104,7 @@ def test_local_class_serialization():
# Register the necessary types
fory.register_type(tuple)
fory.register_type(list)
- fory.register_type(dict)
+ # dict is already registered by default with MapSerializer
def create_local_class():
from dataclasses import dataclass
diff --git a/python/pyfory/tests/test_serializer.py
b/python/pyfory/tests/test_serializer.py
index 3f6cbcb49..6df8875b4 100644
--- a/python/pyfory/tests/test_serializer.py
+++ b/python/pyfory/tests/test_serializer.py
@@ -469,6 +469,8 @@ def test_pickle_fallback():
def test_unsupported_callback():
fory = Fory(language=Language.PYTHON, ref_tracking=True,
require_type_registration=False)
+ # Test with functions that now have proper serialization support
+ # Functions should no longer be treated as unsupported
def f1(x):
return x
@@ -478,10 +480,18 @@ def test_unsupported_callback():
obj1 = [1, True, f1, f2, {1: 2}]
unsupported_objects = []
binary1 = fory.serialize(obj1,
unsupported_callback=unsupported_objects.append)
- assert len(unsupported_objects) == 2
- assert unsupported_objects == [f1, f2]
+ # Functions are now properly supported, so unsupported_objects should be
empty
+ assert len(unsupported_objects) == 0
new_obj1 = fory.deserialize(binary1,
unsupported_objects=unsupported_objects)
- assert new_obj1 == obj1
+ # Functions should roundtrip correctly
+ assert len(new_obj1) == len(obj1)
+ assert new_obj1[0] == obj1[0] # 1
+ assert new_obj1[1] == obj1[1] # True
+ assert new_obj1[2](5) == f1(5) # Test f1 functionality
+ assert new_obj1[3](5) == f2(5) # Test f2 functionality
+ assert new_obj1[4] == obj1[4] # {1: 2}
+ # Don't check full equality since functions are new objects after
deserialization
+ # The functionality test above already confirmed they work correctly
def test_slice():
diff --git a/python/setup.py b/python/setup.py
index 87123efa8..d31f5544f 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -37,8 +37,10 @@ project_dir = abspath(pjoin(setup_dir, os.pardir))
fory_cpp_src_dir = abspath(pjoin(setup_dir, "../src/"))
print(f"setup_dir: {setup_dir}")
+print(f"project_dir: {project_dir}")
print(f"fory_cpp_src_dir: {fory_cpp_src_dir}")
+
class BinaryDistribution(Distribution):
def __init__(self, attrs=None):
super().__init__(attrs=attrs)
@@ -50,7 +52,9 @@ class BinaryDistribution(Distribution):
elif arch in ("aarch64", "arm64"):
bazel_args += ["--copt=-fsigned-char"]
bazel_args += ["//:cp_fory_so"]
- subprocess.check_call(bazel_args)
+ # Ensure Windows path compatibility
+ cwd_path = os.path.normpath(project_dir)
+ subprocess.check_call(bazel_args, cwd=cwd_path)
def has_ext_modules(self):
return True
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]