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 c108cae67 fix(java): stabilize readResolve type metadata (#3831)
c108cae67 is described below

commit c108cae675421a06c4382cbec8f6f88bec8c55e2
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Jul 9 15:26:32 2026 +0530

    fix(java): stabilize readResolve type metadata (#3831)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    Closes #3828
    
    ## AI Contribution Checklist
    
    
    
    - [ ] Substantial AI assistance was used in this PR: `yes` / `no`
    - [ ] 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 or equivalent persisted links of the
    final clean AI review results from both fresh reviewers described in
    `AI_POLICY.md`, the Fory-guided reviewer and the independent general
    reviewer, 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
---
 .../org/apache/fory/resolver/ClassResolver.java    |   4 +
 .../apache/fory/resolver/ClassResolverTest.java    | 132 +++++++++++++++++++++
 2 files changed, 136 insertions(+)

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 e5a144707..a1accbc36 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
@@ -754,6 +754,10 @@ public class ClassResolver extends TypeResolver {
       return Types.ENUM;
     } else if (serializer != null && !isStructSerializer(serializer)) {
       return Types.EXT;
+    } else if (serializer == null && usesNonStructTypeDef(cls)) {
+      // Registered TypeInfo may be created before the natural serializer is 
materialized. Keep
+      // TypeDef classification stable for classes that are known to use 
non-struct serializers.
+      return Types.EXT;
     } else {
       return useStructEvolution(cls, metaContextShareEnabled)
           ? Types.COMPATIBLE_STRUCT
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java 
b/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java
index 5f5a17fc5..b98d6d95c 100644
--- 
a/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java
+++ 
b/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java
@@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.primitives.Primitives;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
+import java.io.Serializable;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
@@ -42,6 +43,8 @@ import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.ArrayBlockingQueue;
@@ -1404,6 +1407,135 @@ public class ClassResolverTest extends ForyTestBase {
     Assert.assertEquals(result[1].getValue(), 2);
   }
 
+  @Test
+  public void testReadResolveTypeDefState() {
+    byte[] groupBytes =
+        newReadResolveFory()
+            .serialize(
+                new ReadResolveGroup(7, new 
HashSet<>(Arrays.asList(ReadResolveCode.of("FR")))));
+    byte[] geoBytes =
+        newReadResolveFory()
+            .serialize(
+                new ReadResolveGeo(new 
ArrayList<>(Arrays.asList(ReadResolveCode.of("GB")))));
+
+    Fory reader = newReadResolveFory();
+    ClassResolver resolver = (ClassResolver) reader.getTypeResolver();
+    assertEquals(resolver.getTypeIdForTypeDef(ReadResolveCode.class), 
Types.EXT);
+    assertEquals(
+        reader.deserialize(groupBytes),
+        new ReadResolveGroup(7, new 
HashSet<>(Arrays.asList(ReadResolveCode.of("FR")))));
+    assertEquals(resolver.getTypeIdForTypeDef(ReadResolveCode.class), 
Types.EXT);
+    assertEquals(
+        reader.deserialize(geoBytes),
+        new ReadResolveGeo(new 
ArrayList<>(Arrays.asList(ReadResolveCode.of("GB")))));
+    assertEquals(resolver.getTypeIdForTypeDef(ReadResolveCode.class), 
Types.EXT);
+  }
+
+  private static Fory newReadResolveFory() {
+    Fory fory =
+        Fory.builder()
+            .withXlang(false)
+            .withRefTracking(true)
+            .requireClassRegistration(true)
+            .withCompatible(true)
+            .build();
+    fory.register(ReadResolveCode.class, 100);
+    fory.register(ReadResolveGroup.class, 101);
+    fory.register(ReadResolveGeo.class, 102);
+    return fory;
+  }
+
+  public static final class ReadResolveCode implements Serializable {
+    public String value;
+
+    public ReadResolveCode() {}
+
+    private ReadResolveCode(String value) {
+      this.value = value;
+    }
+
+    static ReadResolveCode of(String value) {
+      return new ReadResolveCode(value);
+    }
+
+    private Object readResolve() {
+      return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof ReadResolveCode)) {
+        return false;
+      }
+      ReadResolveCode that = (ReadResolveCode) o;
+      return Objects.equals(value, that.value);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(value);
+    }
+  }
+
+  public static final class ReadResolveGroup implements Serializable {
+    public int id;
+    public Set<ReadResolveCode> codes;
+
+    public ReadResolveGroup() {}
+
+    ReadResolveGroup(int id, Set<ReadResolveCode> codes) {
+      this.id = id;
+      this.codes = codes;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof ReadResolveGroup)) {
+        return false;
+      }
+      ReadResolveGroup that = (ReadResolveGroup) o;
+      return id == that.id && Objects.equals(codes, that.codes);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(id, codes);
+    }
+  }
+
+  public static final class ReadResolveGeo implements Serializable {
+    public List<ReadResolveCode> codes;
+
+    public ReadResolveGeo() {}
+
+    ReadResolveGeo(List<ReadResolveCode> codes) {
+      this.codes = codes;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof ReadResolveGeo)) {
+        return false;
+      }
+      ReadResolveGeo that = (ReadResolveGeo) o;
+      return Objects.equals(codes, that.codes);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(codes);
+    }
+  }
+
   private static String captureOutput(Runnable action) throws Exception {
     int previousLogLevel = LoggerFactory.getLogLevel();
     PrintStream previousOut = System.out;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to