json-as 0.9.23 → 0.9.25
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/CHANGELOG +2 -1
- package/README.md +1 -1
- package/assembly/__benches__/misc.bench.ts +33 -0
- package/assembly/custom/util.ts +14 -11
- package/assembly/deserialize/array.ts +8 -0
- package/assembly/deserialize/bool.ts +16 -0
- package/assembly/deserialize/date.ts +17 -9
- package/assembly/deserialize/float.ts +12 -0
- package/assembly/deserialize/integer.ts +9 -0
- package/assembly/deserialize/map.ts +7 -0
- package/assembly/deserialize/object.ts +135 -0
- package/assembly/deserialize/string.ts +8 -2
- package/assembly/index.ts +59 -11
- package/assembly/serialize/bool.ts +0 -12
- package/assembly/serialize/string.ts +0 -11
- package/assembly/test.ts +23 -23
- package/package.json +3 -2
- package/transform/lib/index.js +155 -172
- package/transform/lib/index.js.map +1 -0
- package/transform/package.json +1 -1
- package/transform/src/index.ts +235 -189
- package/transform/tsconfig.json +6 -26
- package/assembly/__benches__/bool.bench.ts +0 -14
- package/assembly/__benches__/simd.bench.ts +0 -36
- package/assembly/__benches__/string.bench.ts +0 -21
- package/assembly/util/strings.ts +0 -0
package/transform/src/index.ts
CHANGED
|
@@ -1,89 +1,119 @@
|
|
|
1
|
-
import {
|
|
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";
|
|
2
17
|
|
|
3
18
|
import { toString, isStdlib } from "visitor-as/dist/utils.js";
|
|
4
19
|
import { BaseVisitor, SimpleParser } from "visitor-as/dist/index.js";
|
|
5
20
|
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"];
|
|
9
21
|
|
|
10
22
|
class JSONTransform extends BaseVisitor {
|
|
11
|
-
public types = json_types;
|
|
12
23
|
public schemasList: SchemaData[] = [];
|
|
13
24
|
public currentClass!: SchemaData;
|
|
14
25
|
public sources = new Set<Source>();
|
|
15
26
|
|
|
16
|
-
|
|
27
|
+
visitMethodDeclaration(): void { }
|
|
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
|
+
|
|
17
49
|
if (node.extendsType) {
|
|
50
|
+
schema.parent = this.schemasList.find(
|
|
51
|
+
(v) => v.name == node.extendsType?.name.identifier.text,
|
|
52
|
+
) as SchemaData | null;
|
|
53
|
+
|
|
18
54
|
if (schema.parent?.members) {
|
|
19
55
|
for (let i = schema.parent.members.length - 1; i >= 0; i--) {
|
|
20
|
-
const replace = schema.members.find(
|
|
56
|
+
const replace = schema.members.find(
|
|
57
|
+
(v) => v.name == schema.parent?.members[i]?.name,
|
|
58
|
+
);
|
|
21
59
|
if (!replace) {
|
|
22
60
|
members.unshift(schema.parent?.members[i]!.node);
|
|
23
61
|
}
|
|
24
62
|
}
|
|
25
|
-
this.appendParentFields(schema.parent.node, schema, members);
|
|
26
63
|
}
|
|
27
64
|
}
|
|
28
|
-
}
|
|
29
|
-
|
|
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}';
|
|
33
|
-
|
|
34
|
-
let INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}";
|
|
35
|
-
|
|
36
|
-
let DESERIALIZE_EMPTY = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}";
|
|
37
|
-
|
|
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
65
|
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
if (!members.length) {
|
|
67
|
+
let SERIALIZE_RAW_EMPTY = '__SERIALIZE(): string {\n return "{}";\n}';
|
|
68
|
+
//let SERIALIZE_PRETTY_EMPTY = "__SERIALIZE_PRETTY(): string {\n return \"{}\";\n}";
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
let INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}";
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
let DESERIALIZE_EMPTY =
|
|
73
|
+
"__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}";
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
if (process.env["JSON_DEBUG"]) {
|
|
76
|
+
console.log(SERIALIZE_RAW_EMPTY);
|
|
77
|
+
//console.log(SERIALIZE_PRETTY_EMPTY);
|
|
78
|
+
console.log(INITIALIZE_EMPTY);
|
|
79
|
+
console.log(DESERIALIZE_EMPTY);
|
|
80
|
+
}
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
const SERIALIZE_RAW_METHOD_EMPTY = SimpleParser.parseClassMember(
|
|
83
|
+
SERIALIZE_RAW_EMPTY,
|
|
84
|
+
node,
|
|
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);
|
|
79
104
|
}
|
|
80
105
|
|
|
81
106
|
for (const member of members) {
|
|
82
107
|
const name = member.name;
|
|
108
|
+
if (!(member instanceof FieldDeclaration)) continue;
|
|
83
109
|
if (!member.type) {
|
|
84
|
-
throw new Error(
|
|
110
|
+
throw new Error(
|
|
111
|
+
"Fields must be strongly typed! Found " +
|
|
112
|
+
toString(member) +
|
|
113
|
+
" at " +
|
|
114
|
+
node.range.source.normalizedPath,
|
|
115
|
+
);
|
|
85
116
|
}
|
|
86
|
-
|
|
87
117
|
const type = toString(member.type!);
|
|
88
118
|
if (type.startsWith("(") && type.includes("=>")) continue;
|
|
89
119
|
const value = member.initializer ? toString(member.initializer!) : null;
|
|
@@ -114,7 +144,11 @@ class JSONTransform extends BaseVisitor {
|
|
|
114
144
|
|
|
115
145
|
switch (decoratorName) {
|
|
116
146
|
case "alias": {
|
|
117
|
-
if (!args.length)
|
|
147
|
+
if (!args.length)
|
|
148
|
+
throw new Error(
|
|
149
|
+
"Expected 1 argument but got zero at @alias in " +
|
|
150
|
+
node.range.source.normalizedPath,
|
|
151
|
+
);
|
|
118
152
|
mem.alias = args[0]!;
|
|
119
153
|
mem.flags.set(PropertyFlags.Alias, args);
|
|
120
154
|
break;
|
|
@@ -124,7 +158,11 @@ class JSONTransform extends BaseVisitor {
|
|
|
124
158
|
break;
|
|
125
159
|
}
|
|
126
160
|
case "omitif": {
|
|
127
|
-
if (!decorator.args?.length)
|
|
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
|
+
);
|
|
128
166
|
mem.flags.set(PropertyFlags.OmitIf, args);
|
|
129
167
|
break;
|
|
130
168
|
}
|
|
@@ -136,10 +174,23 @@ class JSONTransform extends BaseVisitor {
|
|
|
136
174
|
}
|
|
137
175
|
}
|
|
138
176
|
|
|
139
|
-
|
|
177
|
+
mem.generate(false);
|
|
140
178
|
|
|
141
179
|
if (this.schemasList.find((v) => v.name == type)) {
|
|
142
|
-
mem.initialize =
|
|
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()";
|
|
143
194
|
} else if (mem.value) {
|
|
144
195
|
mem.initialize = "this." + name.text + " = " + mem.value;
|
|
145
196
|
} else if (type === "Map") {
|
|
@@ -147,12 +198,22 @@ class JSONTransform extends BaseVisitor {
|
|
|
147
198
|
} else if (type === "string") {
|
|
148
199
|
mem.initialize = "this." + name.text + ' = ""';
|
|
149
200
|
} else if (type === "Array") {
|
|
150
|
-
mem.initialize =
|
|
201
|
+
mem.initialize =
|
|
202
|
+
"this." + name.text + " = instantiate<" + mem.type + ">()";
|
|
151
203
|
} else if (type === "bool" || type === "boolean") {
|
|
152
204
|
mem.initialize = "this." + name.text + " = false";
|
|
153
205
|
} else if (type === "JSON.Raw") {
|
|
154
206
|
mem.initialize = "this." + name.text + ' = ""';
|
|
155
|
-
} else if (
|
|
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
|
+
) {
|
|
156
217
|
mem.initialize = "this." + name.text + " = 0";
|
|
157
218
|
} else if (type === "f32" || type === "f64") {
|
|
158
219
|
mem.initialize = "this." + name.text + " = 0.0";
|
|
@@ -166,41 +227,50 @@ class JSONTransform extends BaseVisitor {
|
|
|
166
227
|
|
|
167
228
|
let INITIALIZE = "__INITIALIZE(): this {\n";
|
|
168
229
|
|
|
169
|
-
let DESERIALIZE =
|
|
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";
|
|
170
232
|
let indent = " ";
|
|
171
233
|
|
|
172
234
|
if (!schema.members.length) return;
|
|
173
235
|
|
|
174
|
-
|
|
236
|
+
found = false;
|
|
175
237
|
|
|
176
|
-
if (
|
|
238
|
+
if (
|
|
239
|
+
schema.members[0]?.flags.has(PropertyFlags.OmitNull) ||
|
|
240
|
+
schema.members[0]?.flags.has(PropertyFlags.OmitIf)
|
|
241
|
+
) {
|
|
177
242
|
SERIALIZE_RAW += schema.members[0]?.serialize;
|
|
178
|
-
SERIALIZE_PRETTY += "\\n" +
|
|
243
|
+
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize;
|
|
179
244
|
} else {
|
|
180
245
|
SERIALIZE_RAW += schema.members[0]?.serialize + ",";
|
|
181
|
-
SERIALIZE_PRETTY += "\\n" +
|
|
246
|
+
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize + ",\\n";
|
|
182
247
|
found = true;
|
|
183
248
|
}
|
|
184
249
|
|
|
185
|
-
if (schema.members[0]?.initialize)
|
|
250
|
+
if (schema.members[0]?.initialize)
|
|
251
|
+
INITIALIZE += " " + schema.members[0]?.initialize + ";\n";
|
|
186
252
|
|
|
187
253
|
for (let i = 1; i < schema.members.length; i++) {
|
|
188
254
|
const member = schema.members[i]!;
|
|
189
255
|
if (member.initialize) INITIALIZE += " " + member.initialize + ";\n";
|
|
190
|
-
if (
|
|
191
|
-
|
|
256
|
+
if (
|
|
257
|
+
member.flags.has(PropertyFlags.OmitNull) ||
|
|
258
|
+
member.flags.has(PropertyFlags.OmitIf)
|
|
259
|
+
) {
|
|
192
260
|
SERIALIZE_RAW += member.serialize;
|
|
193
|
-
SERIALIZE_PRETTY += member.
|
|
261
|
+
SERIALIZE_PRETTY += member.serialize;
|
|
194
262
|
} else {
|
|
195
263
|
SERIALIZE_RAW += member.serialize + ",";
|
|
196
|
-
SERIALIZE_PRETTY += indent + member.
|
|
264
|
+
SERIALIZE_PRETTY += indent + member.serialize + ",\\n";
|
|
197
265
|
found = true;
|
|
198
266
|
}
|
|
199
267
|
}
|
|
200
268
|
|
|
201
269
|
if (found) {
|
|
202
|
-
SERIALIZE_RAW +=
|
|
203
|
-
|
|
270
|
+
SERIALIZE_RAW +=
|
|
271
|
+
"`;\n store<u16>(changetype<usize>(out) + ((out.length - 1) << 1), 125);\n return out;\n}";
|
|
272
|
+
SERIALIZE_PRETTY +=
|
|
273
|
+
"`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}";
|
|
204
274
|
} else {
|
|
205
275
|
SERIALIZE_RAW += "}`;\n return out;\n}";
|
|
206
276
|
SERIALIZE_PRETTY += "}`;\n return out;\n}";
|
|
@@ -209,7 +279,9 @@ class JSONTransform extends BaseVisitor {
|
|
|
209
279
|
INITIALIZE += " return this;\n}";
|
|
210
280
|
|
|
211
281
|
const sortedMembers: Property[][] = [];
|
|
212
|
-
const _sorted = schema.members.sort(
|
|
282
|
+
const _sorted = schema.members.sort(
|
|
283
|
+
(a, b) => (a.alias?.length! || a.name.length) - (b.alias?.length! || b.name.length),
|
|
284
|
+
);
|
|
213
285
|
let len = -1;
|
|
214
286
|
let offset = -1;
|
|
215
287
|
for (let i = 0; i < _sorted.length; i++) {
|
|
@@ -230,24 +302,30 @@ class JSONTransform extends BaseVisitor {
|
|
|
230
302
|
const _name = encodeKey(firstMember.alias || firstMember.name);
|
|
231
303
|
if (_name.length === 1) {
|
|
232
304
|
if (first) {
|
|
233
|
-
DESERIALIZE +=
|
|
305
|
+
DESERIALIZE +=
|
|
306
|
+
" if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
234
307
|
first = false;
|
|
235
308
|
} else {
|
|
236
|
-
DESERIALIZE +=
|
|
309
|
+
DESERIALIZE +=
|
|
310
|
+
"else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
237
311
|
}
|
|
238
312
|
} else if (_name.length === 2) {
|
|
239
313
|
if (first) {
|
|
240
|
-
DESERIALIZE +=
|
|
314
|
+
DESERIALIZE +=
|
|
315
|
+
" if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
241
316
|
first = false;
|
|
242
317
|
} else {
|
|
243
|
-
DESERIALIZE +=
|
|
318
|
+
DESERIALIZE +=
|
|
319
|
+
"else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
|
|
244
320
|
}
|
|
245
321
|
} else if (_name.length === 4) {
|
|
246
322
|
if (first) {
|
|
247
|
-
DESERIALIZE +=
|
|
323
|
+
DESERIALIZE +=
|
|
324
|
+
" if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
|
|
248
325
|
first = false;
|
|
249
326
|
} else {
|
|
250
|
-
DESERIALIZE +=
|
|
327
|
+
DESERIALIZE +=
|
|
328
|
+
"else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
|
|
251
329
|
}
|
|
252
330
|
} else {
|
|
253
331
|
if (first) {
|
|
@@ -271,23 +349,31 @@ class JSONTransform extends BaseVisitor {
|
|
|
271
349
|
f = false;
|
|
272
350
|
DESERIALIZE += ` if (${charCodeAt64(_name, 0)} === code) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
|
|
273
351
|
} else {
|
|
274
|
-
DESERIALIZE =
|
|
352
|
+
DESERIALIZE =
|
|
353
|
+
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
354
|
+
`else if (${charCodeAt64(_name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`;
|
|
275
355
|
}
|
|
276
356
|
} else {
|
|
277
357
|
if (f) {
|
|
278
358
|
f = false;
|
|
279
359
|
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`;
|
|
280
360
|
} else {
|
|
281
|
-
DESERIALIZE =
|
|
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`;
|
|
282
364
|
}
|
|
283
365
|
}
|
|
284
366
|
}
|
|
285
367
|
if (_name.length < 3) {
|
|
286
368
|
DESERIALIZE += ` default: {\n return false;\n }\n }\n`;
|
|
287
369
|
} else if (_name.length == 4) {
|
|
288
|
-
DESERIALIZE =
|
|
370
|
+
DESERIALIZE =
|
|
371
|
+
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
372
|
+
` else {\n return false;\n }\n`;
|
|
289
373
|
} else {
|
|
290
|
-
DESERIALIZE =
|
|
374
|
+
DESERIALIZE =
|
|
375
|
+
DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
|
|
376
|
+
` else {\n return false;\n }\n`;
|
|
291
377
|
}
|
|
292
378
|
DESERIALIZE += " } ";
|
|
293
379
|
}
|
|
@@ -298,20 +384,30 @@ class JSONTransform extends BaseVisitor {
|
|
|
298
384
|
|
|
299
385
|
if (process.env["JSON_DEBUG"]) {
|
|
300
386
|
console.log(SERIALIZE_RAW);
|
|
301
|
-
console.log(SERIALIZE_PRETTY);
|
|
387
|
+
//console.log(SERIALIZE_PRETTY);
|
|
302
388
|
console.log(INITIALIZE);
|
|
303
389
|
console.log(DESERIALIZE);
|
|
304
390
|
}
|
|
305
391
|
|
|
306
|
-
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(
|
|
307
|
-
|
|
392
|
+
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(
|
|
393
|
+
SERIALIZE_RAW,
|
|
394
|
+
node,
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
const DESERIALIZE_SAFE = DESERIALIZE.replaceAll("__DESERIALIZE", "__DESERIALIZE_SAFE")
|
|
398
|
+
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node);
|
|
308
399
|
const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node);
|
|
309
400
|
const DESERIALIZE_METHOD = SimpleParser.parseClassMember(DESERIALIZE, node);
|
|
401
|
+
const DESERIALIZE_SAFE_METHOD = SimpleParser.parseClassMember(DESERIALIZE_SAFE, node);
|
|
310
402
|
|
|
311
|
-
if (!node.members.find((v) => v.name.text == "__SERIALIZE"))
|
|
312
|
-
|
|
313
|
-
if (!node.members.find((v) => v.name.text == "__INITIALIZE"))
|
|
314
|
-
|
|
403
|
+
if (!node.members.find((v) => v.name.text == "__SERIALIZE"))
|
|
404
|
+
node.members.push(SERIALIZE_RAW_METHOD);
|
|
405
|
+
if (!node.members.find((v) => v.name.text == "__INITIALIZE"))
|
|
406
|
+
node.members.push(INITIALIZE_METHOD);
|
|
407
|
+
if (!node.members.find((v) => v.name.text == "__DESERIALIZE"))
|
|
408
|
+
node.members.push(DESERIALIZE_METHOD);
|
|
409
|
+
if (!node.members.find((v) => v.name.text == "__DESERIALIZE_SAFE"))
|
|
410
|
+
node.members.push(DESERIALIZE_SAFE_METHOD);
|
|
315
411
|
|
|
316
412
|
this.schemasList.push(schema);
|
|
317
413
|
}
|
|
@@ -337,13 +433,13 @@ export default class Transformer extends Transform {
|
|
|
337
433
|
.sort((_a, _b) => {
|
|
338
434
|
const a = _a.internalPath;
|
|
339
435
|
const b = _b.internalPath;
|
|
340
|
-
if (a[0] !== "~" && b[0] === "~") {
|
|
341
|
-
return 1;
|
|
342
|
-
}
|
|
343
436
|
if (a[0] === "~" && b[0] !== "~") {
|
|
344
437
|
return -1;
|
|
438
|
+
} else if (a[0] !== "~" && b[0] === "~") {
|
|
439
|
+
return 1;
|
|
440
|
+
} else {
|
|
441
|
+
return 0;
|
|
345
442
|
}
|
|
346
|
-
return 0;
|
|
347
443
|
});
|
|
348
444
|
|
|
349
445
|
// Loop over every source
|
|
@@ -356,85 +452,17 @@ export default class Transformer extends Transform {
|
|
|
356
452
|
// Check that every parent and child class is hooked up correctly
|
|
357
453
|
const schemas = transformer.schemasList;
|
|
358
454
|
for (const schema of schemas) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
455
|
+
if (schema.parent) {
|
|
456
|
+
const parent = schemas.find((v) => v.name === schema.parent?.name);
|
|
457
|
+
if (!parent)
|
|
458
|
+
throw new Error(
|
|
459
|
+
`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.`,
|
|
460
|
+
);
|
|
363
461
|
}
|
|
364
462
|
}
|
|
365
463
|
}
|
|
366
464
|
}
|
|
367
465
|
|
|
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
|
-
|
|
438
466
|
enum PropertyFlags {
|
|
439
467
|
Null,
|
|
440
468
|
Omit,
|
|
@@ -449,10 +477,12 @@ class Property {
|
|
|
449
477
|
public alias: string | null = null;
|
|
450
478
|
public type: string = "";
|
|
451
479
|
public value: string | null = null;
|
|
452
|
-
public flags: Map<PropertyFlags, string[]> = new Map<
|
|
480
|
+
public flags: Map<PropertyFlags, string[]> = new Map<
|
|
481
|
+
PropertyFlags,
|
|
482
|
+
string[]
|
|
483
|
+
>();
|
|
453
484
|
|
|
454
485
|
public serialize: string | null = null;
|
|
455
|
-
public serialize_pretty: string | null = null;
|
|
456
486
|
public deserialize: string | null = null;
|
|
457
487
|
public initialize: string | null = null;
|
|
458
488
|
|
|
@@ -461,7 +491,7 @@ class Property {
|
|
|
461
491
|
private right_s: string = "";
|
|
462
492
|
private right_d: string = "";
|
|
463
493
|
|
|
464
|
-
public generate(): void {
|
|
494
|
+
public generate(safe: boolean): void {
|
|
465
495
|
const name = this.name;
|
|
466
496
|
const escapedName = escapeString(JSON.stringify(this.alias || this.name));
|
|
467
497
|
const type = this.type;
|
|
@@ -469,7 +499,7 @@ class Property {
|
|
|
469
499
|
|
|
470
500
|
if (this.flags.has(PropertyFlags.JSON_Raw)) {
|
|
471
501
|
if (this.flags.has(PropertyFlags.Null)) {
|
|
472
|
-
this.right_s = "(this." + name +
|
|
502
|
+
this.right_s = "(this." + name + " || \"null\")";
|
|
473
503
|
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
504
|
} else {
|
|
475
505
|
this.right_s = "this." + name;
|
|
@@ -477,22 +507,38 @@ class Property {
|
|
|
477
507
|
}
|
|
478
508
|
} else {
|
|
479
509
|
this.right_s = "__SERIALIZE<" + type + ">(this." + name + ")";
|
|
480
|
-
this.right_d =
|
|
510
|
+
this.right_d =
|
|
511
|
+
(safe ? "__DESERIALIZE_SAFE" : "__DESERIALIZE") + "<" + type + ">(data.substring(value_start, value_end))";
|
|
481
512
|
}
|
|
482
513
|
|
|
483
514
|
if (this.flags.has(PropertyFlags.OmitIf)) {
|
|
484
515
|
const condition = this.flags.get(PropertyFlags.OmitIf)![0];
|
|
485
|
-
if (!condition)
|
|
486
|
-
|
|
487
|
-
|
|
516
|
+
if (!condition)
|
|
517
|
+
throw new Error(
|
|
518
|
+
"Could not find condition when using decorator @omitif! Provide at least one condition",
|
|
519
|
+
);
|
|
520
|
+
this.serialize =
|
|
521
|
+
"${" +
|
|
522
|
+
condition +
|
|
523
|
+
' ? "" : \'' +
|
|
524
|
+
escapedName +
|
|
525
|
+
":' + " +
|
|
526
|
+
this.right_s +
|
|
527
|
+
' + ","}';
|
|
488
528
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
489
529
|
} else if (this.flags.has(PropertyFlags.OmitNull)) {
|
|
490
|
-
this.serialize =
|
|
491
|
-
|
|
530
|
+
this.serialize =
|
|
531
|
+
"${changetype<usize>(this." +
|
|
532
|
+
name +
|
|
533
|
+
") == <usize>0" +
|
|
534
|
+
' ? "" : \'' +
|
|
535
|
+
escapedName +
|
|
536
|
+
":' + " +
|
|
537
|
+
this.right_s +
|
|
538
|
+
' + ","}';
|
|
492
539
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
493
540
|
} else {
|
|
494
541
|
this.serialize = escapedName + ":${" + this.right_s + "}";
|
|
495
|
-
this.serialize_pretty = escapedName + ": ${" + this.right_s + "}";
|
|
496
542
|
this.deserialize = "this." + name + " = " + this.right_d + ";";
|
|
497
543
|
}
|
|
498
544
|
}
|
|
@@ -511,7 +557,9 @@ function charCodeAt32(data: string, offset: number): number {
|
|
|
511
557
|
|
|
512
558
|
function charCodeAt64(data: string, offset: number): bigint {
|
|
513
559
|
if (offset + 3 >= data.length) {
|
|
514
|
-
throw new Error(
|
|
560
|
+
throw new Error(
|
|
561
|
+
"The string must have at least 4 characters from the specified offset.",
|
|
562
|
+
);
|
|
515
563
|
}
|
|
516
564
|
|
|
517
565
|
const firstCharCode = BigInt(data.charCodeAt(offset));
|
|
@@ -519,7 +567,11 @@ function charCodeAt64(data: string, offset: number): bigint {
|
|
|
519
567
|
const thirdCharCode = BigInt(data.charCodeAt(offset + 2));
|
|
520
568
|
const fourthCharCode = BigInt(data.charCodeAt(offset + 3));
|
|
521
569
|
|
|
522
|
-
const u64Value =
|
|
570
|
+
const u64Value =
|
|
571
|
+
(fourthCharCode << 48n) |
|
|
572
|
+
(thirdCharCode << 32n) |
|
|
573
|
+
(secondCharCode << 16n) |
|
|
574
|
+
firstCharCode;
|
|
523
575
|
|
|
524
576
|
return u64Value;
|
|
525
577
|
}
|
|
@@ -563,9 +615,3 @@ function getArgs(args: Expression[] | null): string[] {
|
|
|
563
615
|
}
|
|
564
616
|
return out;
|
|
565
617
|
}
|
|
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
|
-
}
|