json-as 0.8.5 → 0.8.7

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 +6 -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 +296 -198
  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 +298 -234
  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 -671
  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,259 +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
+ }
102
+
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;
113
+ }
84
114
 
85
- if (parentSchema) {
86
- for (const mem of parentSchema.node.members) {
87
- if (members.find(v => v.name === mem.name) == undefined) members.unshift(mem);
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;
88
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 ")) continue;
128
+ let INITIALIZE = "@inline __INITIALIZE(): this {\n";
99
129
 
100
- const omitnull = member.decorators?.find(v => v.name.text == "omitnull");
101
- console.log(member.decorators?.find(v => v.name.text === "omitif"))
102
- const omitif = member.decorators?.find(v => v.name.text === "omitif")?.args?.[0]?.value ?? null;
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 = " ";
103
132
 
104
- // @ts-ignore
105
- let type = toString(member.type);
133
+ if (!schema.members.length) return;
106
134
 
107
- const name = member.name.text;
108
- let aliasName = name;
135
+ found = false;
109
136
 
110
- // @ts-ignore
111
- if (member.decorators && member.decorators[0]?.name.text === "alias") {
112
- if (member.decorators[0] && member.decorators[0].args![0]) {
113
- // @ts-ignore
114
- aliasName = member.decorators[0].args![0].value;
115
- }
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;
164
+ }
165
+ }
166
+
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}";
170
+ } else {
171
+ SERIALIZE_RAW += "`;\n};";
172
+ SERIALIZE_PRETTY += "`;\n};";
173
+ }
174
+
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);
191
+ }
192
+ }
193
+
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";
116
204
  }
117
- this.currentClass.keys.push(name);
118
- // @ts-ignore
119
- this.currentClass.types.push(type);
120
- // @ts-ignore
121
- if (
122
- [
123
- "u8",
124
- "i8",
125
- "u16",
126
- "i16",
127
- "u32",
128
- "i32",
129
- "u64",
130
- "i64",
131
- ].includes(type.toLowerCase())
132
- ) {
133
- if (omitif) {
134
- this.currentClass.encodeStmts.push(
135
- `\${${omitif} ? "" : \`,${encodeKey(aliasName)}:\${this.${name}}\`}`
136
- );
137
- } else {
138
- this.currentClass.encodeStmts.push(
139
- `,${encodeKey(aliasName)}:\${this.${name}}`
140
- );
141
- }
142
- // @ts-ignore
143
- this.currentClass.setDataStmts.push(
144
- `if (key.equals(${JSON.stringify(aliasName)})) {
145
- this.${name} = __atoi_fast<${type}>(data, val_start << 1, val_end << 1);
146
- return;
147
- }`
148
- );
149
- if (member.initializer) {
150
- this.currentClass.initializeStmts.push(
151
- `this.${name} = ${toString(member.initializer)}`
152
- );
153
- }
154
- } else if (
155
- [
156
- "f32",
157
- "f64",
158
- ].includes(type.toLowerCase())
159
- ) {
160
- if (omitif) {
161
- this.currentClass.encodeStmts.push(
162
- `\${${omitif} ? "" : \`,${encodeKey(aliasName)}:\${this.${name}}\`}`
163
- );
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
+ }
226
+ }
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`;
164
239
  } else {
165
- this.currentClass.encodeStmts.push(
166
- `,${encodeKey(aliasName)}:\${this.${name}}`
167
- );
168
- }
169
- // @ts-ignore
170
- this.currentClass.setDataStmts.push(
171
- `if (key.equals(${JSON.stringify(aliasName)})) {
172
- this.${name} = __parseObjectValue<${type}>(data.slice(val_start, val_end), initializeDefaultValues);
173
- return;
174
- }`
175
- );
176
- if (member.initializer) {
177
- this.currentClass.initializeStmts.push(
178
- `this.${name} = ${toString(member.initializer)}`
179
- );
240
+ DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + `else if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`;
180
241
  }
181
242
  } else {
182
- if (omitnull) {
183
- this.currentClass.encodeStmts.push(
184
- `${comma}\${changetype<usize>(this.${name}) !== <usize>0 ? "" : \`${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})}\`}`
185
- );
186
- console.log(`\${changetype<usize>(this.${name}) != <usize>0 ? \`${encodeKey(aliasName)}:,\${__JSON_Stringify<${type}>(this.${name})}\` : ""}`)
187
- } else if (omitif) {
188
- this.currentClass.encodeStmts.push(
189
- `\${${omitif} ? "" : \`${encodeKey(aliasName)}:,\${__JSON_Stringify<${type}>(this.${name})}\`}`
190
- );
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`
191
246
  } else {
192
- this.currentClass.encodeStmts.push(
193
- `,${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})}`
194
- );
195
- }
196
- // @ts-ignore
197
- this.currentClass.setDataStmts.push(
198
- `if (key.equals(${JSON.stringify(aliasName)})) {
199
- this.${name} = __parseObjectValue<${type}>(val_start ? data.slice(val_start, val_end) : data, initializeDefaultValues);
200
- return;
201
- }`
202
- );
203
- if (member.initializer) {
204
- this.currentClass.initializeStmts.push(
205
- `this.${name} = ${toString(member.initializer)}`
206
- );
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`
207
248
  }
