json-as 0.9.20 → 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 -0
- package/README.md +7 -16
- 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 +20 -0
- package/assembly/__tests__/float.spec.ts +52 -0
- package/assembly/__tests__/integer.spec.ts +28 -0
- package/assembly/__tests__/obj.spec.ts +4 -0
- package/assembly/__tests__/string.spec.ts +24 -0
- package/assembly/__tests__/test.spec.ts +28 -263
- 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 -5
- 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 +27 -22
- package/assembly/util/strings.ts +0 -0
- package/bench.js +1 -1
- package/package.json +8 -7
- package/transform/lib/index.js +222 -208
- package/transform/lib/visitor.js +448 -0
- package/transform/package.json +1 -1
- package/transform/src/index.ts +226 -258
- 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];
|
|
81
72
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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);
|
|
73
|
+
this.appendParentFields(node, schema, _members);
|
|
74
|
+
|
|
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;
|
|
@@ -128,10 +98,14 @@ class JSONTransform extends BaseVisitor {
|
|
|
128
98
|
mem.value = value;
|
|
129
99
|
mem.node = member;
|
|
130
100
|
|
|
131
|
-
if (type
|
|
101
|
+
if (type.includes("JSON.Raw")) {
|
|
132
102
|
mem.flags.set(PropertyFlags.JSON_Raw, []);
|
|
133
103
|
}
|
|
134
104
|
|
|
105
|
+
if (member.type.isNullable) {
|
|
106
|
+
mem.flags.set(PropertyFlags.Null, []);
|
|
107
|
+
}
|
|
108
|
+
|
|
135
109
|
if (member.decorators) {
|
|
136
110
|
for (const decorator of member.decorators) {
|
|
137
111
|
const decoratorName = (decorator.name as IdentifierExpression).text;
|
|
@@ -140,11 +114,7 @@ class JSONTransform extends BaseVisitor {
|
|
|
140
114
|
|
|
141
115
|
switch (decoratorName) {
|
|
142
116
|
case "alias": {
|
|
143
|
-
if (!args.length)
|
|
144
|
-
throw new Error(
|
|
145
|
-
"Expected 1 argument but got zero at @alias in " +
|
|
146
|
-
node.range.source.normalizedPath,
|
|
147
|
-
);
|
|
117
|
+
if (!args.length) throw new Error("Expected 1 argument but got zero at @alias in " + node.range.source.normalizedPath);
|
|
148
118
|
mem.alias = args[0]!;
|
|
149
119
|
mem.flags.set(PropertyFlags.Alias, args);
|
|
150
120
|
break;
|
|
@@ -154,11 +124,7 @@ class JSONTransform extends BaseVisitor {
|
|
|
154
124
|
break;
|
|
155
125
|
}
|
|
156
126
|
case "omitif": {
|
|
157
|
-
if (!decorator.args?.length)
|
|
158
|
-
throw new Error(
|
|
159
|
-
"Expected 1 argument but got zero at @omitif in " +
|
|
160
|
-
node.range.source.normalizedPath,
|
|
161
|
-
);
|
|
127
|
+
if (!decorator.args?.length) throw new Error("Expected 1 argument but got zero at @omitif in " + node.range.source.normalizedPath);
|
|
162
128
|
mem.flags.set(PropertyFlags.OmitIf, args);
|
|
163
129
|
break;
|
|
164
130
|
}
|
|
@@ -170,23 +136,10 @@ class JSONTransform extends BaseVisitor {
|
|
|
170
136
|
}
|
|
171
137
|
}
|
|
172
138
|
|
|
173
|
-
mem.generate();
|
|
139
|
+
if (!mem.flags.get(PropertyFlags.Omit)) mem.generate();
|
|
174
140
|
|
|
175
141
|
if (this.schemasList.find((v) => v.name == type)) {
|
|
176
|
-
mem.initialize =
|
|
177
|
-
"this." +
|
|
178
|
-
name.text +
|
|
179
|
-
" = changetype<nonnull<" +
|
|
180
|
-
mem.type +
|
|
181
|
-
">>(__new(offsetof<nonnull<" +
|
|
182
|
-
mem.type +
|
|
183
|
-
">>(), idof<nonnull<" +
|
|
184
|
-
mem.type +
|
|
185
|
-
">>()));\n changetype<nonnull<" +
|
|
186
|
-
mem.type +
|
|
187
|
-
">>(this." +
|
|
188
|
-
name.text +
|
|
189
|
-
").__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()";
|
|
190
143
|
} else if (mem.value) {
|
|
191
144
|
mem.initialize = "this." + name.text + " = " + mem.value;
|
|
192
145
|
} else if (type === "Map") {
|
|
@@ -194,22 +147,12 @@ class JSONTransform extends BaseVisitor {
|
|
|
194
147
|
} else if (type === "string") {
|
|
195
148
|
mem.initialize = "this." + name.text + ' = ""';
|
|
196
149
|
} else if (type === "Array") {
|
|
197
|
-
mem.initialize =
|
|
198
|
-
"this." + name.text + " = instantiate<" + mem.type + ">()";
|
|
150
|
+
mem.initialize = "this." + name.text + " = instantiate<" + mem.type + ">()";
|
|
199
151
|
} else if (type === "bool" || type === "boolean") {
|
|
200
152
|
mem.initialize = "this." + name.text + " = false";
|
|
201
153
|
} else if (type === "JSON.Raw") {
|
|
202
154
|
mem.initialize = "this." + name.text + ' = ""';
|
|
203
|
-
} else if (
|
|
204
|
-
type === "u8" ||
|
|
205
|
-
type === "u16" ||
|
|
206
|
-
type === "u32" ||
|
|
207
|
-
type === "u64" ||
|
|
208
|
-
type === "i8" ||
|
|
209
|
-
type === "i16" ||
|
|
210
|
-
type === "i32" ||
|
|
211
|
-
type === "i64"
|
|
212
|
-
) {
|
|
155
|
+
} else if (type === "u8" || type === "u16" || type === "u32" || type === "u64" || type === "i8" || type === "i16" || type === "i32" || type === "i64") {
|
|
213
156
|
mem.initialize = "this." + name.text + " = 0";
|
|
214
157
|
} else if (type === "f32" || type === "f64") {
|
|
215
158
|
mem.initialize = "this." + name.text + " = 0.0";
|
|
@@ -223,154 +166,128 @@ class JSONTransform extends BaseVisitor {
|
|
|
223
166
|
|
|
224
167
|
let INITIALIZE = "__INITIALIZE(): this {\n";
|
|
225
168
|
|
|
226
|
-
let DESERIALIZE =
|
|
227
|
-
"__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";
|
|
228
170
|
let indent = " ";
|
|
229
171
|
|
|
230
172
|
if (!schema.members.length) return;
|
|
231
173
|
|
|
232
|
-
found = false;
|
|
174
|
+
let found = false;
|
|
233
175
|
|
|
234
|
-
if (
|
|
235
|
-
schema.members[0]?.flags.has(PropertyFlags.OmitNull) ||
|
|
236
|
-
schema.members[0]?.flags.has(PropertyFlags.OmitIf)
|
|
237
|
-
) {
|
|
176
|
+
if (schema.members[0]?.flags.has(PropertyFlags.OmitNull) || schema.members[0]?.flags.has(PropertyFlags.OmitIf)) {
|
|
238
177
|
SERIALIZE_RAW += schema.members[0]?.serialize;
|
|
239
|
-
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.
|
|
178
|
+
SERIALIZE_PRETTY += "\\n" + indent + schema.members[0]?.serialize_pretty;
|
|
240
179
|
} else {
|
|
241
180
|
SERIALIZE_RAW += schema.members[0]?.serialize + ",";
|
|
242
|
-
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.
|
|
181
|
+
SERIALIZE_PRETTY += "\\n" + indent + schema.members[0]?.serialize_pretty + ",\\n";
|
|
243
182
|
found = true;
|
|
244
183
|
}
|
|
245
184
|
|
|
246
|
-
if (schema.members[0]?.initialize)
|
|
247
|
-
INITIALIZE += " " + schema.members[0]?.initialize + ";\n";
|
|
185
|
+
if (schema.members[0]?.initialize) INITIALIZE += " " + schema.members[0]?.initialize + ";\n";
|
|
248
186
|
|
|
249
187
|
for (let i = 1; i < schema.members.length; i++) {
|
|
250
188
|
const member = schema.members[i]!;
|
|
251
189
|
if (member.initialize) INITIALIZE += " " + member.initialize + ";\n";
|
|
252
|
-
if (
|
|
253
|
-
|
|
254
|
-
member.flags.has(PropertyFlags.OmitIf)
|
|
255
|
-
) {
|
|
190
|
+
if (member.flags.has(PropertyFlags.Omit)) continue;
|
|
191
|
+
if (member.flags.has(PropertyFlags.OmitNull) || member.flags.has(PropertyFlags.OmitIf)) {
|
|
256
192
|
SERIALIZE_RAW += member.serialize;
|
|
257
|
-
SERIALIZE_PRETTY += member.
|
|
193
|
+
SERIALIZE_PRETTY += member.serialize_pretty;
|
|
258
194
|
} else {
|
|
259
195
|
SERIALIZE_RAW += member.serialize + ",";
|
|
260
|
-
SERIALIZE_PRETTY += indent + member.
|
|
196
|
+
SERIALIZE_PRETTY += indent + member.serialize_pretty + ",\\n";
|
|
261
197
|
found = true;
|
|
262
198
|
}
|
|
263
199
|
}
|
|
264
200
|
|
|
265
201
|
if (found) {
|
|
266
|
-
SERIALIZE_RAW +=
|
|
267
|
-
|
|
268
|
-
SERIALIZE_PRETTY +=
|
|
269
|
-
"`;\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}";
|
|
270
204
|
} else {
|
|
271
|
-
SERIALIZE_RAW += "`;\n}
|
|
272
|
-
SERIALIZE_PRETTY += "`;\n}
|
|
205
|
+
SERIALIZE_RAW += "}`;\n return out;\n}";
|
|
206
|
+
SERIALIZE_PRETTY += "}`;\n return out;\n}";
|
|
273
207
|
}
|
|
274
208
|
|
|
275
209
|
INITIALIZE += " return this;\n}";
|
|
276
210
|
|
|
277
211
|
const sortedMembers: Property[][] = [];
|
|
278
|
-
const _sorted = schema.members.sort(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
let
|
|
282
|
-
let offset = 0;
|
|
283
|
-
sortedMembers.push([_sorted[0]!]);
|
|
284
|
-
len = _sorted[0]?.name.length!;
|
|
285
|
-
for (let i = 1; i < _sorted.length; i++) {
|
|
212
|
+
const _sorted = schema.members.sort((a, b) => (a.alias?.length! || a.name.length) - (b.alias?.length! || b.name.length));
|
|
213
|
+
let len = -1;
|
|
214
|
+
let offset = -1;
|
|
215
|
+
for (let i = 0; i < _sorted.length; i++) {
|
|
286
216
|
const member = _sorted[i]!;
|
|
287
|
-
|
|
217
|
+
const _name = member.alias || member.name;
|
|
218
|
+
if (_name.length === len) {
|
|
219
|
+
sortedMembers[offset]?.push(member);
|
|
220
|
+
} else {
|
|
288
221
|
sortedMembers.push([member]);
|
|
289
|
-
len =
|
|
222
|
+
len = _name.length;
|
|
290
223
|
offset++;
|
|
291
|
-
} else {
|
|
292
|
-
sortedMembers[offset]!.push(member);
|
|
293
224
|
}
|
|
294
225
|
}
|
|
295
226
|
|
|
296
227
|
let first = true;
|
|
297
228
|
for (const memberSet of sortedMembers) {
|
|
298
229
|
const firstMember = memberSet[0]!;
|
|
299
|
-
const
|
|
300
|
-
if (
|
|
230
|
+
const _name = encodeKey(firstMember.alias || firstMember.name);
|
|
231
|
+
if (_name.length === 1) {
|
|
301
232
|
if (first) {
|
|
302
|
-
DESERIALIZE +=
|
|
303
|
-
" 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";
|
|
304
234
|
first = false;
|
|
305
235
|
} else {
|
|
306
|
-
DESERIALIZE +=
|
|
307
|
-
"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";
|
|
308
237
|
}
|
|
309
|
-
} else if (
|
|
238
|
+
} else if (_name.length === 2) {
|
|
310
239
|
if (first) {
|
|
311
|
-
DESERIALIZE +=
|
|
312
|
-
" 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";
|
|
313
241
|
first = false;
|
|
314
242
|
} else {
|
|
315
|
-
DESERIALIZE +=
|
|
316
|
-
"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";
|
|
317
244
|
}
|
|
318
|
-
} else if (
|
|
245
|
+
} else if (_name.length === 4) {
|
|
319
246
|
if (first) {
|
|
320
|
-
DESERIALIZE +=
|
|
321
|
-
" 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";
|
|
322
248
|
first = false;
|
|
323
249
|
} else {
|
|
324
|
-
DESERIALIZE +=
|
|
325
|
-
"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";
|
|
326
251
|
}
|
|
327
252
|
} else {
|
|
328
253
|
if (first) {
|
|
329
|
-
DESERIALIZE += " if (" +
|
|
254
|
+
DESERIALIZE += " if (" + _name.length + " === len) {\n";
|
|
330
255
|
first = false;
|
|
331
256
|
} else {
|
|
332
|
-
DESERIALIZE += "else if (" +
|
|
257
|
+
DESERIALIZE += "else if (" + _name.length + " === len) {\n";
|
|
333
258
|
}
|
|
334
259
|
}
|
|
335
260
|
let f = true;
|
|
336
261
|
for (let i = 0; i < memberSet.length; i++) {
|
|
337
262
|
const member = memberSet[i]!;
|
|
338
263
|
if (!member.deserialize) continue;
|
|
339
|
-
const
|
|
340
|
-
if (
|
|
341
|
-
DESERIALIZE += ` case ${
|
|
342
|
-
} else if (
|
|
343
|
-
DESERIALIZE += ` case ${charCodeAt32(
|
|
344
|
-
} else if (
|
|
264
|
+
const _name = encodeKey(member.alias || member.name);
|
|
265
|
+
if (_name.length === 1) {
|
|
266
|
+
DESERIALIZE += ` case ${_name.charCodeAt(0)}: { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
|
|
267
|
+
} else if (_name.length === 2) {
|
|
268
|
+
DESERIALIZE += ` case ${charCodeAt32(_name, 0)}: { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
|
|
269
|
+
} else if (_name.length === 4) {
|
|
345
270
|
if (f) {
|
|
346
271
|
f = false;
|
|
347
|
-
DESERIALIZE += ` if (${charCodeAt64(
|
|
272
|
+
DESERIALIZE += ` if (${charCodeAt64(_name, 0)} === code) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
|
|
348
273
|
} else {
|
|
349
|
-
DESERIALIZE =
|
|
350
|
-
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
351
|
-
`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`;
|
|
352
275
|
}
|
|
353
276
|
} else {
|
|
354
277
|
if (f) {
|
|
355
278
|
f = false;
|
|
356
|
-
DESERIALIZE += ` if (0
|
|
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`;
|
|
357
280
|
} else {
|
|
358
|
-
DESERIALIZE =
|
|
359
|
-
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
360
|
-
` else if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\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`;
|
|
361
282
|
}
|
|
362
283
|
}
|
|
363
284
|
}
|
|
364
|
-
if (
|
|
285
|
+
if (_name.length < 3) {
|
|
365
286
|
DESERIALIZE += ` default: {\n return false;\n }\n }\n`;
|
|
366
|
-
} else if (
|
|
367
|
-
DESERIALIZE =
|
|
368
|
-
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
369
|
-
` else {\n return false;\n }\n`;
|
|
287
|
+
} else if (_name.length == 4) {
|
|
288
|
+
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`;
|
|
370
289
|
} else {
|
|
371
|
-
DESERIALIZE =
|
|
372
|
-
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
373
|
-
` else {\n return false;\n }\n`;
|
|
290
|
+
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`;
|
|
374
291
|
}
|
|
375
292
|
DESERIALIZE += " } ";
|
|
376
293
|
}
|
|
@@ -381,25 +298,20 @@ class JSONTransform extends BaseVisitor {
|
|
|
381
298
|
|
|
382
299
|
if (process.env["JSON_DEBUG"]) {
|
|
383
300
|
console.log(SERIALIZE_RAW);
|
|
384
|
-
|
|
301
|
+
console.log(SERIALIZE_PRETTY);
|
|
385
302
|
console.log(INITIALIZE);
|
|
386
303
|
console.log(DESERIALIZE);
|
|
387
304
|
}
|
|
388
305
|
|
|
389
|
-
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(
|
|
390
|
-
|
|
391
|
-
node,
|
|
392
|
-
);
|
|
393
|
-
//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);
|
|
394
308
|
const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node);
|
|
395
309
|
const DESERIALIZE_METHOD = SimpleParser.parseClassMember(DESERIALIZE, node);
|
|
396
310
|
|
|
397
|
-
if (!node.members.find((v) => v.name.text == "__SERIALIZE"))
|
|
398
|
-
|
|
399
|
-
if (!node.members.find((v) => v.name.text == "__INITIALIZE"))
|
|
400
|
-
|
|
401
|
-
if (!node.members.find((v) => v.name.text == "__DESERIALIZE"))
|
|
402
|
-
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);
|
|
403
315
|
|
|
404
316
|
this.schemasList.push(schema);
|
|
405
317
|
}
|
|
@@ -425,13 +337,13 @@ export default class Transformer extends Transform {
|
|
|
425
337
|
.sort((_a, _b) => {
|
|
426
338
|
const a = _a.internalPath;
|
|
427
339
|
const b = _b.internalPath;
|
|
340
|
+
if (a[0] !== "~" && b[0] === "~") {
|
|
341
|
+
return 1;
|
|
342
|
+
}
|
|
428
343
|
if (a[0] === "~" && b[0] !== "~") {
|
|
429
344
|
return -1;
|
|
430
|
-
} else if (a[0] !== "~" && b[0] === "~") {
|
|
431
|
-
return 1;
|
|
432
|
-
} else {
|
|
433
|
-
return 0;
|
|
434
345
|
}
|
|
346
|
+
return 0;
|
|
435
347
|
});
|
|
436
348
|
|
|
437
349
|
// Loop over every source
|
|
@@ -444,18 +356,87 @@ export default class Transformer extends Transform {
|
|
|
444
356
|
// Check that every parent and child class is hooked up correctly
|
|
445
357
|
const schemas = transformer.schemasList;
|
|
446
358
|
for (const schema of schemas) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
`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.`,
|
|
452
|
-
);
|
|
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!`);
|
|
453
363
|
}
|
|
454
364
|
}
|
|
455
365
|
}
|
|
456
366
|
}
|
|
457
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
|
+
|
|
458
438
|
enum PropertyFlags {
|
|
439
|
+
Null,
|
|
459
440
|
Omit,
|
|
460
441
|
OmitNull,
|
|
461
442
|
OmitIf,
|
|
@@ -468,12 +449,10 @@ class Property {
|
|
|
468
449
|
public alias: string | null = null;
|
|
469
450
|
public type: string = "";
|
|
470
451
|
public value: string | null = null;
|
|
471
|
-
public flags: Map<PropertyFlags, string[]> = new Map<
|
|
472
|
-
PropertyFlags,
|
|
473
|
-
string[]
|
|
474
|
-
>();
|
|
452
|
+
public flags: Map<PropertyFlags, string[]> = new Map<PropertyFlags, string[]>();
|
|
475
453
|
|
|
476
454
|
public serialize: string | null = null;
|
|
455
|
+
public serialize_pretty: string | null = null;
|
|
477
456
|
public deserialize: string | null = null;
|
|
478
457
|
public initialize: string | null = null;
|
|
479
458
|
|
|
@@ -489,42 +468,31 @@ class Property {
|
|
|
489
468
|
if (this.flags.has(PropertyFlags.Omit)) return;
|
|
490
469
|
|
|
491
470
|
if (this.flags.has(PropertyFlags.JSON_Raw)) {
|
|
492
|
-
|
|
493
|
-
|
|
471
|
+
if (this.flags.has(PropertyFlags.Null)) {
|
|
472
|
+
this.right_s = "(this." + name + ' || "null")';
|
|
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)";
|
|
474
|
+
} else {
|
|
475
|
+
this.right_s = "this." + name;
|
|
476
|
+
this.right_d = "data.substring(value_start, value_end);";
|
|
477
|
+
}
|
|
494
478
|
} else {
|
|
495
479
|
this.right_s = "__SERIALIZE<" + type + ">(this." + name + ")";
|
|
496
|
-
this.right_d =
|
|
497
|
-
"__DESERIALIZE<" + type + ">(data.substring(value_start, value_end))";
|
|
480
|
+
this.right_d = "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end))";
|
|
498
481
|
}
|
|
499
482
|
|
|
500
483
|
if (this.flags.has(PropertyFlags.OmitIf)) {
|
|
501
484
|
const condition = this.flags.get(PropertyFlags.OmitIf)![0];
|
|
502
|
-
if (!condition)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
);
|
|
506
|
-
this.serialize =
|
|
507
|
-
"${" +
|
|
508
|
-
condition +
|
|
509
|
-
' ? "" : \'' +
|
|
510
|
-
escapedName +
|
|
511
|
-
":' + " +
|
|
512
|
-
this.right_s +
|
|
513
|
-
' + ","}';
|
|
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 + ' + ","}';
|
|
514
488
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
515
489
|
} else if (this.flags.has(PropertyFlags.OmitNull)) {
|
|
516
|
-
this.serialize =
|
|
517
|
-
|
|
518
|
-
name +
|
|
519
|
-
") == <usize>0" +
|
|
520
|
-
' ? "" : \'' +
|
|
521
|
-
escapedName +
|
|
522
|
-
":' + " +
|
|
523
|
-
this.right_s +
|
|
524
|
-
' + ","}';
|
|
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 + ' + ","}';
|
|
525
492
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
526
493
|
} else {
|
|
527
494
|
this.serialize = escapedName + ":${" + this.right_s + "}";
|
|
495
|
+
this.serialize_pretty = escapedName + ": ${" + this.right_s + "}";
|
|
528
496
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
529
497
|
}
|
|
530
498
|
}
|
|
@@ -543,9 +511,7 @@ function charCodeAt32(data: string, offset: number): number {
|
|
|
543
511
|
|
|
544
512
|
function charCodeAt64(data: string, offset: number): bigint {
|
|
545
513
|
if (offset + 3 >= data.length) {
|
|
546
|
-
throw new Error(
|
|
547
|
-
"The string must have at least 4 characters from the specified offset.",
|
|
548
|
-
);
|
|
514
|
+
throw new Error("The string must have at least 4 characters from the specified offset.");
|
|
549
515
|
}
|
|
550
516
|
|
|
551
517
|
const firstCharCode = BigInt(data.charCodeAt(offset));
|
|
@@ -553,11 +519,7 @@ function charCodeAt64(data: string, offset: number): bigint {
|
|
|
553
519
|
const thirdCharCode = BigInt(data.charCodeAt(offset + 2));
|
|
554
520
|
const fourthCharCode = BigInt(data.charCodeAt(offset + 3));
|
|
555
521
|
|
|
556
|
-
const u64Value =
|
|
557
|
-
(fourthCharCode << 48n) |
|
|
558
|
-
(thirdCharCode << 32n) |
|
|
559
|
-
(secondCharCode << 16n) |
|
|
560
|
-
firstCharCode;
|
|
522
|
+
const u64Value = (fourthCharCode << 48n) | (thirdCharCode << 32n) | (secondCharCode << 16n) | firstCharCode;
|
|
561
523
|
|
|
562
524
|
return u64Value;
|
|
563
525
|
}
|
|
@@ -601,3 +563,9 @@ function getArgs(args: Expression[] | null): string[] {
|
|
|
601
563
|
}
|
|
602
564
|
return out;
|
|
603
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
|
+
}
|