json-as 0.9.28 → 1.0.0-alpha.1

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 (110) hide show
  1. package/.github/workflows/nodejs.yml +0 -3
  2. package/.prettierrc.json +4 -1
  3. package/CHANGELOG +13 -0
  4. package/LICENSE +1 -1
  5. package/README.md +22 -7
  6. package/as-test.config.json +1 -1
  7. package/asconfig.json +2 -2
  8. package/assembly/__benches__/misc.bench.ts +17 -31
  9. package/assembly/__tests__/bool.spec.ts +1 -1
  10. package/assembly/__tests__/simd/string.spec.ts +32 -0
  11. package/assembly/__tests__/types.ts +17 -0
  12. package/assembly/custom/chars.ts +2 -2
  13. package/assembly/custom/memory.ts +25 -0
  14. package/assembly/custom/types.ts +1 -0
  15. package/assembly/custom/util.ts +59 -140
  16. package/assembly/deserialize/simd/string.ts +103 -0
  17. package/assembly/deserialize/simple/arbitrary.ts +17 -0
  18. package/assembly/deserialize/simple/array/arbitrary.ts +113 -0
  19. package/assembly/deserialize/simple/array/array.ts +18 -0
  20. package/assembly/deserialize/simple/array/bool.ts +17 -0
  21. package/assembly/deserialize/simple/array/float.ts +28 -0
  22. package/assembly/deserialize/simple/array/integer.ts +27 -0
  23. package/assembly/deserialize/simple/array/map.ts +18 -0
  24. package/assembly/deserialize/simple/array/object.ts +18 -0
  25. package/assembly/deserialize/simple/array/string.ts +22 -0
  26. package/assembly/deserialize/simple/array.ts +48 -0
  27. package/assembly/deserialize/simple/bool.ts +9 -0
  28. package/assembly/deserialize/simple/date.ts +11 -0
  29. package/assembly/deserialize/simple/float.ts +10 -0
  30. package/assembly/deserialize/simple/integer.ts +5 -0
  31. package/assembly/deserialize/simple/map.ts +154 -0
  32. package/assembly/deserialize/simple/object.ts +159 -0
  33. package/assembly/deserialize/simple/string.ts +48 -0
  34. package/assembly/globals/tables.ts +417 -0
  35. package/assembly/index.d.ts +9 -13
  36. package/assembly/index.ts +282 -158
  37. package/assembly/serialize/simd/string.ts +176 -0
  38. package/assembly/serialize/simple/arbitrary.ts +36 -0
  39. package/assembly/serialize/simple/array.ts +32 -0
  40. package/assembly/serialize/simple/bool.ts +19 -0
  41. package/assembly/serialize/simple/date.ts +13 -0
  42. package/assembly/serialize/simple/float.ts +7 -0
  43. package/assembly/serialize/simple/integer.ts +7 -0
  44. package/assembly/serialize/simple/map.ts +43 -0
  45. package/assembly/serialize/simple/object.ts +7 -0
  46. package/assembly/serialize/simple/string.ts +48 -0
  47. package/assembly/test.ts +43 -28
  48. package/assembly/tsconfig.json +2 -91
  49. package/assembly/types.ts +0 -0
  50. package/assembly/util/atoi.ts +35 -0
  51. package/assembly/util/bytes.ts +12 -0
  52. package/assembly/util/concat.ts +9 -0
  53. package/assembly/util/getArrayDepth.ts +17 -0
  54. package/assembly/util/index.ts +5 -0
  55. package/assembly/util/isSpace.ts +4 -0
  56. package/assembly/util/nextPowerOf2.ts +4 -0
  57. package/assembly/util/ptrToStr.ts +7 -0
  58. package/assembly/util/snp.ts +69 -0
  59. package/bench.js +5 -5
  60. package/modules/bs/index.ts +167 -0
  61. package/modules/tsconfig.json +8 -0
  62. package/package.json +42 -47
  63. package/transform/lib/builder.js +1353 -0
  64. package/transform/lib/builder.js.map +1 -0
  65. package/transform/lib/index.js +497 -395
  66. package/transform/lib/index.js.map +1 -1
  67. package/transform/lib/index.old.js +404 -0
  68. package/transform/lib/index.old.js.map +1 -0
  69. package/transform/lib/linker.js +18 -0
  70. package/transform/lib/linker.js.map +1 -0
  71. package/transform/lib/types.js +25 -0
  72. package/transform/lib/types.js.map +1 -0
  73. package/transform/lib/util.js +47 -0
  74. package/transform/lib/util.js.map +1 -0
  75. package/transform/lib/visitor.js +529 -446
  76. package/transform/lib/visitor.js.map +1 -0
  77. package/transform/package.json +1 -34
  78. package/transform/src/builder.ts +1371 -0
  79. package/transform/src/index.ts +571 -481
  80. package/transform/src/linker.ts +21 -0
  81. package/transform/src/types.ts +27 -0
  82. package/transform/src/util.ts +56 -0
  83. package/transform/src/visitor.ts +531 -0
  84. package/transform/tsconfig.json +3 -1
  85. package/assembly/__benches__/as-tral.d.ts +0 -1
  86. package/assembly/custom/bs.ts +0 -211
  87. package/assembly/deserialize/array/array.ts +0 -31
  88. package/assembly/deserialize/array/bool.ts +0 -19
  89. package/assembly/deserialize/array/float.ts +0 -24
  90. package/assembly/deserialize/array/integer.ts +0 -24
  91. package/assembly/deserialize/array/map.ts +0 -27
  92. package/assembly/deserialize/array/object.ts +0 -27
  93. package/assembly/deserialize/array/string.ts +0 -29
  94. package/assembly/deserialize/array.ts +0 -46
  95. package/assembly/deserialize/bool.ts +0 -34
  96. package/assembly/deserialize/date.ts +0 -19
  97. package/assembly/deserialize/float.ts +0 -21
  98. package/assembly/deserialize/integer.ts +0 -16
  99. package/assembly/deserialize/map.ts +0 -189
  100. package/assembly/deserialize/object.ts +0 -271
  101. package/assembly/deserialize/string.ts +0 -164
  102. package/assembly/serialize/array.ts +0 -51
  103. package/assembly/serialize/bool.ts +0 -10
  104. package/assembly/serialize/date.ts +0 -4
  105. package/assembly/serialize/float.ts +0 -4
  106. package/assembly/serialize/integer.ts +0 -5
  107. package/assembly/serialize/map.ts +0 -24
  108. package/assembly/serialize/object.ts +0 -13
  109. package/assembly/serialize/string.ts +0 -287
  110. package/logs/test.log.json +0 -1049
@@ -1,424 +1,473 @@
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";
17
-
18
- import { toString, isStdlib } from "visitor-as/dist/utils.js";
19
- import { BaseVisitor, SimpleParser } from "visitor-as/dist/index.js";
1
+ import { ClassDeclaration, FieldDeclaration, IdentifierExpression, Parser, Source, NodeKind, Expression, CommonFlags, StringLiteralExpression, IntegerLiteralExpression, FloatLiteralExpression, NullExpression, TrueExpression, FalseExpression, CallExpression, ImportStatement, NamespaceDeclaration, Node, Statement, Tokenizer, SourceKind, PropertyAccessExpression, Token, CommentHandler, ExpressionStatement, BinaryExpression, NamedTypeNode, Range, FEATURE_SIMD, FunctionExpression } from "assemblyscript/dist/assemblyscript.js";
20
2
  import { Transform } from "assemblyscript/dist/transform.js";
