json-as 0.8.6 → 0.9.0

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.
Files changed (52) hide show
  1. package/.github/workflows/nodejs.yml +7 -1
  2. package/CHANGELOG +4 -1
  3. package/README.md +26 -16
  4. package/assembly/__tests__/deserialize.spec.ts +298 -0
  5. package/assembly/__tests__/serialize.spec.ts +375 -0
  6. package/assembly/deserialize/array/array.ts +31 -0
  7. package/assembly/deserialize/array/bool.ts +19 -0
  8. package/assembly/deserialize/array/float.ts +24 -0
  9. package/assembly/deserialize/array/integer.ts +24 -0
  10. package/assembly/deserialize/array/map.ts +27 -0
  11. package/assembly/deserialize/array/object.ts +27 -0
  12. package/assembly/deserialize/array/string.ts +29 -0
  13. package/assembly/deserialize/array.ts +37 -0
  14. package/assembly/deserialize/bool.ts +18 -0
  15. package/assembly/deserialize/box.ts +17 -0
  16. package/assembly/deserialize/date.ts +11 -0
  17. package/assembly/deserialize/float.ts +9 -0
  18. package/assembly/deserialize/integer.ts +7 -0
  19. package/assembly/deserialize/map.ts +182 -0
  20. package/assembly/deserialize/object.ts +136 -0
  21. package/assembly/deserialize/string.ts +88 -0
  22. package/assembly/index.d.ts +7 -1
  23. package/assembly/index.ts +129 -1
  24. package/assembly/serialize/array.ts +52 -0
  25. package/assembly/serialize/bool.ts +4 -0
  26. package/assembly/serialize/box.ts +10 -0
  27. package/assembly/serialize/date.ts +4 -0
  28. package/assembly/serialize/float.ts +4 -0
  29. package/assembly/serialize/integer.ts +5 -0
  30. package/assembly/serialize/map.ts +24 -0
  31. package/assembly/serialize/object.ts +7 -0
  32. package/assembly/serialize/string.ts +64 -0
  33. package/assembly/src/sink.ts +286 -0
  34. package/assembly/src/util.ts +6 -0
  35. package/assembly/test.ts +34 -16
  36. package/bench/benchmark.ts +7 -3
  37. package/bench.js +14 -3
  38. package/index.ts +1 -1
  39. package/package.json +6 -8
  40. package/transform/lib/index.js +301 -183
  41. package/transform/lib/index.old.js +257 -0
  42. package/transform/lib/types.js +17 -0
  43. package/transform/package.json +1 -1
  44. package/transform/src/index.old.ts +312 -0
  45. package/transform/src/index.ts +301 -215
  46. package/transform/tsconfig.json +2 -2
  47. package/tsconfig.json +94 -102
  48. package/assembly/__benches__/as-json.ts +0 -88
  49. package/assembly/__benches__/as-tral.d.ts +0 -1
  50. package/assembly/__tests__/as-json.spec.ts +0 -673
  51. package/assembly/__tests__/as-pect.d.ts +0 -1
  52. package/assembly/src/json.ts +0 -941
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  ClassDeclaration,
3
3
  FieldDeclaration,
4
+ IdentifierExpression,
5
+ NamedTypeNode,
6
+ StringLiteralExpression,
4
7
  Parser,
5
8
  Source,
6
9
  SourceKind,
