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 7ce550bc1 fix(java): avoid instantiating abstract meta-share types
(#3677)
7ce550bc1 is described below
commit 7ce550bc1ab883e953e760709ead2778d9356249
Author: Shawn Yang <[email protected]>
AuthorDate: Wed May 13 14:30:43 2026 +0800
fix(java): avoid instantiating abstract meta-share types (#3677)
## Why?
Java compatible meta-share can receive a `TypeDef` whose root class is
an interface or abstract class. Instantiating compatible serializers
allocate their root type during read, so those meta roots must not get a
serializer that can call `Unsafe.allocateInstance` on an uninstantiable
declared type.
## What does this PR do?
- Leaves abstract/interface meta-share roots without an instantiating
compatible serializer in `TypeResolver#getMetaSharedTypeInfo`.
- Keeps concrete values on the existing runtime type metadata or
target-class transformation path.
- Adds a resolver-level regression for interface-root meta-share
`TypeDef`s.
- Adds an interface-field compatible meta-share round-trip regression
after generated serializers compile.
## Related issues
- Fixes #3674
## AI Contribution Checklist
- [x] Substantial AI assistance was used in this PR: `yes`
- [x] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence of the final clean AI review results
from both fresh reviewers on the current PR diff or current HEAD after
the latest code changes.
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
N/A. This change prevents instantiating serializers for
abstract/interface meta-share roots and does not change hot-path
serialization for concrete runtime values.
## Tests
- `ENABLE_FORY_DEBUG_OUTPUT=1 mvn -T16 -pl fory-core
-Dtest=org.apache.fory.resolver.MetaShareContextTest#testMetaSharedInterfaceDoesNotBuildInstantiatingSerializer,org.apache.fory.serializer.MetaShareCompatibleTest#testInterfaceFieldCompatibleMetaShare
test`
- `ENABLE_FORY_DEBUG_OUTPUT=1 mvn -T16 -pl fory-core
-Dtest=org.apache.fory.resolver.MetaShareContextTest,org.apache.fory.serializer.MetaShareCompatibleTest
test`
- `mvn -T16 -pl fory-core spotless:check`
- `mvn -T16 -pl fory-core checkstyle:check`
---
.../org/apache/fory/resolver/TypeResolver.java | 6 +++
.../apache/fory/resolver/MetaShareContextTest.java | 23 +++++++++
.../fory/serializer/MetaShareCompatibleTest.java | 57 ++++++++++++++++++++++
3 files changed, 86 insertions(+)
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
index 9be209a86..2a63c8cea 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
@@ -1028,6 +1028,12 @@ public abstract class TypeResolver {
if (clz.isArray() || cls.isEnum()) {
return getTypeInfo(cls);
}
+ if (ReflectionUtils.isAbstract(cls) || cls.isInterface()) {
+ // Compatible serializers allocate their root type during read. A
meta-share TypeDef may name
+ // an abstract declared field type, but the actual value must be read
through concrete runtime
+ // type metadata or a concrete target-class transformation.
+ return typeInfo;
+ }
Class<? extends Serializer> sc =
getCompatibleDeserializerClassFromGraalvmRegistry(cls, typeDef);
if (sc == null) {
diff --git
a/java/fory-core/src/test/java/org/apache/fory/resolver/MetaShareContextTest.java
b/java/fory-core/src/test/java/org/apache/fory/resolver/MetaShareContextTest.java
index e3738b7b1..7eec8145c 100644
---
a/java/fory-core/src/test/java/org/apache/fory/resolver/MetaShareContextTest.java
+++
b/java/fory-core/src/test/java/org/apache/fory/resolver/MetaShareContextTest.java
@@ -28,6 +28,7 @@ import org.apache.fory.Fory;
import org.apache.fory.ForyTestBase;
import org.apache.fory.context.MetaReadContext;
import org.apache.fory.context.MetaWriteContext;
+import org.apache.fory.meta.TypeDef;
import org.apache.fory.test.bean.BeanA;
import org.apache.fory.test.bean.BeanB;
import org.apache.fory.test.bean.Foo;
@@ -35,6 +36,10 @@ import org.testng.Assert;
import org.testng.annotations.Test;
public class MetaShareContextTest extends ForyTestBase {
+ public interface InterfacePrice {
+ int cents();
+ }
+
@Test
public void testShareClassName() {
Fory fory =
@@ -66,6 +71,24 @@ public class MetaShareContextTest extends ForyTestBase {
}
}
+ @Test(dataProvider = "enableCodegen")
+ public void
testMetaSharedInterfaceDoesNotBuildInstantiatingSerializer(boolean
enableCodegen) {
+ Fory fory =
+ Fory.builder()
+ .withXlang(false)
+ .withRefTracking(true)
+ .withMetaShare(true)
+ .withScopedMetaShare(false)
+ .withCompatible(true)
+ .withCodegen(enableCodegen)
+ .requireClassRegistration(false)
+ .build();
+ TypeResolver resolver = fory.getTypeResolver();
+ TypeDef typeDef = TypeDef.buildTypeDef(resolver, InterfacePrice.class);
+ TypeInfo typeInfo = resolver.buildMetaSharedTypeInfo(typeDef);
+ Assert.assertNull(typeInfo.getSerializer());
+ }
+
private void checkMetaShare(Fory fory, Object o) {
MetaWriteContext metaWriteContext = new MetaWriteContext();
MetaReadContext metaReadContext = new MetaReadContext();
diff --git
a/java/fory-core/src/test/java/org/apache/fory/serializer/MetaShareCompatibleTest.java
b/java/fory-core/src/test/java/org/apache/fory/serializer/MetaShareCompatibleTest.java
index 1046e2ff2..85f6a7a00 100644
---
a/java/fory-core/src/test/java/org/apache/fory/serializer/MetaShareCompatibleTest.java
+++
b/java/fory-core/src/test/java/org/apache/fory/serializer/MetaShareCompatibleTest.java
@@ -61,6 +61,37 @@ public class MetaShareCompatibleTest extends ForyTestBase {
return newObj;
}
+ public interface Price {
+ int cents();
+ }
+
+ public static final class ImmutablePrice implements Price {
+ public int cents;
+
+ public ImmutablePrice() {}
+
+ public ImmutablePrice(int cents) {
+ this.cents = cents;
+ }
+
+ @Override
+ public int cents() {
+ return cents;
+ }
+ }
+
+ public static final class InterfaceFieldOrder {
+ public int id;
+ public Price price;
+
+ public InterfaceFieldOrder() {}
+
+ public InterfaceFieldOrder(int id, Price price) {
+ this.id = id;
+ this.price = price;
+ }
+ }
+
@DataProvider
public static Object[][] config1() {
return Sets.cartesianProduct(
@@ -121,6 +152,32 @@ public class MetaShareCompatibleTest extends ForyTestBase {
serDeMetaShareCheck(fory, BeanA.createBeanA(2));
}
+ @Test(dataProvider = "enableCodegen")
+ public void testInterfaceFieldCompatibleMetaShare(boolean enableCodegen) {
+ Fory writer =
foryBuilder().withRefTracking(true).withCodegen(enableCodegen).build();
+ Fory reader =
foryBuilder().withRefTracking(true).withCodegen(enableCodegen).build();
+ InterfaceFieldOrder order = new InterfaceFieldOrder(1, new
ImmutablePrice(100));
+ MetaWriteContext metaWriteContext = new MetaWriteContext();
+ MetaReadContext metaReadContext = new MetaReadContext();
+ setMetaContexts(writer, metaWriteContext, new MetaReadContext());
+ byte[] bytes = writer.serialize(order);
+ setMetaContexts(reader, new MetaWriteContext(), metaReadContext);
+ Object newOrder = reader.deserialize(bytes);
+ assertInterfaceFieldOrder(newOrder);
+ reader.ensureSerializersCompiled();
+ setMetaContexts(writer, metaWriteContext, new MetaReadContext());
+ bytes = writer.serialize(order);
+ setMetaContexts(reader, new MetaWriteContext(), metaReadContext);
+ newOrder = reader.deserialize(bytes);
+ assertInterfaceFieldOrder(newOrder);
+ }
+
+ private static void assertInterfaceFieldOrder(Object newOrder) {
+ Assert.assertEquals(((InterfaceFieldOrder) newOrder).id, 1);
+ Assert.assertEquals(((InterfaceFieldOrder) newOrder).price.getClass(),
ImmutablePrice.class);
+ Assert.assertEquals(((InterfaceFieldOrder) newOrder).price.cents(), 100);
+ }
+
@Test(dataProvider = "config2")
public void testWriteCompatibleBasic(
boolean referenceTracking,
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]