json-as 0.9.21 → 0.9.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc.json +4 -0
- package/CHANGELOG +1 -1
- package/README.md +2 -2
- package/assembly/__benches__/as-tral.d.ts +1 -0
- package/assembly/__benches__/bool.bench.ts +14 -0
- package/assembly/__benches__/simd.bench.ts +36 -0
- package/assembly/__benches__/string.bench.ts +21 -0
- package/assembly/__tests__/bool.spec.ts +7 -7
- package/assembly/__tests__/float.spec.ts +23 -23
- package/assembly/__tests__/integer.spec.ts +11 -11
- package/assembly/__tests__/obj.spec.ts +1 -2
- package/assembly/__tests__/string.spec.ts +17 -41
- package/assembly/__tests__/test.spec.ts +28 -130
- package/assembly/custom/bs.ts +57 -1
- package/assembly/custom/sink.ts +19 -75
- package/assembly/deserialize/array.ts +1 -5
- package/assembly/deserialize/string.ts +71 -1
- package/assembly/index.ts +16 -178
- package/assembly/serialize/bool.ts +13 -0
- package/assembly/serialize/object.ts +5 -0
- package/assembly/serialize/string.ts +218 -6
- package/assembly/test.ts +28 -8
- package/assembly/util/strings.ts +0 -0
- package/bench.js +1 -1
- package/package.json +8 -7
- package/transform/lib/index.js +177 -172
- package/transform/lib/visitor.js +448 -0
- package/transform/package.json +1 -1
- package/transform/src/index.ts +188 -229
- package/transform/tsconfig.json +2 -2
- package/bench/bench-node.js +0 -17
- package/bench/benchmark.ts +0 -77
- package/bench/benchmark.wasm +0 -0
- package/bench/tsconfig.json +0 -97
package/transform/src/index.ts
CHANGED
|
@@ -1,119 +1,89 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ClassDeclaration,
|
|
3
|
-
FieldDeclaration,
|
|
4
|
-
IdentifierExpression,
|
|
5
|
-
Parser,
|
|
6
|
-
Source,
|
|
7
|
-
NodeKind,
|
|
8
|
-
Expression,
|
|
9
|
-
CommonFlags,
|
|
10
|
-
StringLiteralExpression,
|
|
11
|
-
IntegerLiteralExpression,
|
|
12
|
-
FloatLiteralExpression,
|
|
13
|
-
NullExpression,
|
|
14
|
-
TrueExpression,
|
|
15
|
-
FalseExpression,
|
|
16
|
-
} from "assemblyscript/dist/assemblyscript.js";
|
|
1
|
+
import { ClassDeclaration, FieldDeclaration, IdentifierExpression, Parser, Source, Expression, CommonFlags, StringLiteralExpression, IntegerLiteralExpression, FloatLiteralExpression, NullExpression, TrueExpression, FalseExpression, DeclarationStatement, NamedTypeNode } from "assemblyscript/dist/assemblyscript.js";
|
|
17
2
|
|
|
18
3
|
import { toString, isStdlib } from "visitor-as/dist/utils.js";
|
|
19
4
|
import { BaseVisitor, SimpleParser } from "visitor-as/dist/index.js";
|
|
20
5
|
import { Transform } from "assemblyscript/dist/transform.js";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
|
|
8
|
+
const json_types = ["Array", "string", "String", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f32", "f64", "bool", "boolean", "Map", "Date"];
|
|
21
9
|
|
|
22
10
|
class JSONTransform extends BaseVisitor {
|
|
11
|
+
public types = json_types;
|
|
23
12
|
public schemasList: SchemaData[] = [];
|
|
24
13
|
public currentClass!: SchemaData;
|
|
25
14
|
public sources = new Set<Source>();
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
visitClassDeclaration(node: ClassDeclaration): void {
|
|
29
|
-
if (!node.decorators?.length) return;
|
|
30
|
-
|
|
31
|
-
let found = false;
|
|
32
|
-
for (const decorator of node.decorators) {
|
|
33
|
-
const name = (<IdentifierExpression>decorator.name).text;
|
|
34
|
-
if (name === "json" || name === "serializable") {
|
|
35
|
-
found = true;
|
|
36
|
-
break;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (!found) return;
|
|
40
|
-
|
|
41
|
-
const schema = new SchemaData();
|
|
42
|
-
schema.node = node;
|
|
43
|
-
schema.name = node.name.text;
|
|
44
|
-
|
|
45
|
-
const members = [
|
|
46
|
-
...node.members.filter((v) => v.kind === NodeKind.FieldDeclaration),
|
|
47
|
-
];
|
|
48
|
-
|
|
16
|
+
appendParentFields(node: ClassDeclaration, schema: SchemaData, members: DeclarationStatement[]): void {
|
|
49
17
|
if (node.extendsType) {
|
|
50
|
-
schema.parent = this.schemasList.find(
|
|
51
|
-
(v) => v.name == node.extendsType?.name.identifier.text,
|
|
52
|
-
) as SchemaData | null;
|
|
53
|
-
|
|
54
18
|
if (schema.parent?.members) {
|
|
55
19
|
for (let i = schema.parent.members.length - 1; i >= 0; i--) {
|
|
56
|
-
const replace = schema.members.find(
|
|
57
|
-
(v) => v.name == schema.parent?.members[i]?.name,
|
|
58
|
-
);
|
|
20
|
+
const replace = schema.members.find((v) => v.name == schema.parent?.members[i]?.name);
|
|
59
21
|
if (!replace) {
|
|
60
22
|
members.unshift(schema.parent?.members[i]!.node);
|
|
61
23
|
}
|
|
62
24
|
}
|
|
25
|
+
this.appendParentFields(schema.parent.node, schema, members);
|
|
63
26
|
}
|
|
64
27
|
}
|
|
28
|
+
}
|
|
65
29
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
30
|
+
handleEmptyClass(node: ClassDeclaration, schema: SchemaData, members: DeclarationStatement[]): void {
|
|
31
|
+
let SERIALIZE_RAW_EMPTY = '__SERIALIZE(): string {\n return "{}";\n}';
|
|
32
|
+
let SERIALIZE_PRETTY_EMPTY = '__SERIALIZE_PRETTY(): string {\n return "{}";\n}';
|
|
69
33
|
|
|
70
|
-
|
|
34
|
+
let INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}";
|
|
71
35
|
|
|
72
|
-
|
|
73
|
-
"__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}";
|
|
36
|
+
let DESERIALIZE_EMPTY = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}";
|
|
74
37
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
38
|
+
if (process.env["JSON_DEBUG"]) {
|
|
39
|
+
console.log(SERIALIZE_RAW_EMPTY);
|
|
40
|
+
console.log(SERIALIZE_PRETTY_EMPTY);
|
|
41
|
+
console.log(INITIALIZE_EMPTY);
|
|
42
|
+
console.log(DESERIALIZE_EMPTY);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const SERIALIZE_RAW_METHOD_EMPTY = SimpleParser.parseClassMember(SERIALIZE_RAW_EMPTY, node);
|
|
46
|
+
const SERIALIZE_PRETTY_METHOD_EMPTY = SimpleParser.parseClassMember(SERIALIZE_PRETTY_EMPTY, node);
|
|
47
|
+
const INITIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(INITIALIZE_EMPTY, node);
|
|
48
|
+
const DESERIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(DESERIALIZE_EMPTY, node);
|
|
49
|
+
|
|
50
|
+
if (!node.members.find((v) => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_RAW_METHOD_EMPTY);
|
|
51
|
+
if (!node.members.find((v) => v.name.text == "__SERIALIZE_PRETTY")) node.members.push(SERIALIZE_PRETTY_METHOD_EMPTY);
|
|
52
|
+
if (!node.members.find((v) => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD_EMPTY);
|
|
53
|
+
if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD_EMPTY);
|
|
54
|
+
}
|
|
55
|
+
filterMembers(members: DeclarationStatement[]): FieldDeclaration[] {
|
|
56
|
+
return members.filter((v) => v instanceof FieldDeclaration && !v.decorators?.find((v) => (<IdentifierExpression>v.name).text == "omit")) as FieldDeclaration[];
|
|
57
|
+
}
|
|
58
|
+
visitClassDeclaration(node: ClassDeclaration): void {
|
|
59
|
+
if (!node.decorators?.length) return;
|
|
60
|
+
if (!node.decorators.find((v) => (<IdentifierExpression>v.name).text == "json" || (<IdentifierExpression>v.name).text == "serializable")) return;
|
|
61
|
+
this.types = json_types;
|
|
62
|
+
|
|
63
|
+
const schema = new SchemaData();
|
|
64
|
+
schema.node = node;
|
|
65
|
+
schema.name = node.name.text;
|
|
66
|
+
|
|
67
|
+
if (node.extendsType) {
|
|
68
|
+
schema.parent = this.schemasList.find((v) => v.name == node.extendsType?.name.identifier.text) as SchemaData | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const _members = [...node.members];
|
|
72
|
+
|
|
73
|
+
this.appendParentFields(node, schema, _members);
|
|
81
74
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
);
|
|
86
|
-
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node);
|
|
87
|
-
const INITIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(
|
|
88
|
-
INITIALIZE_EMPTY,
|
|
89
|
-
node,
|
|
90
|
-
);
|
|
91
|
-
const DESERIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(
|
|
92
|
-
DESERIALIZE_EMPTY,
|
|
93
|
-
node,
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
if (!node.members.find((v) => v.name.text == "__SERIALIZE"))
|
|
97
|
-
node.members.push(SERIALIZE_RAW_METHOD_EMPTY);
|
|
98
|
-
if (!node.members.find((v) => v.name.text == "__INITIALIZE"))
|
|
99
|
-
node.members.push(INITIALIZE_METHOD_EMPTY);
|
|
100
|
-
if (!node.members.find((v) => v.name.text == "__DESERIALIZE"))
|
|
101
|
-
node.members.push(DESERIALIZE_METHOD_EMPTY);
|
|
102
|
-
|
|
103
|
-
this.schemasList.push(schema);
|
|
75
|
+
const members = this.filterMembers(_members);
|
|
76
|
+
|
|
77
|
+
if (!members.length) {
|
|
78
|
+
this.handleEmptyClass(node, schema, members);
|
|
104
79
|
}
|
|
105
80
|
|
|
106
81
|
for (const member of members) {
|
|
107
82
|
const name = member.name;
|
|
108
|
-
if (!(member instanceof FieldDeclaration)) continue;
|
|
109
83
|
if (!member.type) {
|
|
110
|
-
throw new Error(
|
|
111
|
-
"Fields must be strongly typed! Found " +
|
|
112
|
-
toString(member) +
|
|
113
|
-
" at " +
|
|
114
|
-
node.range.source.normalizedPath,
|
|
115
|
-
);
|
|
84
|
+
throw new Error("Fields must be strongly typed! Found " + toString(member) + " at " + node.range.source.normalizedPath);
|
|
116
85
|
}
|
|
86
|
+
|
|
117
87
|
const type = toString(member.type!);
|
|
118
88
|
if (type.startsWith("(") && type.includes("=>")) continue;
|
|
119
89
|
const value = member.initializer ? toString(member.initializer!) : null;
|
|
@@ -144,11 +114,7 @@ class JSONTransform extends BaseVisitor {
|
|
|
144
114
|
|
|
145
115
|
switch (decoratorName) {
|
|
146
116
|
case "alias": {
|
|
147
|
-
if (!args.length)
|
|
148
|
-
throw new Error(
|
|
149
|
-
"Expected 1 argument but got zero at @alias in " +
|
|
150
|
-
node.range.source.normalizedPath,
|
|
151
|
-
);
|
|
117
|
+
if (!args.length) throw new Error("Expected 1 argument but got zero at @alias in " + node.range.source.normalizedPath);
|
|
152
118
|
mem.alias = args[0]!;
|
|
153
119
|
mem.flags.set(PropertyFlags.Alias, args);
|
|
154
120
|
break;
|
|
@@ -158,11 +124,7 @@ class JSONTransform extends BaseVisitor {
|
|
|
158
124
|
break;
|
|
159
125
|
}
|
|
160
126
|
case "omitif": {
|
|
161
|
-
if (!decorator.args?.length)
|
|
162
|
-
throw new Error(
|
|
163
|
-
"Expected 1 argument but got zero at @omitif in " +
|
|
164
|
-
node.range.source.normalizedPath,
|
|
165
|
-
);
|
|
127
|
+
if (!decorator.args?.length) throw new Error("Expected 1 argument but got zero at @omitif in " + node.range.source.normalizedPath);
|
|
166
128
|
mem.flags.set(PropertyFlags.OmitIf, args);
|
|
167
129
|
break;
|
|
168
130
|
}
|
|
@@ -174,23 +136,10 @@ class JSONTransform extends BaseVisitor {
|
|
|
174
136
|
}
|
|
175
137
|
}
|
|
176
138
|
|
|
177
|
-
mem.generate();
|
|
139
|
+
if (!mem.flags.get(PropertyFlags.Omit)) mem.generate();
|
|
178
140
|
|
|
179
141
|
if (this.schemasList.find((v) => v.name == type)) {
|
|
180
|
-
mem.initialize =
|
|
181
|
-
"this." +
|
|
182
|
-
name.text +
|
|
183
|
-
" = changetype<nonnull<" +
|
|
184
|
-
mem.type +
|
|
185
|
-
">>(__new(offsetof<nonnull<" +
|
|
186
|
-
mem.type +
|
|
187
|
-
">>(), idof<nonnull<" +
|
|
188
|
-
mem.type +
|
|
189
|
-
">>()));\n changetype<nonnull<" +
|
|
190
|
-
mem.type +
|
|
191
|
-
">>(this." +
|
|
192
|
-
name.text +
|
|
193
|
-
").__INITIALIZE()";
|
|
142
|
+
mem.initialize = "this." + name.text + " = changetype<nonnull<" + mem.type + ">>(__new(offsetof<nonnull<" + mem.type + ">>(), idof<nonnull<" + mem.type + ">>()));\n changetype<nonnull<" + mem.type + ">>(this." + name.text + ").__INITIALIZE()";
|
|
194
143
|
} else if (mem.value) {
|
|
195
144
|
mem.initialize = "this." + name.text + " = " + mem.value;
|
|
196
145
|
} else if (type === "Map") {
|
|
@@ -198,22 +147,12 @@ class JSONTransform extends BaseVisitor {
|
|
|
198
147
|
} else if (type === "string") {
|
|
199
148
|
mem.initialize = "this." + name.text + ' = ""';
|
|
200
149
|
} else if (type === "Array") {
|
|
201
|
-
mem.initialize =
|
|
202
|
-
"this." + name.text + " = instantiate<" + mem.type + ">()";
|
|
150
|
+
mem.initialize = "this." + name.text + " = instantiate<" + mem.type + ">()";
|
|
203
151
|
} else if (type === "bool" || type === "boolean") {
|
|
204
152
|
mem.initialize = "this." + name.text + " = false";
|
|
205
153
|
} else if (type === "JSON.Raw") {
|
|
206
154
|
mem.initialize = "this." + name.text + ' = ""';
|
|
207
|
-
} else if (
|
|
208
|
-
type === "u8" ||
|
|
209
|
-
type === "u16" ||
|
|
210
|
-
type === "u32" ||
|
|
211
|
-
type === "u64" ||
|
|
212
|
-
type === "i8" ||
|
|
213
|
-
type === "i16" ||
|
|
214
|
-
type === "i32" ||
|
|
215
|
-
type === "i64"
|
|
216
|
-
) {
|
|
155
|
+
} else if (type === "u8" || type === "u16" || type === "u32" || type === "u64" || type === "i8" || type === "i16" || type === "i32" || type === "i64") {
|
|
217
156
|
mem.initialize = "this." + name.text + " = 0";
|
|
218
157
|
} else if (type === "f32" || type === "f64") {
|
|
219
158
|
mem.initialize = "this." + name.text + " = 0.0";
|
|
@@ -227,50 +166,41 @@ class JSONTransform extends BaseVisitor {
|
|
|
227
166
|
|
|
228
167
|
let INITIALIZE = "__INITIALIZE(): this {\n";
|
|
229
168
|
|
|
230
|
-
let DESERIALIZE =
|
|
231
|
-
"__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n const len = key_end - key_start;\n";
|
|
169
|
+
let DESERIALIZE = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n const len = key_end - key_start;\n";
|
|
232
170
|
let indent = " ";
|
|
233
171
|
|
|
234
172
|
if (!schema.members.length) return;
|
|
235
173
|
|
|
236
|
-
found = false;
|
|
174
|
+
let found = false;
|
|
237
175
|
|
|
238
|
-
if (
|
|
239
|
-
schema.members[0]?.flags.has(PropertyFlags.OmitNull) ||
|
|
240
|
-
schema.members[0]?.flags.has(PropertyFlags.OmitIf)
|
|
241
|
-
) {
|
|
176
|
+
if (schema.members[0]?.flags.has(PropertyFlags.OmitNull) || schema.members[0]?.flags.has(PropertyFlags.OmitIf)) {
|
|
242
177
|
SERIALIZE_RAW += schema.members[0]?.serialize;
|
|
243
|
-
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.
|
|
178
|
+
SERIALIZE_PRETTY += "\\n" + indent + schema.members[0]?.serialize_pretty;
|
|
244
179
|
} else {
|
|
245
180
|
SERIALIZE_RAW += schema.members[0]?.serialize + ",";
|
|
246
|
-
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.
|
|
181
|
+
SERIALIZE_PRETTY += "\\n" + indent + schema.members[0]?.serialize_pretty + ",\\n";
|
|
247
182
|
found = true;
|
|
248
183
|
}
|
|
249
184
|
|
|
250
|
-
if (schema.members[0]?.initialize)
|
|
251
|
-
INITIALIZE += " " + schema.members[0]?.initialize + ";\n";
|
|
185
|
+
if (schema.members[0]?.initialize) INITIALIZE += " " + schema.members[0]?.initialize + ";\n";
|
|
252
186
|
|
|
253
187
|
for (let i = 1; i < schema.members.length; i++) {
|
|
254
188
|
const member = schema.members[i]!;
|
|
255
189
|
if (member.initialize) INITIALIZE += " " + member.initialize + ";\n";
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
member.flags.has(PropertyFlags.OmitIf)
|
|
259
|
-
) {
|
|
190
|
+
if (member.flags.has(PropertyFlags.Omit)) continue;
|
|
191
|
+
if (member.flags.has(PropertyFlags.OmitNull) || member.flags.has(PropertyFlags.OmitIf)) {
|
|
260
192
|
SERIALIZE_RAW += member.serialize;
|
|
261
|
-
SERIALIZE_PRETTY += member.
|
|
193
|
+
SERIALIZE_PRETTY += member.serialize_pretty;
|
|
262
194
|
} else {
|
|
263
195
|
SERIALIZE_RAW += member.serialize + ",";
|
|
264
|
-
SERIALIZE_PRETTY += indent + member.
|
|
196
|
+
SERIALIZE_PRETTY += indent + member.serialize_pretty + ",\\n";
|
|
265
197
|
found = true;
|
|
266
198
|
}
|
|
267
199
|
}
|
|
268
200
|
|
|
269
201
|
if (found) {
|
|
270
|
-
SERIALIZE_RAW +=
|
|
271
|
-
|
|
272
|
-
SERIALIZE_PRETTY +=
|
|
273
|
-
"`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}";
|
|
202
|
+
SERIALIZE_RAW += "`;\n store<u16>(changetype<usize>(out) + ((out.length - 1) << 1), 125);\n return out;\n}";
|
|
203
|
+
SERIALIZE_PRETTY += "`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}";
|
|
274
204
|
} else {
|
|
275
205
|
SERIALIZE_RAW += "}`;\n return out;\n}";
|
|
276
206
|
SERIALIZE_PRETTY += "}`;\n return out;\n}";
|
|
@@ -279,9 +209,7 @@ class JSONTransform extends BaseVisitor {
|
|
|
279
209
|
INITIALIZE += " return this;\n}";
|
|
280
210
|
|
|
281
211
|
const sortedMembers: Property[][] = [];
|
|
282
|
-
const _sorted = schema.members.sort(
|
|
283
|
-
(a, b) => (a.alias?.length! || a.name.length) - (b.alias?.length! || b.name.length),
|
|
284
|
-
);
|
|
212
|
+
const _sorted = schema.members.sort((a, b) => (a.alias?.length! || a.name.length) - (b.alias?.length! || b.name.length));
|
|
285
213
|
let len = -1;
|
|
286
214
|
let offset = -1;
|
|
287
215
|
for (let i = 0; i < _sorted.length; i++) {
|
|
@@ -302,30 +230,24 @@ class JSONTransform extends BaseVisitor {
|
|
|
302
230
|
const _name = encodeKey(firstMember.alias || firstMember.name);
|
|
303
231
|
if (_name.length === 1) {
|
|
304
232
|
if (first) {
|
|
305
|
-
DESERIALIZE +=
|
|
306
|
-
" if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
233
|
+
DESERIALIZE += " if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
307
234
|
first = false;
|
|
308
235
|
} else {
|
|
309
|
-
DESERIALIZE +=
|
|
310
|
-
"else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
236
|
+
DESERIALIZE += "else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
311
237
|
}
|
|
312
238
|
} else if (_name.length === 2) {
|
|
313
239
|
if (first) {
|
|
314
|
-
DESERIALIZE +=
|
|
315
|
-
" if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
240
|
+
DESERIALIZE += " if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
316
241
|
first = false;
|
|
317
242
|
} else {
|
|
318
|
-
DESERIALIZE +=
|
|
319
|
-
"else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
243
|
+
DESERIALIZE += "else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
320
244
|
}
|
|
321
245
|
} else if (_name.length === 4) {
|
|
322
246
|
if (first) {
|
|
323
|
-
DESERIALIZE +=
|
|
324
|
-
" if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
|
|
247
|
+
DESERIALIZE += " if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
|
|
325
248
|
first = false;
|
|
326
249
|
} else {
|
|
327
|
-
DESERIALIZE +=
|
|
328
|
-
"else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
|
|
250
|
+
DESERIALIZE += "else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
|
|
329
251
|
}
|
|
330
252
|
} else {
|
|
331
253
|
if (first) {
|
|
@@ -349,31 +271,23 @@ class JSONTransform extends BaseVisitor {
|
|
|
349
271
|
f = false;
|
|
350
272
|
DESERIALIZE += ` if (${charCodeAt64(_name, 0)} === code) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
|
|
351
273
|
} else {
|
|
352
|
-
DESERIALIZE =
|
|
353
|
-
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
354
|
-
`else if (${charCodeAt64(_name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`;
|
|
274
|
+
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + `else if (${charCodeAt64(_name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`;
|
|
355
275
|
}
|
|
356
276
|
} else {
|
|
357
277
|
if (f) {
|
|
358
278
|
f = false;
|
|
359
279
|
DESERIALIZE += ` if (0 === memory.compare(changetype<usize>("${escapeQuote(escapeSlash(_name))}"), changetype<usize>(data) + (key_start << 1), ${_name.length << 1})) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
|
|
360
280
|
} else {
|
|
361
|
-
DESERIALIZE =
|
|
362
|
-
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
363
|
-
` else if (0 === memory.compare(changetype<usize>("${escapeQuote(escapeSlash(_name))}"), changetype<usize>(data) + (key_start << 1), ${_name.length << 1})) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
|
|
281
|
+
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else if (0 === memory.compare(changetype<usize>("${escapeQuote(escapeSlash(_name))}"), changetype<usize>(data) + (key_start << 1), ${_name.length << 1})) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
|
|
364
282
|
}
|
|
365
283
|
}
|
|
366
284
|
}
|
|
367
285
|
if (_name.length < 3) {
|
|
368
286
|
DESERIALIZE += ` default: {\n return false;\n }\n }\n`;
|
|
369
287
|
} else if (_name.length == 4) {
|
|
370
|
-
DESERIALIZE =
|
|
371
|
-
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
372
|
-
` else {\n return false;\n }\n`;
|
|
288
|
+
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`;
|
|
373
289
|
} else {
|
|
374
|
-
DESERIALIZE =
|
|
375
|
-
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
376
|
-
` else {\n return false;\n }\n`;
|
|
290
|
+
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`;
|
|
377
291
|
}
|
|
378
292
|
DESERIALIZE += " } ";
|
|
379
293
|
}
|
|
@@ -384,25 +298,20 @@ class JSONTransform extends BaseVisitor {
|
|
|
384
298
|
|
|
385
299
|
if (process.env["JSON_DEBUG"]) {
|
|
386
300
|
console.log(SERIALIZE_RAW);
|
|
387
|
-
|
|
301
|
+
console.log(SERIALIZE_PRETTY);
|
|
388
302
|
console.log(INITIALIZE);
|
|
389
303
|
console.log(DESERIALIZE);
|
|
390
304
|
}
|
|
391
305
|
|
|
392
|
-
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(
|
|
393
|
-
|
|
394
|
-
node,
|
|
395
|
-
);
|
|
396
|
-
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node);
|
|
306
|
+
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(SERIALIZE_RAW, node);
|
|
307
|
+
const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node);
|
|
397
308
|
const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node);
|
|
398
309
|
const DESERIALIZE_METHOD = SimpleParser.parseClassMember(DESERIALIZE, node);
|
|
399
310
|
|
|
400
|
-
if (!node.members.find((v) => v.name.text == "__SERIALIZE"))
|
|
401
|
-
|
|
402
|
-
if (!node.members.find((v) => v.name.text == "__INITIALIZE"))
|
|
403
|
-
|
|
404
|
-
if (!node.members.find((v) => v.name.text == "__DESERIALIZE"))
|
|
405
|
-
node.members.push(DESERIALIZE_METHOD);
|
|
311
|
+
if (!node.members.find((v) => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_RAW_METHOD);
|
|
312
|
+
if (!node.members.find((v) => v.name.text == "__SERIALIZE_PRETTY")) node.members.push(SERIALIZE_PRETTY_METHOD);
|
|
313
|
+
if (!node.members.find((v) => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD);
|
|
314
|
+
if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD);
|
|
406
315
|
|
|
407
316
|
this.schemasList.push(schema);
|
|
408
317
|
}
|
|
@@ -428,13 +337,13 @@ export default class Transformer extends Transform {
|
|
|
428
337
|
.sort((_a, _b) => {
|
|
429
338
|
const a = _a.internalPath;
|
|
430
339
|
const b = _b.internalPath;
|
|
340
|
+
if (a[0] !== "~" && b[0] === "~") {
|
|
341
|
+
return 1;
|
|
342
|
+
}
|
|
431
343
|
if (a[0] === "~" && b[0] !== "~") {
|
|
432
344
|
return -1;
|
|
433
|
-
} else if (a[0] !== "~" && b[0] === "~") {
|
|
434
|
-
return 1;
|
|
435
|
-
} else {
|
|
436
|
-
return 0;
|
|
437
345
|
}
|
|
346
|
+
return 0;
|
|
438
347
|
});
|
|
439
348
|
|
|
440
349
|
// Loop over every source
|
|
@@ -447,17 +356,85 @@ export default class Transformer extends Transform {
|
|
|
447
356
|
// Check that every parent and child class is hooked up correctly
|
|
448
357
|
const schemas = transformer.schemasList;
|
|
449
358
|
for (const schema of schemas) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
`Class ${schema.name} extends its parent class ${schema.parent}, but ${schema.parent} does not include a @json or @serializable decorator! Add the decorator and rebuild.`,
|
|
455
|
-
);
|
|
359
|
+
checkInheritance(schema, schemas);
|
|
360
|
+
const invalidType = checkTypeCorrectness(schema, schemas);
|
|
361
|
+
if (invalidType) {
|
|
362
|
+
logError(`Type ${invalidType.type} implemented in property ${invalidType.path} is not a JSON-compatible type!\nEither decorate it with @omit or fix it!`);
|
|
456
363
|
}
|
|
457
364
|
}
|
|
458
365
|
}
|
|
459
366
|
}
|
|
460
367
|
|
|
368
|
+
function checkInheritance(schema: SchemaData, schemas: SchemaData[]): void {
|
|
369
|
+
if (!schema.parent && schema.node.extendsType) {
|
|
370
|
+
if (schemas.find(v => v.node.name.text === schema.node.extendsType?.name.identifier.text!)) return;
|
|
371
|
+
const extending = toString(schema.node.extendsType);
|
|
372
|
+
logError(`Schema ${schema.name} extends ${extending}, but ${extending} does not include the @json decorator!`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function checkTypeCorrectness(
|
|
377
|
+
schema: SchemaData,
|
|
378
|
+
schemas: SchemaData[]
|
|
379
|
+
): {
|
|
380
|
+
type: string;
|
|
381
|
+
path: string;
|
|
382
|
+
} | null {
|
|
383
|
+
const parent = schemas.find((v) => v.name === schema.parent?.name);
|
|
384
|
+
const generic_types = [...(schema?.node.typeParameters?.map<string>((v) => v.name.text) || []), ...(parent?.node.typeParameters?.map<string>((v) => v.name.text) || [])];
|
|
385
|
+
const member_types = [...(schema.members.map((v) => (<NamedTypeNode>v.node.type).name.identifier.text) || [])];
|
|
386
|
+
const scopeTypes = new Set<string>([...json_types, ...generic_types, ...member_types]);
|
|
387
|
+
|
|
388
|
+
for (const typ of member_types) {
|
|
389
|
+
if (typ === "JSON") continue; // JSON.Raw, JSON.Box, JSON.Any, ect...
|
|
390
|
+
if (json_types.includes(typ)) continue;
|
|
391
|
+
if (generic_types.includes(typ)) continue;
|
|
392
|
+
const check = schemas.find((v) => v.name == typ);
|
|
393
|
+
if (!check) logError(`Type ${typ} is not a JSON compatible type or does not include the @json flag!`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
for (const member of schema.members) {
|
|
397
|
+
const invalidType = checkType(schema, schemas, member.node.type as NamedTypeNode, member, scopeTypes, schema.name);
|
|
398
|
+
if (invalidType) logError(`Type ${invalidType.type} in ${invalidType.path} does not implement a JSON compatible type!\n${chalk.dim(` at ${member.node.range.source.normalizedPath.replace("~lib/", "./node_modules/")}`)}`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function checkType(
|
|
405
|
+
schema: SchemaData,
|
|
406
|
+
schemas: SchemaData[],
|
|
407
|
+
typ: NamedTypeNode,
|
|
408
|
+
member: Property,
|
|
409
|
+
scopeTypes: Set<string>,
|
|
410
|
+
path: string,
|
|
411
|
+
): {
|
|
412
|
+
type: string;
|
|
413
|
+
path: string;
|
|
414
|
+
} | null {
|
|
415
|
+
path += "." + member.name;
|
|
416
|
+
if (schemas.find(v => v.node.name.text === typ.name.identifier.text)) scopeTypes.add(typ.name.identifier.text);
|
|
417
|
+
if (!scopeTypes.has(typ.name.identifier.text)) return { type: toString(typ), path };
|
|
418
|
+
|
|
419
|
+
if (typ.isNullable && isPrimitive(typ)) return { type: toString(typ), path };
|
|
420
|
+
|
|
421
|
+
if (typ.typeArguments?.length && typ.typeArguments?.length > 0) {
|
|
422
|
+
for (const ty of typ.typeArguments.filter((v) => v instanceof NamedTypeNode)) {
|
|
423
|
+
const check = checkType(schema, schemas, ty, member, scopeTypes, path);
|
|
424
|
+
if (check) return { type: toString(typ), path };
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
if (scopeTypes.has(typ.name.identifier.text)) return null;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function logError(message: string): never {
|
|
434
|
+
console.log("\n" + chalk.bold.bgRed(" Error ") + chalk.dim(":") + " " + message + "\n");
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
|
|
461
438
|
enum PropertyFlags {
|
|
462
439
|
Null,
|
|
463
440
|
Omit,
|
|
@@ -472,12 +449,10 @@ class Property {
|
|
|
472
449
|
public alias: string | null = null;
|
|
473
450
|
public type: string = "";
|
|
474
451
|
public value: string | null = null;
|
|
475
|
-
public flags: Map<PropertyFlags, string[]> = new Map<
|
|
476
|
-
PropertyFlags,
|
|
477
|
-
string[]
|
|
478
|
-
>();
|
|
452
|
+
public flags: Map<PropertyFlags, string[]> = new Map<PropertyFlags, string[]>();
|
|
479
453
|
|
|
480
454
|
public serialize: string | null = null;
|
|
455
|
+
public serialize_pretty: string | null = null;
|
|
481
456
|
public deserialize: string | null = null;
|
|
482
457
|
public initialize: string | null = null;
|
|
483
458
|
|
|
@@ -494,7 +469,7 @@ class Property {
|
|
|
494
469
|
|
|
495
470
|
if (this.flags.has(PropertyFlags.JSON_Raw)) {
|
|
496
471
|
if (this.flags.has(PropertyFlags.Null)) {
|
|
497
|
-
this.right_s = "(this." + name +
|
|
472
|
+
this.right_s = "(this." + name + ' || "null")';
|
|
498
473
|
this.right_d = "value_start === value_end - 4 && 30399761348886638 === load<u64>(changetype<usize>(data) + (value_start << 1)) ? null : data.substring(value_start, value_end)";
|
|
499
474
|
} else {
|
|
500
475
|
this.right_s = "this." + name;
|
|
@@ -502,38 +477,22 @@ class Property {
|
|
|
502
477
|
}
|
|
503
478
|
} else {
|
|
504
479
|
this.right_s = "__SERIALIZE<" + type + ">(this." + name + ")";
|
|
505
|
-
this.right_d =
|
|
506
|
-
"__DESERIALIZE<" + type + ">(data.substring(value_start, value_end))";
|
|
480
|
+
this.right_d = "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end))";
|
|
507
481
|
}
|
|
508
482
|
|
|
509
483
|
if (this.flags.has(PropertyFlags.OmitIf)) {
|
|
510
484
|
const condition = this.flags.get(PropertyFlags.OmitIf)![0];
|
|
511
|
-
if (!condition)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
);
|
|
515
|
-
this.serialize =
|
|
516
|
-
"${" +
|
|
517
|
-
condition +
|
|
518
|
-
' ? "" : \'' +
|
|
519
|
-
escapedName +
|
|
520
|
-
":' + " +
|
|
521
|
-
this.right_s +
|
|
522
|
-
' + ","}';
|
|
485
|
+
if (!condition) throw new Error("Could not find condition when using decorator @omitif! Provide at least one condition");
|
|
486
|
+
this.serialize = "${" + condition + ' ? "" : \'' + escapedName + ":' + " + this.right_s + ' + ","}';
|
|
487
|
+
this.serialize_pretty = "${" + condition + ' ? "" : \'' + escapedName + ": ' + " + this.right_s + ' + ","}';
|
|
523
488
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
524
489
|
} else if (this.flags.has(PropertyFlags.OmitNull)) {
|
|
525
|
-
this.serialize =
|
|
526
|
-
|
|
527
|
-
name +
|
|
528
|
-
") == <usize>0" +
|
|
529
|
-
' ? "" : \'' +
|
|
530
|
-
escapedName +
|
|
531
|
-
":' + " +
|
|
532
|
-
this.right_s +
|
|
533
|
-
' + ","}';
|
|
490
|
+
this.serialize = "${changetype<usize>(this." + name + ") == <usize>0" + ' ? "" : \'' + escapedName + ":' + " + this.right_s + ' + ","}';
|
|
491
|
+
this.serialize_pretty = "${changetype<usize>(this." + name + ") == <usize>0" + ' ? "" : \'' + escapedName + ": ' + " + this.right_s + ' + ","}';
|
|
534
492
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
535
493
|
} else {
|
|
536
494
|
this.serialize = escapedName + ":${" + this.right_s + "}";
|
|
495
|
+
this.serialize_pretty = escapedName + ": ${" + this.right_s + "}";
|
|
537
496
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
538
497
|
}
|
|
539
498
|
}
|
|
@@ -552,9 +511,7 @@ function charCodeAt32(data: string, offset: number): number {
|
|
|
552
511
|
|
|
553
512
|
function charCodeAt64(data: string, offset: number): bigint {
|
|
554
513
|
if (offset + 3 >= data.length) {
|
|
555
|
-
throw new Error(
|
|
556
|
-
"The string must have at least 4 characters from the specified offset.",
|
|
557
|
-
);
|
|
514
|
+
throw new Error("The string must have at least 4 characters from the specified offset.");
|
|
558
515
|
}
|
|
559
516
|
|
|
560
517
|
const firstCharCode = BigInt(data.charCodeAt(offset));
|
|
@@ -562,11 +519,7 @@ function charCodeAt64(data: string, offset: number): bigint {
|
|
|
562
519
|
const thirdCharCode = BigInt(data.charCodeAt(offset + 2));
|
|
563
520
|
const fourthCharCode = BigInt(data.charCodeAt(offset + 3));
|
|
564
521
|
|
|
565
|
-
const u64Value =
|
|
566
|
-
(fourthCharCode << 48n) |
|
|
567
|
-
(thirdCharCode << 32n) |
|
|
568
|
-
(secondCharCode << 16n) |
|
|
569
|
-
firstCharCode;
|
|
522
|
+
const u64Value = (fourthCharCode << 48n) | (thirdCharCode << 32n) | (secondCharCode << 16n) | firstCharCode;
|
|
570
523
|
|
|
571
524
|
return u64Value;
|
|
572
525
|
}
|
|
@@ -610,3 +563,9 @@ function getArgs(args: Expression[] | null): string[] {
|
|
|
610
563
|
}
|
|
611
564
|
return out;
|
|
612
565
|
}
|
|
566
|
+
|
|
567
|
+
function isPrimitive(type: NamedTypeNode): boolean {
|
|
568
|
+
const primitives = new Set(["u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f32", "f64", "bool", "boolean"]);
|
|
569
|
+
|
|
570
|
+
return primitives.has(type.name.identifier.text);
|
|
571
|
+
}
|
package/transform/tsconfig.json
CHANGED
package/bench/bench-node.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "fs";
|
|
2
|
-
import { WASI } from "wasi";
|
|
3
|
-
import { argv, env } from "node:process";
|
|
4
|
-
|
|
5
|
-
const wasm = readFileSync("./benchmark.wasm");
|
|
6
|
-
|
|
7
|
-
const wasi = new WASI({
|
|
8
|
-
version: "preview1",
|
|
9
|
-
args: argv,
|
|
10
|
-
env,
|
|
11
|
-
preopens: {},
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const mod = new WebAssembly.Module(wasm);
|
|
15
|
-
const instance = new WebAssembly.Instance(mod, wasi.getImportObject());
|
|
16
|
-
|
|
17
|
-
wasi.start(instance);
|