208
249
  }
209
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 += " } ";
210
259
  }
211
260
 
212
- let serializeFunc = "";
213
-
214
- if (this.currentClass.encodeStmts.length > 0) {
215
- const stmt =
216
- this.currentClass.encodeStmts[0]!;
217
- this.currentClass.encodeStmts[0] = stmt!.slice(1);
218
- serializeFunc = `
219
- __JSON_Serialize(): string {
220
- return \`{${this.currentClass.encodeStmts.join("")}}\`;
221
- }`;
222
- } else {
223
- serializeFunc = `
224
- __JSON_Serialize(): string {
225
- return "{}";
226
- }`;
227
- }
228
-
229
- const setKeyFunc = `
230
- __JSON_Set_Key(key: __Virtual<string>, data: string, val_start: i32, val_end: i32, initializeDefaultValues: boolean): void {
231
- ${this.currentClass.setDataStmts.join("\n ")}
232
- }
233
- `;
261
+ DESERIALIZE += "\n return false;\n}"
234
262
 
235
- let initializeFunc = "";
263
+ //console.log(sortedMembers);
236
264
 
237
- if (this.currentClass.initializeStmts.length > 0) {
238
- initializeFunc = `
239
- __JSON_Initialize(): void {
240
- ${this.currentClass.initializeStmts.join(";\n")};
241
- }
242
- `;
243
- } else {
244
- initializeFunc = `
245
- __JSON_Initialize(): void {}
246
- `;
265
+ if (process.env["JSON_DEBUG"]) {
266
+ console.log(SERIALIZE_RAW);
267
+ //console.log(SERIALIZE_PRETTY);
268
+ console.log(INITIALIZE);
269
+ console.log(DESERIALIZE);
247
270
  }
248
- const serializeMethod = SimpleParser.parseClassMember(serializeFunc, node);
249
- node.members.push(serializeMethod);
250
-
251
- const setDataMethod = SimpleParser.parseClassMember(setKeyFunc, node);
252
- node.members.push(setDataMethod);
253
271
 
254
- const initializeMethod = SimpleParser.parseClassMember(initializeFunc, node);
255
- node.members.push(initializeMethod);
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);
256
276
 
257
- this.schemasList.push(this.currentClass);
258
- this.sources.add(node.name.range.source);
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);
259
280
 
260
- // Uncomment to see the generated code for debugging.
261
- console.log(serializeFunc);
262
- //console.log(setKeyFunc);
263
- //console.log(initializeFunc);
281
+ this.schemasList.push(schema);
264
282
  }
265
-
266
283
  visitSource(node: Source): void {
267
284
  super.visitSource(node);
268
285
 
@@ -270,35 +287,14 @@ class AsJSONTransform extends BaseVisitor {
270
287
  if (!this.sources.has(node)) {
271
288
  return;
272
289
  }
273
-
274
- // Note, the following one liner would be easier, but it fails with an assertion error
275
- // because as-virtual's SimpleParser doesn't set the parser.currentSource correctly.
276
- //
277
- // const stmt = SimpleParser.parseTopLevelStatement('import { Virtual as __Virtual } from "as-virtual/assembly";');
278
-
279
- // ... So we have to do it the long way:
280
- const txt = 'import { Virtual as __Virtual } from "as-virtual/assembly";'
281
- const tokenizer = new Tokenizer(new Source(SourceKind.User, node.normalizedPath, txt));
282
- const parser = new Parser();
283
- parser.currentSource = tokenizer.source;
284
- const stmt = parser.parseTopLevelStatement(tokenizer)!;
285
-
286
- // Add the import statement to the top of the source.
287
- node.statements.unshift(stmt);
288
290
  }
289
291
  }
290
292
 
291
- function encodeKey(aliasName: string): string {
292
- return JSON.stringify(aliasName)
293
- .replace(/\\/g, "\\\\")
294
- .replace(/\`/g, '\\`');
295
- }
296
-
297
293
  export default class Transformer extends Transform {
298
294
  // Trigger the transform after parse.
299
295
  afterParse(parser: Parser): void {
300
296
  // Create new transform
301
- const transformer = new AsJSONTransform();
297
+ const transformer = new JSONTransform();
302
298
 
303
299
  // Sort the sources so that user scripts are visited last
304
300
  const sources = parser.sources
@@ -326,9 +322,77 @@ export default class Transformer extends Transform {
326
322
  const schemas = transformer.schemasList;
327
323
  for (const schema of schemas) {
328
324
  if (schema.parent) {
329
- const parent = schemas.find((v) => v.name === schema.parent);
325
+ const parent = schemas.find((v) => v.name === schema.parent?.name);
330
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.`);
331
327
  }
332
328
  }
333
329
  }
334
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, "\\\"");
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. */