This is an automated email from the ASF dual-hosted git repository.

wangweipeng 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 e13399532 feat(JavaScript):  Impl xlang Map and Collection (#3249)
e13399532 is described below

commit e13399532c21a7413381f7a7fc53feaa7bd7f210
Author: weipeng <[email protected]>
AuthorDate: Sun Feb 1 00:57:38 2026 +0800

    feat(JavaScript):  Impl xlang Map and Collection (#3249)
---
 javascript/packages/fory/lib/gen/any.ts        |   2 +-
 javascript/packages/fory/lib/gen/collection.ts | 214 ++++++++++-------
 javascript/packages/fory/lib/gen/map.ts        | 303 +++++++++++++------------
 javascript/packages/fory/lib/meta/TypeMeta.ts  |   2 +-
 javascript/packages/fory/lib/type.ts           |   2 +-
 5 files changed, 298 insertions(+), 225 deletions(-)

diff --git a/javascript/packages/fory/lib/gen/any.ts 
b/javascript/packages/fory/lib/gen/any.ts
index fb2163316..96da6d219 100644
--- a/javascript/packages/fory/lib/gen/any.ts
+++ b/javascript/packages/fory/lib/gen/any.ts
@@ -25,7 +25,7 @@ import { Mode, RefFlags, Serializer, TypeId } from "../type";
 import { Scope } from "./scope";
 import Fory from "../fory";
 
-class AnyHelper {
+export class AnyHelper {
   static detectSerializer(fory: Fory) {
     const typeId = fory.binaryReader.readVarUint32Small7();
     let serializer: Serializer | undefined;
diff --git a/javascript/packages/fory/lib/gen/collection.ts 
b/javascript/packages/fory/lib/gen/collection.ts
index c9741373a..34ddd828a 100644
--- a/javascript/packages/fory/lib/gen/collection.ts
+++ b/javascript/packages/fory/lib/gen/collection.ts
@@ -24,6 +24,7 @@ import { CodegenRegistry } from "./router";
 import { TypeId, RefFlags, Serializer } from "../type";
 import { Scope } from "./scope";
 import Fory from "../fory";
+import { AnyHelper } from "./any";
 
 export const CollectionFlags = {
   /** Whether track elements ref. */
@@ -33,10 +34,10 @@ export const CollectionFlags = {
   HAS_NULL: 0b10,
 
   /** Whether collection elements type is not declare type. */
-  NOT_DECL_ELEMENT_TYPE: 0b100,
+  DECL_ELEMENT_TYPE: 0b100,
 
   /** Whether collection elements type different. */
-  NOT_SAME_TYPE: 0b1000,
+  SAME_TYPE: 0b1000,
 };
 
 class CollectionAnySerializer {
@@ -49,13 +50,20 @@ class CollectionAnySerializer {
     let isSame = true;
     let serializer: Serializer | null | undefined = null;
     let includeNone = false;
+    let trackingRef = false;
 
     for (const item of arr) {
       if ((item === undefined || item === null) && !includeNone) {
         includeNone = true;
       }
+      const current = this.fory.classResolver.getSerializerByData(item);
+      if (!current) {
+        throw new Error("can't detect the type of item in list");
+      }
+      if (!trackingRef) {
+        trackingRef = current.needToWriteRef();
+      }
       if (isSame) {
-        const current = this.fory.classResolver.getSerializerByData(item);
         if (serializer !== null && serializer !== undefined && current !== 
serializer) {
           isSame = false;
         } else {
@@ -64,74 +72,133 @@ class CollectionAnySerializer {
       }
     }
 
-    if (!isSame) {
-      flag |= CollectionFlags.NOT_SAME_TYPE;
+    if (isSame) {
+      flag |= CollectionFlags.SAME_TYPE;
     }
     if (includeNone) {
       flag |= CollectionFlags.HAS_NULL;
     }
-    if (serializer && serializer!.needToWriteRef()) {
+    if (trackingRef) {
       flag |= CollectionFlags.TRACKING_REF;
     }
     this.fory.binaryWriter.uint8(flag);
-    if (isSame) {
-      this.fory.binaryWriter.int16(serializer ? serializer!.getTypeId() : 0);
-    }
     return {
       serializer,
       isSame,
       flag,
       includeNone,
+      trackingRef,
     };
   }
 
   write(value: any, size: number) {
-    const { serializer, isSame, includeNone } = 
this.writeElementsHeader(value);
-    this.fory.binaryWriter.varUInt32(size);
-    for (const item of value) {
-      if (includeNone) {
-        if (item === null || item === undefined) {
-          this.fory.binaryWriter.uint8(RefFlags.NullFlag);
-          continue;
-        } else {
-          this.fory.binaryWriter.uint8(RefFlags.NotNullValueFlag);
+    this.fory.binaryWriter.writeVarUint32Small7(size);
+    const { serializer, isSame, includeNone, trackingRef } = 
this.writeElementsHeader(value);
+    if (isSame) {
+      serializer!.writeClassInfo(value);
+      if (trackingRef) {
+        for (const item of value) {
+          if (!serializer!.writeRefOrNull(item)) {
+            serializer!.write(item);
+          }
+        }
+      } else if (includeNone) {
+        for (const item of value) {
+          if (item === null || item === undefined) {
+            this.fory.binaryWriter.uint8(RefFlags.NullFlag);
+          } else {
+            this.fory.binaryWriter.uint8(RefFlags.NotNullValueFlag);
+            serializer!.write(item);
+          }
+        }
+      } else {
+        for (const item of value) {
+          serializer!.write(item);
         }
       }
-      let finalSerializer = serializer;
-      if (!isSame) {
-        finalSerializer = this.fory.classResolver.getSerializerByData(item);
-        this.fory.binaryWriter.uint16(finalSerializer!.getTypeId());
+    } else {
+      if (trackingRef) {
+        for (const item of value) {
+          const serializer = this.fory.classResolver.getSerializerByData(item);
+          serializer?.writeRef(item);
+        }
+      } else if (includeNone) {
+        for (const item of value) {
+          if (item === null || item === undefined) {
+            this.fory.binaryWriter.uint8(RefFlags.NullFlag);
+          } else {
+            const serializer = 
this.fory.classResolver.getSerializerByData(item);
+            this.fory.binaryWriter.uint8(RefFlags.NotNullValueFlag);
+            serializer!.write(item);
+          }
+        }
+      } else {
+        for (const item of value) {
+          serializer!.write(item);
+        }
       }
-      finalSerializer!.write(item);
     }
   }
 
   read(accessor: (result: any, index: number, v: any) => void, 
createCollection: (len: number) => any, fromRef: boolean): any {
+    const len = this.fory.binaryReader.readVarUint32Small7();
     const flags = this.fory.binaryReader.uint8();
-    const isSame = !(flags & CollectionFlags.NOT_SAME_TYPE);
+    const isSame = flags & CollectionFlags.SAME_TYPE;
     const includeNone = flags & CollectionFlags.HAS_NULL;
+    const refTracking = flags & CollectionFlags.TRACKING_REF;
+    const result = createCollection(len);
 
-    let serializer: Serializer;
     if (isSame) {
-      serializer = 
this.fory.classResolver.getSerializerById(this.fory.binaryReader.int16());
-    }
-    const len = this.fory.binaryReader.varUInt32();
-    const result = createCollection(len);
-    if (fromRef) {
-      this.fory.referenceResolver.reference(result);
-    }
-    for (let index = 0; index < len; index++) {
-      if (includeNone) {
-        const refFlag = this.fory.binaryReader.uint8();
-        if (RefFlags.NullFlag === refFlag) {
-          accessor(result, index, null);
-          continue;
+      const serializer = AnyHelper.detectSerializer(this.fory);
+      if (refTracking) {
+        for (let i = 0; i < len; i++) {
+          serializer.readRef();
+          const refFlag = this.fory.referenceResolver.readRefFlag();
+          if (refFlag === RefFlags.RefFlag) {
+            const refId = this.fory.binaryReader.varUInt32();
+            accessor(result, i, 
this.fory.referenceResolver.getReadObject(refId));
+          } else if (refFlag === RefFlags.RefValueFlag) {
+            accessor(result, i, serializer!.read(true));
+          } else {
+            accessor(result, i, null);
+          }
+        }
+      } else if (includeNone) {
+        for (let i = 0; i < len; i++) {
+          const flag = this.fory.binaryReader.uint8();
+          if (flag === RefFlags.NullFlag) {
+            accessor(result, i, null);
+          } else {
+            accessor(result, i, serializer!.read(false));
+          }
+        }
+      } else {
+        for (let i = 0; i < len; i++) {
+          accessor(result, i, serializer!.read(false));
         }
       }
-      if (!isSame) {
-        serializer = 
this.fory.classResolver.getSerializerById(this.fory.binaryReader.int16());
+    } else {
+      if (refTracking) {
+        for (let i = 0; i < len; i++) {
+          const itemSerializer = AnyHelper.detectSerializer(this.fory);
+          accessor(result, i, itemSerializer!.readRef());
+        }
+      } else if (includeNone) {
+        for (let i = 0; i < len; i++) {
+          const flag = this.fory.binaryReader.uint8();
+          if (flag === RefFlags.NullFlag) {
+            accessor(result, i, null);
+          } else {
+            const itemSerializer = AnyHelper.detectSerializer(this.fory);
+            accessor(result, i, itemSerializer!.read(false));
+          }
+        }
+      } else {
+        for (let i = 0; i < len; i++) {
+          const itemSerializer = AnyHelper.detectSerializer(this.fory);
+          accessor(result, i, itemSerializer!.read(false));
+        }
       }
-      accessor(result, index, serializer!.read(false));
     }
     return result;
   }
@@ -164,9 +231,6 @@ export abstract class CollectionSerializerGenerator extends 
BaseSerializerGenera
     const item = this.scope.uniqueName("item");
     const stmts = [
     ];
-    if (this.innerGenerator.needToWriteRef()) {
-      stmts.push(`${flagAccessor} |= ${CollectionFlags.TRACKING_REF}`);
-    }
     stmts.push(`
         for (const ${item} of ${accessor}) {
             if (${item} === null || ${item} === undefined) {
@@ -183,15 +247,13 @@ export abstract class CollectionSerializerGenerator 
extends BaseSerializerGenera
     const item = this.scope.uniqueName("item");
     const flags = this.scope.uniqueName("flags");
     const existsId = this.scope.uniqueName("existsId");
-
+    const flag = CollectionFlags.SAME_TYPE | CollectionFlags.DECL_ELEMENT_TYPE;
     return `
-            let ${flags} = 0;
+            let ${flags} = ${(this.innerGenerator.needToWriteRef() ? 
CollectionFlags.TRACKING_REF : 0) | flag};
+            
${this.builder.writer.writeVarUint32Small7(`${accessor}.${this.sizeProp()}`)}
             ${this.writeElementsHeader(accessor, flags)}
-            ${this.builder.writer.int16(this.typeInfo.typeId)}
-            ${this.builder.writer.varUInt32(`${accessor}.${this.sizeProp()}`)}
             
${this.builder.writer.reserve(`${this.innerGenerator.getFixedSize()} * 
${accessor}.${this.sizeProp()}`)};
             if (${flags} & ${CollectionFlags.TRACKING_REF}) {
-                
                 for (const ${item} of ${accessor}) {
                     if (${accessor} !== null && ${accessor} !== undefined) {
                         const ${existsId} = 
${this.builder.referenceResolver.existsWriteObject(item)};
@@ -207,40 +269,33 @@ export abstract class CollectionSerializerGenerator 
extends BaseSerializerGenera
                         ${this.builder.writer.int8(RefFlags.NullFlag)};
                     }
                 }
-            } else {
-                if (${flags} & ${CollectionFlags.HAS_NULL}) {
-                    for (const ${item} of ${accessor}) {
-                        if (${accessor} !== null && ${accessor} !== undefined) 
{
-                            
${this.builder.writer.int8(RefFlags.NotNullValueFlag)};
-                            ${this.innerGenerator.writeEmbed().write(item)}
-                        } else {
-                            ${this.builder.writer.int8(RefFlags.NullFlag)};
-                        }
-                    }
-                } else {
-                    for (const ${item} of ${accessor}) {
+            } else if (${flags} & ${CollectionFlags.HAS_NULL}) {
+                for (const ${item} of ${accessor}) {
+                    if (${accessor} !== null && ${accessor} !== undefined) {
+                        ${this.builder.writer.int8(RefFlags.NotNullValueFlag)};
                         ${this.innerGenerator.writeEmbed().write(item)}
+                    } else {
+                        ${this.builder.writer.int8(RefFlags.NullFlag)};
                     }
                 }
+            } else {
+                for (const ${item} of ${accessor}) {
+                    ${this.innerGenerator.writeEmbed().write(item)}
+                }
             }
         `;
   }
 
-  readStmtSpecificType(accessor: (expr: string) => string, refState: string): 
string {
+  readSpecificType(accessor: (expr: string) => string, refState: string): 
string {
     const result = this.scope.uniqueName("result");
     const len = this.scope.uniqueName("len");
     const flags = this.scope.uniqueName("flags");
     const idx = this.scope.uniqueName("idx");
     const refFlag = this.scope.uniqueName("refFlag");
 
-    // If track elements ref, use first bit 0b1 of header to flag it.
-    // If collection has null, use second bit 0b10 of header to flag it. If 
ref tracking is enabled for this element type, this flag is invalid.
-    // If collection element types is not declared type, use 3rd bit 0b100 of 
header to flag it.
-    // If collection element types different, use 4th bit 0b1000 of header to 
flag it.
     return `
+            const ${len} = ${this.builder.reader.readVarUint32Small7()};
             const ${flags} = ${this.builder.reader.uint8()};
-            ${this.builder.reader.skip(2)};
-            const ${len} = ${this.builder.reader.varUInt32()};
             const ${result} = ${this.newCollection(len)};
             ${this.maybeReference(result, refState)}
             if (${flags} & ${CollectionFlags.TRACKING_REF}) {
@@ -259,19 +314,18 @@ export abstract class CollectionSerializerGenerator 
extends BaseSerializerGenera
                             break;
                     }
                 }
-            } else {
-                if (!(${flags} & ${CollectionFlags.HAS_NULL})) {
-                    for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
+            } else if (${flags} & ${CollectionFlags.HAS_NULL}) {
+                for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
+                    if (${this.builder.reader.uint8()} == 
${RefFlags.NullFlag}) {
+                        ${this.putAccessor(result, "null", idx)}
+                    } else {
                         ${this.innerGenerator.read(x => 
`${this.putAccessor(result, x, idx)}`, "false")}
                     }
-                } else {
-                    for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
-                        if (${this.builder.reader.uint8()} == 
${RefFlags.NullFlag}) {
-                            ${this.putAccessor(result, "null", idx)}
-                        } else {
-                            ${this.innerGenerator.read(x => 
`${this.putAccessor(result, x, idx)}`, "false")}
-                        }
-                    }
+                }
+
+            } else {
+                for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
+                    ${this.innerGenerator.read(x => 
`${this.putAccessor(result, x, idx)}`, "false")}
                 }
             }
             ${accessor(result)}
@@ -294,7 +348,7 @@ export abstract class CollectionSerializerGenerator extends 
BaseSerializerGenera
           }, (len) => ${this.newCollection("len")}, ${refState});
       `);
     }
-    return this.readStmtSpecificType(accessor, refState);
+    return this.readSpecificType(accessor, refState);
   }
 }
 
diff --git a/javascript/packages/fory/lib/gen/map.ts 
b/javascript/packages/fory/lib/gen/map.ts
index ea9b5870c..8632b005e 100644
--- a/javascript/packages/fory/lib/gen/map.ts
+++ b/javascript/packages/fory/lib/gen/map.ts
@@ -24,6 +24,7 @@ import { CodegenRegistry } from "./router";
 import { TypeId, RefFlags, Serializer } from "../type";
 import { Scope } from "./scope";
 import Fory from "../fory";
+import { AnyHelper } from "./any";
 
 const MapFlags = {
   /** Whether track elements ref. */
@@ -33,10 +34,7 @@ const MapFlags = {
   HAS_NULL: 0b10,
 
   /** Whether collection elements type is not declare type. */
-  NOT_DECL_ELEMENT_TYPE: 0b100,
-
-  /** Whether collection elements type different. */
-  NOT_SAME_TYPE: 0b1000,
+  DECL_ELEMENT_TYPE: 0b100,
 };
 
 class MapHeadUtil {
@@ -63,25 +61,31 @@ class MapChunkWriter {
   private chunkOffset = 0;
   private header = 0;
 
-  constructor(private fory: Fory) {
+  constructor(private fory: Fory, private keySerializer?: Serializer | null, 
private valueSerializer?: Serializer | null) {
 
   }
 
   private getHead(keyInfo: number, valueInfo: number) {
     let flag = 0;
-    if (MapHeadUtil.isNull(keyInfo)) {
+    if (MapHeadUtil.isNull(valueInfo)) {
       flag |= MapFlags.HAS_NULL;
     }
-    if (MapHeadUtil.trackingRef(keyInfo)) {
+    if (MapHeadUtil.trackingRef(valueInfo)) {
       flag |= MapFlags.TRACKING_REF;
     }
-    flag <<= 4;
-    if (MapHeadUtil.isNull(valueInfo)) {
+    if (this.valueSerializer) {
+      flag |= MapFlags.DECL_ELEMENT_TYPE;
+    }
+    flag <<= 3;
+    if (MapHeadUtil.isNull(keyInfo)) {
       flag |= MapFlags.HAS_NULL;
     }
-    if (MapHeadUtil.trackingRef(valueInfo)) {
+    if (MapHeadUtil.trackingRef(keyInfo)) {
       flag |= MapFlags.TRACKING_REF;
     }
+    if (this.keySerializer) {
+      flag |= MapFlags.DECL_ELEMENT_TYPE;
+    }
     return flag;
   }
 
@@ -91,9 +95,13 @@ class MapChunkWriter {
     // KV header
     const header = this.getHead(keyInfo, valueInfo);
     // chunkSize default 0 | KV header
-    this.fory.binaryWriter.uint16(header << 8);
-    // key TypeId | value TypeId
-    this.fory.binaryWriter.uint32((keyInfo >> 16) | (valueInfo & 0xFFFF0000));
+    this.fory.binaryWriter.uint16(header);
+    if (this.keySerializer) {
+      this.keySerializer.writeClassInfo(null);
+    }
+    if (this.valueSerializer) {
+      this.valueSerializer.writeClassInfo(null);
+    }
     return header;
   }
 
@@ -117,7 +125,7 @@ class MapChunkWriter {
 
   endChunk() {
     if (this.chunkOffset > 0) {
-      this.fory.binaryWriter.setUint8Position(this.chunkOffset, 
this.chunkSize);
+      this.fory.binaryWriter.setUint8Position(this.chunkOffset + 1, 
this.chunkSize);
       this.chunkSize = 0;
     }
   }
@@ -136,30 +144,36 @@ class MapAnySerializer {
     }
   }
 
-  private writeHead(header: number, v: any) {
-    if (header !== 0) {
-      if (header & MapFlags.HAS_NULL) {
-        if (v === null || v === undefined) {
-          this.fory.binaryWriter.uint8(RefFlags.NullFlag);
-        }
+  private writeFlag(header: number, v: any) {
+    if (header & MapFlags.TRACKING_REF) {
+      if (v === null || v === undefined) {
+        this.fory.binaryWriter.uint8(RefFlags.NullFlag);
+        return true;
       }
-      if (header & MapFlags.TRACKING_REF) {
-        const keyRef = this.fory.referenceResolver.existsWriteObject(v);
-        if (keyRef !== undefined) {
-          this.fory.binaryWriter.uint8(RefFlags.RefFlag);
-          this.fory.binaryWriter.uint16(keyRef);
-        } else {
-          this.fory.binaryWriter.uint8(RefFlags.RefValueFlag);
-        }
+      const keyRef = this.fory.referenceResolver.existsWriteObject(v);
+      if (keyRef !== undefined) {
+        this.fory.binaryWriter.uint8(RefFlags.RefFlag);
+        this.fory.binaryWriter.uint16(keyRef);
+        return true;
+      } else {
+        this.fory.binaryWriter.uint8(RefFlags.RefValueFlag);
+        return false;
+      }
+    } else if (header & MapFlags.HAS_NULL) {
+      if (v === null || v === undefined) {
+        this.fory.binaryWriter.uint8(RefFlags.NullFlag);
+        return true;
       } else {
         this.fory.binaryWriter.uint8(RefFlags.NotNullValueFlag);
+        return false;
       }
     }
+    return false;
   }
 
   write(value: Map<any, any>) {
-    const mapChunkWriter = new MapChunkWriter(this.fory);
-    this.fory.binaryWriter.varInt32(value.size);
+    const mapChunkWriter = new MapChunkWriter(this.fory, this.keySerializer, 
this.valueSerializer);
+    this.fory.binaryWriter.writeVarUint32Small7(value.size);
     for (const [k, v] of value.entries()) {
       const keySerializer = this.keySerializer !== null ? this.keySerializer : 
this.fory.classResolver.getSerializerByData(k);
       const valueSerializer = this.valueSerializer !== null ? 
this.valueSerializer : this.fory.classResolver.getSerializerByData(v);
@@ -168,30 +182,36 @@ class MapAnySerializer {
         MapHeadUtil.elementInfo(keySerializer!.getTypeId()!, k == null ? 1 : 
0, keySerializer!.needToWriteRef() ? 1 : 0),
         MapHeadUtil.elementInfo(valueSerializer!.getTypeId()!, v == null ? 1 : 
0, valueSerializer!.needToWriteRef() ? 1 : 0)
       );
-
-      this.writeHead(header >> 4, k);
-      keySerializer!.write(k);
-      this.writeHead(header & 0b00001111, v);
-      valueSerializer!.write(v);
+      if (!this.writeFlag(header & 0b00001111, k)) {
+        if (this.keySerializer) {
+          keySerializer!.write(k);
+        } else {
+          keySerializer!.writeNoRef(k);
+        }
+      }
+      if (!this.writeFlag(header >> 4, v)) {
+        if (this.valueSerializer) {
+          valueSerializer!.write(v);
+        } else {
+          valueSerializer!.writeNoRef(v);
+        }
+      }
     }
     mapChunkWriter.endChunk();
   }
 
   private readElement(header: number, serializer: Serializer | null) {
-    if (header === 0) {
-      return serializer!.read(false);
-    }
-    const isSame = !(header & MapFlags.NOT_SAME_TYPE);
+    const declared = header & MapFlags.DECL_ELEMENT_TYPE;
     const includeNone = header & MapFlags.HAS_NULL;
     const trackingRef = header & MapFlags.TRACKING_REF;
 
-    let flag = 0;
-    if (trackingRef || includeNone) {
-      flag = this.fory.binaryReader.uint8();
+    if (!declared) {
+      serializer = AnyHelper.detectSerializer(this.fory);
     }
-    if (!isSame) {
-      serializer = 
this.fory.classResolver.getSerializerById(this.fory.binaryReader.readVarUint32Small7());
+    if (!trackingRef && !includeNone) {
+      return serializer!.read(false);
     }
+    const flag = this.fory.binaryReader.uint8();
     switch (flag) {
       case RefFlags.RefValueFlag:
         return serializer!.read(true);
@@ -205,30 +225,32 @@ class MapAnySerializer {
   }
 
   read(fromRef: boolean): any {
-    let count = this.fory.binaryReader.varInt32();
+    let count = this.fory.binaryReader.readVarUint32Small7();
     const result = new Map();
     if (fromRef) {
       this.fory.referenceResolver.reference(result);
     }
     while (count > 0) {
       const header = this.fory.binaryReader.uint16();
-      const keyHeader = header >> 12;
-      const valueHeader = (header >> 8) & 0b00001111;
-      const chunkSize = header & 0b11111111;
+      const valueHeader = (header >> 3) & 0b111;
+      const keyHeader = header & 0b111;
+      const chunkSize = header >> 8;
 
       let keySerializer = null;
       let valueSerializer = null;
 
-      if (!(keyHeader & MapFlags.NOT_SAME_TYPE)) {
-        keySerializer = 
this.fory.classResolver.getSerializerById(this.fory.binaryReader.uint16());
+      if (keyHeader & MapFlags.DECL_ELEMENT_TYPE) {
+        keySerializer = AnyHelper.detectSerializer(this.fory);
       }
-      if (!(valueHeader & MapFlags.NOT_SAME_TYPE)) {
-        valueSerializer = 
this.fory.classResolver.getSerializerById(this.fory.binaryReader.uint16());
+      if (valueHeader & MapFlags.DECL_ELEMENT_TYPE) {
+        valueSerializer = AnyHelper.detectSerializer(this.fory);
       }
       for (let index = 0; index < chunkSize; index++) {
+        const key = this.readElement(keyHeader, keySerializer);
+        const value = this.readElement(valueHeader, valueSerializer);
         result.set(
-          this.readElement(keyHeader, keySerializer),
-          this.readElement(valueHeader, valueSerializer)
+          key,
+          value
         );
         count--;
       }
@@ -256,9 +278,10 @@ export class MapSerializerGenerator extends 
BaseSerializerGenerator {
   private writeSpecificType(accessor: string) {
     const k = this.scope.uniqueName("k");
     const v = this.scope.uniqueName("v");
-    const keyHeader = (this.keyGenerator.needToWriteRef() ? 
MapFlags.TRACKING_REF : 0);
-    const valueHeader = (this.valueGenerator.needToWriteRef() ? 
MapFlags.TRACKING_REF : 0);
-    const typeId = (this.keyGenerator.getTypeId()! << 8) | 
(this.valueGenerator.getTypeId()!);
+    let keyHeader = (this.keyGenerator.needToWriteRef() ? 
MapFlags.TRACKING_REF : 0);
+    keyHeader |= MapFlags.DECL_ELEMENT_TYPE;
+    let valueHeader = (this.valueGenerator.needToWriteRef() ? 
MapFlags.TRACKING_REF : 0);
+    valueHeader |= MapFlags.DECL_ELEMENT_TYPE;
     const lastKeyIsNull = this.scope.uniqueName("lastKeyIsNull");
     const lastValueIsNull = this.scope.uniqueName("lastValueIsNull");
     const chunkSize = this.scope.uniqueName("chunkSize");
@@ -267,7 +290,7 @@ export class MapSerializerGenerator extends 
BaseSerializerGenerator {
     const valueRef = this.scope.uniqueName("valueRef");
 
     return `
-      ${this.builder.writer.varInt32(`${accessor}.size`)}
+      ${this.builder.writer.writeVarUint32Small7(`${accessor}.size`)}
       let ${lastKeyIsNull} = false;
       let ${lastValueIsNull} = false;
       let ${chunkSize} = 0;
@@ -276,61 +299,58 @@ export class MapSerializerGenerator extends 
BaseSerializerGenerator {
       for (const [${k}, ${v}] of ${accessor}.entries()) {
         let keyIsNull = ${k} === null || ${k} === undefined;
         let valueIsNull = ${v} === null || ${v} === undefined;
-
         if (${lastKeyIsNull} !== keyIsNull || ${lastValueIsNull} !== 
valueIsNull || ${chunkSize} === 0 || ${chunkSize} === 255) {
           if (${chunkSize} > 0) {
-            ${this.builder.writer.setUint8Position(chunkSizeOffset, 
chunkSize)};
+            ${this.builder.writer.setUint8Position(`${chunkSizeOffset} + 1`, 
chunkSize)};
             ${chunkSize} = 0;
           }
           ${chunkSizeOffset} = ${this.builder.writer.getCursor()}
-          ${
-            this.builder.writer.uint16(
-              `(((${keyHeader} | (keyIsNull ? ${MapFlags.HAS_NULL} : 0)) << 4) 
| (${valueHeader} | (valueIsNull ? ${MapFlags.HAS_NULL} : 0))) << 8`
+          debugger;
+          ${this.builder.writer.uint16(
+              `((${valueHeader} | (valueIsNull ? ${MapFlags.HAS_NULL} : 0)) << 
3) | (${keyHeader} | (keyIsNull ? ${MapFlags.HAS_NULL} : 0))`
             )
           }
-          ${this.builder.writer.uint32(typeId)};
-
           ${lastKeyIsNull} = keyIsNull;
           ${lastValueIsNull} = valueIsNull;
         }
         if (keyIsNull) {
           ${this.builder.writer.uint8(RefFlags.NullFlag)}
+        } else {
+          ${this.keyGenerator.needToWriteRef()
+          ? `
+              const ${keyRef} = 
${this.builder.referenceResolver.existsWriteObject(v)};
+              if (${keyRef} !== undefined) {
+                ${this.builder.writer.uint8(RefFlags.RefFlag)};
+                ${this.builder.writer.uint16(keyRef)};
+              } else {
+                ${this.builder.writer.uint8(RefFlags.RefValueFlag)};
+                ${this.keyGenerator.writeEmbed().write(k)}
+              }
+          `
+          : this.keyGenerator.writeEmbed().write(k)}
         }
-        ${this.keyGenerator.needToWriteRef()
-? `
-            const ${keyRef} = 
${this.builder.referenceResolver.existsWriteObject(v)};
-            if (${keyRef} !== undefined) {
-              ${this.builder.writer.uint8(RefFlags.RefFlag)};
-              ${this.builder.writer.uint16(keyRef)};
-            } else {
-              ${this.builder.writer.uint8(RefFlags.RefValueFlag)};
-              ${this.keyGenerator.writeEmbed().write(k)}
-            }
-        `
-: this.keyGenerator.writeEmbed().write(k)}
-       
 
         if (valueIsNull) {
           ${this.builder.writer.uint8(RefFlags.NullFlag)}
+        } else {
+          ${this.valueGenerator.needToWriteRef()
+          ? `
+              const ${valueRef} = 
${this.builder.referenceResolver.existsWriteObject(v)};
+              if (${valueRef} !== undefined) {
+                ${this.builder.writer.uint8(RefFlags.RefFlag)};
+                ${this.builder.writer.uint16(valueRef)};
+              } else {
+                ${this.builder.writer.uint8(RefFlags.RefValueFlag)};
+                ${this.valueGenerator.writeEmbed().write(v)};
+              }
+          `
+          : this.valueGenerator.writeEmbed().write(v)}
         }
-        ${this.valueGenerator.needToWriteRef()
-? `
-            const ${valueRef} = 
${this.builder.referenceResolver.existsWriteObject(v)};
-            if (${valueRef} !== undefined) {
-              ${this.builder.writer.uint8(RefFlags.RefFlag)};
-              ${this.builder.writer.uint16(valueRef)};
-            } else {
-              ${this.builder.writer.uint8(RefFlags.RefValueFlag)};
-              ${this.valueGenerator.writeEmbed().write(v)};
-            }
-        `
-: this.valueGenerator.writeEmbed().write(v)}
-        
 
         ${chunkSize}++;
       }
       if (${chunkSize} > 0) {
-        ${this.builder.writer.setUint8Position(chunkSizeOffset, chunkSize)};
+        ${this.builder.writer.setUint8Position(`${chunkSizeOffset} + 1`, 
chunkSize)};
       }
     `;
   }
@@ -340,73 +360,74 @@ export class MapSerializerGenerator extends 
BaseSerializerGenerator {
     if (!this.isAny()) {
       return this.writeSpecificType(accessor);
     }
-    return `new (${anySerializer})(${this.builder.getForyName()}, ${
-      this.typeInfo.options.key.typeId !== TypeId.UNKNOWN ? 
this.typeInfo.options.key.typeId : null
-    }, ${
-      this.typeInfo.options.value.typeId !== TypeId.UNKNOWN ? 
this.typeInfo.options.value.typeId : null
-    }).write(${accessor})`;
+    return `new (${anySerializer})(${this.builder.getForyName()}, 
${this.typeInfo.options.key.typeId !== TypeId.UNKNOWN ? 
this.typeInfo.options.key.typeId : null
+      }, ${this.typeInfo.options.value.typeId !== TypeId.UNKNOWN ? 
this.typeInfo.options.value.typeId : null
+      }).write(${accessor})`;
   }
 
-  private readStmtSpecificType(accessor: (expr: string) => string, refState: 
string) {
+  private readSpecificType(accessor: (expr: string) => string, refState: 
string) {
     const count = this.scope.uniqueName("count");
     const result = this.scope.uniqueName("result");
 
     return `
-      let ${count} = ${this.builder.reader.varInt32()};
+      let ${count} = ${this.builder.reader.readVarUint32Small7()};
+      debugger;
       const ${result} = new Map();
       if (${refState}) {
         ${this.builder.referenceResolver.reference(result)}
       }
       while (${count} > 0) {
         const header = ${this.builder.reader.uint16()};
-        const keyHeader = header >> 12;
-        const valueHeader = (header >> 8) & 0b00001111;
-        const chunkSize = header & 0b11111111;
-        ${this.builder.reader.skip(4)};
+        const keyHeader = header & 0b111;
+        const valueHeader = (header >> 3) & 0b111;
+        const chunkSize = header >> 8;
         const keyIncludeNone = keyHeader & ${MapFlags.HAS_NULL};
         const keyTrackingRef = keyHeader & ${MapFlags.TRACKING_REF};
         const valueIncludeNone = valueHeader & ${MapFlags.HAS_NULL};
         const valueTrackingRef = valueHeader & ${MapFlags.TRACKING_REF};
-    
         for (let index = 0; index < chunkSize; index++) {
           let key;
           let value;
-          let flag = 0;
           if (keyTrackingRef || keyIncludeNone) {
-            flag = ${this.builder.reader.uint8()};
-          }
-          switch (flag) {
-            case ${RefFlags.RefValueFlag}:
-              ${this.keyGenerator.read(x => `key = ${x}`, "true")}
-              break;
-            case ${RefFlags.RefFlag}:
-              key = 
${this.builder.referenceResolver.getReadObject(this.builder.reader.varInt32())}
-              break;
-            case ${RefFlags.NullFlag}:
-              key = null;
-              break;
-            case ${RefFlags.NotNullValueFlag}:
-              ${this.keyGenerator.read(x => `key = ${x}`, "true")}
-              break;
+            const flag = ${this.builder.reader.uint8()};
+            switch (flag) {
+              case ${RefFlags.RefValueFlag}:
+                ${this.keyGenerator.read(x => `key = ${x}`, "true")}
+                break;
+              case ${RefFlags.RefFlag}:
+                key = 
${this.builder.referenceResolver.getReadObject(this.builder.reader.varInt32())}
+                break;
+              case ${RefFlags.NullFlag}:
+                key = null;
+                break;
+              case ${RefFlags.NotNullValueFlag}:
+                ${this.keyGenerator.read(x => `key = ${x}`, "false")}
+                break;
+            }
+          } else {
+              ${this.keyGenerator.read(x => `key = ${x}`, "false")}
           }
-          flag = 0;
+          
           if (valueTrackingRef || valueIncludeNone) {
-            flag = ${this.builder.reader.uint8()};
-          }
-          switch (flag) {
-            case ${RefFlags.RefValueFlag}:
-              ${this.valueGenerator.read(x => `value = ${x}`, "true")}
-              break;
-            case ${RefFlags.RefFlag}:
-              value = 
${this.builder.referenceResolver.getReadObject(this.builder.reader.varInt32())}
-              break;
-            case ${RefFlags.NullFlag}:
-              value = null;
-              break;
-            case ${RefFlags.NotNullValueFlag}:
-              ${this.valueGenerator.read(x => `value = ${x}`, "false")}
-              break;
+            const flag = ${this.builder.reader.uint8()};
+            switch (flag) {
+              case ${RefFlags.RefValueFlag}:
+                ${this.valueGenerator.read(x => `value = ${x}`, "true")}
+                break;
+              case ${RefFlags.RefFlag}:
+                value = 
${this.builder.referenceResolver.getReadObject(this.builder.reader.varInt32())}
+                break;
+              case ${RefFlags.NullFlag}:
+                value = null;
+                break;
+              case ${RefFlags.NotNullValueFlag}:
+                ${this.valueGenerator.read(x => `value = ${x}`, "false")}
+                break;
+            }
+          } else {
+            ${this.valueGenerator.read(x => `value = ${x}`, "false")}
           }
+          
           ${result}.set(
             key,
             value
@@ -421,13 +442,11 @@ export class MapSerializerGenerator extends 
BaseSerializerGenerator {
   read(accessor: (expr: string) => string, refState: string): string {
     const anySerializer = this.builder.getExternal(MapAnySerializer.name);
     if (!this.isAny()) {
-      return this.readStmtSpecificType(accessor, refState);
+      return this.readSpecificType(accessor, refState);
     }
-    return accessor(`new (${anySerializer})(${this.builder.getForyName()}, ${
-      this.typeInfo.options.key.typeId !== TypeId.UNKNOWN ? 
(this.typeInfo.options.key.typeId) : null
-    }, ${
-      this.typeInfo.options.value.typeId !== TypeId.UNKNOWN ? 
(this.typeInfo.options.value.typeId) : null
-    }).read(${refState})`);
+    return accessor(`new (${anySerializer})(${this.builder.getForyName()}, 
${this.typeInfo.options.key.typeId !== TypeId.UNKNOWN ? 
(this.typeInfo.options.key.typeId) : null
+      }, ${this.typeInfo.options.value.typeId !== TypeId.UNKNOWN ? 
(this.typeInfo.options.value.typeId) : null
+      }).read(${refState})`);
   }
 
   getFixedSize(): number {
diff --git a/javascript/packages/fory/lib/meta/TypeMeta.ts 
b/javascript/packages/fory/lib/meta/TypeMeta.ts
index 111128e7d..89b8cd320 100644
--- a/javascript/packages/fory/lib/meta/TypeMeta.ts
+++ b/javascript/packages/fory/lib/meta/TypeMeta.ts
@@ -51,7 +51,7 @@ export const isPrimitiveTypeId = (typeId: number): boolean => 
{
 };
 
 export const refTrackingAbleTypeId = (typeId: number): boolean => {
-  return PRIMITIVE_TYPE_IDS.includes(typeId as any) || [TypeId.DURATION, 
TypeId.DATE, TypeId.TIMESTAMP].includes(typeId as any);
+  return PRIMITIVE_TYPE_IDS.includes(typeId as any) || [TypeId.DURATION, 
TypeId.DATE, TypeId.TIMESTAMP, TypeId.STRING].includes(typeId as any);
 };
 
 export const isInternalTypeId = (typeId: number): boolean => {
diff --git a/javascript/packages/fory/lib/type.ts 
b/javascript/packages/fory/lib/type.ts
index 0ab273661..8be2fd953 100644
--- a/javascript/packages/fory/lib/type.ts
+++ b/javascript/packages/fory/lib/type.ts
@@ -182,7 +182,7 @@ export type Serializer<T = any, T2 = any> = {
   write: (v: T2) => void;
   writeRef: (v: T2) => void;
   writeNoRef: (v: T2) => void;
-  writeRefOrNull: (v: T2) => void;
+  writeRefOrNull: (v: T2) => boolean;
   writeClassInfo: (v: T2) => void;
 
   read: (fromRef: boolean) => T2;


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


Reply via email to