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 34b312ed0 feat(JavaScript): Impl xlang JavaScript testcase (#3263)
34b312ed0 is described below
commit 34b312ed091f4d7cb4e3b3fdb7e101cab1f6b557
Author: weipeng <[email protected]>
AuthorDate: Thu Feb 5 15:54:39 2026 +0800
feat(JavaScript): Impl xlang JavaScript testcase (#3263)
## Why?
## What does this PR do?
Impl xlang JavaScript testcase
(test_simple_struct,test_named_simple_struct,test_list). Other cases
will be submit on next PR.
## Related issues
#3133
## 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
---
javascript/packages/fory/lib/gen/any.ts | 30 +++++---
javascript/packages/fory/lib/gen/collection.ts | 17 ++---
javascript/packages/fory/lib/gen/map.ts | 2 -
javascript/packages/fory/lib/gen/number.ts | 7 ++
javascript/packages/fory/lib/meta/TypeMeta.ts | 8 ++-
javascript/packages/fory/lib/typeInfo.ts | 7 ++
javascript/packages/fory/lib/typeResolver.ts | 29 ++++++--
javascript/test/any.test.ts | 4 +-
javascript/test/crossLanguage.test.ts | 98 ++++++++++++--------------
9 files changed, 121 insertions(+), 81 deletions(-)
diff --git a/javascript/packages/fory/lib/gen/any.ts
b/javascript/packages/fory/lib/gen/any.ts
index d2330f4db..805cd9e28 100644
--- a/javascript/packages/fory/lib/gen/any.ts
+++ b/javascript/packages/fory/lib/gen/any.ts
@@ -24,6 +24,7 @@ import { CodegenRegistry } from "./router";
import { Mode, Serializer, TypeId } from "../type";
import { Scope } from "./scope";
import Fory from "../fory";
+import { TypeMeta } from "../meta/TypeMeta";
export class AnyHelper {
static detectSerializer(fory: Fory) {
@@ -33,26 +34,37 @@ export class AnyHelper {
userTypeId = fory.binaryReader.readVarUint32Small7();
}
let serializer: Serializer | undefined;
+
+ function tryUpdateSerializer(serializer: Serializer | undefined | null,
typeMeta: TypeMeta) {
+ if (!serializer) {
+ throw new Error(`can't find implements of typeId: ${typeId}`);
+ }
+ const hash = serializer.getHash();
+ if (hash !== typeMeta.getHash()) {
+ return fory.typeMetaResolver.genSerializerByTypeMetaRuntime(typeMeta);
+ }
+ return serializer;
+ }
+
switch (typeId) {
case TypeId.COMPATIBLE_STRUCT:
+ {
+ const typeMeta =
fory.typeMetaResolver.readTypeMeta(fory.binaryReader);
+ serializer = fory.typeResolver.getSerializerById(typeId,
typeMeta.getUserTypeId());
+ serializer = tryUpdateSerializer(serializer, typeMeta);
+ }
+ break;
case TypeId.NAMED_ENUM:
case TypeId.NAMED_STRUCT:
case TypeId.NAMED_EXT:
case TypeId.NAMED_UNION:
case TypeId.NAMED_COMPATIBLE_STRUCT:
- if (fory.config.mode === Mode.Compatible || typeId ===
TypeId.COMPATIBLE_STRUCT) {
+ if (fory.config.mode === Mode.Compatible || typeId ===
TypeId.NAMED_COMPATIBLE_STRUCT) {
const typeMeta =
fory.typeMetaResolver.readTypeMeta(fory.binaryReader);
const ns = typeMeta.getNs();
const typeName = typeMeta.getTypeName();
const named = `${ns}$${typeName}`;
- serializer = fory.typeResolver.getSerializerByName(named);
- if (!serializer) {
- throw new Error(`can't find implements of typeId: ${typeId}`);
- }
- const hash = serializer.getHash();
- if (hash !== typeMeta.getHash()) {
- serializer =
fory.typeMetaResolver.genSerializerByTypeMetaRuntime(typeMeta);
- }
+ serializer =
tryUpdateSerializer(fory.typeResolver.getSerializerByName(named), typeMeta);
} else {
const ns = fory.metaStringResolver.readNamespace(fory.binaryReader);
const typeName =
fory.metaStringResolver.readTypeName(fory.binaryReader);
diff --git a/javascript/packages/fory/lib/gen/collection.ts
b/javascript/packages/fory/lib/gen/collection.ts
index 74c18946b..2d7941bfa 100644
--- a/javascript/packages/fory/lib/gen/collection.ts
+++ b/javascript/packages/fory/lib/gen/collection.ts
@@ -53,8 +53,9 @@ class CollectionAnySerializer {
let trackingRef = false;
for (const item of arr) {
- if ((item === undefined || item === null) && !includeNone) {
+ if ((item === undefined || item === null)) {
includeNone = true;
+ continue;
}
const current = this.fory.typeResolver.getSerializerByData(item);
if (!current) {
@@ -105,9 +106,9 @@ class CollectionAnySerializer {
} else if (includeNone) {
for (const item of value) {
if (item === null || item === undefined) {
- this.fory.binaryWriter.uint8(RefFlags.NullFlag);
+ this.fory.binaryWriter.int8(RefFlags.NullFlag);
} else {
- this.fory.binaryWriter.uint8(RefFlags.NotNullValueFlag);
+ this.fory.binaryWriter.int8(RefFlags.NotNullValueFlag);
serializer!.write(item);
}
}
@@ -125,10 +126,10 @@ class CollectionAnySerializer {
} else if (includeNone) {
for (const item of value) {
if (item === null || item === undefined) {
- this.fory.binaryWriter.uint8(RefFlags.NullFlag);
+ this.fory.binaryWriter.int8(RefFlags.NullFlag);
} else {
const serializer =
this.fory.typeResolver.getSerializerByData(item);
- this.fory.binaryWriter.uint8(RefFlags.NotNullValueFlag);
+ this.fory.binaryWriter.int8(RefFlags.NotNullValueFlag);
serializer!.write(item);
}
}
@@ -166,7 +167,7 @@ class CollectionAnySerializer {
}
} else if (includeNone) {
for (let i = 0; i < len; i++) {
- const flag = this.fory.binaryReader.uint8();
+ const flag = this.fory.binaryReader.int8();
if (flag === RefFlags.NullFlag) {
accessor(result, i, null);
} else {
@@ -186,7 +187,7 @@ class CollectionAnySerializer {
}
} else if (includeNone) {
for (let i = 0; i < len; i++) {
- const flag = this.fory.binaryReader.uint8();
+ const flag = this.fory.binaryReader.int8();
if (flag === RefFlags.NullFlag) {
accessor(result, i, null);
} else {
@@ -317,7 +318,7 @@ export abstract class CollectionSerializerGenerator extends
BaseSerializerGenera
}
} else if (${flags} & ${CollectionFlags.HAS_NULL}) {
for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
- if (${this.builder.reader.uint8()} ==
${RefFlags.NullFlag}) {
+ if (${this.builder.reader.int8()} == ${RefFlags.NullFlag})
{
${this.putAccessor(result, "null", idx)}
} else {
${this.innerGenerator.read(x =>
`${this.putAccessor(result, x, idx)}`, "false")}
diff --git a/javascript/packages/fory/lib/gen/map.ts
b/javascript/packages/fory/lib/gen/map.ts
index 7cb32e130..89066af8d 100644
--- a/javascript/packages/fory/lib/gen/map.ts
+++ b/javascript/packages/fory/lib/gen/map.ts
@@ -305,7 +305,6 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
${chunkSize} = 0;
}
${chunkSizeOffset} = ${this.builder.writer.getCursor()}
- debugger;
${this.builder.writer.uint16(
`((${valueHeader} | (valueIsNull ? ${MapFlags.HAS_NULL} : 0)) <<
3) | (${keyHeader} | (keyIsNull ? ${MapFlags.HAS_NULL} : 0))`
)
@@ -371,7 +370,6 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
return `
let ${count} = ${this.builder.reader.readVarUint32Small7()};
- debugger;
const ${result} = new Map();
if (${refState}) {
${this.builder.referenceResolver.reference(result)}
diff --git a/javascript/packages/fory/lib/gen/number.ts
b/javascript/packages/fory/lib/gen/number.ts
index a477c3d16..a33033975 100644
--- a/javascript/packages/fory/lib/gen/number.ts
+++ b/javascript/packages/fory/lib/gen/number.ts
@@ -155,3 +155,10 @@ CodegenRegistry.register(TypeId.TAGGED_UINT64,
builder => builder.reader.varUInt64()
)
);
+
+CodegenRegistry.register(TypeId.VARINT64,
+ buildNumberSerializer(
+ (builder, accessor) => builder.writer.varUInt64(accessor),
+ builder => builder.reader.varUInt64()
+ )
+);
diff --git a/javascript/packages/fory/lib/meta/TypeMeta.ts
b/javascript/packages/fory/lib/meta/TypeMeta.ts
index 649afc57e..985710c6f 100644
--- a/javascript/packages/fory/lib/meta/TypeMeta.ts
+++ b/javascript/packages/fory/lib/meta/TypeMeta.ts
@@ -228,6 +228,7 @@ export class TypeMeta {
}
static fromTypeInfo(typeInfo: StructTypeInfo) {
+ const structTypeInfo = typeInfo;
let fieldInfo = Object.entries(typeInfo.options.props!).map(([fieldName,
typeInfo]) => {
let fieldTypeId = typeInfo.typeId;
if (fieldTypeId === TypeId.NAMED_ENUM) {
@@ -235,12 +236,13 @@ export class TypeMeta {
} else if (fieldTypeId === TypeId.NAMED_UNION || fieldTypeId ===
TypeId.TYPED_UNION) {
fieldTypeId = TypeId.UNION;
}
+ const { trackingRef, nullable } =
structTypeInfo.options.fieldInfo?.[fieldName] || {};
return new FieldInfo(
fieldName,
fieldTypeId,
- -1,
- false,
- false,
+ typeInfo.userTypeId,
+ trackingRef,
+ nullable,
typeInfo.options,
);
});
diff --git a/javascript/packages/fory/lib/typeInfo.ts
b/javascript/packages/fory/lib/typeInfo.ts
index a4dd7e151..9a1b519cb 100644
--- a/javascript/packages/fory/lib/typeInfo.ts
+++ b/javascript/packages/fory/lib/typeInfo.ts
@@ -384,6 +384,8 @@ export type HintInput<T> = T extends {
| typeof TypeId.UINT8
| typeof TypeId.UINT16
| typeof TypeId.UINT32
+ | typeof TypeId.UINT64
+ | typeof TypeId.VAR_UINT64
| typeof TypeId.VAR_UINT32
| typeof TypeId.FLOAT8
| typeof TypeId.FLOAT16
@@ -657,6 +659,11 @@ export const Type = {
(TypeId.VAR_UINT64),
);
},
+ varInt64() {
+ return TypeInfo.fromNonParam<typeof TypeId.VARINT64>(
+ (TypeId.VARINT64),
+ );
+ },
taggedUInt64() {
return TypeInfo.fromNonParam<typeof TypeId.TAGGED_UINT64>(
(TypeId.TAGGED_UINT64),
diff --git a/javascript/packages/fory/lib/typeResolver.ts
b/javascript/packages/fory/lib/typeResolver.ts
index e7af28b48..f4c5f5423 100644
--- a/javascript/packages/fory/lib/typeResolver.ts
+++ b/javascript/packages/fory/lib/typeResolver.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { ForyTypeInfoSymbol, WithForyClsInfo, Serializer, TypeId } from
"./type";
+import { ForyTypeInfoSymbol, WithForyClsInfo, Serializer, TypeId, MaxInt32,
MinInt32 } from "./type";
import { Gen } from "./gen";
import { Type, TypeInfo } from "./typeInfo";
import Fory from "./fory";
@@ -91,6 +91,8 @@ export default class TypeResolver {
registerSerializer(Type.int16());
registerSerializer(Type.int32());
registerSerializer(Type.varInt32());
+ registerSerializer(Type.varUInt64());
+ registerSerializer(Type.varInt64());
registerSerializer(Type.int64());
registerSerializer(Type.sliInt64());
registerSerializer(Type.float16());
@@ -109,7 +111,10 @@ export default class TypeResolver {
registerSerializer(Type.float32Array());
registerSerializer(Type.float64Array());
- this.numberSerializer = this.getSerializerById(TypeId.FLOAT64);
+ this.float64Serializer = this.getSerializerById(TypeId.FLOAT64);
+ this.float32Serializer = this.getSerializerById(TypeId.FLOAT32);
+ this.varint32Serializer = this.getSerializerById(TypeId.VARINT32);
+ this.taggedint64Serializer = this.getSerializerById(TypeId.TAGGED_INT64);
this.int64Serializer = this.getSerializerById((TypeId.INT64));
this.boolSerializer = this.getSerializerById((TypeId.BOOL));
this.dateSerializer = this.getSerializerById((TypeId.TIMESTAMP));
@@ -127,7 +132,10 @@ export default class TypeResolver {
this.int64ArraySerializer = this.getSerializerById(TypeId.INT64_ARRAY);
}
- private numberSerializer: null | Serializer = null;
+ private float64Serializer: null | Serializer = null;
+ private float32Serializer: null | Serializer = null;
+ private varint32Serializer: null | Serializer = null;
+ private taggedint64Serializer: null | Serializer = null;
private int64Serializer: null | Serializer = null;
private boolSerializer: null | Serializer = null;
private dateSerializer: null | Serializer = null;
@@ -240,7 +248,20 @@ export default class TypeResolver {
getSerializerByData(v: any) {
// internal types
if (typeof v === "number") {
- return this.numberSerializer;
+ if (Number.isInteger(v)) {
+ if (v > MaxInt32 || v < MinInt32) {
+ return this.taggedint64Serializer;
+ }
+ return this.varint32Serializer;
+ }
+ if (v > MaxInt32 || v < MinInt32) {
+ return this.float64Serializer;
+ }
+ return this.float32Serializer;
+ }
+
+ if (typeof v === "bigint") {
+ return this.taggedint64Serializer;
}
if (typeof v === "string") {
diff --git a/javascript/test/any.test.ts b/javascript/test/any.test.ts
index 135363fbd..d27f4f7a2 100644
--- a/javascript/test/any.test.ts
+++ b/javascript/test/any.test.ts
@@ -47,7 +47,7 @@ describe('bool', () => {
test('should write big number work', () => {
const fory = new Fory();
const bin = fory.serialize(3000000000);
- expect(fory.deserialize(bin)).toBe(3000000000);
+ expect(fory.deserialize(bin)).toBe(3000000000n);
});
test('should write INFINITY work', () => {
@@ -59,7 +59,7 @@ describe('bool', () => {
test('should write float work', () => {
const fory = new Fory();
const bin = fory.serialize(123.123);
- expect(fory.deserialize(bin)).toBe(123.123)
+ expect(fory.deserialize(bin).toFixed(3)).toBe("123.123")
});
test('should write bigint work', () => {
diff --git a/javascript/test/crossLanguage.test.ts
b/javascript/test/crossLanguage.test.ts
index 71601ab88..bc1e8aa67 100644
--- a/javascript/test/crossLanguage.test.ts
+++ b/javascript/test/crossLanguage.test.ts
@@ -218,29 +218,25 @@ describe("bool", () => {
writeToFile(writer.dump() as Buffer);
});
test("test_string_serializer", () => {
- const fory = new Fory();
- const reader = new BinaryReader({});
- reader.reset(content);
-
+ const fory = new Fory({
+ mode: Mode.Compatible
+ });
// Deserialize strings from Java
const deserializedStrings = [];
let cursor = 0;
for (let i = 0; i < 7; i++) { // 7 test strings
- const deserializedString = fory.deserialize(content.slice(cursor));
+ const deserializedString = fory.deserialize(content.subarray(cursor));
cursor += fory.binaryReader.getCursor();
deserializedStrings.push(deserializedString);
}
-
- const writer = new BinaryWriter();
- writer.reserve(1024);
-
+ const bfs = []
// Serialize each deserialized string back
for (const testString of deserializedStrings) {
const serializedData = fory.serialize(testString);
- writer.buffer(serializedData);
+ bfs.push(serializedData);
}
- writeToFile(writer.dump() as Buffer);
+ writeToFile(Buffer.concat(bfs));
});
test("test_cross_language_serializer", () => {
if (Boolean("1")) { return; }
@@ -306,10 +302,12 @@ describe("bool", () => {
// Define SimpleStruct class with field type registration
@Type.struct(103, {
+ f1: Type.map(Type.varInt32(), Type.float64()),
f2: Type.varInt32(),
f3: Type.struct(102),
f4: Type.string(),
f5: Type.enum(101, Color),
+ f6: Type.array(Type.string()),
f7: Type.varInt32(),
f8: Type.varInt32(),
last: Type.varInt32()
@@ -319,6 +317,8 @@ describe("bool", () => {
f3: Item | null = null;
f4: string = "";
f5: number = 0; // Color enum value
+ f1 = new Map([[1, 1.0], [2, 2.0]])
+ f6 = ["f6"]
f7: number = 0;
f8: number = 0;
last: number = 0;
@@ -334,15 +334,13 @@ describe("bool", () => {
// Serialize the deserialized object back
const serializedData = fory.serialize(deserializedObj);
- const deserializedObj2 = fory.deserialize(serializedData);
-
- console.log('===')
- // writeToFile(serializedData as Buffer);
+ writeToFile(serializedData as Buffer);
});
test("test_named_simple_struct", () => {
- if (Boolean("1")) { return; }
// Same as test_simple_struct but with named registration
- const fory = new Fory();
+ const fory = new Fory({
+ mode: Mode.Compatible
+ });
// Define Color enum
const Color = {
@@ -364,15 +362,15 @@ describe("bool", () => {
// Define SimpleStruct class with field type registration
@Type.struct({ namespace: "demo", typeName: "simple_struct" }, {
- f1: Type.map(Type.int32(), Type.float64()),
- f2: Type.int32(),
+ f1: Type.map(Type.varInt32(), Type.float64()),
+ f2: Type.varInt32(),
f3: Type.struct({ namespace: "demo", typeName: "item" }),
f4: Type.string(),
f5: Type.enum({ namespace: 'demo', typeName: "color" }, Color),
f6: Type.array(Type.string()),
- f7: Type.int32(),
- f8: Type.int32(),
- last: Type.int32()
+ f7: Type.varInt32(),
+ f8: Type.varInt32(),
+ last: Type.varInt32()
})
class SimpleStruct {
f1: Map<number, number> = new Map();
@@ -387,20 +385,17 @@ describe("bool", () => {
}
fory.registerSerializer(SimpleStruct);
- const reader = new BinaryReader({});
- reader.reset(content);
-
// Deserialize the object from Java
- const deserializedObj =
fory.deserialize(reader.buffer(reader.varUInt32()));
-
+ const deserializedObj = fory.deserialize(content);
// Serialize the deserialized object back
const serializedData = fory.serialize(deserializedObj);
writeToFile(serializedData as Buffer);
});
test("test_list", () => {
- if (Boolean("1")) { return; }
- const fory = new Fory();
+ const fory = new Fory({
+ mode: Mode.Compatible
+ });
@Type.struct(102, {
name: Type.string()
@@ -410,26 +405,24 @@ describe("bool", () => {
}
fory.registerSerializer(Item);
- const reader = new BinaryReader({});
- reader.reset(content);
// Deserialize all lists from Java
const deserializedLists = [];
+ let cursor = 0;
for (let i = 0; i < 4; i++) { // 4 lists
- const deserializedList =
fory.deserialize(reader.buffer(reader.varUInt32()));
+ const deserializedList = fory.deserialize(content.subarray(cursor));
+ cursor += fory.binaryReader.getCursor();
deserializedLists.push(deserializedList);
}
- const writer = new BinaryWriter();
- writer.reserve(512);
+ const bfs = [];
// Serialize each deserialized list back
for (const list of deserializedLists) {
const serializedData = fory.serialize(list);
- writer.buffer(serializedData);
+ bfs.push(serializedData)
}
-
- writeToFile(writer.dump() as Buffer);
+ writeToFile(Buffer.concat(bfs));
});
test("test_map", () => {
@@ -468,16 +461,17 @@ describe("bool", () => {
});
test("test_integer", () => {
- if (Boolean("1")) { return; }
- const fory = new Fory();
+ const fory = new Fory({
+ mode: Mode.Compatible
+ });
@Type.struct(101, {
- f1: Type.int32(),
- f2: Type.int32(),
- f3: Type.int32(),
- f4: Type.int32(),
- f5: Type.int32(),
- f6: Type.int32()
+ f1: Type.varInt32(),
+ f2: Type.varInt32(),
+ f3: Type.varInt32(),
+ f4: Type.varInt32(),
+ f5: Type.varInt32(),
+ f6: Type.varInt32()
})
class Item1 {
f1: number = 0;
@@ -490,26 +484,24 @@ describe("bool", () => {
fory.registerSerializer(Item1);
- const reader = new BinaryReader({});
- reader.reset(content);
// Deserialize item and individual integers from Java
const deserializedData = [];
+ let cursor = 0;
for (let i = 0; i < 7; i++) { // 1 item + 6 integers
- const deserializedItem =
fory.deserialize(reader.buffer(reader.varUInt32()));
+ const deserializedItem = fory.deserialize(content.subarray(cursor));
+ cursor += fory.binaryReader.getCursor();
deserializedData.push(deserializedItem);
}
- const writer = new BinaryWriter();
- writer.reserve(256);
-
+ const bfs = []
// Serialize each deserialized item back
for (const item of deserializedData) {
const serializedData = fory.serialize(item);
- writer.buffer(serializedData);
+ bfs.push(serializedData);
}
- writeToFile(writer.dump() as Buffer);
+ writeToFile(Buffer.concat(bfs));
});
test("test_item", () => {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]