You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fury.apache.org by wa...@apache.org on 2024/01/16 08:20:22 UTC
(incubator-fury) branch main updated: feat(JavaScript): Implement the collection protocol (#1337)
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/incubator-fury.git
The following commit(s) were added to refs/heads/main by this push:
new 96ace333 feat(JavaScript): Implement the collection protocol (#1337)
96ace333 is described below
commit 96ace3331f34f8a2bb64436632d8371766a665c2
Author: weipeng <wa...@apache.org>
AuthorDate: Tue Jan 16 16:20:17 2024 +0800
feat(JavaScript): Implement the collection protocol (#1337)
Now we have introduced a new binary layout for Collection, which is
utilized by both Array and Set. The Collection is defined in the
[SPEC](https://github.com/apache/incubator-fury/blob/3da32797beb25fd39b06c42f8c8b9e67eec5184d/docs/protocols/java_object_graph_spec.md#unsigned-int).
This PR implements the Collection in JavaScript.
1. Array and Set will extend CollectionSerializerGenerator to generate
code.
2. The Xlang Collection is not stable yet; it may undergo changes in the
future, particularly regarding polymorphism, although the changes are
expected to be minimal.
3. We have refined the reference checks, which were previously
disorganized.
4. The refactor of collection types has resulted in performance
improvements. In our sample benchmark, we observed an increase in
performance of nearly 20%.
#1330
---
javascript/packages/fury/index.ts | 41 +-
javascript/packages/fury/lib/any.ts | 150 -----
javascript/packages/fury/lib/classResolver.ts | 48 +-
javascript/packages/fury/lib/fury.ts | 115 ++--
javascript/packages/fury/lib/gen/any.ts | 96 +++-
javascript/packages/fury/lib/gen/array.ts | 48 +-
javascript/packages/fury/lib/gen/binary.ts | 6 +-
javascript/packages/fury/lib/gen/builder.ts | 32 +-
javascript/packages/fury/lib/gen/collection.ts | 322 +++++++++++
javascript/packages/fury/lib/gen/index.ts | 12 +-
javascript/packages/fury/lib/gen/map.ts | 6 +-
javascript/packages/fury/lib/gen/object.ts | 22 +-
javascript/packages/fury/lib/gen/router.ts | 10 +
javascript/packages/fury/lib/gen/serializer.ts | 173 ++++--
javascript/packages/fury/lib/gen/set.ts | 48 +-
javascript/packages/fury/lib/gen/tuple.ts | 6 +-
javascript/packages/fury/lib/gen/typedArray.ts | 11 +-
javascript/packages/fury/lib/meta.ts | 68 ++-
javascript/packages/fury/lib/referenceResolver.ts | 12 +-
javascript/packages/fury/lib/type.ts | 6 +-
javascript/test/__snapshots__/codeGen.test.ts.snap | 606 ---------------------
javascript/test/any.test.ts | 6 +-
javascript/test/array.test.ts | 7 +-
javascript/test/codeGen.test.ts | 41 --
javascript/test/object.test.ts | 2 +-
25 files changed, 779 insertions(+), 1115 deletions(-)
diff --git a/javascript/packages/fury/index.ts b/javascript/packages/fury/index.ts
index 5334f007..165b7f38 100644
--- a/javascript/packages/fury/index.ts
+++ b/javascript/packages/fury/index.ts
@@ -17,18 +17,14 @@
* under the License.
*/
-import {
- generateSerializer,
-} from "./lib/gen";
import {
ObjectTypeDescription,
TypeDescription,
ArrayTypeDescription,
Type,
- ToRecordType,
} from "./lib/description";
-import { Serializer, Fury, InternalSerializerType, Config } from "./lib/type";
-import FuryInternal from "./lib/fury";
+import { Serializer, InternalSerializerType } from "./lib/type";
+import Fury from "./lib/fury";
export {
Serializer,
@@ -39,35 +35,4 @@ export {
Type,
};
-export default class {
- constructor(private config?: Config) { }
- private fury: Fury = FuryInternal(this.config || {});
-
- registerSerializer<T extends TypeDescription>(description: T) {
- const serializer = generateSerializer(this.fury, description);
- return {
- serializer,
- serialize: (data: ToRecordType<T>) => {
- return this.fury.serialize(data, serializer);
- },
- serializeVolatile: (data: ToRecordType<T>) => {
- return this.fury.serializeVolatile(data, serializer);
- },
- deserialize: (bytes: Uint8Array) => {
- return this.fury.deserialize(bytes, serializer) as ToRecordType<T>;
- },
- };
- }
-
- serializeVolatile(v: any, serialize?: Serializer) {
- return this.fury.serializeVolatile(v, serialize);
- }
-
- serialize(v: any, serialize?: Serializer) {
- return this.fury.serialize(v, serialize);
- }
-
- deserialize(bytes: Uint8Array) {
- return this.fury.deserialize(bytes);
- }
-}
+export default Fury;
diff --git a/javascript/packages/fury/lib/any.ts b/javascript/packages/fury/lib/any.ts
deleted file mode 100644
index 7fc0d6f1..00000000
--- a/javascript/packages/fury/lib/any.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { Type } from "./description";
-import { getMeta } from "./meta";
-import { Fury, MaxInt32, MinInt32, Serializer } from "./type";
-import { InternalSerializerType, RefFlags } from "./type";
-
-export default (fury: Fury) => {
- const { binaryReader, binaryWriter, referenceResolver, classResolver } = fury;
-
- function detectSerializer() {
- const typeId = binaryReader.int16();
- let serializer: Serializer;
- if (typeId === InternalSerializerType.FURY_TYPE_TAG) {
- const tag = classResolver.readTag(binaryReader)();
- serializer = classResolver.getSerializerByTag(tag);
- } else {
- serializer = classResolver.getSerializerById(typeId);
- }
- if (!serializer) {
- throw new Error(`cant find implements of typeId: ${typeId}`);
- }
- return serializer;
- }
-
- return {
- readInner: () => {
- throw new Error("any can not call readInner");
- },
- writeInner: () => {
- throw new Error("any can not call writeInner");
- },
- read: () => {
- const flag = referenceResolver.readRefFlag();
- switch (flag) {
- case RefFlags.RefValueFlag:
- return detectSerializer().readInner();
- case RefFlags.RefFlag:
- return referenceResolver.getReadObjectByRefId(binaryReader.varUInt32());
- case RefFlags.NullFlag:
- return null;
- case RefFlags.NotNullValueFlag:
- return detectSerializer().readInner();
- }
- },
- write: (v: any) => {
- const { write: writeInt64, meta: int64Meta } = classResolver.getSerializerById(InternalSerializerType.INT64);
- const { write: writeDouble, meta: doubleMeta } = classResolver.getSerializerById(InternalSerializerType.DOUBLE);
- const { write: writeInt32, meta: int32Meta } = classResolver.getSerializerById(InternalSerializerType.INT32);
- const { write: writeBool, meta: boolMeta } = classResolver.getSerializerById(InternalSerializerType.BOOL);
- const { write: stringWrite, meta: stringMeta } = classResolver.getSerializerById(InternalSerializerType.STRING);
- const { write: arrayWrite, meta: arrayMeta } = classResolver.getSerializerById(InternalSerializerType.ARRAY);
- const { write: mapWrite, meta: mapMeta } = classResolver.getSerializerById(InternalSerializerType.MAP);
- const { write: setWrite, meta: setMeta } = classResolver.getSerializerById(InternalSerializerType.FURY_SET);
- const { write: timestampWrite, meta: timestampMeta } = classResolver.getSerializerById(InternalSerializerType.TIMESTAMP);
-
- // NullFlag
- if (v === null || v === undefined) {
- binaryWriter.reserve(1);
- binaryWriter.int8(RefFlags.NullFlag); // null
- return;
- }
-
- // NotNullValueFlag
- if (typeof v === "number") {
- if (Number.isNaN(v) || !Number.isFinite(v)) {
- binaryWriter.reserve(1);
- binaryWriter.int8(RefFlags.NullFlag); // null
- return;
- }
- if (Number.isInteger(v)) {
- if (v > MaxInt32 || v < MinInt32) {
- binaryWriter.reserve(int64Meta.fixedSize);
- writeInt64(BigInt(v));
- return;
- } else {
- binaryWriter.reserve(int32Meta.fixedSize);
- writeInt32(v);
- return;
- }
- } else {
- binaryWriter.reserve(doubleMeta.fixedSize);
- writeDouble(v);
- return;
- }
- }
-
- if (typeof v === "bigint") {
- binaryWriter.reserve(int64Meta.fixedSize);
- writeInt64(v);
- return;
- }
-
- if (typeof v === "boolean") {
- binaryWriter.reserve(boolMeta.fixedSize);
- writeBool(v);
- return;
- }
-
- if (v instanceof Date) {
- binaryWriter.reserve(timestampMeta.fixedSize);
- timestampWrite(v);
- return;
- }
-
- if (typeof v === "string") {
- binaryWriter.reserve(stringMeta.fixedSize);
- stringWrite(v);
- return;
- }
-
- if (v instanceof Map) {
- binaryWriter.reserve(5);
- binaryWriter.reserve(mapMeta.fixedSize);
- mapWrite(v);
- return;
- }
- if (v instanceof Set) {
- binaryWriter.reserve(setMeta.fixedSize);
- setWrite(v);
- return;
- }
- if (Array.isArray(v)) {
- binaryWriter.reserve(arrayMeta.fixedSize);
- arrayWrite(v);
- return;
- }
-
- throw new Error(`serializer not support ${typeof v} yet`);
- },
- meta: getMeta(Type.any(), fury),
- };
-};
diff --git a/javascript/packages/fury/lib/classResolver.ts b/javascript/packages/fury/lib/classResolver.ts
index 751b36c9..62cc1b0b 100644
--- a/javascript/packages/fury/lib/classResolver.ts
+++ b/javascript/packages/fury/lib/classResolver.ts
@@ -17,13 +17,13 @@
* under the License.
*/
-import { InternalSerializerType, Serializer, Fury, BinaryReader, BinaryWriter as TBinaryWriter } from "./type";
-import anySerializer from "./any";
+import { InternalSerializerType, Serializer, BinaryReader, BinaryWriter as TBinaryWriter } from "./type";
import { fromString } from "./platformBuffer";
import { x64hash128 } from "./murmurHash3";
import { BinaryWriter } from "./writer";
import { generateSerializer } from "./gen";
import { Type, TypeDescription } from "./description";
+import Fury from "./fury";
const USESTRINGVALUE = 0;
const USESTRINGID = 1;
@@ -69,7 +69,8 @@ const uninitSerialize = {
},
meta: {
fixedSize: 0,
- noneable: false,
+ type: InternalSerializerType.ANY,
+ needToWriteRef: false,
},
};
@@ -87,7 +88,6 @@ export default class SerializerResolver {
}
private initInternalSerializer(fury: Fury) {
- this.internalSerializer[InternalSerializerType.ANY] = anySerializer(fury);
this.registerSerializer(fury, Type.string());
this.registerSerializer(fury, Type.array(Type.any()));
this.registerSerializer(fury, Type.map(Type.any(), Type.any()));
@@ -206,4 +206,44 @@ export default class SerializerResolver {
};
}
}
+
+ getSerializerByData(v: any) {
+ if (v === null || v === undefined) {
+ return null;
+ }
+
+ if (typeof v === "number") {
+ return this.getSerializerById(InternalSerializerType.DOUBLE);
+ }
+
+ if (typeof v === "bigint") {
+ return this.getSerializerById(InternalSerializerType.INT64);
+ }
+
+ if (typeof v === "boolean") {
+ return this.getSerializerById(InternalSerializerType.BOOL);
+ }
+
+ if (v instanceof Date) {
+ return this.getSerializerById(InternalSerializerType.TIMESTAMP);
+ }
+
+ if (typeof v === "string") {
+ return this.getSerializerById(InternalSerializerType.STRING);
+ }
+
+ if (v instanceof Map) {
+ return this.getSerializerById(InternalSerializerType.MAP);
+ }
+
+ if (v instanceof Set) {
+ return this.getSerializerById(InternalSerializerType.FURY_SET);
+ }
+
+ if (Array.isArray(v)) {
+ return this.getSerializerById(InternalSerializerType.ARRAY);
+ }
+
+ throw new Error(`Failed to detect the Fury type from JavaScript type: ${typeof v}`);
+ }
}
diff --git a/javascript/packages/fury/lib/fury.ts b/javascript/packages/fury/lib/fury.ts
index b2c104b3..ffc99563 100644
--- a/javascript/packages/fury/lib/fury.ts
+++ b/javascript/packages/fury/lib/fury.ts
@@ -21,33 +21,52 @@ import ClassResolver from "./classResolver";
import { BinaryWriter } from "./writer";
import { BinaryReader } from "./reader";
import { ReferenceResolver } from "./referenceResolver";
-import { ConfigFlags, Serializer, Config, InternalSerializerType, Language } from "./type";
+import { ConfigFlags, Serializer, Config, Language, BinaryReader as BinaryReaderType, BinaryWriter as BinaryWriterType } from "./type";
import { OwnershipError } from "./error";
+import { ToRecordType, TypeDescription } from "./description";
+import { generateSerializer, AnySerializer } from "./gen";
-export default (config: Config) => {
- const binaryReader = BinaryReader(config);
- const binaryWriter = BinaryWriter(config);
+export default class {
+ binaryReader: BinaryReaderType;
+ binaryWriter: BinaryWriterType;
+ classResolver = new ClassResolver();
+ referenceResolver: ReturnType<typeof ReferenceResolver>;
+ anySerializer: AnySerializer;
- const classResolver = new ClassResolver();
- const referenceResolver = ReferenceResolver(config, binaryWriter, binaryReader);
+ constructor(public config: Config = {
+ refTracking: false,
+ useSliceString: false,
+ hooks: {
+ },
+ }) {
+ this.binaryReader = BinaryReader(config);
+ this.binaryWriter = BinaryWriter(config);
+ this.referenceResolver = ReferenceResolver(config, this.binaryWriter, this.binaryReader);
+ this.classResolver.init(this);
+ this.anySerializer = new AnySerializer(this);
+ }
- const fury = {
- config,
- deserialize,
- serialize,
- referenceResolver,
- classResolver,
- binaryReader,
- binaryWriter,
- serializeVolatile,
- };
- classResolver.init(fury);
+ registerSerializer<T extends TypeDescription>(description: T) {
+ const serializer = generateSerializer(this, description);
+ return {
+ serializer,
+ serialize: (data: ToRecordType<T>) => {
+ return this.serialize(data, serializer);
+ },
+ serializeVolatile: (data: ToRecordType<T>) => {
+ return this.serializeVolatile(data, serializer);
+ },
+ deserialize: (bytes: Uint8Array) => {
+ return this.deserialize(bytes, serializer) as ToRecordType<T>;
+ },
+ };
+ }
- function deserialize<T = any>(bytes: Uint8Array, serializer?: Serializer): T | null {
- referenceResolver.reset();
- classResolver.reset();
- binaryReader.reset(bytes);
- const bitmap = binaryReader.uint8();
+ deserialize<T = any>(bytes: Uint8Array, serializer: Serializer = this.anySerializer): T | null {
+ this.referenceResolver.reset();
+ this.classResolver.reset();
+ this.binaryReader.reset(bytes);
+ const bitmap = this.binaryReader.uint8();
if ((bitmap & ConfigFlags.isNullFlag) === ConfigFlags.isNullFlag) {
return null;
}
@@ -59,61 +78,51 @@ export default (config: Config) => {
if (!isCrossLanguage) {
throw new Error("support crosslanguage mode only");
}
- binaryReader.uint8(); // skip language
+ this.binaryReader.uint8(); // skip language
const isOutOfBandEnabled = (bitmap & ConfigFlags.isOutOfBandFlag) === ConfigFlags.isOutOfBandFlag;
if (isOutOfBandEnabled) {
throw new Error("outofband mode is not supported now");
}
- binaryReader.int32(); // native object offset. should skip. javascript support cross mode only
- binaryReader.int32(); // native object size. should skip.
- if (serializer) {
- return serializer.read();
- } else {
- return classResolver.getSerializerById(InternalSerializerType.ANY).read();
- }
+ this.binaryReader.int32(); // native object offset. should skip. javascript support cross mode only
+ this.binaryReader.int32(); // native object size. should skip.
+ return serializer.read();
}
- function serializeInternal<T = any>(data: T, serializer?: Serializer) {
+ private serializeInternal<T = any>(data: T, serializer: Serializer) {
try {
- binaryWriter.reset();
+ this.binaryWriter.reset();
} catch (e) {
if (e instanceof OwnershipError) {
throw new Error("Permission denied. To release the serialization ownership, you must call the dispose function returned by serializeVolatile.");
}
throw e;
}
- referenceResolver.reset();
- classResolver.reset();
+ this.referenceResolver.reset();
+ this.classResolver.reset();
let bitmap = 0;
if (data === null) {
bitmap |= ConfigFlags.isNullFlag;
}
bitmap |= ConfigFlags.isLittleEndianFlag;
bitmap |= ConfigFlags.isCrossLanguageFlag;
- binaryWriter.uint8(bitmap);
- binaryWriter.uint8(Language.XLANG);
- const cursor = binaryWriter.getCursor();
- binaryWriter.skip(4); // preserve 4-byte for nativeObjects start offsets.
- binaryWriter.uint32(0); // nativeObjects length.
- if (!serializer) {
- serializer = classResolver.getSerializerById(InternalSerializerType.ANY);
- }
+ this.binaryWriter.uint8(bitmap);
+ this.binaryWriter.uint8(Language.XLANG);
+ const cursor = this.binaryWriter.getCursor();
+ this.binaryWriter.skip(4); // preserve 4-byte for nativeObjects start offsets.
+ this.binaryWriter.uint32(0); // nativeObjects length.
// reserve fixed size
- binaryWriter.reserve(serializer.meta.fixedSize);
+ this.binaryWriter.reserve(serializer.meta.fixedSize);
// start write
serializer.write(data);
-
- binaryWriter.setUint32Position(cursor, binaryWriter.getCursor()); // nativeObjects start offsets;
- return binaryWriter;
+ this.binaryWriter.setUint32Position(cursor, this.binaryWriter.getCursor()); // nativeObjects start offsets;
+ return this.binaryWriter;
}
- function serialize<T = any>(data: T, serializer?: Serializer) {
- return serializeInternal(data, serializer).dump();
+ serialize<T = any>(data: T, serializer: Serializer = this.anySerializer) {
+ return this.serializeInternal(data, serializer).dump();
}
- function serializeVolatile<T = any>(data: T, serializer?: Serializer) {
- return serializeInternal(data, serializer).dumpAndOwn();
+ serializeVolatile<T = any>(data: T, serializer: Serializer = this.anySerializer) {
+ return this.serializeInternal(data, serializer).dumpAndOwn();
}
-
- return fury;
-};
+}
diff --git a/javascript/packages/fury/lib/gen/any.ts b/javascript/packages/fury/lib/gen/any.ts
index 47994c05..1117e46a 100644
--- a/javascript/packages/fury/lib/gen/any.ts
+++ b/javascript/packages/fury/lib/gen/any.ts
@@ -17,12 +17,74 @@
* under the License.
*/
-import { TypeDescription } from "../description";
+import { Type, TypeDescription } from "../description";
import { CodecBuilder } from "./builder";
import { BaseSerializerGenerator } from "./serializer";
import { CodegenRegistry } from "./router";
-import { InternalSerializerType } from "../type";
+import { InternalSerializerType, RefFlags, Serializer } from "../type";
import { Scope } from "./scope";
+import Fury from "../fury";
+import { Meta, getMeta } from "../meta";
+
+export class AnySerializer {
+ meta: Meta;
+
+ constructor(private fury: Fury) {
+ this.meta = getMeta(Type.any(), fury);
+ }
+
+ readInner() {
+ throw new Error("Anonymous serializer can't call directly");
+ }
+
+ writeInner() {
+ throw new Error("Anonymous serializer can't call directly");
+ }
+
+ detectSerializer() {
+ const typeId = this.fury.binaryReader.int16();
+ let serializer: Serializer;
+ if (typeId === InternalSerializerType.FURY_TYPE_TAG) {
+ const tag = this.fury.classResolver.readTag(this.fury.binaryReader)();
+ serializer = this.fury.classResolver.getSerializerByTag(tag);
+ } else {
+ serializer = this.fury.classResolver.getSerializerById(typeId);
+ }
+ if (!serializer) {
+ throw new Error(`cant find implements of typeId: ${typeId}`);
+ }
+ return serializer;
+ }
+
+ read() {
+ const flag = this.fury.referenceResolver.readRefFlag();
+ switch (flag) {
+ case RefFlags.RefValueFlag:
+ return this.detectSerializer().readInner(true);
+ case RefFlags.RefFlag:
+ return this.fury.referenceResolver.getReadObject(this.fury.binaryReader.varUInt32());
+ case RefFlags.NullFlag:
+ return null;
+ case RefFlags.NotNullValueFlag:
+ return this.detectSerializer().readInner(false);
+ }
+ }
+
+ write(v: any) {
+ if (v === null || v === undefined) {
+ this.fury.binaryWriter.reserve(1);
+ this.fury.binaryWriter.int8(RefFlags.NullFlag); // null
+ return;
+ }
+
+ const serializer = this.fury.classResolver.getSerializerByData(v);
+ if (!serializer) {
+ throw new Error(`Failed to detect the Fury serializer from JavaScript type: ${typeof v}`);
+ }
+ this.fury.binaryWriter.reserve(serializer?.meta.fixedSize);
+ serializer?.write(v);
+ }
+}
class AnySerializerGenerator extends BaseSerializerGenerator {
description: TypeDescription;
@@ -32,32 +94,28 @@ class AnySerializerGenerator extends BaseSerializerGenerator {
this.description = description;
}
- private addDep() {
- return this.scope.declare(
- "any_ser",
- this.builder.classResolver.getSerializerById(InternalSerializerType.ANY)
- );
- }
-
writeStmt(accessor: string): string {
- const name = this.addDep();
- return `${name}.writeInner(${accessor})`;
+ return `${this.builder.furyName()}.anySerializer.writeInner(${accessor})`;
}
readStmt(accessor: (expr: string) => string): string {
- const name = this.addDep();
- return `${name}.readInner(${accessor})`;
+ return `${this.builder.furyName()}.anySerializer.readInner(${accessor})`;
}
- toReadEmbed(accessor: (expr: string) => string): string {
- const name = this.addDep();
- return accessor(`${name}.read()`);
+ toReadEmbed(accessor: (expr: string) => string, excludeHead = false): string {
+ if (excludeHead) {
+ throw new Error("Anonymous can't excludeHead");
+ }
+ return accessor(`${this.builder.furyName()}.anySerializer.read()`);
}
- toWriteEmbed(accessor: string): string {
- const name = this.addDep();
- return `${name}.write(${accessor})`;
+ toWriteEmbed(accessor: string, excludeHead = false): string {
+ if (excludeHead) {
+ throw new Error("Anonymous can't excludeHead");
+ }
+ return `${this.builder.furyName()}.anySerializer.write(${accessor})`;
}
}
CodegenRegistry.register(InternalSerializerType.ANY, AnySerializerGenerator);
+CodegenRegistry.registerExternal(AnySerializer);
diff --git a/javascript/packages/fury/lib/gen/array.ts b/javascript/packages/fury/lib/gen/array.ts
index 002928f2..478de2e2 100644
--- a/javascript/packages/fury/lib/gen/array.ts
+++ b/javascript/packages/fury/lib/gen/array.ts
@@ -19,12 +19,12 @@
import { ArrayTypeDescription, TypeDescription } from "../description";
import { CodecBuilder } from "./builder";
-import { BaseSerializerGenerator } from "./serializer";
import { CodegenRegistry } from "./router";
import { InternalSerializerType } from "../type";
import { Scope } from "./scope";
+import { CollectionSerializerGenerator } from "./collection";
-class ArraySerializerGenerator extends BaseSerializerGenerator {
+class ArraySerializerGenerator extends CollectionSerializerGenerator {
description: ArrayTypeDescription;
constructor(description: TypeDescription, builder: CodecBuilder, scope: Scope) {
@@ -32,48 +32,20 @@ class ArraySerializerGenerator extends BaseSerializerGenerator {
this.description = <ArrayTypeDescription>description;
}
- private innerMeta() {
- const inner = this.description.options.inner;
- return this.builder.meta(inner);
+ genericTypeDescriptin(): TypeDescription {
+ return this.description.options.inner;
}
- private innerGenerator() {
- const inner = this.description.options.inner;
- const InnerGeneratorClass = CodegenRegistry.get(inner.type);
- if (!InnerGeneratorClass) {
- throw new Error(`${inner.type} generator not exists`);
- }
- return new InnerGeneratorClass(inner, this.builder, this.scope);
+ sizeProp() {
+ return "length";
}
- writeStmt(accessor: string): string {
- const innerMeta = this.innerMeta();
- const innerGenerator = this.innerGenerator();
- const item = this.scope.uniqueName("item");
- return `
- ${this.builder.writer.varUInt32(`${accessor}.length`)}
- ${this.builder.writer.reserve(`${innerMeta.fixedSize} * ${accessor}.length`)};
- for (const ${item} of ${accessor}) {
- ${innerGenerator.toWriteEmbed(item)}
- }
- `;
+ newCollection(lenAccessor: string): string {
+ return `new Array(${lenAccessor})`;
}
- readStmt(accessor: (expr: string) => string): string {
- const innerGenerator = this.innerGenerator();
- const result = this.scope.uniqueName("result");
- const len = this.scope.uniqueName("len");
- const idx = this.scope.uniqueName("idx");
-
- return `
- const ${len} = ${this.builder.reader.varUInt32()};
- const ${result} = new Array(${len});
- ${this.pushReadRefStmt(result)}
- for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
- ${innerGenerator.toReadEmbed(x => `${result}[${idx}] = ${x};`)}
- }
- ${accessor(result)}
- `;
+ putAccessor(result: string, item: string, index: string): string {
+ return `${result}[${index}] = ${item}`;
}
}
diff --git a/javascript/packages/fury/lib/gen/binary.ts b/javascript/packages/fury/lib/gen/binary.ts
index e18329e1..cdb80288 100644
--- a/javascript/packages/fury/lib/gen/binary.ts
+++ b/javascript/packages/fury/lib/gen/binary.ts
@@ -19,7 +19,7 @@
import { TypeDescription } from "../description";
import { CodecBuilder } from "./builder";
-import { BaseSerializerGenerator } from "./serializer";
+import { BaseSerializerGenerator, RefState } from "./serializer";
import { CodegenRegistry } from "./router";
import { InternalSerializerType } from "../type";
import { Scope } from "./scope";
@@ -40,12 +40,12 @@ class BinarySerializerGenerator extends BaseSerializerGenerator {
`;
}
- readStmt(accessor: (expr: string) => string): string {
+ readStmt(accessor: (expr: string) => string, refState: RefState): string {
const result = this.scope.uniqueName("result");
return `
${this.builder.reader.uint8()}
${result} = ${this.builder.reader.buffer(this.builder.reader.int32())};
- ${this.pushReadRefStmt(result)};
+ ${this.maybeReference(result, refState)};
${accessor(result)}
`;
}
diff --git a/javascript/packages/fury/lib/gen/builder.ts b/javascript/packages/fury/lib/gen/builder.ts
index 4fb41424..8e34451a 100644
--- a/javascript/packages/fury/lib/gen/builder.ts
+++ b/javascript/packages/fury/lib/gen/builder.ts
@@ -17,10 +17,10 @@
* under the License.
*/
-import { Fury } from "../type";
import { Scope } from "./scope";
import { getMeta } from "../meta";
import { TypeDescription } from "../description";
+import Fury from "../fury";
class BinaryReaderBuilder {
constructor(private holder: string) {
@@ -107,8 +107,8 @@ class BinaryReaderBuilder {
return `${this.holder}.uint64()`;
}
- skip() {
- return `${this.holder}.skip()`;
+ skip(v: number) {
+ return `${this.holder}.skip(${v})`;
}
int64() {
@@ -243,16 +243,16 @@ class ReferenceResolverBuilder {
return this.holder;
}
- getReadObjectByRefId(id: string | number) {
- return `${this.holder}.getReadObjectByRefId(${id})`;
+ getReadObject(id: string | number) {
+ return `${this.holder}.getReadObject(${id})`;
}
- pushReadObject(obj: string) {
- return `${this.holder}.pushReadObject(${obj})`;
+ reference(obj: string) {
+ return `${this.holder}.reference(${obj})`;
}
- pushWriteObject(obj: string) {
- return `${this.holder}.pushWriteObject(${obj})`;
+ writeRef(obj: string) {
+ return `${this.holder}.writeRef(${obj})`;
}
existsWriteObject(obj: string) {
@@ -284,6 +284,10 @@ class ClassResolverBuilder {
readTag(binaryReader: string) {
return `${this.holder}.readTag(${binaryReader})`;
}
+
+ getSerializerByData(v: string) {
+ return `${this.holder}.readTag(${v})`;
+ }
}
export class CodecBuilder {
@@ -292,7 +296,7 @@ export class CodecBuilder {
referenceResolver: ReferenceResolverBuilder;
classResolver: ClassResolverBuilder;
- constructor(scope: Scope, private fury: Fury) {
+ constructor(scope: Scope, public fury: Fury) {
const br = scope.declareByName("br", "fury.binaryReader");
const bw = scope.declareByName("bw", "fury.binaryWriter");
const cr = scope.declareByName("cr", "fury.classResolver");
@@ -303,6 +307,10 @@ export class CodecBuilder {
this.referenceResolver = new ReferenceResolverBuilder(rr);
}
+ furyName() {
+ return "fury";
+ }
+
meta(description: TypeDescription) {
return getMeta(description, this.fury);
}
@@ -336,4 +344,8 @@ export class CodecBuilder {
}
return prop;
}
+
+ getExternal(key: string) {
+ return `external.${key}`;
+ }
}
diff --git a/javascript/packages/fury/lib/gen/collection.ts b/javascript/packages/fury/lib/gen/collection.ts
new file mode 100644
index 00000000..7dfe2297
--- /dev/null
+++ b/javascript/packages/fury/lib/gen/collection.ts
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { TypeDescription } from "../description";
+import { CodecBuilder } from "./builder";
+import { BaseSerializerGenerator, RefState } from "./serializer";
+import { CodegenRegistry } from "./router";
+import { InternalSerializerType, RefFlags, Serializer } from "../type";
+import { Scope } from "./scope";
+import Fury from "../fury";
+
+export const CollectionFlags = {
+ /** Whether track elements ref. */
+ TRACKING_REF: 0b1,
+
+ /** Whether collection has null. */
+ 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,
+};
+
+class CollectionAnySerializer {
+ constructor(private fury: Fury) {
+
+ }
+
+ protected writeElementsHeader(arr: any) {
+ let flag = 0;
+ let isSame = true;
+ let serializer: Serializer | null = null;
+ let includeNone = false;
+
+ for (const item of arr) {
+ if ((item === undefined || item === null) && !includeNone) {
+ includeNone = true;
+ }
+ if (isSame) {
+ const current = this.fury.classResolver.getSerializerByData(item);
+ if (serializer !== null && current !== null && current !== serializer) {
+ isSame = false;
+ }
+ if (current !== null) {
+ serializer = current;
+ }
+ }
+ }
+
+ if (!isSame) {
+ flag |= CollectionFlags.NOT_SAME_TYPE;
+ }
+ if (includeNone) {
+ flag |= CollectionFlags.HAS_NULL;
+ }
+ if (serializer !== null && serializer.meta.needToWriteRef) {
+ flag |= CollectionFlags.TRACKING_REF;
+ }
+ this.fury.binaryWriter.uint8(flag);
+ if (isSame) {
+ if (serializer) {
+ this.fury.binaryWriter.int16(serializer.meta.type);
+ } else {
+ this.fury.binaryWriter.skip(2);
+ }
+ }
+ return {
+ serializer,
+ isSame,
+ flag,
+ includeNone,
+ };
+ }
+
+ write(value: any, size: number) {
+ const { serializer, isSame, includeNone } = this.writeElementsHeader(value);
+ this.fury.binaryWriter.varUInt32(size);
+ for (const item of value) {
+ if (includeNone) {
+ if (item === null || item === undefined) {
+ this.fury.binaryWriter.uint8(RefFlags.NullFlag);
+ continue;
+ } else {
+ this.fury.binaryWriter.uint8(RefFlags.NotNullValueFlag);
+ }
+ }
+ let finalSerializer = serializer;
+ if (!isSame) {
+ finalSerializer = this.fury.classResolver.getSerializerByData(item);
+ this.fury.binaryWriter.uint16(finalSerializer!.meta.type);
+ }
+ finalSerializer!.write(item);
+ }
+ }
+
+ read(accessor: (result: any, index: number, v: any) => void, createCollection: (len: number) => any, fromRef: boolean): any {
+ const flags = this.fury.binaryReader.uint8();
+ const isSame = !(flags & CollectionFlags.NOT_SAME_TYPE);
+ const includeNone = flags & CollectionFlags.HAS_NULL;
+
+ let serializer: Serializer;
+ if (isSame) {
+ serializer = this.fury.classResolver.getSerializerById(this.fury.binaryReader.int16());
+ }
+ const len = this.fury.binaryReader.varUInt32();
+ const result = createCollection(len);
+ if (fromRef) {
+ this.fury.referenceResolver.reference(result);
+ }
+ for (let index = 0; index < len; index++) {
+ if (includeNone) {
+ const refFlag = this.fury.binaryReader.uint8();
+ if (RefFlags.NullFlag === refFlag) {
+ accessor(result, index, null);
+ continue;
+ }
+ }
+ if (!isSame) {
+ serializer = this.fury.classResolver.getSerializerById(this.fury.binaryReader.int16());
+ }
+ accessor(result, index, serializer!.read());
+ }
+ return result;
+ }
+}
+
+export abstract class CollectionSerializerGenerator extends BaseSerializerGenerator {
+ description: TypeDescription;
+
+ constructor(description: TypeDescription, builder: CodecBuilder, scope: Scope) {
+ super(description, builder, scope);
+ this.description = description;
+ }
+
+ abstract genericTypeDescriptin(): TypeDescription;
+
+ private isAny() {
+ return this.genericTypeDescriptin().type === InternalSerializerType.ANY;
+ }
+
+ private innerMeta() {
+ const inner = this.genericTypeDescriptin();
+ return this.builder.meta(inner);
+ }
+
+ private innerGenerator() {
+ const inner = this.genericTypeDescriptin();
+ const InnerGeneratorClass = CodegenRegistry.get(inner.type);
+ if (!InnerGeneratorClass) {
+ throw new Error(`${inner.type} generator not exists`);
+ }
+ return new InnerGeneratorClass(inner, this.builder, this.scope);
+ }
+
+ abstract newCollection(lenAccessor: string): string;
+
+ abstract putAccessor(result: string, item: string, index: string): string;
+
+ abstract sizeProp(): string;
+
+ protected writeElementsHeader(accessor: string, flagAccessor: string) {
+ const meta = this.innerMeta();
+ const item = this.scope.uniqueName("item");
+ const stmts = [
+ ];
+ if (meta.needToWriteRef) {
+ stmts.push(`${flagAccessor} |= ${CollectionFlags.TRACKING_REF}`);
+ }
+ stmts.push(`
+ for (const ${item} of ${accessor}) {
+ if (${item} === null || ${item} === undefined) {
+ ${flagAccessor} |= ${CollectionFlags.HAS_NULL};
+ break;
+ }
+ }
+ `);
+ stmts.push(`${this.builder.writer.uint8(flagAccessor)}`);
+ return stmts.join("\n");
+ }
+
+ writeStmtSpecificType(accessor: string): string {
+ const innerMeta = this.innerMeta();
+ const innerGenerator = this.innerGenerator();
+ const item = this.scope.uniqueName("item");
+ const flags = this.scope.uniqueName("flags");
+ const existsId = this.scope.uniqueName("existsId");
+
+ return `
+ let ${flags} = 0;
+ ${this.writeElementsHeader(accessor, flags)}
+ ${this.builder.writer.int16(this.description.type)}
+ ${this.builder.writer.varUInt32(`${accessor}.${this.sizeProp()}`)}
+ ${this.builder.writer.reserve(`${innerMeta.fixedSize} * ${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)};
+ if (typeof ${existsId} === "number") {
+ ${this.builder.writer.int8(RefFlags.RefFlag)}
+ ${this.builder.writer.varUInt32(existsId)}
+ } else {
+ ${this.builder.referenceResolver.writeRef(accessor)}
+ ${this.builder.writer.int8(RefFlags.RefValueFlag)};
+ ${innerGenerator.toWriteEmbed(item, true)}
+ }
+ } else {
+ ${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)};
+ ${innerGenerator.toWriteEmbed(item, true)}
+ } else {
+ ${this.builder.writer.int8(RefFlags.NullFlag)};
+ }
+ }
+ } else {
+ for (const ${item} of ${accessor}) {
+ ${innerGenerator.toWriteEmbed(item, true)}
+ }
+ }
+ }
+ `;
+ }
+
+ readStmtSpecificType(accessor: (expr: string) => string, refState: RefState): string {
+ const innerGenerator = this.innerGenerator();
+ 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 4rd bit 0b1000 of header to flag it.
+ return `
+ 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}) {
+ for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
+ const ${refFlag} = ${this.builder.reader.int8()};
+ switch (${refFlag}) {
+ case ${RefFlags.NotNullValueFlag}:
+ case ${RefFlags.RefValueFlag}:
+ ${innerGenerator.toReadEmbed(x => `${this.putAccessor(result, x, idx)}`, true, RefState.fromCondition(`${refFlag} === ${RefFlags.RefValueFlag}`))}
+ break;
+ case ${RefFlags.RefFlag}:
+ ${this.putAccessor(result, this.builder.referenceResolver.getReadObject(this.builder.reader.varUInt32()), idx)}
+ break;
+ case ${RefFlags.NullFlag}:
+ ${this.putAccessor(result, "null", idx)}
+ break;
+ }
+ }
+ } else {
+ if (!(${flags} & ${CollectionFlags.HAS_NULL})) {
+ for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
+ ${innerGenerator.toReadEmbed(x => `${this.putAccessor(result, x, idx)}`, true, RefState.fromFalse())}
+ }
+ } else {
+ for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
+ if (${this.builder.reader.uint8()} == ${RefFlags.NullFlag}) {
+ ${this.putAccessor(result, "null", idx)}
+ } else {
+ ${innerGenerator.toReadEmbed(x => `${this.putAccessor(result, x, idx)}`, true, RefState.fromFalse())}
+ }
+ }
+ }
+ }
+ ${accessor(result)}
+ `;
+ }
+
+ writeStmt(accessor: string): string {
+ if (this.isAny()) {
+ return `
+ new (${this.builder.getExternal(CollectionAnySerializer.name)})(${this.builder.furyName()}).write(${accessor}, ${accessor}.${this.sizeProp()})
+ `;
+ }
+ return this.writeStmtSpecificType(accessor);
+ }
+
+ readStmt(accessor: (expr: string) => string, refState: RefState): string {
+ if (this.isAny()) {
+ return accessor(`new (${this.builder.getExternal(CollectionAnySerializer.name)})(${this.builder.furyName()}).read((result, i, v) => {
+ ${this.putAccessor("result", "v", "i")};
+ }, (len) => ${this.newCollection("len")}, ${refState.toConditionExpr()});
+ `);
+ }
+ return this.readStmtSpecificType(accessor, refState);
+ }
+}
+
+CodegenRegistry.registerExternal(CollectionSerializerGenerator);
+CodegenRegistry.registerExternal(CollectionAnySerializer);
diff --git a/javascript/packages/fury/lib/gen/index.ts b/javascript/packages/fury/lib/gen/index.ts
index 4a9d1e3a..3971b7a6 100644
--- a/javascript/packages/fury/lib/gen/index.ts
+++ b/javascript/packages/fury/lib/gen/index.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { InternalSerializerType, Fury } from "../type";
+import { InternalSerializerType } from "../type";
import { ArrayTypeDescription, MapTypeDescription, ObjectTypeDescription, SetTypeDescription, TupleTypeDescription, TypeDescription } from "../description";
import { CodegenRegistry } from "./router";
import { CodecBuilder } from "./builder";
@@ -31,11 +31,15 @@ import "./datetime";
import "./map";
import "./number";
import "./set";
-import "./any";
import "./tuple";
import "./typedArray";
+import Fury from "../fury";
import "./enum";
+export { AnySerializer } from "./any";
+
+const external = CodegenRegistry.getExternal();
+
export const generate = (fury: Fury, description: TypeDescription) => {
const InnerGeneratorClass = CodegenRegistry.get(description.type);
if (!InnerGeneratorClass) {
@@ -64,7 +68,7 @@ function regDependencies(fury: Fury, description: TypeDescription) {
regDependencies(fury, x);
});
const func = generate(fury, description);
- fury.classResolver.registerSerializerByTag(options.tag, func()(fury, {}));
+ fury.classResolver.registerSerializerByTag(options.tag, func()(fury, external));
}
}
if (description.type === InternalSerializerType.ARRAY) {
@@ -90,5 +94,5 @@ export const generateSerializer = (fury: Fury, description: TypeDescription) =>
return fury.classResolver.getSerializerByTag((<ObjectTypeDescription>description).options.tag);
}
const func = generate(fury, description);
- return func()(fury, {});
+ return func()(fury, external);
};
diff --git a/javascript/packages/fury/lib/gen/map.ts b/javascript/packages/fury/lib/gen/map.ts
index 6ee9f1b7..7976491d 100644
--- a/javascript/packages/fury/lib/gen/map.ts
+++ b/javascript/packages/fury/lib/gen/map.ts
@@ -19,7 +19,7 @@
import { MapTypeDescription, TypeDescription } from "../description";
import { CodecBuilder } from "./builder";
-import { BaseSerializerGenerator } from "./serializer";
+import { BaseSerializerGenerator, RefState } from "./serializer";
import { CodegenRegistry } from "./router";
import { InternalSerializerType } from "../type";
import { Scope } from "./scope";
@@ -69,7 +69,7 @@ class MapSerializerGenerator extends BaseSerializerGenerator {
`;
}
- readStmt(accessor: (expr: string) => string): string {
+ readStmt(accessor: (expr: string) => string, refState: RefState): string {
const [keyGenerator, valueGenerator] = this.innerGenerator();
const key = this.scope.uniqueName("key");
const value = this.scope.uniqueName("value");
@@ -80,7 +80,7 @@ class MapSerializerGenerator extends BaseSerializerGenerator {
return `
const ${result} = new Map();
- ${this.pushReadRefStmt(result)};
+ ${this.maybeReference(result, refState)};
const ${len} = ${this.builder.reader.varUInt32()};
for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
let ${key};
diff --git a/javascript/packages/fury/lib/gen/object.ts b/javascript/packages/fury/lib/gen/object.ts
index 0d97ce49..1b86e028 100644
--- a/javascript/packages/fury/lib/gen/object.ts
+++ b/javascript/packages/fury/lib/gen/object.ts
@@ -23,7 +23,7 @@ import { CodecBuilder } from "./builder";
import { ObjectTypeDescription, TypeDescription } from "../description";
import { fromString } from "../platformBuffer";
import { CodegenRegistry } from "./router";
-import { BaseSerializerGenerator } from "./serializer";
+import { BaseSerializerGenerator, RefState } from "./serializer";
function computeFieldHash(hash: number, id: number): number {
let newHash = (hash) * 31 + (id);
@@ -70,10 +70,8 @@ class ObjectSerializerGenerator extends BaseSerializerGenerator {
writeStmt(accessor: string): string {
const options = this.description.options;
const expectHash = computeStructHash(this.description);
- const tagWriter = this.scope.declare("tagWriter", `${this.builder.classResolver.createTagWriter(this.safeTag())}`);
return `
- ${tagWriter}.write(${this.builder.writer.ownName()});
${this.builder.writer.int32(expectHash)};
${Object.entries(options.props).sort().map(([key, inner]) => {
const InnerGeneratorClass = CodegenRegistry.get(inner.type);
@@ -87,7 +85,7 @@ class ObjectSerializerGenerator extends BaseSerializerGenerator {
`;
}
- readStmt(accessor: (expr: string) => string): string {
+ readStmt(accessor: (expr: string) => string, refState: RefState): string {
const options = this.description.options;
const expectHash = computeStructHash(this.description);
const result = this.scope.uniqueName("result");
@@ -100,7 +98,7 @@ class ObjectSerializerGenerator extends BaseSerializerGenerator {
return `${CodecBuilder.safePropName(key)}: null`;
}).join(",\n")}
};
- ${this.pushReadRefStmt(result)}
+ ${this.maybeReference(result, refState)}
${Object.entries(options.props).sort().map(([key, inner]) => {
const InnerGeneratorClass = CodegenRegistry.get(inner.type);
if (!InnerGeneratorClass) {
@@ -118,20 +116,26 @@ class ObjectSerializerGenerator extends BaseSerializerGenerator {
return CodecBuilder.replaceBackslashAndQuote(this.description.options.tag);
}
- toReadEmbed(accessor: (expr: string) => string): string {
+ toReadEmbed(accessor: (expr: string) => string, excludeHead?: boolean, refState?: RefState): string {
const name = this.scope.declare(
"tag_ser",
`fury.classResolver.getSerializerByTag("${this.safeTag()}")`
);
- return accessor(`${name}.read()`);
+ if (!excludeHead) {
+ return accessor(`${name}.read()`);
+ }
+ return accessor(`${name}.readInner(${refState!.toConditionExpr()})`);
}
- toWriteEmbed(accessor: string): string {
+ toWriteEmbed(accessor: string, excludeHead?: boolean): string {
const name = this.scope.declare(
"tag_ser",
`fury.classResolver.getSerializerByTag("${this.safeTag()}")`
);
- return `${name}.write(${accessor})`;
+ if (!excludeHead) {
+ return `${name}.write(${accessor})`;
+ }
+ return `${name}.writeInner(${accessor})`;
}
}
diff --git a/javascript/packages/fury/lib/gen/router.ts b/javascript/packages/fury/lib/gen/router.ts
index a8cd043a..0260767c 100644
--- a/javascript/packages/fury/lib/gen/router.ts
+++ b/javascript/packages/fury/lib/gen/router.ts
@@ -27,12 +27,22 @@ type SerializerGeneratorConstructor = new (description: TypeDescription, builder
export class CodegenRegistry {
static map = new Map<string, SerializerGeneratorConstructor>();
+ static external = new Map<string, any>();
static register(type: InternalSerializerType, generator: SerializerGeneratorConstructor) {
this.map.set(InternalSerializerType[type], generator);
+ this.external.set(generator.name, generator);
+ }
+
+ static registerExternal(object: { name: string }) {
+ this.external.set(object.name, object);
}
static get(type: InternalSerializerType) {
return this.map.get(InternalSerializerType[type]);
}
+
+ static getExternal() {
+ return Object.fromEntries(Array.from(CodegenRegistry.external.entries()).map(([key, value]) => [key, value]));
+ }
}
diff --git a/javascript/packages/fury/lib/gen/serializer.ts b/javascript/packages/fury/lib/gen/serializer.ts
index 34eaa17a..f9adc564 100644
--- a/javascript/packages/fury/lib/gen/serializer.ts
+++ b/javascript/packages/fury/lib/gen/serializer.ts
@@ -22,14 +22,77 @@ import { CodecBuilder } from "./builder";
import { makeHead } from "../referenceResolver";
import { RefFlags } from "../type";
import { Scope } from "./scope";
-import { TypeDescription } from "../description";
+import { TypeDescription, ObjectTypeDescription } from "../description";
export interface SerializerGenerator {
writeStmt(accessor: string): string
- readStmt(accessor: (expr: string) => string): string
+ readStmt(accessor: (expr: string) => string, refState: RefState): string
toSerializer(): string
- toWriteEmbed(accessor: string, withHead?: boolean): string
- toReadEmbed(accessor: (expr: string) => string, withHead?: boolean): string
+ toWriteEmbed(accessor: string, excludeHead?: boolean): string
+ toReadEmbed(accessor: (expr: string) => string, excludeHead?: boolean, refState?: RefState): string
+}
+
+export enum RefStateType {
+ Condition = "condition",
+ True = "true",
+ False = "false",
+}
+export class RefState {
+ constructor(private state: RefStateType, private conditionAccessor?: string) {
+
+ }
+
+ getState() {
+ return this.state;
+ }
+
+ getCondition() {
+ return this.conditionAccessor;
+ }
+
+ static fromCondition(conditionAccessor: string) {
+ return new RefState(RefStateType.Condition, conditionAccessor);
+ }
+
+ static fromTrue() {
+ return new RefState(RefStateType.True);
+ }
+
+ static fromFalse() {
+ return new RefState(RefStateType.False);
+ }
+
+ static fromBool(v: boolean) {
+ return v ? new RefState(RefStateType.True) : new RefState(RefStateType.False);
+ }
+
+ toConditionExpr() {
+ const ref = this!.getState();
+ if (ref === RefStateType.Condition) {
+ return this.conditionAccessor;
+ } else if (ref === RefStateType.False) {
+ return "false";
+ } else {
+ return "true";
+ }
+ }
+
+ wrap(inner: (need: boolean) => string) {
+ const ref = this!.getState();
+ if (ref === RefStateType.Condition) {
+ return `
+ if (${this.conditionAccessor}) {
+ ${inner(true)}
+ } else {
+ ${inner(false)}
+ }
+ `;
+ } else if (ref === RefStateType.False) {
+ return inner(false);
+ } else {
+ return inner(true);
+ }
+ }
}
export abstract class BaseSerializerGenerator implements SerializerGenerator {
@@ -43,68 +106,86 @@ export abstract class BaseSerializerGenerator implements SerializerGenerator {
abstract writeStmt(accessor: string): string;
- abstract readStmt(accessor: (expr: string) => string): string;
+ abstract readStmt(accessor: (expr: string) => string, refState: RefState): string;
- protected pushReadRefStmt(accessor: string) {
- if (!this.builder.config().refTracking) {
+ protected maybeReference(accessor: string, refState: RefState) {
+ if (refState.getState() === RefStateType.False) {
return "";
}
- return this.builder.referenceResolver.pushReadObject(accessor);
+ if (refState.getState() === RefStateType.True) {
+ return this.builder.referenceResolver.reference(accessor);
+ }
+ if (refState.getState() === RefStateType.Condition) {
+ return `
+ if (${refState.getCondition()}) {
+ ${this.builder.referenceResolver.reference(accessor)}
+ }
+ `;
+ }
+ }
+
+ safeTag() {
+ return CodecBuilder.replaceBackslashAndQuote((<ObjectTypeDescription> this.description).options.tag);
}
protected wrapWriteHead(accessor: string, stmt: (accessor: string) => string) {
const meta = this.builder.meta(this.description);
- const noneable = meta.noneable;
- if (noneable) {
- const head = makeHead(RefFlags.RefValueFlag, this.description.type);
- const normaStmt = `
- if (${accessor} !== null && ${accessor} !== undefined) {
- ${this.builder.writer.int24(head)};
- ${stmt(accessor)};
- } else {
- ${this.builder.writer.int8(RefFlags.NullFlag)};
- }
- `;
- if (this.builder.config().refTracking) {
- const existsId = this.scope.uniqueName("existsId");
- return `
- const ${existsId} = ${this.builder.referenceResolver.existsWriteObject(accessor)};
- if (typeof ${existsId} === "number") {
- ${this.builder.writer.int8(RefFlags.RefFlag)}
- ${this.builder.writer.varUInt32(existsId)}
+ const maybeTag = () => {
+ if (this.description.type !== InternalSerializerType.FURY_TYPE_TAG) {
+ return "";
+ }
+ const tagWriter = this.scope.declare("tagWriter", `${this.builder.classResolver.createTagWriter(this.safeTag())}`);
+ return `${tagWriter}.write(${this.builder.writer.ownName()})`;
+ };
+
+ if (meta.needToWriteRef) {
+ const head = makeHead(RefFlags.RefValueFlag, this.description.type);
+ const existsId = this.scope.uniqueName("existsId");
+ return `
+ if (${accessor} !== null && ${accessor} !== undefined) {
+ const ${existsId} = ${this.builder.referenceResolver.existsWriteObject(accessor)};
+ if (typeof ${existsId} === "number") {
+ ${this.builder.writer.int8(RefFlags.RefFlag)}
+ ${this.builder.writer.varUInt32(existsId)}
+ } else {
+ ${this.builder.referenceResolver.writeRef(accessor)}
+ ${this.builder.writer.int24(head)};
+ ${maybeTag()}
+ ${stmt(accessor)};
+ }
} else {
- ${this.builder.referenceResolver.pushWriteObject(accessor)}
- ${normaStmt}
+ ${this.builder.writer.int8(RefFlags.NullFlag)};
}
`;
- } else {
- return normaStmt;
- }
} else {
const head = makeHead(RefFlags.NotNullValueFlag, this.description.type);
return `
- ${this.builder.writer.int24(head)};
if (${accessor} !== null && ${accessor} !== undefined) {
+ ${this.builder.writer.int24(head)};
+ ${maybeTag()}
${stmt(accessor)};
} else {
- ${typeof meta.default === "string" ? stmt(`"${meta.default}"`) : stmt(meta.default)};
+ ${this.builder.writer.int8(RefFlags.NullFlag)};
}`;
}
}
- protected wrapReadHead(accessor: (expr: string) => string, stmt: (accessor: (expr: string) => string) => string) {
+ protected wrapReadHead(accessor: (expr: string) => string, stmt: (accessor: (expr: string) => string, refState: RefState) => string) {
+ const refFlag = this.scope.uniqueName("refFlag");
+
return `
- switch (${this.builder.reader.int8()}) {
+ const ${refFlag} = ${this.builder.reader.int8()};
+ switch (${refFlag}) {
case ${RefFlags.NotNullValueFlag}:
case ${RefFlags.RefValueFlag}:
if (${this.builder.reader.int16()} === ${InternalSerializerType.FURY_TYPE_TAG}) {
${this.builder.classResolver.readTag(this.builder.reader.ownName())};
}
- ${stmt(accessor)}
+ ${stmt(accessor, RefState.fromCondition(`${refFlag} === ${RefFlags.RefValueFlag}`))}
break;
case ${RefFlags.RefFlag}:
- ${accessor(this.builder.referenceResolver.getReadObjectByRefId(this.builder.reader.varUInt32()))}
+ ${accessor(this.builder.referenceResolver.getReadObject(this.builder.reader.varUInt32()))}
break;
case ${RefFlags.NullFlag}:
${accessor("null")}
@@ -113,18 +194,18 @@ export abstract class BaseSerializerGenerator implements SerializerGenerator {
`;
}
- toWriteEmbed(accessor: string, withHead = true) {
- if (!withHead) {
+ toWriteEmbed(accessor: string, excludeHead = false) {
+ if (excludeHead) {
return this.writeStmt(accessor);
}
return this.wrapWriteHead(accessor, accessor => this.writeStmt(accessor));
}
- toReadEmbed(accessor: (expr: string) => string, withHead = true) {
- if (!withHead) {
- return this.readStmt(accessor);
+ toReadEmbed(accessor: (expr: string) => string, excludeHead = false, refState?: RefState) {
+ if (excludeHead) {
+ return this.readStmt(accessor, refState!);
}
- return this.wrapReadHead(accessor, accessor => this.readStmt(accessor));
+ return this.wrapReadHead(accessor, (accessor, refState) => this.readStmt(accessor, refState));
}
toSerializer() {
@@ -134,11 +215,11 @@ export abstract class BaseSerializerGenerator implements SerializerGenerator {
this.scope.assertNameNotDuplicate("writeInner");
const declare = `
- const readInner = () => {
- ${this.readStmt(x => `return ${x}`)}
+ const readInner = (fromRef) => {
+ ${this.readStmt(x => `return ${x}`, RefState.fromCondition("fromRef"))}
};
const read = () => {
- ${this.wrapReadHead(x => `return ${x}`, accessor => accessor(`readInner()`))}
+ ${this.wrapReadHead(x => `return ${x}`, (accessor, refState) => accessor(`readInner(${refState.getCondition()})`))}
};
const writeInner = (v) => {
${this.writeStmt("v")}
diff --git a/javascript/packages/fury/lib/gen/set.ts b/javascript/packages/fury/lib/gen/set.ts
index 3a595b16..6d806a55 100644
--- a/javascript/packages/fury/lib/gen/set.ts
+++ b/javascript/packages/fury/lib/gen/set.ts
@@ -19,12 +19,12 @@
import { SetTypeDescription, TypeDescription } from "../description";
import { CodecBuilder } from "./builder";
-import { BaseSerializerGenerator } from "./serializer";
import { CodegenRegistry } from "./router";
import { InternalSerializerType } from "../type";
import { Scope } from "./scope";
+import { CollectionSerializerGenerator } from "./collection";
-class SetSerializerGenerator extends BaseSerializerGenerator {
+class SetSerializerGenerator extends CollectionSerializerGenerator {
description: SetTypeDescription;
constructor(description: TypeDescription, builder: CodecBuilder, scope: Scope) {
@@ -32,48 +32,20 @@ class SetSerializerGenerator extends BaseSerializerGenerator {
this.description = <SetTypeDescription>description;
}
- private innerMeta() {
- const inner = this.description.options.key;
- return this.builder.meta(inner);
+ genericTypeDescriptin(): TypeDescription {
+ return this.description.options.key;
}
- private innerGenerator() {
- const inner = this.description.options.key;
- const InnerGeneratorClass = CodegenRegistry.get(inner.type);
- if (!InnerGeneratorClass) {
- throw new Error(`${inner.type} generator not exists`);
- }
- return new InnerGeneratorClass(inner, this.builder, this.scope);
+ newCollection(): string {
+ return `new Set()`;
}
- writeStmt(accessor: string): string {
- const innerMeta = this.innerMeta();
- const innerGenerator = this.innerGenerator();
- const item = this.scope.uniqueName("item");
- return `
- ${this.builder.writer.varUInt32(`${accessor}.size`)}
- ${this.builder.writer.reserve(`${innerMeta.fixedSize} * ${accessor}.size`)};
- for (const ${item} of ${accessor}.values()) {
- ${innerGenerator.toWriteEmbed(item)}
- }
- `;
+ sizeProp() {
+ return "size";
}
- readStmt(accessor: (expr: string) => string): string {
- const innerGenerator = this.innerGenerator();
- const result = this.scope.uniqueName("result");
- const idx = this.scope.uniqueName("idx");
- const len = this.scope.uniqueName("len");
-
- return `
- const ${result} = new Set();
- ${this.pushReadRefStmt(result)}
- const ${len} = ${this.builder.reader.varUInt32()};
- for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
- ${innerGenerator.toReadEmbed(x => `${result}.add(${x});`)}
- }
- ${accessor(result)}
- `;
+ putAccessor(result: string, item: string): string {
+ return `${result}.add(${item})`;
}
}
diff --git a/javascript/packages/fury/lib/gen/tuple.ts b/javascript/packages/fury/lib/gen/tuple.ts
index ee3d8ce1..0d96cbbc 100644
--- a/javascript/packages/fury/lib/gen/tuple.ts
+++ b/javascript/packages/fury/lib/gen/tuple.ts
@@ -19,7 +19,7 @@
import { TupleTypeDescription, TypeDescription } from "../description";
import { CodecBuilder } from "./builder";
-import { BaseSerializerGenerator } from "./serializer";
+import { BaseSerializerGenerator, RefState } from "./serializer";
import { CodegenRegistry } from "./router";
import { InternalSerializerType } from "../type";
import { Scope } from "./scope";
@@ -64,7 +64,7 @@ class TupleSerializerGenerator extends BaseSerializerGenerator {
`;
}
- readStmt(accessor: (expr: string) => string): string {
+ readStmt(accessor: (expr: string) => string, refState: RefState): string {
const innerGenerator = this.innerGenerator();
const result = this.scope.uniqueName("result");
const len = this.scope.uniqueName("len");
@@ -72,7 +72,7 @@ class TupleSerializerGenerator extends BaseSerializerGenerator {
return `
const ${len} = ${this.builder.reader.varUInt32()};
const ${result} = new Array(${len});
- ${this.pushReadRefStmt(result)}
+ ${this.maybeReference(result, refState)}
${
innerGenerator.map((generator, index) => {
return `
diff --git a/javascript/packages/fury/lib/gen/typedArray.ts b/javascript/packages/fury/lib/gen/typedArray.ts
index 4d830190..57d8c0fa 100644
--- a/javascript/packages/fury/lib/gen/typedArray.ts
+++ b/javascript/packages/fury/lib/gen/typedArray.ts
@@ -19,7 +19,7 @@
import { Type, TypeDescription } from "../description";
import { CodecBuilder } from "./builder";
-import { BaseSerializerGenerator } from "./serializer";
+import { BaseSerializerGenerator, RefState } from "./serializer";
import { CodegenRegistry } from "./router";
import { InternalSerializerType } from "../type";
import { Scope } from "./scope";
@@ -53,12 +53,12 @@ function build(inner: TypeDescription) {
${this.builder.writer.varUInt32(`${accessor}.length`)}
${this.builder.writer.reserve(`${innerMeta.fixedSize} * ${accessor}.length`)};
for (const ${item} of ${accessor}) {
- ${innerGenerator.toWriteEmbed(item, false)}
+ ${innerGenerator.toWriteEmbed(item, true)}
}
`;
}
- readStmt(accessor: (expr: string) => string): string {
+ readStmt(accessor: (expr: string) => string, refState: RefState): string {
const innerGenerator = this.innerGenerator();
const result = this.scope.uniqueName("result");
const len = this.scope.uniqueName("len");
@@ -67,15 +67,16 @@ function build(inner: TypeDescription) {
return `
const ${len} = ${this.builder.reader.varUInt32()};
const ${result} = new Array(${len});
- ${this.pushReadRefStmt(result)}
+ ${this.maybeReference(result, refState)}
for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
- ${innerGenerator.toReadEmbed(x => `${result}[${idx}] = ${x};`, false)}
+ ${innerGenerator.toReadEmbed(x => `${result}[${idx}] = ${x};`, true, RefState.fromFalse())}
}
${accessor(result)}
`;
}
};
}
+
CodegenRegistry.register(InternalSerializerType.FURY_STRING_ARRAY, build(Type.string()));
CodegenRegistry.register(InternalSerializerType.FURY_PRIMITIVE_BOOL_ARRAY, build(Type.bool()));
CodegenRegistry.register(InternalSerializerType.FURY_PRIMITIVE_LONG_ARRAY, build(Type.int64()));
diff --git a/javascript/packages/fury/lib/meta.ts b/javascript/packages/fury/lib/meta.ts
index eb81bd5d..d46dafb6 100644
--- a/javascript/packages/fury/lib/meta.ts
+++ b/javascript/packages/fury/lib/meta.ts
@@ -17,89 +17,92 @@
* under the License.
*/
+import Fury from "./fury";
import ClassResolver from "./classResolver";
import { ObjectTypeDescription, TypeDescription } from "./description";
-import { Fury, InternalSerializerType } from "./type";
+import { InternalSerializerType } from "./type";
-export type Meta<T> = {
+export type Meta = {
fixedSize: number
- noneable: boolean
- default?: T
+ needToWriteRef: boolean
+ type: InternalSerializerType
};
-const epochDate = new Date("1970/01/01 00:00");
-
-export const getMeta = (description: TypeDescription, fury: Fury): Meta<any> => {
+export const getMeta = (description: TypeDescription, fury: Fury): Meta => {
const type = description.type;
switch (type) {
case InternalSerializerType.STRING:
return {
fixedSize: 8,
- noneable: false,
- default: "",
+ needToWriteRef: Boolean(fury.config.refTracking) && false,
+ type,
};
case InternalSerializerType.ARRAY:
return {
fixedSize: 7,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && true,
+ type,
};
case InternalSerializerType.TUPLE:
return {
fixedSize: 7,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && true,
+ type,
};
case InternalSerializerType.MAP:
return {
fixedSize: 7,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && true,
+ type,
};
case InternalSerializerType.BOOL:
case InternalSerializerType.UINT8:
case InternalSerializerType.INT8:
return {
fixedSize: 4,
- noneable: false,
- default: 0,
+ needToWriteRef: Boolean(fury.config.refTracking) && false,
+ type,
};
case InternalSerializerType.UINT16:
case InternalSerializerType.INT16:
return {
fixedSize: 5,
- noneable: false,
- default: 0,
+ needToWriteRef: Boolean(fury.config.refTracking) && false,
+ type,
};
case InternalSerializerType.UINT32:
case InternalSerializerType.INT32:
case InternalSerializerType.FLOAT:
return {
fixedSize: 7,
- noneable: false,
- default: 0,
+ needToWriteRef: Boolean(fury.config.refTracking) && false,
+ type,
};
case InternalSerializerType.UINT64:
case InternalSerializerType.INT64:
case InternalSerializerType.DOUBLE:
return {
fixedSize: 11,
- noneable: false,
- default: 0,
+ needToWriteRef: Boolean(fury.config.refTracking) && false,
+ type,
};
case InternalSerializerType.BINARY:
return {
fixedSize: 8,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && true,
+ type,
};
case InternalSerializerType.DATE:
return {
fixedSize: 7,
- noneable: false,
- default: epochDate.getTime(),
+ needToWriteRef: Boolean(fury.config.refTracking) && false,
+ type,
};
case InternalSerializerType.TIMESTAMP:
return {
fixedSize: 11,
- noneable: false,
- default: epochDate.getTime(),
+ needToWriteRef: Boolean(fury.config.refTracking) && false,
+ type,
};
case InternalSerializerType.FURY_TYPE_TAG:
{
@@ -112,14 +115,16 @@ export const getMeta = (description: TypeDescription, fury: Fury): Meta<any> =>
}
return {
fixedSize,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && true,
+ type,
};
}
case InternalSerializerType.FURY_SET:
return {
fixedSize: 7,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && true,
+ type,
};
case InternalSerializerType.FURY_PRIMITIVE_BOOL_ARRAY:
case InternalSerializerType.FURY_PRIMITIVE_SHORT_ARRAY:
@@ -130,17 +135,20 @@ export const getMeta = (description: TypeDescription, fury: Fury): Meta<any> =>
case InternalSerializerType.FURY_STRING_ARRAY:
return {
fixedSize: 7,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && true,
+ type,
};
case InternalSerializerType.ANY:
return {
fixedSize: 11,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && true,
+ type,
};
case InternalSerializerType.ENUM:
return {
fixedSize: 7,
- noneable: true,
+ needToWriteRef: Boolean(fury.config.refTracking) && false,
+ type,
};
default:
throw new Error(`Meta of ${description.type} not exists`);
diff --git a/javascript/packages/fury/lib/referenceResolver.ts b/javascript/packages/fury/lib/referenceResolver.ts
index 702bfd2f..a1fbb5af 100644
--- a/javascript/packages/fury/lib/referenceResolver.ts
+++ b/javascript/packages/fury/lib/referenceResolver.ts
@@ -43,7 +43,7 @@ export const ReferenceResolver = (
writeObjects = [];
}
- function getReadObjectByRefId(refId: number) {
+ function getReadObject(refId: number) {
return readObjects[refId];
}
@@ -51,11 +51,11 @@ export const ReferenceResolver = (
return binaryReader.int8() as RefFlags;
}
- function pushReadObject(object: any) {
+ function reference(object: any) {
readObjects.push(object);
}
- function pushWriteObject(object: any) {
+ function writeRef(object: any) {
writeObjects.push(object);
}
@@ -69,10 +69,10 @@ export const ReferenceResolver = (
return {
existsWriteObject,
- pushWriteObject,
- pushReadObject,
+ writeRef,
+ reference,
readRefFlag,
- getReadObjectByRefId,
+ getReadObject,
reset,
};
};
diff --git a/javascript/packages/fury/lib/type.ts b/javascript/packages/fury/lib/type.ts
index fe68a127..fe503632 100644
--- a/javascript/packages/fury/lib/type.ts
+++ b/javascript/packages/fury/lib/type.ts
@@ -19,10 +19,8 @@
import type { BinaryWriter } from "./writer";
import type { BinaryReader } from "./reader";
-import type FuryFunc from "./fury";
import { Meta } from "./meta";
-export type Fury = ReturnType<typeof FuryFunc>;
export type BinaryWriter = ReturnType<typeof BinaryWriter>;
export type BinaryReader = ReturnType<typeof BinaryReader>;
@@ -69,9 +67,9 @@ export enum ConfigFlags {
export type Serializer<T = any, T2 = any> = {
read: () => T2
write: (v: T2) => T
- readInner: () => T2
+ readInner: (refValue?: boolean) => T2
writeInner: (v: T2) => T
- meta: Meta<T2>
+ meta: Meta
};
export enum RefFlags {
diff --git a/javascript/test/__snapshots__/codeGen.test.ts.snap b/javascript/test/__snapshots__/codeGen.test.ts.snap
deleted file mode 100644
index fdcc52af..00000000
--- a/javascript/test/__snapshots__/codeGen.test.ts.snap
+++ /dev/null
@@ -1,606 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`codeGen can generate tuple declaration code 1`] = `
-"function anonymous(
-) {
-return function(fury, external) {
- const br = fury.binaryReader;
- const bw = fury.binaryWriter;
- const cr = fury.classResolver;
- const rr = fury.referenceResolver;
- const tag_ser_3 = fury.classResolver.getSerializerByTag("example.foo.1");
- const tag_ser_4 = fury.classResolver.getSerializerByTag("example.foo.2");
- const tag_ser_12 = fury.classResolver.getSerializerByTag("example.bar.1");
- const tag_ser_13 = fury.classResolver.getSerializerByTag("example.bar.2");
- const tagWriter_19 = cr.createTagWriter("tuple-object-wrapper");
-
- const readInner = () => {
-
- if (br.int32() !== 16469457) {
- throw new Error("validate hash failed: tuple-object-wrapper. expect 16469457");
- }
- const result_0 = {
- tuple1: null,
- tuple1_: null,
- tuple2: null,
- tuple2_: null
- };
- rr.pushReadObject(result_0)
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
-
- const len_2 = br.varUInt32();
- const result_1 = new Array(len_2);
- rr.pushReadObject(result_1)
-
- if (len_2 > 0) {
- result_1[0] = tag_ser_3.read()
- }
-
-
- if (len_2 > 1) {
- result_1[1] = tag_ser_4.read()
- }
-
- result_0.tuple1 = result_1
-
- break;
- case -2:
- result_0.tuple1 = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_0.tuple1 = null
- break;
- };
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
-
- const len_6 = br.varUInt32();
- const result_5 = new Array(len_6);
- rr.pushReadObject(result_5)
-
- if (len_6 > 0) {
- result_5[0] = tag_ser_3.read()
- }
-
-
- if (len_6 > 1) {
- result_5[1] = tag_ser_4.read()
- }
-
- result_0.tuple1_ = result_5
-
- break;
- case -2:
- result_0.tuple1_ = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_0.tuple1_ = null
- break;
- };
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
-
- const len_10 = br.varUInt32();
- const result_9 = new Array(len_10);
- rr.pushReadObject(result_9)
-
- if (len_10 > 0) {
- result_9[0] = tag_ser_3.read()
- }
-
-
- if (len_10 > 1) {
- result_9[1] = tag_ser_12.read()
- }
-
-
- if (len_10 > 2) {
- result_9[2] = tag_ser_13.read()
- }
-
- result_0.tuple2 = result_9
-
- break;
- case -2:
- result_0.tuple2 = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_0.tuple2 = null
- break;
- };
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
-
- const len_15 = br.varUInt32();
- const result_14 = new Array(len_15);
- rr.pushReadObject(result_14)
-
- if (len_15 > 0) {
- result_14[0] = tag_ser_3.read()
- }
-
-
- if (len_15 > 1) {
- result_14[1] = tag_ser_12.read()
- }
-
-
- if (len_15 > 2) {
- result_14[2] = tag_ser_13.read()
- }
-
- result_0.tuple2_ = result_14
-
- break;
- case -2:
- result_0.tuple2_ = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_0.tuple2_ = null
- break;
- }
-
- return result_0
-
- };
- const read = () => {
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
- return readInner()
- break;
- case -2:
- return rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- return null
- break;
- }
-
- };
- const writeInner = (v) => {
-
- tagWriter_19.write(bw);
- bw.int32(16469457);
-
- const existsId_22 = rr.existsWriteObject(v.tuple1);
- if (typeof existsId_22 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_22)
- } else {
- rr.pushWriteObject(v.tuple1)
-
- if (v.tuple1 !== null && v.tuple1 !== undefined) {
- bw.int24(6400);
-
- bw.varUInt32(2)
- bw.reserve(148);
- tag_ser_3.write(v.tuple1[0])
- tag_ser_4.write(v.tuple1[1]);
- } else {
- bw.int8(-3);
- }
-
- };
-
- const existsId_25 = rr.existsWriteObject(v.tuple1_);
- if (typeof existsId_25 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_25)
- } else {
- rr.pushWriteObject(v.tuple1_)
-
- if (v.tuple1_ !== null && v.tuple1_ !== undefined) {
- bw.int24(6400);
-
- bw.varUInt32(2)
- bw.reserve(148);
- tag_ser_3.write(v.tuple1_[0])
- tag_ser_4.write(v.tuple1_[1]);
- } else {
- bw.int8(-3);
- }
-
- };
-
- const existsId_29 = rr.existsWriteObject(v.tuple2);
- if (typeof existsId_29 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_29)
- } else {
- rr.pushWriteObject(v.tuple2)
-
- if (v.tuple2 !== null && v.tuple2 !== undefined) {
- bw.int24(6400);
-
- bw.varUInt32(3)
- bw.reserve(222);
- tag_ser_3.write(v.tuple2[0])
- tag_ser_12.write(v.tuple2[1])
- tag_ser_13.write(v.tuple2[2]);
- } else {
- bw.int8(-3);
- }
-
- };
-
- const existsId_33 = rr.existsWriteObject(v.tuple2_);
- if (typeof existsId_33 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_33)
- } else {
- rr.pushWriteObject(v.tuple2_)
-
- if (v.tuple2_ !== null && v.tuple2_ !== undefined) {
- bw.int24(6400);
-
- bw.varUInt32(3)
- bw.reserve(222);
- tag_ser_3.write(v.tuple2_[0])
- tag_ser_12.write(v.tuple2_[1])
- tag_ser_13.write(v.tuple2_[2]);
- } else {
- bw.int8(-3);
- }
-
- }
-
-
- };
- const write = (v) => {
-
- const existsId_34 = rr.existsWriteObject(v);
- if (typeof existsId_34 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_34)
- } else {
- rr.pushWriteObject(v)
-
- if (v !== null && v !== undefined) {
- bw.int24(65536);
- writeInner(v);
- } else {
- bw.int8(-3);
- }
-
- }
-
- };
-
- return {
- read,
- readInner,
- write,
- writeInner,
- meta: {
- "fixedSize": 67,
- "noneable": true
- }
- };
-}
-}"
-`;
-
-exports[`codeGen can generate tuple declaration code 2`] = `
-"function anonymous(
-) {
-return function(fury, external) {
- const br = fury.binaryReader;
- const bw = fury.binaryWriter;
- const cr = fury.classResolver;
- const rr = fury.referenceResolver;
- const tagWriter_6 = cr.createTagWriter("tuple-object-type3-tag");
-
- const readInner = () => {
-
- if (br.int32() !== 552) {
- throw new Error("validate hash failed: tuple-object-type3-tag. expect 552");
- }
- const result_0 = {
- tuple: null
- };
- rr.pushReadObject(result_0)
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
-
- const len_2 = br.varUInt32();
- const result_1 = new Array(len_2);
- rr.pushReadObject(result_1)
-
- if (len_2 > 0) {
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
- result_1[0] = br.stringOfVarUInt32()
- break;
- case -2:
- result_1[0] = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_1[0] = null
- break;
- }
-
- }
-
-
- if (len_2 > 1) {
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
- result_1[1] = br.uint8() === 1
- break;
- case -2:
- result_1[1] = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_1[1] = null
- break;
- }
-
- }
-
-
- if (len_2 > 2) {
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
- result_1[2] = br.uint32()
- break;
- case -2:
- result_1[2] = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_1[2] = null
- break;
- }
-
- }
-
-
- if (len_2 > 3) {
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
-
- const len_4 = br.varUInt32();
- const result_3 = new Array(len_4);
- rr.pushReadObject(result_3)
-
- if (len_4 > 0) {
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
-
- br.uint8()
- result_5 = br.buffer(br.int32());
- rr.pushReadObject(result_5);
- result_3[0] = result_5
-
- break;
- case -2:
- result_3[0] = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_3[0] = null
- break;
- }
-
- }
-
- result_1[3] = result_3
-
- break;
- case -2:
- result_1[3] = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_1[3] = null
- break;
- }
-
- }
-
- result_0.tuple = result_1
-
- break;
- case -2:
- result_0.tuple = rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- result_0.tuple = null
- break;
- }
-
- return result_0
-
- };
- const read = () => {
-
- switch (br.int8()) {
- case -1:
- case 0:
- if (br.int16() === 256) {
- cr.readTag(br);
- }
- return readInner()
- break;
- case -2:
- return rr.getReadObjectByRefId(br.varUInt32())
- break;
- case -3:
- return null
- break;
- }
-
- };
- const writeInner = (v) => {
-
- tagWriter_6.write(bw);
- bw.int32(552);
-
- const existsId_9 = rr.existsWriteObject(v.tuple);
- if (typeof existsId_9 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_9)
- } else {
- rr.pushWriteObject(v.tuple)
-
- if (v.tuple !== null && v.tuple !== undefined) {
- bw.int24(6400);
-
- bw.varUInt32(4)
- bw.reserve(26);
-
- bw.int24(3583);
- if (v.tuple[0] !== null && v.tuple[0] !== undefined) {
- bw.stringOfVarUInt32(v.tuple[0]);
- } else {
- bw.stringOfVarUInt32("");
- }
-
- bw.int24(511);
- if (v.tuple[1] !== null && v.tuple[1] !== undefined) {
- bw.uint8(v.tuple[1] ? 1 : 0);
- } else {
- bw.uint8(0 ? 1 : 0);
- }
-
- bw.int24(1791);
- if (v.tuple[2] !== null && v.tuple[2] !== undefined) {
- bw.uint32(v.tuple[2]);
- } else {
- bw.uint32(0);
- }
-
- const existsId_8 = rr.existsWriteObject(v.tuple[3]);
- if (typeof existsId_8 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_8)
- } else {
- rr.pushWriteObject(v.tuple[3])
-
- if (v.tuple[3] !== null && v.tuple[3] !== undefined) {
- bw.int24(6400);
-
- bw.varUInt32(1)
- bw.reserve(8);
-
- const existsId_7 = rr.existsWriteObject(v.tuple[3][0]);
- if (typeof existsId_7 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_7)
- } else {
- rr.pushWriteObject(v.tuple[3][0])
-
- if (v.tuple[3][0] !== null && v.tuple[3][0] !== undefined) {
- bw.int24(3584);
-
- bw.uint8(1)
- bw.uint32(v.tuple[3][0].byteLength)
- bw.buffer(v.tuple[3][0]);
- } else {
- bw.int8(-3);
- }
-
- }
-
- ;
- } else {
- bw.int8(-3);
- }
-
- }
-
- ;
- } else {
- bw.int8(-3);
- }
-
- }
-
-
- };
- const write = (v) => {
-
- const existsId_10 = rr.existsWriteObject(v);
- if (typeof existsId_10 === "number") {
- bw.int8(-2)
- bw.varUInt32(existsId_10)
- } else {
- rr.pushWriteObject(v)
-
- if (v !== null && v !== undefined) {
- bw.int24(65536);
- writeInner(v);
- } else {
- bw.int8(-3);
- }
-
- }
-
- };
-
- return {
- read,
- readInner,
- write,
- writeInner,
- meta: {
- "fixedSize": 48,
- "noneable": true
- }
- };
-}
-}"
-`;
diff --git a/javascript/test/any.test.ts b/javascript/test/any.test.ts
index 05f791b7..a41b8548 100644
--- a/javascript/test/any.test.ts
+++ b/javascript/test/any.test.ts
@@ -41,19 +41,19 @@ describe('bool', () => {
test('should write NaN work', () => {
const fury = new Fury();
const bin = fury.serialize(NaN);
- expect(fury.deserialize(bin)).toBe(null)
+ expect(fury.deserialize(bin)).toBe(NaN)
});
test('should write big number work', () => {
const fury = new Fury();
const bin = fury.serialize(3000000000);
- expect(fury.deserialize(bin)).toBe(3000000000n);
+ expect(fury.deserialize(bin)).toBe(3000000000);
});
test('should write INFINITY work', () => {
const fury = new Fury();
const bin = fury.serialize(Number.NEGATIVE_INFINITY);
- expect(fury.deserialize(bin)).toBe(null)
+ expect(fury.deserialize(bin)).toBe(Number.NEGATIVE_INFINITY)
});
test('should write float work', () => {
diff --git a/javascript/test/array.test.ts b/javascript/test/array.test.ts
index b862226e..6646e431 100644
--- a/javascript/test/array.test.ts
+++ b/javascript/test/array.test.ts
@@ -19,6 +19,7 @@
import Fury, { TypeDescription, InternalSerializerType, ObjectTypeDescription, Type } from '../packages/fury/index';
import { describe, expect, test } from '@jest/globals';
+import * as beautify from 'js-beautify';
describe('array', () => {
test('should array work', () => {
@@ -29,7 +30,11 @@ describe('array', () => {
a: Type.string()
}))
});
- const fury = new Fury({ refTracking: true });
+ const fury = new Fury({ refTracking: true, hooks: {
+ afterCodeGenerated: (code: string) => {
+ return beautify.js(code, { indent_size: 2, space_in_empty_paren: true, indent_empty_lines: true });
+ }
+ } });
const { serialize, deserialize } = fury.registerSerializer(description);
const o = { a: "123" };
expect(deserialize(serialize({ c: [o, o] }))).toEqual({ c: [o, o] })
diff --git a/javascript/test/codeGen.test.ts b/javascript/test/codeGen.test.ts
deleted file mode 100644
index 54323965..00000000
--- a/javascript/test/codeGen.test.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { describe, expect, test } from '@jest/globals';
-import { tupleObjectDescription, tupleObjectType3Description } from './fixtures/tuple';
-import { generate } from '../packages/fury/lib/gen/index';
-import FuryInternal from '../packages/fury/lib/fury';
-import * as beautify from 'js-beautify';
-
-describe('codeGen', () => {
- test('can generate tuple declaration code', () => {
- const fury = FuryInternal({
- refTracking: true, hooks: {
- afterCodeGenerated: (code: string) => {
- return beautify.js(code, { indent_size: 2, space_in_empty_paren: true, indent_empty_lines: true });
- }
- }
- });
- const fn = generate(fury, tupleObjectDescription);
- expect(fn.toString()).toMatchSnapshot();
-
- const fn2 = generate(fury, tupleObjectType3Description);
- expect(fn2.toString()).toMatchSnapshot();
- })
-})
diff --git a/javascript/test/object.test.ts b/javascript/test/object.test.ts
index 8e8676ac..4f9eb720 100644
--- a/javascript/test/object.test.ts
+++ b/javascript/test/object.test.ts
@@ -231,7 +231,7 @@ describe('object', () => {
kind: "123",
});
const obj = deserialize(bin);
- expect({kind: "123", path: ""}).toEqual(obj)
+ expect({kind: "123", path: null}).toEqual(obj)
})
});
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@fury.apache.org
For additional commands, e-mail: commits-help@fury.apache.org