21
-
22
- class JSONTransform extends BaseVisitor {
23
- public schemasList: SchemaData[] = [];
24
- public currentClass!: SchemaData;
3
+ import { Visitor } from "./visitor.js";
4
+ import { SimpleParser, toString } from "./util.js";
5
+ import * as path from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { Property, PropertyFlags, Schema } from "./types.js";
8
+ import { getClasses, getImportedClass } from "./linker.js";
9
+
10
+ let indent = " ";
11
+
12
+ class JSONTransform extends Visitor {
13
+ public parser!: Parser;
14
+ public schemas: Schema[] = [];
15
+ public schema!: Schema;
25
16
  public sources = new Set<Source>();
17
+ public imports: ImportStatement[] = [];
18
+
19
+ public jsonImport: string | null = null;
20
+ public bsImport: string | null = null;
21
+ public newStmts: {
22
+ simd: string[];
23
+ } = { simd: [] };
26
24
 
27
- visitMethodDeclaration(): void { }
28
25
  visitClassDeclaration(node: ClassDeclaration): void {
29
26
  if (!node.decorators?.length) return;
30
27
 
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;
28
+ if (
29
+ !node.decorators.some((decorator) => {
30
+ const name = (<IdentifierExpression>decorator.name).text;
31
+ return name === "json" || name === "serializable";
32
+ })
33
+ )
34
+ return;
40
35
 
41
- const schema = new SchemaData();
42
- schema.node = node;
43
- schema.name = node.name.text;
36
+ this.schema = new Schema();
37
+ this.schema.node = node;
38
+ this.schema.name = node.name.text;
44
39
 
45
- const members = [
46
- ...node.members.filter((v) => v.kind === NodeKind.FieldDeclaration),
47
- ];
40
+ this.schemas.push(this.schema);
41
+ if (process.env["JSON_DEBUG"]) console.log("Created schema: " + this.schema.name);
42
+
43
+ const members: FieldDeclaration[] = [...(node.members.filter((v) => v.kind === NodeKind.FieldDeclaration && v.flags !== CommonFlags.Static && v.flags !== CommonFlags.Private && v.flags !== CommonFlags.Protected && !v.decorators?.some((decorator) => (<IdentifierExpression>decorator.name).text === "omit")) as FieldDeclaration[])];
48
44
 
49
45
  if (node.extendsType) {
50
- schema.parent = this.schemasList.find(
51
- (v) => v.name == node.extendsType?.name.identifier.text,
52
- ) as SchemaData | null;
53
-
54
- if (schema.parent?.members) {
55
- 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
- );
46
+ const extendsName = node.extendsType?.name.identifier.text;
47
+ this.schema.parent = this.schemas.find((v) => v.name == extendsName) as Schema | null;
48
+ if (!this.schema.parent) {
49
+ const internalSearch = getClasses(node.range.source).find((v) => v.name.text == extendsName);
50
+ if (internalSearch) {
51
+ if (process.env["JSON_DEBUG"]) console.log("Found " + extendsName + " internally");
52
+ this.visitClassDeclaration(internalSearch);
53
+ this.visitClassDeclaration(node);
54
+ return;
55
+ }
56
+
57
+ const externalSearch = getImportedClass(extendsName, node.range.source, this.parser);
58
+ if (externalSearch) {
59
+ if (process.env["JSON_DEBUG"]) console.log("Found " + extendsName + " externally");
60
+ this.visitClassDeclaration(externalSearch);
61
+ this.visitClassDeclaration(node);
62
+ return;
63
+ }
64
+ }
65
+ if (this.schema.parent?.members) {
66
+ for (let i = this.schema.parent.members.length - 1; i >= 0; i--) {
67
+ const replace = this.schema.members.find((v) => v.name == this.schema.parent?.members[i]?.name);
59
68
  if (!replace) {
60
- members.unshift(schema.parent?.members[i]!.node);
69
+ members.unshift(this.schema.parent?.members[i]!.node);
61
70
  }
62
71
  }
63
72
  }
64
73
  }
65
74
 
66
75
  if (!members.length) {
67
- let SERIALIZE_RAW_EMPTY = '__SERIALIZE(): string {\n return "{}";\n}';
68
- //let SERIALIZE_PRETTY_EMPTY = "__SERIALIZE_PRETTY(): string {\n return \"{}\";\n}";
69
-
70
- let INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}";
71
-
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
-
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
- }
81
-
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);
76
+ this.generateEmptyMethods(node);
77
+ return;
104
78
  }
105
79
 
80
+ this.addRequiredImports(node);
81
+
106
82
  for (const member of members) {
107
- const name = member.name;
108
- if (!(member instanceof FieldDeclaration)) continue;
109
- 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
- );
116
- }
83
+ if (!member.type) throwError("Fields must be strongly typed", node.range);
84
+
117
85
  const type = toString(member.type!);
118
- if (type.startsWith("(") && type.includes("=>")) continue;
86
+ const name = member.name;
119
87
  const value = member.initializer ? toString(member.initializer!) : null;
120
88
 
121
- if (member.flags == CommonFlags.Static) continue;
122
- if (member.flags === CommonFlags.Private) continue;
123
- if (member.flags === CommonFlags.Protected) continue;
89
+ if (type.startsWith("(") && type.includes("=>")) continue;
124
90
 
125
91
  const mem = new Property();
126
92
  mem.name = name.text;
127
93
  mem.type = type;
128
94
  mem.value = value;
129
95
  mem.node = member;
96
+ mem.byteSize = sizeof(mem.type);
130
97
 
131
- if (type.includes("JSON.Raw")) {
132
- mem.flags.set(PropertyFlags.JSON_Raw, []);
133
- }
98
+ this.schema.byteSize += mem.byteSize;
134
99
 
135
- if (member.type.isNullable) {
136
- mem.flags.set(PropertyFlags.Null, []);
137
- }
100
+ if (type.includes("JSON.Raw")) mem.flags.set(PropertyFlags.Raw, null);
138
101
 