@@ -10,237 +13,273 @@ import {
10
13
  import { toString, isStdlib } from "visitor-as/dist/utils.js";
11
14
  import { BaseVisitor, SimpleParser } from "visitor-as/dist/index.js";
12
15
  import { Transform } from "assemblyscript/dist/transform.js";
16
+ import { CommonFlags } from "types:assemblyscript/src/common";
13
17
 
14
- class SchemaData {
15
- public keys: string[] = [];
16
- public values: string[] = [];
17
- public types: string[] = [];
18
- public name: string = "";
19
- public parent: string = "";
20
- public node!: ClassDeclaration;
21
- public encodeStmts: string[] = [];
22
- public setDataStmts: string[] = [];
23
- public initializeStmts: string[] = [];
24
- }
25
-
26
- class AsJSONTransform extends BaseVisitor {
18
+ class JSONTransform extends BaseVisitor {
27
19
  public schemasList: SchemaData[] = [];
28
20
  public currentClass!: SchemaData;
29
21
  public sources = new Set<Source>();
30
22
 
31
23
  visitMethodDeclaration(): void { }
32
24
  visitClassDeclaration(node: ClassDeclaration): void {
33
- const className = node.name.text;
34
25
  if (!node.decorators?.length) return;
35
- let foundDecorator = false;
36
- for (const decorator of node.decorators!) {
37
- if (
38
- // @ts-ignore
39
- decorator.name.text.toLowerCase() == "json" ||
40
- // @ts-ignore
41
- decorator.name.text.toLowerCase() == "serializable"
42
- )
43
- foundDecorator = true;
26
+ if (!node.members?.length) return;
27
+
28
+ let found = false;
29
+ for (const decorator of node.decorators) {
30
+ const name = (<IdentifierExpression>decorator.name).text;
31
+ if (name === "json" || name === "serializable") {
32
+ found = true;
33
+ break;
34
+ }
44
35
  }
45
- if (!foundDecorator) return;
36
+ if (!found) return;
46
37
 
47
- // Prevent from being triggered twice.
48
- for (const member of node.members) {
49
- if (member.name.text == "__JSON_Serialize") return;
50
- }
38
+ const schema = new SchemaData();
39
+ schema.node = node;
40
+ schema.name = node.name.text;
41
+
42
+ const members = [
43
+ ...node.members
44
+ ];
51
45
 
52
- this.currentClass = {
53
- name: className,
54
- keys: [],
55
- values: [],
56
- types: [],
57
- parent: node.extendsType ? toString(node.extendsType) : "",
58
- node: node,
59
- encodeStmts: [],
60
- setDataStmts: [],
61
- initializeStmts: []
62
- };
63
-
64
- if (this.currentClass.parent.length) {
65
- const parentSchema = this.schemasList.find(
66
- (v) => v.name == this.currentClass.parent
67
- );
68
- if (parentSchema?.encodeStmts) {
69
- parentSchema?.encodeStmts.push(parentSchema?.encodeStmts.pop() + ",");
70
- for (let i = 0; i < parentSchema.keys.length; i++) {
71
- const key = parentSchema.keys[i];
72
- if (node.members.filter(v => v.name.text == key) == undefined) this.currentClass.encodeStmts.unshift(parentSchema.encodeStmts[i]!);
46
+ if (node.extendsType) {
47
+ schema.parent = this.schemasList.find(
48
+ (v) => v.name == node.extendsType?.name.identifier.text
49
+ ) as SchemaData | null;
50
+
51
+ if (schema.parent?.members) {
52
+ for (let i = 0; i < schema.parent.members.length; i++) {
53
+ const replace = schema.members.find(
54
+ (v) => v.name == schema.parent?.members[i]?.name
55
+ )
56
+ if (!replace) schema.members.unshift(schema.parent.members[i]!);
73
57
  }
74
58
  }
75
59
  }
76
60
 
77
- const parentSchema = this.schemasList.find(
78
- (v) => v.name == this.currentClass.parent
79
- );
61
+ for (const member of members) {
62
+ if (!(member instanceof FieldDeclaration)) continue;
63
+ const name = member.name;
64
+ if (!member.type) {
65
+ throw new Error("Fields must be strongly typed! Found " + toString(member) + " at " + node.range.source.normalizedPath);
66
+ }
67
+ const type = toString(member.type!);
68
+ const value = member.initializer ? toString(member.initializer!) : null;
69
+
70
+ if (member.flags == CommonFlags.Static) continue;
71
+ if (member.flags === CommonFlags.Private) continue;
72
+ if (member.flags === CommonFlags.Protected) continue;
73
+
74
+ const mem = new Property();
75
+ mem.name = name.text;
76
+ mem.type = type;
77
+ mem.value = value;
78
+ mem.node = member;
79
+
80
+ for (const decorator of member.decorators || []) {
81
+ if ((<IdentifierExpression>decorator.name).text == "alias") {
82
+ if (!decorator.args?.length) throw new Error("Expected 1 argument but got zero at @alias in " + node.range.source.normalizedPath);
83
+ mem.flag = PropertyFlags.Alias;
84
+ mem.alias = (decorator.args[0] as StringLiteralExpression).value;
85
+ } else if ((<IdentifierExpression>decorator.name).text == "omit") {
86
+ mem.flag = PropertyFlags.Omit;
87
+ } else if ((<IdentifierExpression>decorator.name).text == "omitnull") {
88
+ mem.flag = PropertyFlags.OmitNull;
89
+ } else if ((<IdentifierExpression>decorator.name).text == "omitif") {
90
+ if (!decorator.args?.length) throw new Error("Expected 1 argument but got zero at @omitif in " + node.range.source.normalizedPath);
91
+ mem.args?.push((decorator.args[0] as StringLiteralExpression).value);
92
+ mem.flag = PropertyFlags.OmitIf;
93
+ }
94
+ }
80
95
 
81
- const members = [
82
- ...node.members
83
- ];
96
+ if (mem.flag === PropertyFlags.Alias) {
97
+ mem.name = mem.alias!;
98
+ } else if (mem.flag === PropertyFlags.None) {
99
+ mem.serialize = escapeString(JSON.stringify(mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}";
100
+ mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"
101
+ }
84
102
 
85
- if (parentSchema) {
86
- for (const mem of parentSchema.node.members) {
87
- if (members.find(v => v.name === mem.name) == undefined) members.unshift(mem);
103
+ if (mem.flag == PropertyFlags.OmitNull) {
104
+ mem.serialize = "${changetype<usize>(this." + mem.name + ") == <usize>0" + " ? \"\" : '" + escapeString(JSON.stringify(mem.name)) + ":' + __SERIALIZE<" + type + ">(this." + name.text + ") + \",\"}";
105
+ mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"
106
+ } else if (mem.flag == PropertyFlags.OmitIf) {
107
+ mem.serialize = "${" + mem.args![0]! + " ? \"\" : '" + escapeString(JSON.stringify(mem.name)) + ":' + __SERIALIZE<" + type + ">(this." + name.text + ")}";
108
+ mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"
109
+ } else if (mem.flag == PropertyFlags.Alias) {
110
+ mem.serialize = escapeString(JSON.stringify(mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}";
111
+ mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"
112
+ mem.name = name.text;
88
113
  }
114
+
115
+ const t = (mem.node.type as NamedTypeNode).name.identifier.text;
116
+ if (this.schemasList.find(v => v.name == t)) {
117
+ mem.initialize = "this." + name.text + " = changetype<nonnull<" + t + ">>(__new(offsetof<nonnull<" + t + ">>(), idof<nonnull<" + t + ">>()));\n changetype<nonnull<" + t + ">>(this." + name.text + ").__INITIALIZE()"
118
+ } else if (mem.value) {
119
+ mem.initialize = "this." + name.text + " = " + mem.value;
120
+ }
121
+
122
+ schema.members.push(mem);
89
123
  }
90
124
 
91
- for (const mem of members) {
92
- // @ts-ignore
93
- if (mem.type && mem.type.name && mem.type.name.identifier.text) {
94
- const member = mem as FieldDeclaration;
95
- const lineText = toString(member);
96
- //console.log("Member: " + lineText)
125
+ let SERIALIZE_RAW = "@inline __SERIALIZE(): string {\n let out = `{";
126
+ let SERIALIZE_PRETTY = "@inline __SERIALIZE_PRETTY(): string {\n let out = `{";
97
127
 
98
- if (!lineText.startsWith("private ") && !lineText.startsWith("static ")) {
128
+ let INITIALIZE = "@inline __INITIALIZE(): this {\n";
99
129
 
100
- // @ts-ignore
101
- let type = toString(member.type);
130
+ let DESERIALIZE = "@inline __DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n const len = key_end - key_start;\n"
131
+ let indent = " ";
102
132
 
103
- const name = member.name.text;
104
- let aliasName = name;
133
+ if (!schema.members.length) return;
105
134
 
106
- // @ts-ignore
107
- if (member.decorators && member.decorators[0]?.name.text === "alias") {
108
- if (member.decorators[0] && member.decorators[0].args![0]) {
109
- // @ts-ignore
110
- aliasName = member.decorators[0].args![0].value;
111
- }
112
- }
113
- this.currentClass.keys.push(name);
114
- // @ts-ignore
115
- this.currentClass.types.push(type);
116
- // @ts-ignore
117
- if (
118
- [
119
- "u8",
120
- "i8",
121
- "u16",
122
- "i16",
123
- "u32",
124
- "i32",
125
- "u64",
126
- "i64",
127
- ].includes(type.toLowerCase())
128
- ) {
129
- this.currentClass.encodeStmts.push(
130
- `${encodeKey(aliasName)}:\${this.${name}},`
131
- );
132
- // @ts-ignore
133
- this.currentClass.setDataStmts.push(
134
- `if (key.equals(${JSON.stringify(aliasName)})) {
135
- this.${name} = __atoi_fast<${type}>(data, val_start << 1, val_end << 1);
136
- return;
137
- }`
138
- );
139
- if (member.initializer) {
140
- this.currentClass.initializeStmts.push(
141
- `this.${name} = ${toString(member.initializer)}`
142
- );
143
- }
144
- } else // @ts-ignore
145
- if (
146
- [
147
- "f32",
148
- "f64",
149
- ].includes(type.toLowerCase())
150
- ) {
151
- this.currentClass.encodeStmts.push(
152
- `${encodeKey(aliasName)}:\${this.${name}},`
153
- );
154
- // @ts-ignore
155
- this.currentClass.setDataStmts.push(
156
- `if (key.equals(${JSON.stringify(aliasName)})) {
157
- this.${name} = __parseObjectValue<${type}>(data.slice(val_start, val_end), initializeDefaultValues);
158
- return;
159
- }`
160
- );
161
- if (member.initializer) {
162
- this.currentClass.initializeStmts.push(
163
- `this.${name} = ${toString(member.initializer)}`
164
- );
165
- }
166
- } else {
167
- this.currentClass.encodeStmts.push(
168
- `${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})},`
169
- );
170
- // @ts-ignore
171
- this.currentClass.setDataStmts.push(
172
- `if (key.equals(${JSON.stringify(aliasName)})) {
173
- this.${name} = __parseObjectValue<${type}>(val_start ? data.slice(val_start, val_end) : data, initializeDefaultValues);
174
- return;
175
- }`
176
- );
177
- if (member.initializer) {
178
- this.currentClass.initializeStmts.push(
179
- `this.${name} = ${toString(member.initializer)}`
180
- );
181
- }
182
- }
183
- }
135
+ found = false;
136
+
137
+ if (
138
+ schema.members[0]?.flag === PropertyFlags.OmitNull
139
+ || schema.members[0]?.flag === PropertyFlags.OmitIf
140
+ ) {
141
+ SERIALIZE_RAW += schema.members[0]?.serialize;
142
+ SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize;
143
+ } else {
144
+ SERIALIZE_RAW += schema.members[0]?.serialize + ",";
145
+ SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize + ",\\n";
146
+ found = true;
147
+ }
148
+
149
+ if (schema.members[0]?.initialize) INITIALIZE += " " + schema.members[0]?.initialize + ";\n";
150
+
151
+ for (let i = 1; i < schema.members.length; i++) {
152
+ const member = schema.members[i]!;
153
+ if (member.initialize) INITIALIZE += " " + member.initialize + ";\n";
154
+ if (
155
+ member.flag === PropertyFlags.OmitNull
156
+ || member.flag === PropertyFlags.OmitIf
157
+ ) {
158
+ SERIALIZE_RAW += member.serialize;
159
+ SERIALIZE_PRETTY += member.serialize;
160
+ } else {
161
+ SERIALIZE_RAW += member.serialize + ",";
162
+ SERIALIZE_PRETTY += indent + member.serialize + ",\\n";
163
+ found = true;
184
164
  }
185
165
  }
186
166
 
187
- let serializeFunc = "";
188
-
189
- if (this.currentClass.encodeStmts.length > 0) {
190
- const stmt =
191
- this.currentClass.encodeStmts[
192
- this.currentClass.encodeStmts.length - 1
193
- ]!;
194
- this.currentClass.encodeStmts[this.currentClass.encodeStmts.length - 1] =
195
- stmt!.slice(0, stmt.length - 1);
196
- serializeFunc = `
197
- __JSON_Serialize(): string {
198
- return \`{${this.currentClass.encodeStmts.join("")}}\`;
199
- }`;
167
+ if (found) {
168
+ SERIALIZE_RAW += "`;\n store<u16>(changetype<usize>(out) + ((out.length - 1) << 1), 125);\n return out;\n}";
169
+ SERIALIZE_PRETTY += "`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}";
200
170
  } else {
201
- serializeFunc = `
202
- __JSON_Serialize(): string {
203
- return "{}";
204
- }`;
171
+ SERIALIZE_RAW += "`;\n};";
172
+ SERIALIZE_PRETTY += "`;\n};";
205
173
  }
206
174
 
207
- const setKeyFunc = `
208
- __JSON_Set_Key(key: __Virtual<string>, data: string, val_start: i32, val_end: i32, initializeDefaultValues: boolean): void {
209
- ${this.currentClass.setDataStmts.join("\n ")}
175
+ INITIALIZE += " return this;\n}"
176
+
177
+ const sortedMembers: Property[][] = [];
178
+ const _sorted = schema.members.sort((a, b) => a.name.length - b.name.length);
179
+ let len = 0;
180
+ let offset = 0;
181
+ sortedMembers.push([_sorted[0]!]);
182
+ len = _sorted[0]?.name.length!;
183
+ for (let i = 1; i < _sorted.length; i++) {
184
+ const member = _sorted[i]!;
185
+ if (member.alias?.length || member.name.length > len) {
186
+ sortedMembers.push([member]);
187
+ len = member.alias?.length || member.name.length
188
+ offset++;
189
+ } else {
190
+ sortedMembers[offset]!.push(member);
210
191
  }
211
- `;
212
-
213
- let initializeFunc = "";
192
+ }
214
193
 
215
- if (this.currentClass.initializeStmts.length > 0) {
216
- initializeFunc = `
217
- __JSON_Initialize(): void {
218
- ${this.currentClass.initializeStmts.join(";\n")};
194
+ let first = true;
195
+ for (const memberSet of sortedMembers) {
196
+ const firstMember = memberSet[0]!;
197
+ const name = encodeKey(firstMember.alias || firstMember.name);
198
+ if (name.length === 1) {
199
+ if (first) {
200
+ DESERIALIZE += " if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
201
+ first = false;
202
+ } else {
203
+ DESERIALIZE += "else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
204
+ }
205
+ } else if (name.length === 2) {
206
+ if (first) {
207
+ DESERIALIZE += " if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
208
+ first = false;
209
+ } else {
210
+ DESERIALIZE += "else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
211
+ }
212
+ } else if (name.length === 4) {
213
+ if (first) {
214
+ DESERIALIZE += " if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
215
+ first = false;
216
+ } else {
217
+ DESERIALIZE += "else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
218
+ }
219
+ } else {
220
+ if (first) {
221
+ DESERIALIZE += " if (" + name.length + " === len) {\n";
222
+ first = false;
223
+ } else {
224
+ DESERIALIZE += "else if (" + name.length + " === len) {\n";
225
+ }
219
226
  }
220
- `;
221
- } else {
222
- initializeFunc = `
223
- __JSON_Initialize(): void {}
224
- `;
227
+ let f = true;
228
+ for (let i = 0; i < memberSet.length; i++) {
229
+ const member = memberSet[i]!;
230
+ const name = encodeKey(member.alias || member.name);
231
+ if (name.length === 1) {
232
+ DESERIALIZE += ` case ${name.charCodeAt(0)}: {\n ${member.deserialize}\n return true;\n }\n`;
233
+ } else if (name.length === 2) {
234
+ DESERIALIZE += ` case ${charCodeAt32(name, 0)}: {\n ${member.deserialize}\n return true;\n }\n`;
235
+ } else if (name.length === 4) {
236
+ if (f) {
237
+ f = false;
238
+ DESERIALIZE += ` if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`;
239
+ } else {
240
+ DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + `else if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`;
241
+ }
242
+ } else {
243
+ if (f) {
244
+ f = false;
245
+ DESERIALIZE += ` 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`
246
+ } else {
247
+ 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})) {\n ${member.deserialize}\n return true;\n }\n`
248
+ }
249
+ }
250
+ }
251
+ if (name.length < 3) {
252
+ DESERIALIZE += ` default: {\n return false;\n }\n }\n`
253
+ } else if (name.length == 4) {
254
+ DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`
255
+ } else {
256
+ DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`
257
+ }
258
+ DESERIALIZE += " } ";
225
259
  }
226
- const serializeMethod = SimpleParser.parseClassMember(serializeFunc, node);
227
- node.members.push(serializeMethod);
228
260
 
229
- const setDataMethod = SimpleParser.parseClassMember(setKeyFunc, node);
230
- node.members.push(setDataMethod);
261
+ DESERIALIZE += "\n return false;\n}"
231
262
 
232
- const initializeMethod = SimpleParser.parseClassMember(initializeFunc, node);
233
- node.members.push(initializeMethod);
263
+ //console.log(sortedMembers);
234
264
 
235
- this.schemasList.push(this.currentClass);
236
- this.sources.add(node.name.range.source);
265
+ if (process.env["JSON_DEBUG"]) {
266
+ console.log(SERIALIZE_RAW);
267
+ //console.log(SERIALIZE_PRETTY);
268
+ console.log(INITIALIZE);
269
+ console.log(DESERIALIZE);
270
+ }
237
271
 
238
- // Uncomment to see the generated code for debugging.
239
- //console.log(serializeFunc);
240
- //console.log(setKeyFunc);
241
- //console.log(initializeFunc);
242
- }
272
+ const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(SERIALIZE_RAW, node);
273
+ //const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node);
274
+ const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node);
275
+ const DESERIALIZE_METHOD = SimpleParser.parseClassMember(DESERIALIZE, node);
243
276
 
277
+ if (!node.members.find(v => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_RAW_METHOD);
278
+ if (!node.members.find(v => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD);
279
+ if (!node.members.find(v => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD);
280
+
281
+ this.schemasList.push(schema);
282
+ }
244
283
  visitSource(node: Source): void {
245
284
  super.visitSource(node);
246
285
 
@@ -248,35 +287,14 @@ class AsJSONTransform extends BaseVisitor {
248
287
  if (!this.sources.has(node)) {
249
288
  return;
250
289
  }
251
-
252
- // Note, the following one liner would be easier, but it fails with an assertion error
253
- // because as-virtual's SimpleParser doesn't set the parser.currentSource correctly.
254
- //
255
- // const stmt = SimpleParser.parseTopLevelStatement('import { Virtual as __Virtual } from "as-virtual/assembly";');
256
-
257
- // ... So we have to do it the long way:
258
- const txt = 'import { Virtual as __Virtual } from "as-virtual/assembly";'
259
- const tokenizer = new Tokenizer(new Source(SourceKind.User, node.normalizedPath, txt));
260
- const parser = new Parser();
261
- parser.currentSource = tokenizer.source;
262
- const stmt = parser.parseTopLevelStatement(tokenizer)!;
263
-
264
- // Add the import statement to the top of the source.
265
- node.statements.unshift(stmt);
266
290
  }
267
291
  }
268
292
 
269
- function encodeKey(aliasName: string): string {
270
- return JSON.stringify(aliasName)
271
- .replace(/\\/g, "\\\\")
272
- .replace(/\`/g, '\\`');
273
- }
274
-
275
293
  export default class Transformer extends Transform {
276
294
  // Trigger the transform after parse.
277
295
  afterParse(parser: Parser): void {
278
296
  // Create new transform
279
- const transformer = new AsJSONTransform();
297
+ const transformer = new JSONTransform();
280
298
 
281
299
  // Sort the sources so that user scripts are visited last
282
300
  const sources = parser.sources
@@ -304,9 +322,77 @@ export default class Transformer extends Transform {
304
322
  const schemas = transformer.schemasList;
305
323
  for (const schema of schemas) {
306
324
  if (schema.parent) {
307
- const parent = schemas.find((v) => v.name === schema.parent);
325
+ const parent = schemas.find((v) => v.name === schema.parent?.name);
308
326
  if (!parent) throw new Error(`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.`);
309
327
  }
310
328
  }
311
329
  }
330
+ }
331
+
332
+ enum PropertyFlags {
333
+ None,
334
+ Omit,
335
+ OmitNull,
336
+ OmitIf,
337
+ Alias
338
+ }
339
+
340
+ class Property {
341
+ public name: string = ""
342
+ public alias: string | null = null;
343
+ public type: string = "";
344
+ public value: string | null = null;
345
+ public flag: PropertyFlags = PropertyFlags.None;
346
+ public args: string[] | null = [];
347
+
348
+ public serialize: string | null = null;
349
+ public deserialize: string | null = null;
350
+ public initialize: string | null = null;
351
+
352
+ public node!: FieldDeclaration;
353
+ }
354
+
355
+ class SchemaData {
356
+ public name: string = "";
357
+ public members: Property[] = []
358
+ public parent: SchemaData | null = null;
359
+ public node!: ClassDeclaration;
360
+ }
361
+
362
+ function charCodeAt32(data: string, offset: number): number {
363
+ return (data.charCodeAt(offset + 1) << 16) | data.charCodeAt(offset);
364
+ }
365
+
366
+ function charCodeAt64(data: string, offset: number): bigint {
367
+ if (offset + 3 >= data.length) {
368
+ throw new Error("The string must have at least 4 characters from the specified offset.");
369
+ }
370
+
371
+ const firstCharCode = BigInt(data.charCodeAt(offset));
372
+ const secondCharCode = BigInt(data.charCodeAt(offset + 1));
373
+ const thirdCharCode = BigInt(data.charCodeAt(offset + 2));
374
+ const fourthCharCode = BigInt(data.charCodeAt(offset + 3));
375
+
376
+ const u64Value = (fourthCharCode << 48n) | (thirdCharCode << 32n) | (secondCharCode << 16n) | firstCharCode;
377
+
378
+ return u64Value;
379
+ }
380
+
381
+ function encodeKey(key: string): string {
382
+ const data = JSON.stringify(key);
383
+ return data.slice(1, data.length - 1);
384
+ }
385
+
386
+ function escapeString(data: string): string {
387
+ return data.replace(/\\/g, "\\\\")
388
+ .replace(/\`/g, '\\`');
389
+ }
390
+
391
+ function escapeSlash(data: string): string {
392
+ return data.replace(/\\/g, "\\\\")
393
+ .replace(/\`/g, '\\`');
394
+ }
395
+
396
+ function escapeQuote(data: string): string {
397
+ return data.replace(/\"/g, "\\\"");
312
398
  }
@@ -4,8 +4,8 @@
4
4
 
5
5
  /* Basic Options */
6
6
  // "incremental": true, /* Enable incremental compilation */
7
- "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
8
- "module": "es6" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
7
+ "target": "es2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
8
+ "module": "es2020" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
9
9
  // "lib": [], /* Specify library files to be included in the compilation. */
10
10
  // "allowJs": true, /* Allow javascript files to be compiled. */
11
11
  // "checkJs": true, /* Report errors in .js files. */