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 c080d7b4c feat(JavaScript): fix testcase (#3334)
c080d7b4c is described below
commit c080d7b4cbac6c2a2f27f01136e293a06d831727
Author: weipeng <[email protected]>
AuthorDate: Sat Feb 14 14:45:21 2026 +0800
feat(JavaScript): fix testcase (#3334)
## Why?
1. fix testcase, now all the testcases were passed.
## What does this PR do?
## 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
---
.github/workflows/ci.yml | 4 +-
.../org/apache/fory/xlang/JavaScriptXlangTest.java | 9 +-
javascript/package.json | 1 +
javascript/packages/fory/index.ts | 6 -
javascript/packages/fory/lib/fory.ts | 6 +-
javascript/packages/fory/lib/gen/any.ts | 2 +-
javascript/packages/fory/lib/gen/array.ts | 8 +-
javascript/packages/fory/lib/gen/collection.ts | 13 +-
javascript/packages/fory/lib/gen/enum.ts | 18 +-
javascript/packages/fory/lib/gen/ext.ts | 10 +-
javascript/packages/fory/lib/gen/index.ts | 23 ++-
javascript/packages/fory/lib/gen/map.ts | 40 ++--
javascript/packages/fory/lib/gen/serializer.ts | 9 +-
javascript/packages/fory/lib/gen/set.ts | 10 +-
javascript/packages/fory/lib/gen/struct.ts | 49 ++---
javascript/packages/fory/lib/meta/TypeMeta.ts | 25 +--
javascript/packages/fory/lib/type.ts | 5 +-
javascript/packages/fory/lib/typeInfo.ts | 228 ++++++++++-----------
javascript/packages/fory/lib/typeMetaResolver.ts | 74 ++++---
javascript/packages/fory/lib/typeResolver.ts | 30 +--
javascript/test/crossLanguage.test.ts | 191 ++++++++---------
javascript/test/object.test.ts | 15 +-
javascript/test/protocol/struct.test.ts | 11 +-
23 files changed, 386 insertions(+), 401 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d4ddfe533..8071b79da 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -463,14 +463,14 @@ jobs:
${{ runner.os }}-maven-
- name: Run JavaScript Xlang Test
env:
- FORY_JAVASCRIPT_JAVA_CI: "0"
+ FORY_JAVASCRIPT_JAVA_CI: "1"
run: |
cd javascript
npm install
cd ../java
mvn -T16 --no-transfer-progress clean install -DskipTests
cd fory-core
- mvn -T16 --no-transfer-progress test
-Dtest=org.apache.fory.xlang.JavaScriptXlangTest
+ mvn --no-transfer-progress test
-Dtest=org.apache.fory.xlang.JavaScriptXlangTest -DforkCount=0
cpp_examples:
name: C++ Examples
diff --git
a/java/fory-core/src/test/java/org/apache/fory/xlang/JavaScriptXlangTest.java
b/java/fory-core/src/test/java/org/apache/fory/xlang/JavaScriptXlangTest.java
index 8cb87be1c..1ff742642 100644
---
a/java/fory-core/src/test/java/org/apache/fory/xlang/JavaScriptXlangTest.java
+++
b/java/fory-core/src/test/java/org/apache/fory/xlang/JavaScriptXlangTest.java
@@ -34,13 +34,12 @@ import org.testng.annotations.Test;
/** Executes cross-language tests against the Rust implementation. */
@Test
public class JavaScriptXlangTest extends XlangTestBase {
- private static final String NODE_EXECUTABLE = "npx";
- private static final String NODE_MODULE = "crossLanguage.test.ts";
+ private static final String NODE_EXECUTABLE = "npm";
private static final List<String> RUST_BASE_COMMAND =
- Arrays.asList(NODE_EXECUTABLE, "jest", NODE_MODULE, "-t", "caseName");
+ Arrays.asList(NODE_EXECUTABLE, "run", "test:crosslanguage", "-s", "--",
"caseName");
- private static final int NODE_TESTCASE_INDEX = 4;
+ private static final int NODE_TESTCASE_INDEX = 5;
@Override
protected void ensurePeerReady() {
@@ -182,7 +181,7 @@ public class JavaScriptXlangTest extends XlangTestBase {
@Override
@Test(dataProvider = "enableCodegen")
public void testCollectionElementRefOverride(boolean enableCodegen) throws
java.io.IOException {
- throw new SkipException("Skipping: JavaScript xlang test not implemented
for this case");
+ super.testCollectionElementRefOverride(enableCodegen);
}
@Test(dataProvider = "enableCodegen")
diff --git a/javascript/package.json b/javascript/package.json
index 17deca025..fca8923ba 100644
--- a/javascript/package.json
+++ b/javascript/package.json
@@ -1,5 +1,6 @@
{
"scripts": {
+ "test:crosslanguage": "f() { npx jest crossLanguage.test.ts -t \"$@\" >
/dev/null 2>&1; }; f",
"test": "npm run build && jest",
"clear": "rm -rf ./packages/fory/dist && rm -rf ./packages/hps/dist",
"build": "npm run clear && npm run build -w packages/fory -w packages/hps",
diff --git a/javascript/packages/fory/index.ts
b/javascript/packages/fory/index.ts
index efb460796..3e2a7a88c 100644
--- a/javascript/packages/fory/index.ts
+++ b/javascript/packages/fory/index.ts
@@ -18,11 +18,8 @@
*/
import {
- StructTypeInfo,
TypeInfo,
- ArrayTypeInfo,
Type,
- ForyField,
Dynamic,
} from "./lib/typeInfo";
import { Serializer, Mode } from "./lib/type";
@@ -34,12 +31,9 @@ import { BFloat16, BFloat16Array } from "./lib/bfloat16";
export {
Serializer,
TypeInfo,
- ArrayTypeInfo,
- StructTypeInfo,
Type,
Mode,
BinaryWriter,
- ForyField,
Dynamic,
BinaryReader,
BFloat16,
diff --git a/javascript/packages/fory/lib/fory.ts
b/javascript/packages/fory/lib/fory.ts
index 526a576f9..a554eb6df 100644
--- a/javascript/packages/fory/lib/fory.ts
+++ b/javascript/packages/fory/lib/fory.ts
@@ -23,7 +23,7 @@ import { BinaryReader } from "./reader";
import { ReferenceResolver } from "./referenceResolver";
import { ConfigFlags, Serializer, Config, ForyTypeInfoSymbol, WithForyClsInfo,
TypeId, CustomSerializer } from "./type";
import { OwnershipError } from "./error";
-import { InputType, ResultType, StructTypeInfo, TypeInfo } from "./typeInfo";
+import { InputType, ResultType, TypeInfo } from "./typeInfo";
import { Gen } from "./gen";
import { TypeMeta } from "./meta/TypeMeta";
import { PlatformBuffer } from "./platformBuffer";
@@ -98,10 +98,12 @@ export default class {
TypeInfo.attach(this);
if (constructor.prototype?.[ForyTypeInfoSymbol]) {
const typeInfo: TypeInfo =
(<WithForyClsInfo>(constructor.prototype[ForyTypeInfoSymbol])).structTypeInfo;
+ typeInfo.freeze();
serializer = new Gen(this, { creator: constructor, customSerializer
}).generateSerializer(typeInfo);
this.typeResolver.registerSerializer(typeInfo, serializer);
} else {
const typeInfo = constructor;
+ typeInfo.freeze();
serializer = new Gen(this).generateSerializer(typeInfo);
this.typeResolver.registerSerializer(typeInfo, serializer);
}
@@ -125,7 +127,7 @@ export default class {
replaceSerializerReader(typeInfo: TypeInfo) {
TypeInfo.attach(this);
- const serializer = new Gen(this, { creator: (typeInfo as
StructTypeInfo).options.creator }).reGenerateSerializer(typeInfo);
+ const serializer = new Gen(this, { creator: (typeInfo).options!.creator!
}).reGenerateSerializer(typeInfo);
const result = this.typeResolver.registerSerializer(typeInfo, {
getHash: serializer.getHash,
read: serializer.read,
diff --git a/javascript/packages/fory/lib/gen/any.ts
b/javascript/packages/fory/lib/gen/any.ts
index cf941ca60..9874cc6bc 100644
--- a/javascript/packages/fory/lib/gen/any.ts
+++ b/javascript/packages/fory/lib/gen/any.ts
@@ -41,7 +41,7 @@ export class AnyHelper {
}
const hash = serializer.getHash();
if (hash !== typeMeta.getHash()) {
- return fory.typeMetaResolver.genSerializerByTypeMetaRuntime(typeMeta);
+ return fory.typeMetaResolver.genSerializerByTypeMetaRuntime(typeMeta,
serializer);
}
return serializer;
}
diff --git a/javascript/packages/fory/lib/gen/array.ts
b/javascript/packages/fory/lib/gen/array.ts
index 0b7d59e9c..7027353f1 100644
--- a/javascript/packages/fory/lib/gen/array.ts
+++ b/javascript/packages/fory/lib/gen/array.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { ArrayTypeInfo, TypeInfo } from "../typeInfo";
+import { TypeInfo } from "../typeInfo";
import { CodecBuilder } from "./builder";
import { CodegenRegistry } from "./router";
import { TypeId } from "../type";
@@ -25,15 +25,15 @@ import { Scope } from "./scope";
import { CollectionSerializerGenerator } from "./collection";
class ArraySerializerGenerator extends CollectionSerializerGenerator {
- typeInfo: ArrayTypeInfo;
+ typeInfo: TypeInfo;
constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
super(typeInfo, builder, scope);
- this.typeInfo = <ArrayTypeInfo>typeInfo;
+ this.typeInfo = typeInfo;
}
genericTypeDescriptin(): TypeInfo {
- return this.typeInfo.options.inner;
+ return this.typeInfo.options!.inner!;
}
sizeProp() {
diff --git a/javascript/packages/fory/lib/gen/collection.ts
b/javascript/packages/fory/lib/gen/collection.ts
index 16cc6689e..c8b7566e4 100644
--- a/javascript/packages/fory/lib/gen/collection.ts
+++ b/javascript/packages/fory/lib/gen/collection.ts
@@ -214,14 +214,14 @@ export abstract class CollectionSerializerGenerator
extends BaseSerializerGenera
constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
super(typeInfo, builder, scope);
this.typeInfo = typeInfo;
- const inner = this.genericTypeDescriptin();
+ const inner = this.genericTypeDescriptin()!;
this.innerGenerator = CodegenRegistry.newGeneratorByTypeInfo(inner,
this.builder, this.scope);
}
- abstract genericTypeDescriptin(): TypeInfo;
+ abstract genericTypeDescriptin(): TypeInfo | undefined;
private isAny() {
- return this.genericTypeDescriptin().typeId === TypeId.UNKNOWN;
+ return this.genericTypeDescriptin()?.typeId === TypeId.UNKNOWN;
}
abstract newCollection(lenAccessor: string): string;
@@ -295,7 +295,6 @@ export abstract class CollectionSerializerGenerator extends
BaseSerializerGenera
const flags = this.scope.uniqueName("flags");
const idx = this.scope.uniqueName("idx");
const refFlag = this.scope.uniqueName("refFlag");
-
return `
const ${len} = ${this.builder.reader.readVarUint32Small7()};
const ${flags} = ${this.builder.reader.uint8()};
@@ -307,7 +306,7 @@ export abstract class CollectionSerializerGenerator extends
BaseSerializerGenera
switch (${refFlag}) {
case ${RefFlags.NotNullValueFlag}:
case ${RefFlags.RefValueFlag}:
- ${this.innerGenerator.read(x =>
`${this.putAccessor(result, x, idx)}`, `${refFlag} ===
${RefFlags.RefValueFlag}`)}
+ ${this.innerGenerator.readEmbed().read((x: any) =>
`${this.putAccessor(result, x, idx)}`, `${refFlag} ===
${RefFlags.RefValueFlag}`)}
break;
case ${RefFlags.RefFlag}:
${this.putAccessor(result,
this.builder.referenceResolver.getReadObject(this.builder.reader.varUInt32()),
idx)}
@@ -322,13 +321,13 @@ export abstract class CollectionSerializerGenerator
extends BaseSerializerGenera
if (${this.builder.reader.int8()} == ${RefFlags.NullFlag})
{
${this.putAccessor(result, "null", idx)}
} else {
- ${this.innerGenerator.read(x =>
`${this.putAccessor(result, x, idx)}`, "false")}
+ ${this.innerGenerator.readEmbed().read((x: any) =>
`${this.putAccessor(result, x, idx)}`, "false")}
}
}
} else {
for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
- ${this.innerGenerator.read(x =>
`${this.putAccessor(result, x, idx)}`, "false")}
+ ${this.innerGenerator.readEmbed().read((x: any) =>
`${this.putAccessor(result, x, idx)}`, "false")}
}
}
${accessor(result)}
diff --git a/javascript/packages/fory/lib/gen/enum.ts
b/javascript/packages/fory/lib/gen/enum.ts
index 6a7218780..cc2a31498 100644
--- a/javascript/packages/fory/lib/gen/enum.ts
+++ b/javascript/packages/fory/lib/gen/enum.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { EnumTypeInfo, TypeInfo } from "../typeInfo";
+import { TypeInfo } from "../typeInfo";
import { CodecBuilder } from "./builder";
import { BaseSerializerGenerator } from "./serializer";
import { CodegenRegistry } from "./router";
@@ -25,22 +25,22 @@ import { TypeId, MaxUInt32 } from "../type";
import { Scope } from "./scope";
class EnumSerializerGenerator extends BaseSerializerGenerator {
- typeInfo: EnumTypeInfo;
+ typeInfo: TypeInfo;
constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
super(typeInfo, builder, scope);
- this.typeInfo = <EnumTypeInfo>typeInfo;
+ this.typeInfo = typeInfo;
}
write(accessor: string): string {
- if (!this.typeInfo.options?.inner) {
+ if (!this.typeInfo.options?.enumProps) {
return this.builder.writer.varUInt32(accessor);
}
- if (Object.values(this.typeInfo.options.inner).length < 1) {
+ if (Object.values(this.typeInfo.options.enumProps).length < 1) {
throw new Error("An enum must contain at least one field");
}
return `
- ${Object.values(this.typeInfo.options.inner).map((value, index) => {
+ ${Object.values(this.typeInfo.options.enumProps).map((value, index) =>
{
if (typeof value !== "string" && typeof value !== "number") {
throw new Error("Enum value must be string or number");
}
@@ -104,7 +104,7 @@ class EnumSerializerGenerator extends
BaseSerializerGenerator {
break;
case TypeId.NAMED_ENUM:
{
- const typeInfo = this.typeInfo.castToStruct();
+ const typeInfo = this.typeInfo;
const nsBytes = this.scope.declare("nsBytes",
this.builder.metaStringResolver.encodeNamespace(CodecBuilder.replaceBackslashAndQuote(typeInfo.namespace)));
const typeNameBytes = this.scope.declare("typeNameBytes",
this.builder.metaStringResolver.encodeTypeName(CodecBuilder.replaceBackslashAndQuote(typeInfo.typeName)));
typeMeta = `
@@ -124,14 +124,14 @@ class EnumSerializerGenerator extends
BaseSerializerGenerator {
}
read(accessor: (expr: string) => string): string {
- if (!this.typeInfo.options?.inner) {
+ if (!this.typeInfo.options?.enumProps) {
return accessor(this.builder.reader.varUInt32());
}
const enumValue = this.scope.uniqueName("enum_v");
return `
const ${enumValue} = ${this.builder.reader.varUInt32()};
switch(${enumValue}) {
- ${Object.values(this.typeInfo.options.inner).map((value, index) =>
{
+ ${Object.values(this.typeInfo.options.enumProps).map((value,
index) => {
if (typeof value !== "string" && typeof value !== "number") {
throw new Error("Enum value must be string or number");
}
diff --git a/javascript/packages/fory/lib/gen/ext.ts
b/javascript/packages/fory/lib/gen/ext.ts
index 72081a10d..4569b202a 100644
--- a/javascript/packages/fory/lib/gen/ext.ts
+++ b/javascript/packages/fory/lib/gen/ext.ts
@@ -20,18 +20,18 @@
import { TypeId } from "../type";
import { Scope } from "./scope";
import { CodecBuilder } from "./builder";
-import { StructTypeInfo, TypeInfo } from "../typeInfo";
+import { TypeInfo } from "../typeInfo";
import { CodegenRegistry } from "./router";
import { BaseSerializerGenerator } from "./serializer";
import { TypeMeta } from "../meta/TypeMeta";
class ExtSerializerGenerator extends BaseSerializerGenerator {
- typeInfo: StructTypeInfo;
+ typeInfo: TypeInfo;
typeMeta: TypeMeta;
constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
super(typeInfo, builder, scope);
- this.typeInfo = <StructTypeInfo>typeInfo;
+ this.typeInfo = typeInfo;
this.typeMeta = TypeMeta.fromTypeInfo(this.typeInfo);
}
@@ -44,7 +44,7 @@ class ExtSerializerGenerator extends BaseSerializerGenerator {
read(accessor: (expr: string) => string, refState: string): string {
const result = this.scope.uniqueName("result");
return `
- ${this.typeInfo.options.withConstructor
+ ${this.typeInfo.options!.withConstructor
? `
const ${result} = new ${this.builder.getOptions("creator")}();
`
@@ -158,7 +158,7 @@ class ExtSerializerGenerator extends
BaseSerializerGenerator {
${this.builder.metaStringResolver.writeBytes(this.builder.writer.ownName(),
typeNameBytes)}
`;
} else {
- const bytes = this.scope.declare("typeInfoBytes", `new
Uint8Array([${TypeMeta.fromTypeInfo(<StructTypeInfo>
this.typeInfo).toBytes().join(",")}])`);
+ const bytes = this.scope.declare("typeInfoBytes", `new
Uint8Array([${TypeMeta.fromTypeInfo(this.typeInfo).toBytes().join(",")}])`);
typeMeta =
this.builder.typeMetaResolver.writeTypeMeta(this.builder.getTypeInfo(),
this.builder.writer.ownName(), bytes);
}
break;
diff --git a/javascript/packages/fory/lib/gen/index.ts
b/javascript/packages/fory/lib/gen/index.ts
index 6df62034e..df757d974 100644
--- a/javascript/packages/fory/lib/gen/index.ts
+++ b/javascript/packages/fory/lib/gen/index.ts
@@ -18,7 +18,7 @@
*/
import { TypeId, Serializer } from "../type";
-import { ArrayTypeInfo, MapTypeInfo, StructTypeInfo, SetTypeInfo, TypeInfo }
from "../typeInfo";
+import { TypeInfo } from "../typeInfo";
import { CodegenRegistry } from "./router";
import { CodecBuilder } from "./builder";
import { Scope } from "./scope";
@@ -62,12 +62,12 @@ export class Gen {
return new Function(funcString);
}
- private register(typeInfo: StructTypeInfo, serializer?: Serializer) {
+ private register(typeInfo: TypeInfo, serializer?: Serializer) {
this.fory.typeResolver.registerSerializer(typeInfo, serializer);
}
private isRegistered(typeInfo: TypeInfo) {
- return !!this.fory.typeResolver.typeInfoExists(typeInfo);
+ return !!this.fory.typeResolver.getSerializerByTypeInfo(typeInfo);
}
private traversalContainer(typeInfo: TypeInfo) {
@@ -75,25 +75,28 @@ export class Gen {
if (this.isRegistered(typeInfo)) {
return;
}
- const options = (<StructTypeInfo>typeInfo).options;
+ const options = (typeInfo).options;
if (options?.props) {
- this.register(<StructTypeInfo>typeInfo);
+ this.register(typeInfo);
Object.values(options.props).forEach((x) => {
this.traversalContainer(x);
});
const func = this.generate(typeInfo);
- this.register(<StructTypeInfo>typeInfo, func()(this.fory,
Gen.external, typeInfo, this.regOptions));
+ this.register(typeInfo, func()(this.fory, Gen.external, typeInfo,
this.regOptions));
}
}
if (typeInfo.typeId === TypeId.LIST) {
- this.traversalContainer((<ArrayTypeInfo>typeInfo).options.inner);
+ this.traversalContainer(typeInfo.options!.inner!);
}
if (typeInfo.typeId === TypeId.SET) {
- this.traversalContainer((<SetTypeInfo>typeInfo).options.key);
+ this.traversalContainer((typeInfo).options!.key!);
}
if (typeInfo.typeId === TypeId.MAP) {
- this.traversalContainer((<MapTypeInfo>typeInfo).options.key);
- this.traversalContainer((<MapTypeInfo>typeInfo).options.value);
+ if (!typeInfo.options?.key || !typeInfo.options?.value) {
+ throw new Error("map type must have key and value");
+ }
+ this.traversalContainer((typeInfo).options!.key!);
+ this.traversalContainer((typeInfo).options!.value!);
}
}
diff --git a/javascript/packages/fory/lib/gen/map.ts
b/javascript/packages/fory/lib/gen/map.ts
index 372ac5c73..f1af7b1e8 100644
--- a/javascript/packages/fory/lib/gen/map.ts
+++ b/javascript/packages/fory/lib/gen/map.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { MapTypeInfo, TypeInfo } from "../typeInfo";
+import { TypeInfo } from "../typeInfo";
import { CodecBuilder } from "./builder";
import { BaseSerializerGenerator, SerializerGenerator } from "./serializer";
import { CodegenRegistry } from "./router";
@@ -118,8 +118,8 @@ class MapChunkWriter {
// max size of chunk is 255
if (this.chunkSize == 255
|| this.chunkOffset == 0
- || keyInfo.equalTo(this.preKeyInfo)
- || valueInfo.equalTo(this.preValueInfo)
+ || !keyInfo.equalTo(this.preKeyInfo)
+ || !valueInfo.equalTo(this.preValueInfo)
) {
// new chunk
this.endChunk();
@@ -153,7 +153,7 @@ class MapAnySerializer {
const keyRef = this.fory.referenceResolver.existsWriteObject(v);
if (keyRef !== undefined) {
this.fory.binaryWriter.int8(RefFlags.RefFlag);
- this.fory.binaryWriter.uint16(keyRef);
+ this.fory.binaryWriter.varUInt32(keyRef);
return true;
} else {
this.fory.binaryWriter.int8(RefFlags.RefValueFlag);
@@ -277,19 +277,19 @@ class MapAnySerializer {
}
export class MapSerializerGenerator extends BaseSerializerGenerator {
- typeInfo: MapTypeInfo;
+ typeInfo: TypeInfo;
keyGenerator: SerializerGenerator;
valueGenerator: SerializerGenerator;
constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
super(typeInfo, builder, scope);
- this.typeInfo = <MapTypeInfo>typeInfo;
- this.keyGenerator =
CodegenRegistry.newGeneratorByTypeInfo(this.typeInfo.options.key, this.builder,
this.scope);
- this.valueGenerator =
CodegenRegistry.newGeneratorByTypeInfo(this.typeInfo.options.value,
this.builder, this.scope);
+ this.typeInfo = typeInfo;
+ this.keyGenerator =
CodegenRegistry.newGeneratorByTypeInfo(this.typeInfo.options!.key!,
this.builder, this.scope);
+ this.valueGenerator =
CodegenRegistry.newGeneratorByTypeInfo(this.typeInfo.options!.value!,
this.builder, this.scope);
}
private isAny() {
- return this.typeInfo.options.key.typeId === TypeId.UNKNOWN ||
this.typeInfo.options.value.typeId === TypeId.UNKNOWN;
+ return this.typeInfo.options?.key!.typeId === TypeId.UNKNOWN ||
this.typeInfo.options?.value!.typeId === TypeId.UNKNOWN ||
!this.typeInfo.options?.key?.isMonomorphic() ||
!this.typeInfo.options?.value?.isMonomorphic();
}
private writeSpecificType(accessor: string) {
@@ -342,7 +342,7 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
const ${keyRef} =
${this.builder.referenceResolver.existsWriteObject(v)};
if (${keyRef} !== undefined) {
${this.builder.writer.int8(RefFlags.RefFlag)};
- ${this.builder.writer.uint16(keyRef)};
+ ${this.builder.writer.varUInt32(keyRef)};
} else {
${this.builder.writer.int8(RefFlags.RefValueFlag)};
${this.keyGenerator.writeEmbed().write(k)}
@@ -357,7 +357,7 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
const ${valueRef} =
${this.builder.referenceResolver.existsWriteObject(v)};
if (${valueRef} !== undefined) {
${this.builder.writer.int8(RefFlags.RefFlag)};
- ${this.builder.writer.uint16(valueRef)};
+ ${this.builder.writer.varUInt32(valueRef)};
} else {
${this.builder.writer.int8(RefFlags.RefValueFlag)};
${this.valueGenerator.writeEmbed().write(v)};
@@ -381,6 +381,9 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
return this.writeSpecificType(accessor);
}
const innerSerializer = (innerTypeInfo: TypeInfo) => {
+ if (!innerTypeInfo.isMonomorphic()) {
+ return null;
+ }
return this.scope.declare(
"map_inner_ser",
TypeId.isNamedType(innerTypeInfo.typeId)
@@ -388,8 +391,8 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
: this.builder.typeResolver.getSerializerById(innerTypeInfo.typeId,
innerTypeInfo.userTypeId)
);
};
- return `new (${anySerializer})(${this.builder.getForyName()},
${this.typeInfo.options.key.typeId !== TypeId.UNKNOWN ?
innerSerializer(this.typeInfo.options.key) : null
- }, ${this.typeInfo.options.value.typeId !== TypeId.UNKNOWN ?
innerSerializer(this.typeInfo.options.value) : null
+ return `new (${anySerializer})(${this.builder.getForyName()},
${this.typeInfo.options!.key!.typeId !== TypeId.UNKNOWN ?
innerSerializer(this.typeInfo.options!.key!) : null
+ }, ${this.typeInfo.options!.value!.typeId !== TypeId.UNKNOWN ?
innerSerializer(this.typeInfo.options!.value!) : null
}).write(${accessor})`;
}
@@ -427,7 +430,7 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
${this.keyGenerator.read(x => `key = ${x}`, "true")}
break;
case ${RefFlags.RefFlag}:
- key =
${this.builder.referenceResolver.getReadObject(this.builder.reader.varInt32())}
+ key =
${this.builder.referenceResolver.getReadObject(this.builder.reader.varUInt32())}
break;
case ${RefFlags.NullFlag}:
key = null;
@@ -449,7 +452,7 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
${this.valueGenerator.read(x => `value = ${x}`, "true")}
break;
case ${RefFlags.RefFlag}:
- value =
${this.builder.referenceResolver.getReadObject(this.builder.reader.varInt32())}
+ value =
${this.builder.referenceResolver.getReadObject(this.builder.reader.varUInt32())}
break;
case ${RefFlags.NullFlag}:
value = null;
@@ -479,6 +482,9 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
return this.readSpecificType(accessor, refState);
}
const innerSerializer = (innerTypeInfo: TypeInfo) => {
+ if (!innerTypeInfo.isMonomorphic()) {
+ return null;
+ }
return this.scope.declare(
"map_inner_ser",
TypeId.isNamedType(innerTypeInfo.typeId)
@@ -486,8 +492,8 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
: this.builder.typeResolver.getSerializerById(innerTypeInfo.typeId,
innerTypeInfo.userTypeId)
);
};
- return accessor(`new (${anySerializer})(${this.builder.getForyName()},
${this.typeInfo.options.key.typeId !== TypeId.UNKNOWN ?
innerSerializer(this.typeInfo.options.key) : null
- }, ${this.typeInfo.options.value.typeId !== TypeId.UNKNOWN ?
innerSerializer(this.typeInfo.options.value) : null
+ return accessor(`new (${anySerializer})(${this.builder.getForyName()},
${this.typeInfo.options!.key!.typeId! !== TypeId.UNKNOWN ?
innerSerializer(this.typeInfo.options!.key!) : null
+ }, ${this.typeInfo.options!.value!.typeId !== TypeId.UNKNOWN ?
innerSerializer(this.typeInfo.options!.value!) : null
}).read(${refState})`);
}
diff --git a/javascript/packages/fory/lib/gen/serializer.ts
b/javascript/packages/fory/lib/gen/serializer.ts
index 0923fddaa..df8fd499c 100644
--- a/javascript/packages/fory/lib/gen/serializer.ts
+++ b/javascript/packages/fory/lib/gen/serializer.ts
@@ -76,7 +76,13 @@ export abstract class BaseSerializerGenerator implements
SerializerGenerator {
if (refTrackingUnableTypeId(this.typeInfo.typeId)) {
return false;
}
- return this.builder.fory.config.refTracking === true;
+ if (this.builder.fory.config.refTracking !== true) {
+ return false;
+ }
+ if (typeof this.typeInfo.trackingRef === "boolean") {
+ return this.typeInfo.trackingRef;
+ }
+ return true;
}
abstract write(accessor: string): string;
@@ -308,6 +314,7 @@ export abstract class BaseSerializerGenerator implements
SerializerGenerator {
needToWriteRef: () => ${this.needToWriteRef()},
getTypeId: () => ${this.getTypeId()},
getUserTypeId: () => ${this.getUserTypeId()},
+ getTypeInfo: () => typeInfo,
getHash,
write,
diff --git a/javascript/packages/fory/lib/gen/set.ts
b/javascript/packages/fory/lib/gen/set.ts
index 916e4cb53..f6a9ffb52 100644
--- a/javascript/packages/fory/lib/gen/set.ts
+++ b/javascript/packages/fory/lib/gen/set.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { SetTypeInfo, TypeInfo } from "../typeInfo";
+import { TypeInfo } from "../typeInfo";
import { CodecBuilder } from "./builder";
import { CodegenRegistry } from "./router";
import { TypeId } from "../type";
@@ -25,15 +25,15 @@ import { Scope } from "./scope";
import { CollectionSerializerGenerator } from "./collection";
class SetSerializerGenerator extends CollectionSerializerGenerator {
- typeInfo: SetTypeInfo;
+ typeInfo: TypeInfo;
constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
super(typeInfo, builder, scope);
- this.typeInfo = <SetTypeInfo>typeInfo;
+ this.typeInfo = typeInfo;
}
- genericTypeDescriptin(): TypeInfo {
- return this.typeInfo.options.key;
+ genericTypeDescriptin(): TypeInfo | undefined {
+ return this.typeInfo.options?.key;
}
newCollection(): string {
diff --git a/javascript/packages/fory/lib/gen/struct.ts
b/javascript/packages/fory/lib/gen/struct.ts
index e715f7f73..d753e16ae 100644
--- a/javascript/packages/fory/lib/gen/struct.ts
+++ b/javascript/packages/fory/lib/gen/struct.ts
@@ -20,14 +20,14 @@
import { TypeId, RefFlags } from "../type";
import { Scope } from "./scope";
import { CodecBuilder } from "./builder";
-import { StructTypeInfo, TypeInfo } from "../typeInfo";
+import { TypeInfo } from "../typeInfo";
import { CodegenRegistry } from "./router";
import { BaseSerializerGenerator, SerializerGenerator } from "./serializer";
import { TypeMeta } from "../meta/TypeMeta";
-const sortProps = (typeInfo: StructTypeInfo) => {
+const sortProps = (typeInfo: TypeInfo) => {
const names = TypeMeta.fromTypeInfo(typeInfo).getFieldInfo();
- const props = typeInfo.options.props;
+ const props = typeInfo.options!.props;
return names.map((x) => {
return {
key: x.fieldName,
@@ -48,7 +48,7 @@ enum RefMode {
}
-function toRefMode(trackingRef: boolean, nullable: boolean) {
+function toRefMode(trackingRef?: boolean, nullable?: boolean) {
if (trackingRef) {
return RefMode.TRACKING;
} else if (nullable) {
@@ -59,26 +59,21 @@ function toRefMode(trackingRef: boolean, nullable: boolean)
{
}
class StructSerializerGenerator extends BaseSerializerGenerator {
- typeInfo: StructTypeInfo;
+ typeInfo: TypeInfo;
sortedProps: { key: string; typeInfo: TypeInfo }[];
metaChangedSerializer: string;
typeMeta: TypeMeta;
constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
super(typeInfo, builder, scope);
- this.typeInfo = <StructTypeInfo>typeInfo;
+ this.typeInfo = typeInfo;
this.sortedProps = sortProps(this.typeInfo);
this.metaChangedSerializer =
this.scope.declareVar("metaChangedSerializer", "null");
this.typeMeta = TypeMeta.fromTypeInfo(this.typeInfo);
}
- readField(fieldName: string, fieldTypeInfo: TypeInfo, assignStmt: (expr:
string) => string, embedGenerator: SerializerGenerator, needToWriteRef:
boolean) {
- const { nullable = false } = this.typeInfo.options.fieldInfo?.[fieldName]
|| {};
- let { trackingRef } = this.typeInfo.options.fieldInfo?.[fieldName] || {};
- const { dynamic } = this.typeInfo.options.fieldInfo?.[fieldName] || {};
- if (typeof trackingRef !== "boolean") {
- trackingRef = needToWriteRef;
- }
+ readField(fieldTypeInfo: TypeInfo, assignStmt: (expr: string) => string,
embedGenerator: SerializerGenerator) {
+ const { nullable = false, dynamic, trackingRef } = fieldTypeInfo;
const refMode = toRefMode(trackingRef, nullable);
let stmt = "";
// polymorphic type
@@ -100,13 +95,8 @@ class StructSerializerGenerator extends
BaseSerializerGenerator {
return stmt;
}
- writeField(fieldName: string, fieldTypeInfo: TypeInfo, fieldAccessor:
string, embedGenerator: SerializerGenerator, needToWriteRef: boolean) {
- const { nullable = false } = this.typeInfo.options.fieldInfo?.[fieldName]
|| {};
- let { trackingRef } = this.typeInfo.options.fieldInfo?.[fieldName] || {};
- const { dynamic } = this.typeInfo.options.fieldInfo?.[fieldName] || {};
- if (typeof trackingRef !== "boolean") {
- trackingRef = needToWriteRef;
- }
+ writeField(fieldName: string, fieldTypeInfo: TypeInfo, fieldAccessor:
string, embedGenerator: SerializerGenerator) {
+ const { nullable = false, dynamic, trackingRef } = fieldTypeInfo;
const refMode = toRefMode(trackingRef, nullable);
let stmt = "";
// polymorphic type
@@ -175,7 +165,7 @@ class StructSerializerGenerator extends
BaseSerializerGenerator {
const innerGenerator = new InnerGeneratorClass(typeInfo, this.builder,
this.scope);
const fieldAccessor = `${accessor}${CodecBuilder.safePropAccessor(key)}`;
- return this.writeField(key, typeInfo, fieldAccessor,
innerGenerator.writeEmbed(), innerGenerator.needToWriteRef());
+ return this.writeField(key, typeInfo, fieldAccessor,
innerGenerator.writeEmbed());
}).join(";\n")}
`;
}
@@ -191,7 +181,7 @@ class StructSerializerGenerator extends
BaseSerializerGenerator {
}
`
: ""}
- ${this.typeInfo.options.withConstructor
+ ${this.typeInfo.options!.withConstructor
? `
const ${result} = new ${this.builder.getOptions("creator")}();
`
@@ -210,8 +200,7 @@ class StructSerializerGenerator extends
BaseSerializerGenerator {
throw new Error(`${typeInfo.typeId} generator not exists`);
}
const innerGenerator = new InnerGeneratorClass(typeInfo, this.builder,
this.scope);
- const needToWriteRef = innerGenerator.needToWriteRef();
- return this.readField(key, typeInfo, expr =>
`${result}${CodecBuilder.safePropAccessor(key)} = ${expr}`,
innerGenerator.readEmbed(), needToWriteRef);
+ return this.readField(typeInfo, expr =>
`${result}${CodecBuilder.safePropAccessor(key)} = ${expr}`,
innerGenerator.readEmbed());
}).join(";\n")}
${accessor(result)}
`;
@@ -337,13 +326,13 @@ class StructSerializerGenerator extends
BaseSerializerGenerator {
case TypeId.NAMED_COMPATIBLE_STRUCT:
case TypeId.COMPATIBLE_STRUCT:
{
- const bytes = this.scope.declare("typeInfoBytes", `new
Uint8Array([${TypeMeta.fromTypeInfo(<StructTypeInfo>
this.typeInfo).toBytes().join(",")}])`);
+ const bytes = this.scope.declare("typeInfoBytes", `new
Uint8Array([${TypeMeta.fromTypeInfo(this.typeInfo).toBytes().join(",")}])`);
typeMeta =
this.builder.typeMetaResolver.writeTypeMeta(this.builder.getTypeInfo(),
this.builder.writer.ownName(), bytes);
}
break;
case TypeId.NAMED_STRUCT:
if (!this.builder.fory.isCompatible()) {
- const typeInfo = this.typeInfo.castToStruct();
+ const typeInfo = this.typeInfo;
const nsBytes = this.scope.declare("nsBytes",
this.builder.metaStringResolver.encodeNamespace(CodecBuilder.replaceBackslashAndQuote(typeInfo.namespace)));
const typeNameBytes = this.scope.declare("typeNameBytes",
this.builder.metaStringResolver.encodeTypeName(CodecBuilder.replaceBackslashAndQuote(typeInfo.typeName)));
typeMeta = `
@@ -351,7 +340,7 @@ class StructSerializerGenerator extends
BaseSerializerGenerator {
${this.builder.metaStringResolver.writeBytes(this.builder.writer.ownName(),
typeNameBytes)}
`;
} else {
- const bytes = this.scope.declare("typeInfoBytes", `new
Uint8Array([${TypeMeta.fromTypeInfo(<StructTypeInfo>
this.typeInfo).toBytes().join(",")}])`);
+ const bytes = this.scope.declare("typeInfoBytes", `new
Uint8Array([${TypeMeta.fromTypeInfo(this.typeInfo).toBytes().join(",")}])`);
typeMeta =
this.builder.typeMetaResolver.writeTypeMeta(this.builder.getTypeInfo(),
this.builder.writer.ownName(), bytes);
}
break;
@@ -366,11 +355,11 @@ class StructSerializerGenerator extends
BaseSerializerGenerator {
}
getFixedSize(): number {
- const typeInfo = <StructTypeInfo> this.typeInfo;
+ const typeInfo = this.typeInfo;
const options = typeInfo.options;
let fixedSize = 8;
- if (options.props) {
- Object.values(options.props).forEach((x) => {
+ if (options!.props) {
+ Object.values(options!.props).forEach((x) => {
const propGenerator = new (CodegenRegistry.get(x.typeId)!)(x,
this.builder, this.scope);
fixedSize += propGenerator.getFixedSize();
});
diff --git a/javascript/packages/fory/lib/meta/TypeMeta.ts
b/javascript/packages/fory/lib/meta/TypeMeta.ts
index f52114baa..d407c6622 100644
--- a/javascript/packages/fory/lib/meta/TypeMeta.ts
+++ b/javascript/packages/fory/lib/meta/TypeMeta.ts
@@ -20,7 +20,7 @@
import { BinaryWriter } from "../writer";
import { BinaryReader } from "../reader";
import { Encoding, MetaStringDecoder, MetaStringEncoder } from "./MetaString";
-import { StructTypeInfo, TypeInfo } from "../typeInfo";
+import { TypeInfo } from "../typeInfo";
import { TypeId } from "../type";
import { x64hash128 } from "../murmurHash3";
import { fromString } from "../platformBuffer";
@@ -102,16 +102,16 @@ function getPrimitiveTypeSize(typeId: number) {
}
}
-type InnerFieldInfoOptions = { key?: InnerFieldInfo; value?: InnerFieldInfo;
inner?: InnerFieldInfo };
-interface InnerFieldInfo {
+export type InnerFieldInfoOptions = { key?: InnerFieldInfo; value?:
InnerFieldInfo; inner?: InnerFieldInfo };
+export interface InnerFieldInfo {
typeId: number;
userTypeId: number;
- trackingRef: boolean;
- nullable: boolean;
+ trackingRef?: boolean;
+ nullable?: boolean;
options?: InnerFieldInfoOptions;
fieldId?: number;
}
-class FieldInfo {
+export class FieldInfo {
constructor(
public fieldName: string,
public typeId: number,
@@ -242,28 +242,29 @@ export class TypeMeta {
const fingerprint = this.computeStructFingerprint(fields);
const bytes = fromString(fingerprint);
const hashLong = x64hash128(bytes, 47).getBigInt64(0);
- return Number(BigInt.asIntN(32, hashLong));
+ const result = Number(BigInt.asIntN(32, hashLong));
+ return result;
}
static fromTypeInfo(typeInfo: TypeInfo) {
let fieldInfo: FieldInfo[] = [];
if (TypeId.structType(typeInfo.typeId)) {
- const structTypeInfo = typeInfo as StructTypeInfo;
- fieldInfo =
Object.entries(structTypeInfo.options.props!).map(([fieldName, typeInfo]) => {
+ const structTypeInfo = typeInfo;
+ fieldInfo =
Object.entries(structTypeInfo.options!.props!).map(([fieldName, typeInfo]) => {
let fieldTypeId = typeInfo.typeId;
if (fieldTypeId === TypeId.NAMED_ENUM) {
fieldTypeId = TypeId.ENUM;
} else if (fieldTypeId === TypeId.NAMED_UNION || fieldTypeId ===
TypeId.TYPED_UNION) {
fieldTypeId = TypeId.UNION;
}
- const { trackingRef, nullable, id } =
structTypeInfo.options.fieldInfo?.[fieldName] || {};
+ const { trackingRef, nullable, id, userTypeId, options } = typeInfo;
return new FieldInfo(
fieldName,
fieldTypeId,
- typeInfo.userTypeId,
+ userTypeId,
trackingRef,
nullable,
- typeInfo.options,
+ options!,
id
);
});
diff --git a/javascript/packages/fory/lib/type.ts
b/javascript/packages/fory/lib/type.ts
index 8141b20fa..983c4797c 100644
--- a/javascript/packages/fory/lib/type.ts
+++ b/javascript/packages/fory/lib/type.ts
@@ -19,7 +19,7 @@
import Fory from "./fory";
import { BinaryReader } from "./reader";
-import { StructTypeInfo } from "./typeInfo";
+import { TypeInfo } from "./typeInfo";
import { BinaryWriter } from "./writer";
export const TypeId = {
@@ -210,6 +210,7 @@ export type CustomSerializer<T> = {
// read, write
export type Serializer<T = any> = {
fixedSize: number;
+ getTypeInfo: () => TypeInfo;
needToWriteRef: () => boolean;
getTypeId: () => number;
getUserTypeId: () => number;
@@ -270,7 +271,7 @@ export interface Config {
}
export interface WithForyClsInfo {
- structTypeInfo: StructTypeInfo;
+ structTypeInfo: TypeInfo;
}
export const ForyTypeInfoSymbol = Symbol("foryTypeInfo");
diff --git a/javascript/packages/fory/lib/typeInfo.ts
b/javascript/packages/fory/lib/typeInfo.ts
index cfa6e69fb..21f54afaf 100644
--- a/javascript/packages/fory/lib/typeInfo.ts
+++ b/javascript/packages/fory/lib/typeInfo.ts
@@ -18,64 +18,10 @@
*/
import Fory from "./fory";
+import { refTrackingUnableTypeId } from "./meta/TypeMeta";
import { ForyTypeInfoSymbol, TypeId, Mode } from "./type";
import { BFloat16 } from "./bfloat16";
-const targetFieldInfo = new WeakMap<new () => any, { [key: string]:
StructFieldInfo }>();
-
-export const ForyField = (fieldInfo: {
- nullable?: boolean,
- trackingRef?: boolean,
- id?: number,
- dynamic?: Dynamic,
-}) => {
- return (target: any, key: string | {name?: string}) => {
- const creator = target.constructor;
- if (!targetFieldInfo.has(creator)) {
- targetFieldInfo.set(creator, {});
- }
- const keyString = typeof key === "string" ? key : key?.name;
- if (!keyString) {
- throw new Error("Decorators can only be placed on classes and fields");
- }
- targetFieldInfo.get(creator)![keyString] = fieldInfo;
- };
-}
-
-const initMeta = (target: new () => any, typeInfo: TypeInfo) => {
- if (!target.prototype) {
- target.prototype = {};
- }
- if (!typeInfo.options) {
- typeInfo.options = {};
- }
- typeInfo.options.withConstructor = true;
- typeInfo.options.creator = target;
- if (!typeInfo.options.props) {
- typeInfo.options.props = {}
- }
- if(targetFieldInfo.has(target)) {
- const structTypeInfo = (typeInfo as StructTypeInfo);
- if (!structTypeInfo.options.fieldInfo) {
- structTypeInfo.options.fieldInfo = {};
- }
- Object.assign(structTypeInfo.options.fieldInfo,
targetFieldInfo.get(target));
- }
- Object.assign(typeInfo.options.props, targetFields.get(target) || {})
- Object.defineProperties(target.prototype, {
- [ForyTypeInfoSymbol]: {
- get() {
- return {
- structTypeInfo: typeInfo
- };
- },
- enumerable: false,
- set(_) {
- throw new Error("fory type info is readonly")
- },
- },
- })
-};
const targetFields = new WeakMap<new () => any, { [key: string]: TypeInfo }>();
@@ -94,6 +40,16 @@ class ExtensibleFunction extends Function {
}
}
+interface TypeInfoOptions {
+ props?: { [key: string]: TypeInfo };
+ withConstructor?: boolean;
+ creator?: Function;
+ key?: TypeInfo;
+ value?: TypeInfo;
+ inner?: TypeInfo;
+ enumProps?: { [key: string]: number };
+}
+
/**
* T is for type matching
*/
@@ -103,23 +59,93 @@ export class TypeInfo<T = unknown> extends
ExtensibleFunction {
named = "";
namespace = "";
typeName = "";
- // Stored as unsigned 32-bit; -1 (0xffffffff) means "unset".
userTypeId = -1;
- options?: any;
+ options?: TypeInfoOptions;
+ _typeId: number;
+ nullable: boolean = false;
+
+ setNullable(v: boolean) {
+ this.nullable = v;
+ return this;
+ }
+ trackingRef?: boolean;
+ setTrackingRef(v: boolean) {
+ this.trackingRef = v;
+ return this;
+ }
+ id?: number;
+ setId(v: number | undefined) {
+ this.id = v;
+ return this;
+ }
+ dynamic?: Dynamic;
+ setDynamic(v: Dynamic) {
+ this.dynamic = v;
+ return this;
+ }
+
static fory: WeakRef<Fory> | null = null;
static attach(fory: Fory) {
TypeInfo.fory = new WeakRef(fory);
}
-
+
static detach() {
TypeInfo.fory = null;
}
- public constructor(private _typeId: number, userTypeId = -1) {
+ initMeta(target: new () => any) {
+ if (!target.prototype) {
+ target.prototype = {};
+ }
+ if (!this.options) {
+ this.options = {}
+ }
+ this.options!.withConstructor = true;
+ this.options!.creator = target;
+ if (!this.options!.props) {
+ this.options!.props = {};
+ }
+ Object.assign(this.options!.props, targetFields.get(target) || {})
+ const that = this;
+ Object.defineProperties(target.prototype, {
+ [ForyTypeInfoSymbol]: {
+ get() {
+ return {
+ structTypeInfo: that
+ };
+ },
+ enumerable: false,
+ set(_) {
+ throw new Error("fory type info is readonly")
+ },
+ },
+ });
+ }
+
+ public freeze() {
+ Object.defineProperties(this, {
+ 'named': { writable: false, configurable: false },
+ 'namespace': { writable: false, configurable: false },
+ 'typeName': { writable: false, configurable: false },
+ 'userTypeId': { writable: false, configurable: false },
+ 'options': { writable: false, configurable: false },
+ '_typeId': { writable: false, configurable: false },
+ 'nullable': { writable: false, configurable: false },
+ });
+ Object.freeze(this.options);
+ if (this.options?.props) {
+ Object.freeze(this.options!.props);
+ }
+ if (this.options?.enumProps) {
+ Object.freeze(this.options!.enumProps);
+ }
+ }
+
+ public constructor(typeId: number, userTypeId = -1) {
super(function (target: any, key?: string | { name?: string }) {
if (key === undefined) {
- initMeta(target, that as unknown as StructTypeInfo);
+ that.initMeta(target);
} else {
const keyString = typeof key === "string" ? key : key?.name;
if (!keyString) {
@@ -135,9 +161,25 @@ export class TypeInfo<T = unknown> extends
ExtensibleFunction {
throw new Error("userTypeId must be in range [0, 0xfffffffe]");
}
}
+ this._typeId = typeId;
this.userTypeId = userTypeId;
}
+ clone() {
+ const result = new TypeInfo(this._typeId, this.userTypeId);
+ result.named = this.named;
+ result.namespace = this.namespace;
+ result.typeName = this.typeName;
+ result.options = { ...this.options };
+ result.dynamicTypeId = this.dynamicTypeId;
+ result.nullable = this.nullable;
+ result.trackingRef = this.trackingRef;
+ result.id = this.id;
+ result.dynamic = this.dynamic;
+ return result;
+ }
+
+
computeTypeId(fory?: Fory) {
const internalTypeId = this._typeId;
if (internalTypeId !== TypeId.STRUCT && internalTypeId !==
TypeId.NAMED_STRUCT) {
@@ -238,7 +280,7 @@ export class TypeInfo<T = unknown> extends
ExtensibleFunction {
} else {
finalTypeId = TypeId.NAMED_EXT;
}
- const typeInfo = new TypeInfo<T>(finalTypeId,
userTypeId).cast<StructTypeInfo>();
+ const typeInfo = new TypeInfo<T>(finalTypeId, userTypeId)
typeInfo.options = {
withConstructor,
};
@@ -254,10 +296,8 @@ export class TypeInfo<T = unknown> extends
ExtensibleFunction {
typeName?: string;
} | string | number, props?: Record<string, TypeInfo>, {
withConstructor = false,
- fieldInfo = {},
}: {
withConstructor?: boolean;
- fieldInfo?: Record<string, StructFieldInfo>
} = {}) {
let typeId: number | undefined;
let namespace: string | undefined;
@@ -294,11 +334,10 @@ export class TypeInfo<T = unknown> extends
ExtensibleFunction {
} else {
finalTypeId = TypeId.NAMED_STRUCT;
}
- const typeInfo = new TypeInfo<T>(finalTypeId,
userTypeId).cast<StructTypeInfo>();
+ const typeInfo = new TypeInfo<T>(finalTypeId, userTypeId);
typeInfo.options = {
props: props || {},
withConstructor,
- fieldInfo
};
typeInfo.namespace = namespace || "";
typeInfo.typeName = typeId !== undefined ? "" : typeName!;
@@ -311,7 +350,7 @@ export class TypeInfo<T = unknown> extends
ExtensibleFunction {
type: T;
options: T2;
}>(typeId);
- typeInfo.options = options;
+ typeInfo.options = options as any;
return typeInfo;
}
@@ -319,7 +358,7 @@ export class TypeInfo<T = unknown> extends
ExtensibleFunction {
typeId?: number;
namespace?: string;
typeName?: string;
- } | string | number, props: { [key: string]: any }) {
+ } | string | number, props?: { [key: string]: any }) {
let typeId: number | undefined;
let namespace: string | undefined;
let typeName: string | undefined;
@@ -350,22 +389,14 @@ export class TypeInfo<T = unknown> extends
ExtensibleFunction {
const finalTypeId = typeId !== undefined ? TypeId.ENUM : TypeId.NAMED_ENUM;
const userTypeId = typeId !== undefined ? typeId : -1;
const typeInfo = new TypeInfo<T>(finalTypeId, userTypeId);
- typeInfo.cast<EnumTypeInfo>().options = {
- inner: props,
+ typeInfo.options = {
+ enumProps: props,
};
typeInfo.namespace = namespace || "";
typeInfo.typeName = typeId !== undefined ? "" : typeName!;
typeInfo.named = `${typeInfo.namespace}$${typeInfo.typeName}`;
return typeInfo;
}
-
- castToStruct() {
- return this as unknown as StructTypeInfo;
- }
-
- cast<T>() {
- return this as unknown as T;
- }
}
export enum Dynamic {
@@ -374,40 +405,6 @@ export enum Dynamic {
AUTO = "AUTO"
}
-type StructFieldInfo = {nullable?: boolean, trackingRef?: boolean, id?:
number, dynamic?: Dynamic}
-export interface StructTypeInfo extends TypeInfo {
- options: {
- props?: { [key: string]: TypeInfo };
- fieldInfo?: {[key: string]: StructFieldInfo};
- withConstructor?: boolean;
- creator?: Function;
- };
-}
-
-export interface EnumTypeInfo extends TypeInfo {
- options: {
- inner: { [key: string]: any };
- };
-}
-
-export interface ArrayTypeInfo extends TypeInfo {
- options: {
- inner: TypeInfo;
- };
-}
-
-export interface SetTypeInfo extends TypeInfo {
- options: {
- key: TypeInfo;
- };
-}
-
-export interface MapTypeInfo extends TypeInfo {
- options: {
- key: TypeInfo;
- value: TypeInfo;
- };
-}
type Props<T> = T extends {
options: {
@@ -600,7 +597,7 @@ export type HintResult<T> = T extends never ? any : T
extends {
? Uint8Array : T extends {
type: typeof TypeId.ENUM;
}
- ? EnumProps<T>: unknown;
+ ? EnumProps<T> : unknown;
export const Type = {
any() {
@@ -634,11 +631,11 @@ export const Type = {
typeId?: number;
namespace?: string;
typeName?: string;
- } | string | number, t1: T1) {
+ } | string | number, t1?: T1) {
return TypeInfo.fromEnum<{
type: typeof TypeId.ENUM;
options: {
- inner: T1;
+ enumProps: T1;
};
}>(nameInfo, t1);
},
@@ -666,10 +663,8 @@ export const Type = {
typeName?: string;
} | string | number, props?: T, {
withConstructor = false,
- fieldInfo,
}: {
withConstructor?: boolean;
- fieldInfo?: Record<string, StructFieldInfo>
} = {}) {
return TypeInfo.fromStruct<{
type: typeof TypeId.STRUCT;
@@ -678,7 +673,6 @@ export const Type = {
};
}>(nameInfo, props, {
withConstructor,
- fieldInfo
});
},
string() {
diff --git a/javascript/packages/fory/lib/typeMetaResolver.ts
b/javascript/packages/fory/lib/typeMetaResolver.ts
index f7de94461..6db2fe148 100644
--- a/javascript/packages/fory/lib/typeMetaResolver.ts
+++ b/javascript/packages/fory/lib/typeMetaResolver.ts
@@ -17,15 +17,15 @@
* under the License.
*/
-import { StructTypeInfo, Type, TypeInfo } from "./typeInfo";
+import { Type, TypeInfo } from "./typeInfo";
import fory from "./fory";
-import { TypeMeta } from "./meta/TypeMeta";
+import { InnerFieldInfo, TypeMeta } from "./meta/TypeMeta";
import { BinaryReader } from "./reader";
import { Serializer, TypeId } from "./type";
import { BinaryWriter } from "./writer";
export class TypeMetaResolver {
- private disposeTypeInfo: StructTypeInfo[] = [];
+ private disposeTypeInfo: TypeInfo[] = [];
private dynamicTypeId = 0;
private typeMeta: TypeMeta[] = [];
@@ -33,46 +33,56 @@ export class TypeMetaResolver {
}
- private updateTypeInfo(typeMeta: TypeMeta, typeInfo: TypeInfo) {
- typeInfo.options.props =
Object.fromEntries(typeMeta.getFieldInfo().map((x) => {
- const typeId = x.getTypeId();
- const fieldName = x.getFieldName();
- const declared = typeInfo.options.props?.[fieldName];
- let fieldTypeInfo = declared ??
this.fory.typeResolver.getTypeInfo(typeId);
- if (!fieldTypeInfo) {
- fieldTypeInfo = Type.any();
- }
- if (!typeInfo.options.fieldInfo) {
- typeInfo.options.fieldInfo = {};
+ private fieldInfoToTypeInfo(fieldInfo: InnerFieldInfo): TypeInfo {
+ const typeId = fieldInfo.typeId;
+
+ switch (typeId) {
+ case TypeId.MAP:
+ return Type.map(this.fieldInfoToTypeInfo(fieldInfo.options!.key!),
this.fieldInfoToTypeInfo(fieldInfo.options!.value!));
+ case TypeId.LIST:
+ return Type.array(this.fieldInfoToTypeInfo(fieldInfo.options!.inner!));
+ case TypeId.SET:
+ return Type.set(this.fieldInfoToTypeInfo(fieldInfo.options!.key!));
+ default:
+ {
+ const serializer = this.fory.typeResolver.getSerializerById(typeId,
fieldInfo.userTypeId);
+ // registered type
+ if (serializer) {
+ return serializer.getTypeInfo().clone();
+ }
+ return Type.any();
}
- typeInfo.options.fieldInfo[x.fieldName] = {
- nullable: x.nullable,
- trackingRef: x.trackingRef,
- ...typeInfo.options.fieldInfo[x.fieldName],
- };
- return [fieldName, fieldTypeInfo];
- }));
+ }
}
- genSerializerByTypeMetaRuntime(typeMeta: TypeMeta): Serializer {
+ genSerializerByTypeMetaRuntime(typeMeta: TypeMeta, original?: Serializer):
Serializer {
const typeName = typeMeta.getTypeName();
const ns = typeMeta.getNs();
const typeId = typeMeta.getTypeId();
const userTypeId = typeMeta.getUserTypeId();
+ if (!TypeId.structType(typeId)) {
+ throw new Error("only support reconstructor struct type");
+ }
let typeInfo;
- if (!TypeId.isNamedType(typeId)) {
- typeInfo = this.fory.typeResolver.getTypeInfo(typeId, userTypeId);
- if (!typeInfo) {
- typeInfo = Type.struct({ typeId });
- }
+ if (original) {
+ typeInfo = original.getTypeInfo().clone();
} else {
- typeInfo = this.fory.typeResolver.getTypeInfo(`${ns}$${typeName}`);
- if (!typeInfo) {
+ if (!TypeId.isNamedType(typeId)) {
+ typeInfo = Type.struct(userTypeId);
+ } else {
typeInfo = Type.struct({ typeName, namespace: ns });
}
}
-
- this.updateTypeInfo(typeMeta, typeInfo);
+ // rebuild props
+ const props = (Object.fromEntries(typeMeta.getFieldInfo().map((x) => {
+ const fieldName = x.getFieldName();
+ const fieldTypeInfo =
this.fieldInfoToTypeInfo(x).setNullable(x.nullable).setTrackingRef(x.trackingRef).setId(x.fieldId);
+ return [fieldName, fieldTypeInfo];
+ })));
+ typeInfo.options! = {
+ ...typeInfo.options,
+ props,
+ };
return this.fory.replaceSerializerReader(typeInfo);
}
@@ -88,7 +98,7 @@ export class TypeMetaResolver {
}
}
- writeTypeMeta(typeInfo: StructTypeInfo, writer: BinaryWriter, bytes:
Uint8Array) {
+ writeTypeMeta(typeInfo: TypeInfo, writer: BinaryWriter, bytes: Uint8Array) {
if (typeInfo.dynamicTypeId !== -1) {
// Reference to previously written type: (index << 1) | 1, LSB=1
writer.varUInt32((typeInfo.dynamicTypeId << 1) | 1);
diff --git a/javascript/packages/fory/lib/typeResolver.ts
b/javascript/packages/fory/lib/typeResolver.ts
index a06c9e7f8..380ae8ed3 100644
--- a/javascript/packages/fory/lib/typeResolver.ts
+++ b/javascript/packages/fory/lib/typeResolver.ts
@@ -25,6 +25,9 @@ import Fory from "./fory";
const uninitSerialize = {
// for writer
fixedSize: 0,
+ getTypeInfo: () => {
+ throw new Error("uninitSerialize");
+ },
getTypeId: () => {
throw new Error("uninitSerialize");
},
@@ -79,7 +82,6 @@ const uninitSerialize = {
export default class TypeResolver {
private internalSerializer: Serializer[] = new Array(300);
private customSerializer: Map<number | string, Serializer> = new Map();
- private typeInfoMap: Map<number | string, TypeInfo> = new Map();
private initInternalSerializer() {
const registerSerializer = (typeInfo: TypeInfo) => {
@@ -185,14 +187,9 @@ export default class TypeResolver {
}
init() {
+ TypeInfo.attach(this.fory);
this.initInternalSerializer();
- }
-
- getTypeInfo(typeIdOrName: number | string, userTypeId?: number) {
- if (typeof typeIdOrName === "number" && userTypeId !== undefined &&
TypeId.needsUserTypeId(typeIdOrName)) {
- return this.typeInfoMap.get(this.makeUserTypeKey(userTypeId));
- }
- return this.typeInfoMap.get(typeIdOrName);
+ TypeInfo.detach();
}
registerSerializer(typeInfo: TypeInfo, serializer: Serializer =
uninitSerialize) {
@@ -200,7 +197,6 @@ export default class TypeResolver {
if (!TypeId.isNamedType(typeId)) {
if (TypeId.needsUserTypeId(typeId) && typeInfo.userTypeId !== -1) {
const key = this.makeUserTypeKey(typeInfo.userTypeId);
- this.typeInfoMap.set(key, typeInfo);
if (this.customSerializer.has(key)) {
Object.assign(this.customSerializer.get(key)!, serializer);
} else {
@@ -209,7 +205,6 @@ export default class TypeResolver {
return this.customSerializer.get(key);
}
const id = typeId;
- this.typeInfoMap.set(id, typeInfo);
if (id <= 0xFF) {
if (this.internalSerializer[id]) {
Object.assign(this.internalSerializer[id], serializer);
@@ -225,32 +220,21 @@ export default class TypeResolver {
}
return this.customSerializer.get(id);
} else {
- const namedTypeInfo = typeInfo.castToStruct();
+ const namedTypeInfo = typeInfo;
const name = namedTypeInfo.named!;
if (this.customSerializer.has(name)) {
Object.assign(this.customSerializer.get(name)!, serializer);
} else {
this.customSerializer.set(name, { ...serializer });
}
- this.typeInfoMap.set(name, typeInfo);
return this.customSerializer.get(name);
}
}
- typeInfoExists(typeInfo: TypeInfo) {
- if (typeInfo.isNamedType()) {
- return this.typeInfoMap.has((typeInfo.castToStruct()).named!);
- }
- if (TypeId.needsUserTypeId(typeInfo.typeId) && typeInfo.userTypeId !== -1)
{
- return this.typeInfoMap.has(this.makeUserTypeKey(typeInfo.userTypeId));
- }
- return this.typeInfoMap.has(typeInfo.typeId);
- }
-
getSerializerByTypeInfo(typeInfo: TypeInfo) {
const typeId = typeInfo.computeTypeId(this.fory);
if (TypeId.isNamedType(typeId)) {
- return this.customSerializer.get((typeInfo.castToStruct()).named!);
+ return this.customSerializer.get((typeInfo).named!);
}
return this.getSerializerById(typeId, typeInfo.userTypeId);
}
diff --git a/javascript/test/crossLanguage.test.ts
b/javascript/test/crossLanguage.test.ts
index af5129efb..27c099760 100644
--- a/javascript/test/crossLanguage.test.ts
+++ b/javascript/test/crossLanguage.test.ts
@@ -20,7 +20,6 @@
import Fory, {
BinaryReader,
BinaryWriter,
- ForyField,
Type,
Dynamic,
} from "../packages/fory/index";
@@ -639,6 +638,57 @@ describe("bool", () => {
writeToFile(writer.dump() as Buffer);
});
+ test("test_collection_element_ref_override", () => {
+ const fory = new Fory({
+ compatible: false,
+ refTracking: true,
+ hooks: {
+ afterCodeGenerated: (code) => {
+ return beautify.js(code, { indent_size: 2, space_in_empty_paren:
true, indent_empty_lines: true });
+ }
+ }
+ });
+
+ const Type701 = Type.struct(701, {
+ id: Type.varInt32(),
+ name: Type.string()
+ });
+
+ @Type701
+ class RefOverrideElement {
+ id: number = 0;
+ name: string = "";
+ }
+
+ @Type.struct(702, {
+ list_field: Type.array(Type701.setTrackingRef(true)),
+ map_field: Type.map(Type.string(), Type701.setTrackingRef(true))
+ })
+ class RefOverrideContainer {
+ list_field: RefOverrideElement[] = [];
+ map_field: Map<string, RefOverrideElement> = new Map();
+ }
+
+ fory.registerSerializer(RefOverrideElement);
+ fory.registerSerializer(RefOverrideContainer);
+
+ const outer = fory.deserialize(content);
+ console.log("Deserialized:", outer);
+
+ expect(outer.list_field).toBeTruthy();
+ expect(outer.list_field.length).toBeGreaterThan(0);
+ const shared = outer.list_field[0];
+ const newOuter = new RefOverrideContainer();
+ newOuter.list_field = [shared, shared];
+ newOuter.map_field = new Map([
+ ["k1", shared],
+ ["k2", shared]
+ ]);
+
+ const newBytes = fory.serialize(newOuter);
+ writeToFile(newBytes as Buffer)
+ });
+
test("test_skip_id_custom", () => {
const fory1 = new Fory({
compatible: true
@@ -877,12 +927,11 @@ describe("bool", () => {
@Type.struct(201, {
f1: Type.varInt32(),
- f2: Type.string(),
+ f2: Type.string().setNullable(true),
f3: Type.float64()
})
class VersionCheckStruct {
f1: number = 0;
- @ForyField({ nullable: true })
f2: string | null = null;
f3: number = 0;
}
@@ -909,11 +958,10 @@ describe("bool", () => {
// Define Animal interface implementations
@Type.struct(302, {
age: Type.varInt32(),
- name: Type.string()
+ name: Type.string().setNullable(true)
})
class Dog {
age: number = 0;
- @ForyField({ nullable: true })
name: string | null = null;
}
fory.registerSerializer(Dog);
@@ -969,11 +1017,10 @@ describe("bool", () => {
// Define Animal interface implementations
@Type.struct(302, {
age: Type.varInt32(),
- name: Type.string()
+ name: Type.string().setNullable(true)
})
class Dog {
age: number = 0;
- @ForyField({ nullable: true })
name: string | null = null;
}
fory.registerSerializer(Dog);
@@ -1028,7 +1075,7 @@ describe("bool", () => {
f1: Type.string()
})
class OneStringFieldStruct {
- @ForyField({ nullable: true })
+ @Type.string().setNullable(true)
f1: string | null = null;
}
fory.registerSerializer(OneStringFieldStruct);
@@ -1048,10 +1095,9 @@ describe("bool", () => {
});
@Type.struct(200, {
- f1: Type.string()
+ f1: Type.string().setNullable(true)
})
class OneStringFieldStruct {
- @ForyField({ nullable: true })
f1: string | null = null;
}
fory.registerSerializer(OneStringFieldStruct);
@@ -1336,42 +1382,33 @@ describe("bool", () => {
@Type.map(Type.string(), Type.string())
mapField: Map<string, string> = new Map();
- // Nullable group 1 - boxed types with @ForyField(nullable=true)
- @ForyField({ nullable: true })
- @Type.varInt32()
+ // Nullable group 1 - boxed types with nullable type decorators
+ @(Type.varInt32().setNullable(true))
nullableInt1: number | null = null;
- @ForyField({ nullable: true })
- @Type.varInt64()
+ @(Type.varInt64().setNullable(true))
nullableLong1: number | null = null;
- @ForyField({ nullable: true })
- @Type.float32()
+ @(Type.float32().setNullable(true))
nullableFloat1: number | null = null;
- @ForyField({ nullable: true })
- @Type.float64()
+ @(Type.float64().setNullable(true))
nullableDouble1: number | null = null;
- @ForyField({ nullable: true })
- @Type.bool()
+ @(Type.bool().setNullable(true))
nullableBool1: boolean | null = null;
- // Nullable group 2 - reference types with @ForyField(nullable=true)
- @ForyField({ nullable: true })
- @Type.string()
+ // Nullable group 2 - reference types with nullable type decorators
+ @(Type.string().setNullable(true))
nullableString2: string | null = null;
- @ForyField({ nullable: true })
- @Type.array(Type.string())
+ @(Type.array(Type.string()).setNullable(true))
nullableList2: string[] | null = null;
- @ForyField({ nullable: true })
- @Type.set(Type.string())
+ @(Type.set(Type.string()).setNullable(true))
nullableSet2: Set<string> | null = null;
- @ForyField({ nullable: true })
- @Type.map(Type.string(), Type.string())
+ @(Type.map(Type.string(), Type.string()).setNullable(true))
nullableMap2: Map<string, string> | null = null;
}
return NullableComprehensiveCompatible;
@@ -1406,42 +1443,33 @@ describe("bool", () => {
@Type.map(Type.string(), Type.string())
mapField: Map<string, string> = new Map();
- // Nullable group 1 - boxed types with @ForyField(nullable=true)
- @ForyField({ nullable: true })
- @Type.varInt32()
+ // Nullable group 1 - boxed types with nullable type decorators
+ @(Type.varInt32().setNullable(true))
nullableInt: number | null = null;
- @ForyField({ nullable: true })
- @Type.varInt64()
+ @(Type.varInt64().setNullable(true))
nullableLong: number | null = null;
- @ForyField({ nullable: true })
- @Type.float32()
+ @(Type.float32().setNullable(true))
nullableFloat: number | null = null;
- @ForyField({ nullable: true })
- @Type.float64()
+ @(Type.float64().setNullable(true))
nullableDouble: number | null = null;
- @ForyField({ nullable: true })
- @Type.bool()
+ @(Type.bool().setNullable(true))
nullableBool: boolean | null = null;
- // Nullable group 2 - reference types with @ForyField(nullable=true)
- @ForyField({ nullable: true })
- @Type.string()
+ // Nullable group 2 - reference types with nullable type decorators
+ @(Type.string().setNullable(true))
nullableString: string | null = null;
- @ForyField({ nullable: true })
- @Type.array(Type.string())
+ @(Type.array(Type.string()).setNullable(true))
nullableList: string[] | null = null;
- @ForyField({ nullable: true })
- @Type.set(Type.string())
+ @(Type.set(Type.string()).setNullable(true))
nullableSet: Set<string> | null = null;
- @ForyField({ nullable: true })
- @Type.map(Type.string(), Type.string())
+ @(Type.map(Type.string(), Type.string()).setNullable(true))
nullableMap: Map<string, string> | null = null;
}
return NullableComprehensiveConsistent
@@ -1543,14 +1571,12 @@ describe("bool", () => {
fory.registerSerializer(RefInner);
@Type.struct(502, {
- inner1: Type.struct(501),
- inner2: Type.struct(501)
+ inner1:
Type.struct(501).setTrackingRef(true).setNullable(true).setDynamic(Dynamic.FALSE),
+ inner2:
Type.struct(501).setTrackingRef(true).setNullable(true).setDynamic(Dynamic.FALSE),
})
class RefOuter {
- @ForyField({ trackingRef: true, nullable: true, dynamic: Dynamic.FALSE })
inner1: RefInner | null = null;
- @ForyField({ trackingRef: true, nullable: true, dynamic: Dynamic.FALSE })
inner2: RefInner | null = null;
}
fory.registerSerializer(RefOuter);
@@ -1582,13 +1608,11 @@ describe("bool", () => {
fory.registerSerializer(RefInner);
@Type.struct(504, {
- inner1: Type.struct(503),
- inner2: Type.struct(503)
+ inner1: Type.struct(503).setTrackingRef(true).setNullable(true),
+ inner2: Type.struct(503).setTrackingRef(true).setNullable(true),
})
class RefOuter {
- @ForyField({ trackingRef: true, nullable: true })
inner1: RefInner | null = null;
- @ForyField({ trackingRef: true, nullable: true })
inner2: RefInner | null = null;
}
fory.registerSerializer(RefOuter);
@@ -1612,11 +1636,10 @@ describe("bool", () => {
@Type.struct(601, {
name: Type.string(),
- selfRef: Type.struct(601)
+ selfRef: Type.struct(601).setNullable(true).setTrackingRef(true)
})
class CircularRefStruct {
name: string = "";
- @ForyField({ nullable: true, trackingRef: true })
selfRef: CircularRefStruct | null = null;
}
fory.registerSerializer(CircularRefStruct);
@@ -1642,11 +1665,10 @@ describe("bool", () => {
@Type.struct(602, {
name: Type.string(),
- selfRef: Type.struct(602)
+ selfRef: Type.struct(602).setNullable(true).setTrackingRef(true)
})
class CircularRefStruct {
name: string = "";
- @ForyField({ nullable: true, trackingRef: true })
selfRef: CircularRefStruct | null = null;
}
fory.registerSerializer(CircularRefStruct);
@@ -1668,12 +1690,11 @@ describe("bool", () => {
@Type.struct(1, {
u64Tagged: Type.taggedUInt64(),
- u64TaggedNullable: Type.taggedUInt64()
+ u64TaggedNullable: Type.taggedUInt64().setNullable(true)
})
class UnsignedSchemaConsistentSimple {
u64Tagged: bigint = 0n;
- @ForyField({ nullable: true })
u64TaggedNullable: bigint | null = null;
}
fory.registerSerializer(UnsignedSchemaConsistentSimple);
@@ -1711,32 +1732,25 @@ describe("bool", () => {
u64FixedField: bigint = 0n;
u64TaggedField: bigint = 0n;
- @ForyField({ nullable: true })
- @Type.uint8()
+ @(Type.uint8().setNullable(true))
u8NullableField: number = 0;
- @ForyField({ nullable: true })
- @Type.uint16()
+ @(Type.uint16().setNullable(true))
u16NullableField: number = 0;
- @ForyField({ nullable: true })
- @Type.varUInt32()
+ @(Type.varUInt32().setNullable(true))
u32VarNullableField: number = 0;
- @ForyField({ nullable: true })
- @Type.uint32()
+ @(Type.uint32().setNullable(true))
u32FixedNullableField: number = 0;
- @ForyField({ nullable: true })
- @Type.varUInt64()
+ @(Type.varUInt64().setNullable(true))
u64VarNullableField: bigint = 0n;
- @ForyField({ nullable: true })
- @Type.uint64()
+ @(Type.uint64().setNullable(true))
u64FixedNullableField: bigint = 0n;
- @ForyField({ nullable: true })
- @Type.taggedUInt64()
+ @(Type.taggedUInt64().setNullable(true))
u64TaggedNullableField: bigint = 0n;
}
fory.registerSerializer(UnsignedSchemaConsistent);
@@ -1774,32 +1788,25 @@ describe("bool", () => {
u64FixedField1: bigint = 0n;
u64TaggedField1: bigint = 0n;
- @ForyField({ nullable: true })
- @Type.uint8()
+ @(Type.uint8().setNullable(true))
u8Field2: number = 0;
- @ForyField({ nullable: true })
- @Type.uint16()
+ @(Type.uint16().setNullable(true))
u16Field2: number = 0;
- @ForyField({ nullable: true })
- @Type.varUInt32()
+ @(Type.varUInt32().setNullable(true))
u32VarField2: number = 0;
- @ForyField({ nullable: true })
- @Type.uint32()
+ @(Type.uint32().setNullable(true))
u32FixedField2: number = 0;
- @ForyField({ nullable: true })
- @Type.varUInt64()
+ @(Type.varUInt64().setNullable(true))
u64VarField2: bigint = 0n;
- @ForyField({ nullable: true })
- @Type.uint64()
+ @(Type.uint64().setNullable(true))
u64FixedField2: bigint = 0n;
- @ForyField({ nullable: true })
- @Type.taggedUInt64()
+ @(Type.taggedUInt64().setNullable(true))
u64TaggedField2: bigint = 0n;
}
fory.registerSerializer(UnsignedSchemaCompatible);
diff --git a/javascript/test/object.test.ts b/javascript/test/object.test.ts
index 7270270b0..6c45f8fed 100644
--- a/javascript/test/object.test.ts
+++ b/javascript/test/object.test.ts
@@ -82,9 +82,7 @@ describe('object', () => {
const typeInfo = Type.struct("example.foo", {
a: Type.struct("example.bar", {
b: Type.string()
- })
- }, {
- fieldInfo: { a: { nullable: true } }
+ }).setNullable(true)
})
const fory = new Fory({ refTracking: true });
const { serialize, deserialize } = fory.registerSerializer(typeInfo);
@@ -137,11 +135,12 @@ describe('object', () => {
a: Type.struct("example.bar", {
b: Type.string(),
}),
- a2: Type.struct("example.foo")
+ a2: Type.struct("example.foo").setTrackingRef(true)
})
const fory = new Fory({
- refTracking: true, hooks: {
+ refTracking: true,
+ hooks: {
afterCodeGenerated: (code) => {
return beautify.js(code, { indent_size: 2, space_in_empty_paren:
true, indent_empty_lines: true });
}
@@ -202,11 +201,7 @@ describe('object', () => {
const hps = undefined;
const typeInfo = Type.struct('ws-channel-protocol', {
kind: Type.string(),
- path: Type.string(),
- }, {
- fieldInfo: {
- path: { nullable: true }
- }
+ path: Type.string().setNullable(true),
});
const fory = new Fory({ hps });
diff --git a/javascript/test/protocol/struct.test.ts
b/javascript/test/protocol/struct.test.ts
index a1fb70760..2593675a2 100644
--- a/javascript/test/protocol/struct.test.ts
+++ b/javascript/test/protocol/struct.test.ts
@@ -60,9 +60,7 @@ describe('protocol', () => {
const nullableUnspecified = Type.struct({
typeName: "example.nullableUnspecified"
}, {
- a: Type.string(),
- }, {
- fieldInfo: {a: { nullable: true }}
+ a: Type.string().setNullable(true),
});
const { serialize, deserialize } =
fory.registerSerializer(nullableUnspecified);
expect(deserialize(serialize({ a: null }))).toEqual({ a: null });
@@ -75,13 +73,8 @@ describe('protocol', () => {
{ typeName: 'example.schemaConsistentNullable' },
{
a: Type.string(),
- b: Type.string(),
+ b: Type.string().setNullable(true),
},
- {
- fieldInfo: {
- b: { nullable: true}
- }
- }
);
const { serialize, deserialize } = fory.registerSerializer(schema);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]