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]

Reply via email to