139
102
  if (member.decorators) {
140
103
  for (const decorator of member.decorators) {
141
- const decoratorName = (decorator.name as IdentifierExpression).text;
142
-
143
- const args = getArgs(decorator.args);
144
-
104
+ const decoratorName = (decorator.name as IdentifierExpression).text.toLowerCase().trim();
145
105
  switch (decoratorName) {
146
106
  case "alias": {
147
- if (!args.length)
148
- throw new Error(
149
- "Expected 1 argument but got zero at @alias in " +
150
- node.range.source.normalizedPath,
151
- );
107
+ const args = getArgs(decorator.args);
108
+ if (!args.length) throwError("@alias must have an argument of type string or number", member.range);
152
109
  mem.alias = args[0]!;
153
- mem.flags.set(PropertyFlags.Alias, args);
154
- break;
155
- }
156
- case "omit": {
157
- mem.flags.set(PropertyFlags.Omit, args);
158
110
  break;
159
111
  }
160
112
  case "omitif": {
161
- if (!decorator.args?.length)
162
- throw new Error(
163
- "Expected 1 argument but got zero at @omitif in " +
164
- node.range.source.normalizedPath,
165
- );
166
- mem.flags.set(PropertyFlags.OmitIf, args);
113
+ let arg = decorator.args[0];
114
+ if (!decorator.args?.length) throwError("@omitif must have an argument or callback that resolves to type bool", member.range);
115
+ mem.flags.set(PropertyFlags.OmitIf, arg);
116
+ this.schema.static = false;
167
117
  break;
168
118
  }
169
119
  case "omitnull": {
170
- mem.flags.set(PropertyFlags.OmitNull, args);
120
+ if (isPrimitive(type)) {
121
+ throwError("@omitnull cannot be used on primitive types!", member.range);
122
+ } else if (!member.type.isNullable) {
123
+ throwError("@omitnull cannot be used on non-nullable types!", member.range);
124
+ }
125
+ mem.flags.set(PropertyFlags.OmitNull, null);
126
+ this.schema.static = false;
171
127
  break;
172
128
  }
173
129
  }
174
130
  }
175
131
  }
176
132
 
177
- mem.generate(false);
178
-
179
- if (this.schemasList.find((v) => v.name == type)) {
180
- mem.initialize =
181
- "this." +
182
- name.text +
183
- " = changetype<nonnull<" +
184
- mem.type +
185
- ">>(__new(offsetof<nonnull<" +
186
- mem.type +
187
- ">>(), idof<nonnull<" +
188
- mem.type +
189
- ">>()));\n changetype<nonnull<" +
190
- mem.type +
191
- ">>(this." +
192
- name.text +
193
- ").__INITIALIZE()";
194
- } else if (mem.value) {
195
- mem.initialize = "this." + name.text + " = " + mem.value;
196
- } else if (type === "Map") {
197
- mem.initialize = "this." + name.text + " = new " + mem.type + "()";
198
- } else if (type === "string") {
199
- mem.initialize = "this." + name.text + ' = ""';
200
- } else if (type === "Array") {
201
- mem.initialize =
202
- "this." + name.text + " = instantiate<" + mem.type + ">()";
203
- } else if (type === "bool" || type === "boolean") {
204
- mem.initialize = "this." + name.text + " = false";
205
- } else if (type === "JSON.Raw") {
206
- mem.initialize = "this." + name.text + ' = ""';
207
- } else if (
208
- type === "u8" ||
209
- type === "u16" ||
210
- type === "u32" ||
211
- type === "u64" ||
212
- type === "i8" ||
213
- type === "i16" ||
214
- type === "i32" ||
215
- type === "i64"
216
- ) {
217
- mem.initialize = "this." + name.text + " = 0";
218
- } else if (type === "f32" || type === "f64") {
219
- mem.initialize = "this." + name.text + " = 0.0";
220
- }
221
-
222
- schema.members.push(mem);
133
+ this.schema.members.push(mem);
223
134
  }
224
135
 
225
- let SERIALIZE_RAW = "__SERIALIZE(): string {\n let out = `{";
226
- let SERIALIZE_PRETTY = "__SERIALIZE_PRETTY(): string {\n let out = `{";
227
-
228
- let INITIALIZE = "__INITIALIZE(): this {\n";
136
+ if (!this.schema.static) this.schema.members = sortMembers(this.schema.members);
229
137
 
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";
232
- let indent = " ";
138
+ let SERIALIZE = "__SERIALIZE(ptr: usize): void {\n";
139
+ let INITIALIZE = "@inline __INITIALIZE(): this {\n";
140
+ let DESERIALIZE = "__DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {\n switch (keyEnd - keyStart) {\n";
141
+ let ALLOCATE = "@inline __ALLOCATE(): void {\n";
233
142
 
234
- if (!schema.members.length) return;
143
+ indent = " ";
235
144
 
236
- found = false;
237
-
238
- if (
239
- schema.members[0]?.flags.has(PropertyFlags.OmitNull) ||
240
- schema.members[0]?.flags.has(PropertyFlags.OmitIf)
241
- ) {
242
- SERIALIZE_RAW += schema.members[0]?.serialize;
243
- SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize;
244
- } else {
245
- SERIALIZE_RAW += schema.members[0]?.serialize + ",";
246
- SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize + ",\\n";
247
- found = true;
145
+ if (this.schema.static == false) {
146
+ if (this.schema.members.some((v) => v.flags.has(PropertyFlags.OmitNull))) {
147
+ SERIALIZE += indent + "let block: usize = 0;\n";
148
+ }
149
+ this.schema.byteSize += 2;
150
+ SERIALIZE += indent + "store<u16>(bs.offset, 123, 0); // {\n";
151
+ SERIALIZE += indent + "bs.offset += 2;\n";
248
152
  }
249
153
 
250
- if (schema.members[0]?.initialize)
251
- INITIALIZE += " " + schema.members[0]?.initialize + ";\n";
252
-
253
- for (let i = 1; i < schema.members.length; i++) {
254
- const member = schema.members[i]!;
255
- if (member.initialize) INITIALIZE += " " + member.initialize + ";\n";
256
- if (
257
- member.flags.has(PropertyFlags.OmitNull) ||
258
- member.flags.has(PropertyFlags.OmitIf)
259
- ) {
260
- SERIALIZE_RAW += member.serialize;
261
- SERIALIZE_PRETTY += member.serialize;
262
- } else {
263
- SERIALIZE_RAW += member.serialize + ",";
264
- SERIALIZE_PRETTY += indent + member.serialize + ",\\n";
265
- found = true;
154
+ let isPure = this.schema.static;
155
+ let isRegular = isPure;
156
+ let isFirst = true;
157
+
158
+ for (let i = 0; i < this.schema.members.length; i++) {
159
+ const member = this.schema.members[i];
160
+ const aliasName = JSON.stringify(member.alias || member.name);
161
+ const realName = member.name;
162
+ const isLast = i == this.schema.members.length - 1;
163
+ const nonNullType = member.type.replace(" | null", "");
164
+
165
+ if (member.value) {
166
+ INITIALIZE += ` this.${member.name} = ${member.value};\n`;
167
+ } else if (this.schemas.find((v) => nonNullType == v.name)) {
168
+ INITIALIZE += ` this.${member.name} = changetype<nonnull<${member.type}>>(__new(offsetof<nonnull<${member.type}>>(), idof<nonnull<${member.type}>>())).__INITIALIZE();\n`;
169
+ } else if (member.type.startsWith("Array<") || member.type.startsWith("Map<")) {
170
+ INITIALIZE += ` this.${member.name} = [];\n`;
171
+ } else if (member.type == "string" || member.type == "String" || member.type == "JSON.Raw") {
172
+ INITIALIZE += ` this.${member.name} = "";\n`;
266
173
  }
267
- }
268
174
 
269
- if (found) {
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}";
274
- } else {
275
- SERIALIZE_RAW += "}`;\n return out;\n}";
276
- SERIALIZE_PRETTY += "}`;\n return out;\n}";
277
- }
175
+ if (!isRegular && !member.flags.has(PropertyFlags.OmitIf) && !member.flags.has(PropertyFlags.OmitNull)) isRegular = true;
176
+ if (isRegular && isPure) {
177
+ const keyPart = (isFirst ? "{" : ",") + aliasName + ":";
178
+ this.schema.byteSize += keyPart.length << 1;
179
+ SERIALIZE += this.getStores(keyPart)
180
+ .map((v) => indent + v + "\n")
181
+ .join("");
182
+ SERIALIZE += indent + `JSON.__serialize<${member.type}>(load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
183
+ if (isFirst) isFirst = false;
184
+ } else if (isRegular && !isPure) {
185
+ const keyPart = (isFirst ? "" : ",") + aliasName + ":";
186
+ this.schema.byteSize += keyPart.length << 1;
187
+ SERIALIZE += this.getStores(keyPart)
188
+ .map((v) => indent + v + "\n")
189
+ .join("");
190
+ SERIALIZE += indent + `JSON.__serialize<${member.type}>(load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
191
+ if (isFirst) isFirst = false;
192
+ } else {
193
+ if (member.flags.has(PropertyFlags.OmitNull)) {
194
+ SERIALIZE += indent + `if ((block = load<usize>(ptr, offsetof<this>(${JSON.stringify(realName)}))) !== 0) {\n`;
195
+ indentInc();
196
+ const keyPart = aliasName + ":";
197
+ this.schema.byteSize += keyPart.length << 1;
198
+ SERIALIZE += this.getStores(keyPart)
199
+ .map((v) => indent + v + "\n")
200
+ .join("");
201
+ SERIALIZE += indent + `JSON.__serialize<${member.type}>(load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
202
+
203
+ if (!isLast) {
204
+ this.schema.byteSize += 2;
205
+ SERIALIZE += indent + `store<u16>(bs.offset, 44, 0); // ,\n`;
206
+ SERIALIZE += indent + `bs.offset += 2;\n`;
207
+ }
278
208
 
279
- INITIALIZE += " return this;\n}";
209
+ indentDec();
210
+ this.schema.byteSize += 2;
211
+ SERIALIZE += indent + `}\n`;
212
+ } else if (member.flags.has(PropertyFlags.OmitIf)) {
213
+ if (member.flags.get(PropertyFlags.OmitIf).kind == NodeKind.Function) {
214
+ const arg = member.flags.get(PropertyFlags.OmitIf) as FunctionExpression;
215
+ // @ts-ignore: type
216
+ arg.declaration.signature.returnType.name = Node.createSimpleTypeName("boolean", arg.declaration.signature.returnType.name.range);
217
+ SERIALIZE += indent + `if ((${toString(member.flags.get(PropertyFlags.OmitIf))})(this)) {\n`;
218
+ } else {
219
+ SERIALIZE += indent + `if (${toString(member.flags.get(PropertyFlags.OmitIf))}) {\n`;
220
+ }
221
+ indentInc();
222
+ SERIALIZE += this.getStores(aliasName + ":")
223
+ .map((v) => indent + v + "\n")
224
+ .join("");
225
+ SERIALIZE += indent + `JSON.__serialize<${member.type}>(load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
226
+
227
+ if (!isLast) {
228
+ this.schema.byteSize += 2;
229
+ SERIALIZE += indent + `store<u16>(bs.offset, 44, 0); // ,\n`;
230
+ SERIALIZE += indent + `bs.offset += 2;\n`;
231
+ }
280
232
 
281
- const sortedMembers: Property[][] = [];
282
- const _sorted = schema.members.sort(
283
- (a, b) => (a.alias?.length! || a.name.length) - (b.alias?.length! || b.name.length),
284
- );
285
- let len = -1;
286
- let offset = -1;
287
- for (let i = 0; i < _sorted.length; i++) {
288
- const member = _sorted[i]!;
289
- const _name = member.alias || member.name;
290
- if (_name.length === len) {
291
- sortedMembers[offset]?.push(member);
292
- } else {
293
- sortedMembers.push([member]);
294
- len = _name.length;
295
- offset++;
233
+ indentDec();
234
+ SERIALIZE += indent + `}\n`;
235
+ }
296
236
  }
297
237
  }
298
238
 
299
- let first = true;
300
- for (const memberSet of sortedMembers) {
301
- const firstMember = memberSet[0]!;
302
- const _name = encodeKey(firstMember.alias || firstMember.name);
303
- if (_name.length === 1) {
304
- if (first) {
305
- DESERIALIZE +=
306
- " if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
307
- first = false;
308
- } else {
309
- DESERIALIZE +=
310
- "else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
311
- }
312
- } else if (_name.length === 2) {
313
- if (first) {
314
- DESERIALIZE +=
315
- " if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
316
- first = false;
317
- } else {
318
- DESERIALIZE +=
319
- "else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
320
- }
321
- } else if (_name.length === 4) {
322
- if (first) {
323
- DESERIALIZE +=
324
- " if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
325
- first = false;
239
+ let sortedMembers: Property[][] = [];
240
+
241
+ let len = -1;
242
+ this.schema.members
243
+ .slice()
244
+ .sort((a, b) => (a.alias?.length || a.name.length) - (b.alias?.length || b.name.length))
245
+ .forEach((member) => {
246
+ const _nameLength = member.alias?.length || member.name.length;
247
+ if (_nameLength === len) {
248
+ sortedMembers[sortedMembers.length - 1].push(member);
326
249
  } else {
327
- DESERIALIZE +=
328
- "else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
250
+ sortedMembers.push([member]);
251
+ len = _nameLength;
329
252
  }
330
- } else {
331
- if (first) {
332
- DESERIALIZE += " if (" + _name.length + " === len) {\n";
333
- first = false;
253
+ });
254
+
255
+ sortedMembers = sortedMembers.sort((a, b) => b.length - a.length);
256
+
257
+ indentInc();
258
+ for (const memberGroup of sortedMembers) {
259
+ const memberLen = (memberGroup[0].alias || memberGroup[0].name).length << 1;
260
+ DESERIALIZE += `${indent}case ${memberLen}: {\n`;
261
+ indentInc();
262
+ if (memberLen == 2) DESERIALIZE += `${indent}switch (load<u16>(keyStart)) {\n`;
263
+ else if (memberLen == 4) DESERIALIZE += `${indent}switch (load<u32>(keyStart)) {\n`;
264
+ else if (memberLen == 6) DESERIALIZE += `${indent}let code = (<u64>load<u32>(keyStart) << 16) | <u64>load<u16>(keyStart, 4);\n`;
265
+ else DESERIALIZE += toMemCDecl(memberLen, indent);
266
+ for (let i = 0; i < memberGroup.length; i++) {
267
+ const member = memberGroup[i];
268
+ const memberName = member.alias || member.name;
269
+ if (memberLen == 2) {
270
+ DESERIALIZE += `${indent} case ${memberName.charCodeAt(0)}: { // ${memberName}\n`;
271
+ DESERIALIZE += `${indent} store<${member.type}>(ptr, JSON.__deserialize<${member.type}>(valStart, valEnd), offsetof<this>(${JSON.stringify(member.name)}));\n`;
272
+ DESERIALIZE += `${indent} return;\n`;
273
+ DESERIALIZE += `${indent} }\n`;
274
+ } else if (memberLen == 4) {
275
+ DESERIALIZE += `${indent} case ${toU32(memberName)}: { // ${memberName}\n`;
276
+ DESERIALIZE += `${indent} store<${member.type}>(ptr, JSON.__deserialize<${member.type}>(valStart, valEnd), offsetof<this>(${JSON.stringify(member.name)}));\n`;
277
+ DESERIALIZE += `${indent} return;\n`;
278
+ DESERIALIZE += `${indent} }\n`;
279
+ } else if (memberLen == 6) {
280
+ DESERIALIZE += i == 0 ? indent : "";
281
+ DESERIALIZE += `if (code == ${toU48(memberName)}) { // ${memberName}\n`;
282
+ DESERIALIZE += `${indent} store<${member.type}>(ptr, JSON.__deserialize<${member.type}>(valStart, valEnd), offsetof<this>(${JSON.stringify(member.name)}));\n`;
283
+ DESERIALIZE += `${indent} return;\n`;
284
+ DESERIALIZE += `${indent}}${i < memberGroup.length - 1 ? " else " : "\n"}`;
334
285
  } else {
335
- DESERIALIZE += "else if (" + _name.length + " === len) {\n";
286
+ DESERIALIZE += i == 0 ? indent : "";
287
+ DESERIALIZE += `if (${toMemCCheck(memberName)}) { // ${memberName}\n`;
288
+ DESERIALIZE += `${indent} store<${member.type}>(ptr, JSON.__deserialize<${member.type}>(valStart, valEnd), offsetof<this>(${JSON.stringify(member.name)}));\n`;
289
+ DESERIALIZE += `${indent} return;\n`;
290
+ DESERIALIZE += `${indent}}${i < memberGroup.length - 1 ? " else " : "\n"}`;
336
291
  }
337
292
  }
338
- let f = true;
339
- for (let i = 0; i < memberSet.length; i++) {
340
- const member = memberSet[i]!;
341
- if (!member.deserialize) continue;
342
- const _name = encodeKey(member.alias || member.name);
343
- if (_name.length === 1) {
344
- DESERIALIZE += ` case ${_name.charCodeAt(0)}: { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
345
- } else if (_name.length === 2) {
346
- DESERIALIZE += ` case ${charCodeAt32(_name, 0)}: { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
347
- } else if (_name.length === 4) {
348
- if (f) {
349
- f = false;
350
- DESERIALIZE += ` if (${charCodeAt64(_name, 0)} === code) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
351
- } else {
352
- DESERIALIZE =
353
- DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
354
- `else if (${charCodeAt64(_name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`;
355
- }
356
- } else {
357
- if (f) {
358
- f = false;
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`;
360
- } else {
361
- DESERIALIZE =
362
- DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
363
- ` else if (0 === memory.compare(changetype<usize>("${escapeQuote(escapeSlash(_name))}"), changetype<usize>(data) + (key_start << 1), ${_name.length << 1})) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
364
- }
365
- }
293
+ if (memberLen < 6) {
294
+ DESERIALIZE += `${indent}}\n`; // Close switch
366
295
  }
367
- if (_name.length < 3) {
368
- DESERIALIZE += ` default: {\n return false;\n }\n }\n`;
369
- } else if (_name.length == 4) {
370
- DESERIALIZE =
371
- DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
372
- ` else {\n return false;\n }\n`;
373
- } else {
374
- DESERIALIZE =
375
- DESERIALIZE.slice(0, DESERIALIZE.length - 1) +
376
- ` else {\n return false;\n }\n`;
377
- }
378
- DESERIALIZE += " } ";
296
+ indentDec();
297
+ DESERIALIZE += `${indent} return;\n`; // Break switch
298
+ DESERIALIZE += `${indent}}\n`; // Close length switch
379
299
  }
380
300
 
381
- DESERIALIZE += "\n return false;\n}";
301
+ indentDec();
302
+ DESERIALIZE += `${indent}}\n`; // Close length switch
303
+ indentDec();
304
+ DESERIALIZE += `${indent}}\n`; // Close function
305
+
306
+ indent = " ";
307
+
308
+ this.schema.byteSize += 2;
309
+ SERIALIZE += indent + "store<u16>(bs.offset, 125, 0); // }\n";
310
+ SERIALIZE += indent + "bs.offset += 2;\n";
311
+ SERIALIZE += "}";
382
312
 
383
- //console.log(sortedMembers);
313
+ ALLOCATE += indent + "bs.ensureSize(" + this.schema.byteSize + ");\n";
314
+ ALLOCATE += "}";
315
+
316
+ INITIALIZE += " return this;\n";
317
+ INITIALIZE += "}";
384
318
 
385
319
  if (process.env["JSON_DEBUG"]) {
386
- console.log(SERIALIZE_RAW);
387
- //console.log(SERIALIZE_PRETTY);
320
+ console.log(SERIALIZE);
388
321
  console.log(INITIALIZE);
389
322
  console.log(DESERIALIZE);
323
+ console.log(ALLOCATE);
390
324
  }
391
325
 
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);
326
+ const SERIALIZE_METHOD = SimpleParser.parseClassMember(SERIALIZE, node);
399
327
  const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node);
400
328
  const DESERIALIZE_METHOD = SimpleParser.parseClassMember(DESERIALIZE, node);
401
- const DESERIALIZE_SAFE_METHOD = SimpleParser.parseClassMember(DESERIALIZE_SAFE, node);
402
-
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);
411
-
412
- this.schemasList.push(schema);
329
+ const ALLOCATE_METHOD = SimpleParser.parseClassMember(ALLOCATE, node);
330
+
331
+ if (!node.members.find((v) => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_METHOD);
332
+ if (!node.members.find((v) => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD);
333
+ if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD);
334
+ if (!node.members.find((v) => v.name.text == "__ALLOCATE")) node.members.push(ALLOCATE_METHOD);
335
+ super.visitClassDeclaration(node);
336
+ }
337
+ generateEmptyMethods(node: ClassDeclaration): void {
338
+ let SERIALIZE_RAW_EMPTY = '@inline __SERIALIZE(ptr: usize = changetype<usize>(this)): string {\n return "{}";\n}';
339
+ let SERIALIZE_BS_EMPTY = "@inline __SERIALIZE(ptr: usize: bool): void {\n store<u32>(bs.offset, 8192123);\n bs.offset += 4;\n}";
340
+ let INITIALIZE_EMPTY = "@inline __INITIALIZE(): this {\n return this;\n}";
341
+ let DESERIALIZE_EMPTY = "@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {\n return false;\n}";
342
+ let ALLOCATE_EMPTY = "@inline __ALLOCATE(): void {\n bs.ensureSize(4);\n}";
343
+
344
+ if (process.env["JSON_DEBUG"]) {
345
+ console.log(SERIALIZE_RAW_EMPTY);
346
+ console.log(SERIALIZE_BS_EMPTY);
347
+ console.log(INITIALIZE_EMPTY);
348
+ console.log(DESERIALIZE_EMPTY);
349
+ console.log(ALLOCATE_EMPTY);
350
+ }
351
+
352
+ const SERIALIZE_RAW_METHOD_EMPTY = SimpleParser.parseClassMember(SERIALIZE_RAW_EMPTY, node);
353
+ const SERIALIZE_BS_METHOD_EMPTY = SimpleParser.parseClassMember(SERIALIZE_BS_EMPTY, node);
354
+ const INITIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(INITIALIZE_EMPTY, node);
355
+ const DESERIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(DESERIALIZE_EMPTY, node);
356
+ const ALLOCATE_METHOD_EMPTY = SimpleParser.parseClassMember(ALLOCATE_EMPTY, node);
357
+
358
+ if (!node.members.find((v) => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_RAW_METHOD_EMPTY);
359
+ if (!node.members.find((v) => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_BS_METHOD_EMPTY);
360
+ if (!node.members.find((v) => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD_EMPTY);
361
+ if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD_EMPTY);
362
+ if (!node.members.find((v) => v.name.text == "__ALLOCATE")) node.members.push(ALLOCATE_METHOD_EMPTY);
363
+ }
364
+ // visitCallExpression(node: CallExpression, ref: Node): void {
365
+ // super.visitCallExpression(node, ref);
366
+ // if (!(node.expression.kind == NodeKind.PropertyAccess && (node.expression as PropertyAccessExpression).property.text == "stringifyTo") && !(node.expression.kind == NodeKind.Identifier && (node.expression as IdentifierExpression).text == "stringifyTo")) return;
367
+
368
+ // const source = node.range.source;
369
+
370
+ // if (ref.kind == NodeKind.Call) {
371
+ // const newNode = Node.createBinaryExpression(Token.Equals, node.args[1], node, node.range);
372
+
373
+ // (<CallExpression>ref).args[(<CallExpression>ref).args.indexOf(node)] = newNode;
374
+ // } else {
375
+ // const newNode = Node.createExpressionStatement(Node.createBinaryExpression(Token.Equals, node.args[1], node, node.range));
376
+
377
+ // const nodeIndex = source.statements.findIndex((n: Node) => {
378
+ // if (n == node) return true;
379
+ // if (n.kind == NodeKind.Expression && (<ExpressionStatement>n).expression == node) return true;
380
+ // return false;
381
+ // });
382
+
383
+ // if (nodeIndex > 0) source.statements[nodeIndex] = newNode;
384
+ // }
385
+ // }
386
+ // visitBinaryExpression(node: BinaryExpression, ref?: Node | null): void {
387
+ // // if (node.right.kind == NodeKind.Call && (<CallExpression>node).)
388
+ // }
389
+ visitImportStatement(node: ImportStatement): void {
390
+ super.visitImportStatement(node);
391
+ const source = this.parser.sources.find((src) => src.internalPath == node.internalPath);
392
+ if (!source) return;
393
+
394
+ if (source.statements.some((stmt) => stmt.kind === NodeKind.NamespaceDeclaration && (stmt as NamespaceDeclaration).name.text === "JSON")) this.imports.push(node);
413
395
  }
414
396
  visitSource(node: Source): void {
397
+ this.imports = [];
415
398
  super.visitSource(node);
399
+ }
400
+ addRequiredImports(node: ClassDeclaration): void {
401
+ // if (!this.imports.find((i) => i.declarations.find((d) => d.foreignName.text == "bs"))) {
402
+ // if (!this.bsImport) {
403
+ // this.bsImport = 'import { bs } from "as-bs"';
404
+ // if (process.env["JSON_DEBUG"]) console.log("Added as-bs import: " + this.bsImport + "\n");
405
+ // }
406
+ // }
407
+ if (!this.imports.find((i) => i.declarations.find((d) => d.foreignName.text == "bs"))) {
408
+ const __filename = fileURLToPath(import.meta.url);
409
+ const __dirname = path.dirname(__filename);
410
+
411
+ let relativePath = path.relative(path.dirname(node.range.source.normalizedPath), path.resolve(__dirname, "../../modules/bs/index.ts"));
412
+
413
+ if (!relativePath.startsWith(".") && !relativePath.startsWith("/")) relativePath = "./" + relativePath;
414
+ // if (!existsSync(relativePath)) {
415
+ // throw new Error("Could not find a valid json-as library to import from! Please add import { JSON } from \"path-to-json-as\"; in " + node.range.source.normalizedPath + "!");
416
+ // }
417
+
418
+ const txt = `import { bs } from "${relativePath}";`;
419
+ if (!this.jsonImport) {
420
+ this.jsonImport = txt;
421
+ if (process.env["JSON_DEBUG"]) console.log("Added bs import: " + txt + "\n");
422
+ }
423
+ }
416
424
 
417
- // Only add the import statement to sources that have JSON decorated classes.
418
- if (!this.sources.has(node)) {
419
- return;
425
+ if (!this.imports.find((i) => i.declarations.find((d) => d.foreignName.text == "JSON"))) {
426
+ const __filename = fileURLToPath(import.meta.url);
427
+ const __dirname = path.dirname(__filename);
428
+
429
+ let relativePath = path.relative(path.dirname(node.range.source.normalizedPath), path.resolve(__dirname, "../../assembly/index.ts"));
430
+
431
+ if (!relativePath.startsWith(".") && !relativePath.startsWith("/")) relativePath = "./" + relativePath;
432
+ // if (!existsSync(relativePath)) {
433
+ // throw new Error("Could not find a valid json-as library to import from! Please add import { JSON } from \"path-to-json-as\"; in " + node.range.source.normalizedPath + "!");
434
+ // }
435
+
436
+ const txt = `import { JSON } from "${relativePath}";`;
437
+ if (!this.jsonImport) {
438
+ this.jsonImport = txt;
439
+ if (process.env["JSON_DEBUG"]) console.log("Added json-as import: " + txt + "\n");
440
+ }
420
441
  }
421
442
  }
443
+
444
+ getStores(data: string, simd: boolean = true): string[] {
445
+ const out: string[] = [];
446
+ const sizes = strToNum(data, simd);
447
+ let offset = 0;
448
+ for (const [size, num] of sizes) {
449
+ if (size == "v128") {
450
+ // This could be put in its own file
451
+ let index = this.newStmts.simd.findIndex((v) => v.includes(num));
452
+ let name = "SIMD_" + (index == -1 ? this.newStmts.simd.length : index);
453
+ if (index && !this.newStmts.simd.includes(`const ${name} = ${num};`)) this.newStmts.simd.push(`const ${name} = ${num};`);
454
+ out.push("store<v128>(bs.offset, " + name + ", " + offset + "); // " + data.slice(offset >> 1, (offset >> 1) + 8));
455
+ offset += 16;
456
+ }
457
+ if (size == "u64") {
458
+ out.push("store<u64>(bs.offset, " + num + ", " + offset + "); // " + data.slice(offset >> 1, (offset >> 1) + 4));
459
+ offset += 8;
460
+ } else if (size == "u32") {
461
+ out.push("store<u32>(bs.offset, " + num + ", " + offset + "); // " + data.slice(offset >> 1, (offset >> 1) + 2));
462
+ offset += 4;
463
+ } else if (size == "u16") {
464
+ out.push("store<u16>(bs.offset, " + num + ", " + offset + "); // " + data.slice(offset >> 1, (offset >> 1) + 1));
465
+ offset += 2;
466
+ }
467
+ }
468
+ out.push("bs.offset += " + offset + ";");
469
+ return out;
470
+ }
422
471
  }
423
472
 
424
473
  export default class Transformer extends Transform {
@@ -428,169 +477,74 @@ export default class Transformer extends Transform {
428
477
  const transformer = new JSONTransform();
429
478
 
430
479
  // Sort the sources so that user scripts are visited last
431
- const sources = parser.sources
432
- .filter((source) => !isStdlib(source))
433
- .sort((_a, _b) => {
434
- const a = _a.internalPath;
435
- const b = _b.internalPath;
436
- if (a[0] === "~" && b[0] !== "~") {
437
- return -1;
438
- } else if (a[0] !== "~" && b[0] === "~") {
439
- return 1;
440
- } else {
441
- return 0;
442
- }
443
- });
480
+ const sources = parser.sources.sort((_a, _b) => {
481
+ const a = _a.internalPath;
482
+ const b = _b.internalPath;
483
+ if (a[0] == "~" && b[0] !== "~") {
484
+ return -1;
485
+ } else if (a[0] !== "~" && b[0] == "~") {
486
+ return 1;
487
+ } else {
488
+ return 0;
489
+ }
490
+ });
444
491
 
492
+ transformer.parser = parser;
445
493
  // Loop over every source
446
494
  for (const source of sources) {
495
+ transformer.imports = [];
496
+ transformer.currentSource = source;
447
497
  // Ignore all lib and std. Visit everything else.
448
- if (!isStdlib(source)) {
449
- transformer.visit(source);
498
+ transformer.visit(source);
499
+
500
+ if (transformer.newStmts.simd) {
501
+ const tokenizer = new Tokenizer(new Source(SourceKind.User, source.normalizedPath, transformer.newStmts.simd.join("\n")));
502
+ parser.currentSource = tokenizer.source;
503
+ for (let i = 0; i < transformer.newStmts.simd.length; i++) source.statements.unshift(parser.parseTopLevelStatement(tokenizer)!);
504
+ parser.currentSource = source;
505
+ transformer.newStmts.simd = [];
506
+ }
507
+
508
+ if (transformer.jsonImport) {
509
+ const tokenizer = new Tokenizer(new Source(SourceKind.User, source.normalizedPath, transformer.jsonImport));
510
+ parser.currentSource = tokenizer.source;
511
+ source.statements.unshift(parser.parseTopLevelStatement(tokenizer)!);
512
+ parser.currentSource = source;
513
+ transformer.jsonImport = null;
514
+ }
515
+
516
+ if (transformer.bsImport) {
517
+ const tokenizer = new Tokenizer(new Source(SourceKind.User, source.normalizedPath, transformer.bsImport));
518
+ parser.currentSource = tokenizer.source;
519
+ source.statements.unshift(parser.parseTopLevelStatement(tokenizer)!);
520
+ parser.currentSource = source;
521
+ transformer.bsImport = null;
450
522
  }
451
523
  }
452
524
  // Check that every parent and child class is hooked up correctly
453
- const schemas = transformer.schemasList;
525
+ const schemas = transformer.schemas;
454
526
  for (const schema of schemas) {
455
527
  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
- );
528
+ const parent = schemas.find((v) => v.name == schema.parent?.name);
529
+ if (!parent) throwError(`Class ${schema.name} extends its parent class ${schema.parent}, but ${schema.parent} does not include a @json or @serializable decorator!`, schema.parent.node.range);
461
530
  }
462
531
  }
463
532
  }
464
533
  }
465
534
 
466
- enum PropertyFlags {
467
- Null,
468
- Omit,
469
- OmitNull,
470
- OmitIf,
471
- Alias,
472
- JSON_Raw,
473
- }
535
+ function sortMembers(members: Property[]): Property[] {
536
+ return members.sort((a, b) => {
537
+ const aMove = a.flags.has(PropertyFlags.OmitIf) || a.flags.has(PropertyFlags.OmitNull);
538
+ const bMove = b.flags.has(PropertyFlags.OmitIf) || b.flags.has(PropertyFlags.OmitNull);
474
539
 
475
- class Property {
476
- public name: string = "";
477
- public alias: string | null = null;
478
- public type: string = "";
479
- public value: string | null = null;
480
- public flags: Map<PropertyFlags, string[]> = new Map<
481
- PropertyFlags,
482
- string[]
483
- >();
484
-
485
- public serialize: string | null = null;
486
- public deserialize: string | null = null;
487
- public initialize: string | null = null;
488
-
489
- public node!: FieldDeclaration;
490
-
491
- private right_s: string = "";
492
- private right_d: string = "";
493
-
494
- public generate(safe: boolean): void {
495
- const name = this.name;
496
- const escapedName = escapeString(JSON.stringify(this.alias || this.name));
497
- const type = this.type;
498
- if (this.flags.has(PropertyFlags.Omit)) return;
499
-
500
- if (this.flags.has(PropertyFlags.JSON_Raw)) {
501
- if (this.flags.has(PropertyFlags.Null)) {
502
- this.right_s = "(this." + name + " || \"null\")";
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)";
504
- } else {
505
- this.right_s = "this." + name;
506
- this.right_d = "data.substring(value_start, value_end);";
507
- }
540
+ if (aMove && !bMove) {
541
+ return -1;
542
+ } else if (!aMove && bMove) {
543
+ return 1;
508
544
  } else {
509
- this.right_s = "__SERIALIZE<" + type + ">(this." + name + ")";
510
- this.right_d =
511
- (safe ? "__DESERIALIZE_SAFE" : "__DESERIALIZE") + "<" + type + ">(data.substring(value_start, value_end))";
545
+ return 0;
512
546
  }
513
-
514
- if (this.flags.has(PropertyFlags.OmitIf)) {
515
- const condition = this.flags.get(PropertyFlags.OmitIf)![0];
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
- ' + ","}';
528
- this.deserialize = "this." + name + " = " + this.right_d + ";";
529
- } else if (this.flags.has(PropertyFlags.OmitNull)) {
530
- this.serialize =
531
- "${changetype<usize>(this." +
532
- name +
533
- ") == <usize>0" +
534
- ' ? "" : \'' +
535
- escapedName +
536
- ":' + " +
537
- this.right_s +
538
- ' + ","}';
539
- this.deserialize = "this." + name + " = " + this.right_d + ";";
540
- } else {
541
- this.serialize = escapedName + ":${" + this.right_s + "}";
542
- this.deserialize = "this." + name + " = " + this.right_d + ";";
543
- }
544
- }
545
- }
546
-
547
- class SchemaData {
548
- public name: string = "";
549
- public members: Property[] = [];
550
- public parent: SchemaData | null = null;
551
- public node!: ClassDeclaration;
552
- }
553
-
554
- function charCodeAt32(data: string, offset: number): number {
555
- return (data.charCodeAt(offset + 1) << 16) | data.charCodeAt(offset);
556
- }
557
-
558
- function charCodeAt64(data: string, offset: number): bigint {
559
- if (offset + 3 >= data.length) {
560
- throw new Error(
561
- "The string must have at least 4 characters from the specified offset.",
562
- );
563
- }
564
-
565
- const firstCharCode = BigInt(data.charCodeAt(offset));
566
- const secondCharCode = BigInt(data.charCodeAt(offset + 1));
567
- const thirdCharCode = BigInt(data.charCodeAt(offset + 2));
568
- const fourthCharCode = BigInt(data.charCodeAt(offset + 3));
569
-
570
- const u64Value =
571
- (fourthCharCode << 48n) |
572
- (thirdCharCode << 32n) |
573
- (secondCharCode << 16n) |
574
- firstCharCode;
575
-
576
- return u64Value;
577
- }
578
-
579
- function encodeKey(key: string): string {
580
- const data = JSON.stringify(key);
581
- return data.slice(1, data.length - 1);
582
- }
583
-
584
- function escapeString(data: string): string {
585
- return data.replace(/\\/g, "\\\\").replace(/\`/g, "\\`");
586
- }
587
-
588
- function escapeSlash(data: string): string {
589
- return data.replace(/\\/g, "\\\\").replace(/\`/g, "\\`");
590
- }
591
-
592
- function escapeQuote(data: string): string {
593
- return data.replace(/\"/g, '\\"');
547
+ });
594
548
  }
595
549
 
596
550
  function getArgs(args: Expression[] | null): string[] {
@@ -615,3 +569,139 @@ function getArgs(args: Expression[] | null): string[] {
615
569
  }
616
570
  return out;
617
571
  }
572
+
573
+ function toU16(data: string, offset: number = 0): string {
574
+ return data.charCodeAt(offset + 0).toString();
575
+ }
576
+
577
+ function toU32(data: string, offset: number = 0): string {
578
+ return ((data.charCodeAt(offset + 1) << 16) | data.charCodeAt(offset + 0)).toString();
579
+ }
580
+
581
+ function toU48(data: string, offset: number = 0): string {
582
+ return ((BigInt(data.charCodeAt(offset + 2)) << 32n) | (BigInt(data.charCodeAt(offset + 1)) << 16n) | BigInt(data.charCodeAt(offset + 0))).toString();
583
+ }
584
+
585
+ function toU64(data: string, offset: number = 0): string {
586
+ return ((BigInt(data.charCodeAt(offset + 3)) << 48n) | (BigInt(data.charCodeAt(offset + 2)) << 32n) | (BigInt(data.charCodeAt(offset + 1)) << 16n) | BigInt(data.charCodeAt(offset + 0))).toString();
587
+ }
588
+
589
+ function toMemCDecl(n: number, indent: string): string {
590
+ let out = "";
591
+ let offset = 0;
592
+ let index = 0;
593
+ while (n >= 8) {
594
+ out += `${indent}const code${index++} = load<u64>(keyStart, ${offset});\n`;
595
+ offset += 8;
596
+ n -= 8;
597
+ }
598
+
599
+ while (n >= 4) {
600
+ out += `${indent}const code${index++} = load<u32>(keyStart, ${offset});\n`;
601
+ offset += 4;
602
+ n -= 4;
603
+ }
604
+
605
+ if (n == 1) out += `${indent}const code${index++} = load<u16>(keyStart, ${offset});\n`;
606
+
607
+ return out;
608
+ }
609
+
610
+ function toMemCCheck(data: string): string {
611
+ let n = data.length << 1;
612
+ let out = "";
613
+ let offset = 0;
614
+ let index = 0;
615
+ while (n >= 8) {
616
+ out += ` && code${index++} == ${toU64(data, offset >> 1)}`;
617
+ offset += 8;
618
+ n -= 8;
619
+ }
620
+
621
+ while (n >= 4) {
622
+ out += ` && code${index++} == ${toU32(data, offset >> 1)}`;
623
+ offset += 4;
624
+ n -= 4;
625
+ }
626
+
627
+ if (n == 1) out += ` && code${index++} == ${toU16(data, offset >> 1)}`;
628
+
629
+ return out.slice(4);
630
+ }
631
+
632
+ function strToNum(data: string, simd: boolean = false, offset: number = 0): string[][] {
633
+ const out: string[][] = [];
634
+ let n = data.length;
635
+
636
+ while (n >= 8 && simd) {
637
+ out.push(["v128", "i16x8(" + data.charCodeAt(offset + 0) + ", " + data.charCodeAt(offset + 1) + ", " + data.charCodeAt(offset + 2) + ", " + data.charCodeAt(offset + 3) + ", " + data.charCodeAt(offset + 4) + ", " + data.charCodeAt(offset + 5) + ", " + data.charCodeAt(offset + 6) + ", " + data.charCodeAt(offset + 7) + ")"]);
638
+
639
+ offset += 8;
640
+ n -= 8;
641
+ }
642
+
643
+ while (n >= 4) {
644
+ const value = (BigInt(data.charCodeAt(offset + 3)) << 48n) | (BigInt(data.charCodeAt(offset + 2)) << 32n) | (BigInt(data.charCodeAt(offset + 1)) << 16n) | BigInt(data.charCodeAt(offset + 0));
645
+ out.push(["u64", value.toString()]);
646
+ offset += 4;
647
+ n -= 4;
648
+ }
649
+
650
+ while (n >= 2) {
651
+ const value = (data.charCodeAt(offset + 1) << 16) | data.charCodeAt(offset + 0);
652
+ out.push(["u32", value.toString()]);
653
+ offset += 2;
654
+ n -= 2;
655
+ }
656
+
657
+ if (n === 1) {
658
+ const value = data.charCodeAt(offset + 0);
659
+ out.push(["u16", value.toString()]);
660
+ }
661
+
662
+ return out;
663
+ }
664
+
665
+ function isPrimitive(type: string): boolean {
666
+ const primitiveTypes = ["u8", "u16", "u32", "u64", "i8", "i16", "i32", "i64", "f32", "f64", "bool", "boolean"];
667
+ return primitiveTypes.some((v) => type.includes(v));
668
+ }
669
+
670
+ function throwError(message: string, range: Range): never {
671
+ const err = new Error();
672
+ err.stack = `${message}\n at ${range.source.normalizedPath}:${range.source.lineAt(range.start)}:${range.source.columnAt()}\n`;
673
+ throw err;
674
+ }
675
+
676
+ function indentInc(): void {
677
+ indent += " ";
678
+ }
679
+
680
+ function indentDec(): void {
681
+ indent = indent.slice(0, Math.max(0, indent.length - 2));
682
+ }
683
+
684
+ function sizeof(type: string): number {
685
+ if (type == "u8")
686
+ return 6; // -127
687
+ else if (type == "i8")
688
+ return 8; // 255
689
+ else if (type == "u16")
690
+ return 10; // 65536
691
+ else if (type == "i16")
692
+ return 12; // -32767
693
+ else if (type == "u32")
694
+ return 20; // 4294967295
695
+ else if (type == "i32")
696
+ return 22; // -2147483647
697
+ else if (type == "u64")
698
+ return 40; // 18446744073709551615
699
+ else if (type == "i64")
700
+ return 40; // -9223372036854775807
701
+ else if (type == "bool" || type == "boolean") return 10;
702
+ else return 0;
703
+ }
704
+
705
+ function allPrimitive(schema: Schema): boolean {
706
+ return !schema.members.some((p) => p.byteSize == 0);
707
+ }