json-as 0.9.29 → 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 (107) hide show
  1. package/.github/workflows/nodejs.yml +0 -3
  2. package/.prettierrc.json +3 -2
  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 -32
  9. package/assembly/__tests__/bool.spec.ts +1 -1
  10. package/assembly/__tests__/simd/string.spec.ts +32 -0
  11. package/assembly/custom/memory.ts +25 -0
  12. package/assembly/custom/util.ts +14 -92
  13. package/assembly/deserialize/simd/string.ts +103 -0
  14. package/assembly/deserialize/simple/arbitrary.ts +17 -0
  15. package/assembly/deserialize/simple/array/arbitrary.ts +113 -0
  16. package/assembly/deserialize/simple/array/array.ts +18 -0
  17. package/assembly/deserialize/simple/array/bool.ts +17 -0
  18. package/assembly/deserialize/simple/array/float.ts +28 -0
  19. package/assembly/deserialize/simple/array/integer.ts +27 -0
  20. package/assembly/deserialize/simple/array/map.ts +18 -0
  21. package/assembly/deserialize/simple/array/object.ts +18 -0
  22. package/assembly/deserialize/simple/array/string.ts +22 -0
  23. package/assembly/deserialize/simple/array.ts +48 -0
  24. package/assembly/deserialize/simple/bool.ts +9 -0
  25. package/assembly/deserialize/simple/date.ts +11 -0
  26. package/assembly/deserialize/simple/float.ts +10 -0
  27. package/assembly/deserialize/simple/integer.ts +5 -0
  28. package/assembly/deserialize/simple/map.ts +154 -0
  29. package/assembly/deserialize/simple/object.ts +159 -0
  30. package/assembly/deserialize/simple/string.ts +48 -0
  31. package/assembly/globals/tables.ts +417 -0
  32. package/assembly/index.d.ts +9 -13
  33. package/assembly/index.ts +266 -146
  34. package/assembly/serialize/simd/string.ts +176 -0
  35. package/assembly/serialize/simple/arbitrary.ts +36 -0
  36. package/assembly/serialize/simple/array.ts +32 -0
  37. package/assembly/serialize/simple/bool.ts +19 -0
  38. package/assembly/serialize/simple/date.ts +13 -0
  39. package/assembly/serialize/simple/float.ts +7 -0
  40. package/assembly/serialize/simple/integer.ts +7 -0
  41. package/assembly/serialize/simple/map.ts +43 -0
  42. package/assembly/serialize/simple/object.ts +7 -0
  43. package/assembly/serialize/simple/string.ts +48 -0
  44. package/assembly/test.ts +41 -27
  45. package/assembly/tsconfig.json +2 -91
  46. package/assembly/types.ts +0 -0
  47. package/assembly/util/atoi.ts +35 -0
  48. package/assembly/util/bytes.ts +12 -0
  49. package/assembly/util/concat.ts +9 -0
  50. package/assembly/util/getArrayDepth.ts +17 -0
  51. package/assembly/util/index.ts +5 -0
  52. package/assembly/util/isSpace.ts +4 -0
  53. package/assembly/util/nextPowerOf2.ts +4 -0
  54. package/assembly/util/ptrToStr.ts +7 -0
  55. package/assembly/util/snp.ts +69 -0
  56. package/bench.js +5 -5
  57. package/modules/bs/index.ts +167 -0
  58. package/modules/tsconfig.json +8 -0
  59. package/package.json +42 -48
  60. package/transform/lib/builder.js +1353 -0
  61. package/transform/lib/builder.js.map +1 -0
  62. package/transform/lib/index.js +497 -332
  63. package/transform/lib/index.js.map +1 -1
  64. package/transform/lib/index.old.js +404 -0
  65. package/transform/lib/index.old.js.map +1 -0
  66. package/transform/lib/linker.js +18 -0
  67. package/transform/lib/linker.js.map +1 -0
  68. package/transform/lib/types.js +25 -0
  69. package/transform/lib/types.js.map +1 -0
  70. package/transform/lib/util.js +47 -0
  71. package/transform/lib/util.js.map +1 -0
  72. package/transform/lib/visitor.js +529 -446
  73. package/transform/lib/visitor.js.map +1 -0
  74. package/transform/package.json +1 -34
  75. package/transform/src/builder.ts +1371 -0
  76. package/transform/src/index.ts +564 -341
  77. package/transform/src/linker.ts +21 -0
  78. package/transform/src/types.ts +27 -0
  79. package/transform/src/util.ts +56 -0
  80. package/transform/src/visitor.ts +531 -0
  81. package/transform/tsconfig.json +3 -1
  82. package/assembly/__benches__/as-tral.d.ts +0 -1
  83. package/assembly/__tests__/date.spec.ts +0 -12
  84. package/assembly/custom/bs.ts +0 -202
  85. package/assembly/deserialize/array/array.ts +0 -31
  86. package/assembly/deserialize/array/bool.ts +0 -19
  87. package/assembly/deserialize/array/float.ts +0 -24
  88. package/assembly/deserialize/array/integer.ts +0 -24
  89. package/assembly/deserialize/array/map.ts +0 -27
  90. package/assembly/deserialize/array/object.ts +0 -27
  91. package/assembly/deserialize/array/string.ts +0 -29
  92. package/assembly/deserialize/array.ts +0 -46
  93. package/assembly/deserialize/bool.ts +0 -34
  94. package/assembly/deserialize/date.ts +0 -19
  95. package/assembly/deserialize/float.ts +0 -21
  96. package/assembly/deserialize/integer.ts +0 -16
  97. package/assembly/deserialize/map.ts +0 -139
  98. package/assembly/deserialize/object.ts +0 -211
  99. package/assembly/deserialize/string.ts +0 -149
  100. package/assembly/serialize/array.ts +0 -44
  101. package/assembly/serialize/bool.ts +0 -10
  102. package/assembly/serialize/date.ts +0 -4
  103. package/assembly/serialize/float.ts +0 -4
  104. package/assembly/serialize/integer.ts +0 -5
  105. package/assembly/serialize/map.ts +0 -24
  106. package/assembly/serialize/object.ts +0 -13
  107. package/assembly/serialize/string.ts +0 -284
@@ -1,321 +1,472 @@
1
- import { ClassDeclaration, FieldDeclaration, IdentifierExpression, Parser, Source, NodeKind, Expression, CommonFlags, StringLiteralExpression, IntegerLiteralExpression, FloatLiteralExpression, NullExpression, TrueExpression, FalseExpression } from "assemblyscript/dist/assemblyscript.js";
2
-
3
- import { toString, isStdlib } from "visitor-as/dist/utils.js";
4
- 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";
5
2
  import { Transform } from "assemblyscript/dist/transform.js";
6
-
7
- class JSONTransform extends BaseVisitor {
8
- public schemasList: SchemaData[] = [];
9
- 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;
10
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: [] };
11
24
 
12
- visitMethodDeclaration(): void {}
13
25
  visitClassDeclaration(node: ClassDeclaration): void {
14
26
  if (!node.decorators?.length) return;
15
27
 
16
- let found = false;
17
- for (const decorator of node.decorators) {
18
- const name = (<IdentifierExpression>decorator.name).text;
19
- if (name === "json" || name === "serializable") {
20
- found = true;
21
- break;
22
- }
23
- }
24
- 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;
25
35
 
26
- const schema = new SchemaData();
27
- schema.node = node;
28
- schema.name = node.name.text;
36
+ this.schema = new Schema();
37
+ this.schema.node = node;
38
+ this.schema.name = node.name.text;
29
39
 
30
- const members = [...node.members.filter((v) => v.kind === NodeKind.FieldDeclaration)];
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[])];
31
44
 
32
45
  if (node.extendsType) {
33
- schema.parent = this.schemasList.find((v) => v.name == node.extendsType?.name.identifier.text) as SchemaData | null;
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
+ }
34
56
 
35
- if (schema.parent?.members) {
36
- for (let i = schema.parent.members.length - 1; i >= 0; i--) {
37
- const replace = schema.members.find((v) => v.name == schema.parent?.members[i]?.name);
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);
38
68
  if (!replace) {
39
- members.unshift(schema.parent?.members[i]!.node);
69
+ members.unshift(this.schema.parent?.members[i]!.node);
40
70
  }
41
71
  }
42
72
  }
43
73
  }
44
74
 
45
75
  if (!members.length) {
46
- let SERIALIZE_RAW_EMPTY = '__SERIALIZE(): string {\n return "{}";\n}';
47
- //let SERIALIZE_PRETTY_EMPTY = "__SERIALIZE_PRETTY(): string {\n return \"{}\";\n}";
48
-
49
- let INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}";
50
-
51
- let DESERIALIZE_EMPTY = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}";
52
-
53
- if (process.env["JSON_DEBUG"]) {
54
- console.log(SERIALIZE_RAW_EMPTY);
55
- //console.log(SERIALIZE_PRETTY_EMPTY);
56
- console.log(INITIALIZE_EMPTY);
57
- console.log(DESERIALIZE_EMPTY);
58
- }
59
-
60
- const SERIALIZE_RAW_METHOD_EMPTY = SimpleParser.parseClassMember(SERIALIZE_RAW_EMPTY, node);
61
- //const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node);
62
- const INITIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(INITIALIZE_EMPTY, node);
63
- const DESERIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(DESERIALIZE_EMPTY, node);
64
-
65
- if (!node.members.find((v) => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_RAW_METHOD_EMPTY);
66
- if (!node.members.find((v) => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD_EMPTY);
67
- if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD_EMPTY);
68
-
69
- this.schemasList.push(schema);
76
+ this.generateEmptyMethods(node);
77
+ return;
70
78
  }
71
79
 
80
+ this.addRequiredImports(node);
81
+
72
82
  for (const member of members) {
73
- const name = member.name;
74
- if (!(member instanceof FieldDeclaration)) continue;
75
- if (!member.type) {
76
- throw new Error("Fields must be strongly typed! Found " + toString(member) + " at " + node.range.source.normalizedPath);
77
- }
83
+ if (!member.type) throwError("Fields must be strongly typed", node.range);
84
+
78
85
  const type = toString(member.type!);
79
- if (type.startsWith("(") && type.includes("=>")) continue;
86
+ const name = member.name;
80
87
  const value = member.initializer ? toString(member.initializer!) : null;
81
88
 
82
- if (member.flags == CommonFlags.Static) continue;
83
- if (member.flags === CommonFlags.Private) continue;
84
- if (member.flags === CommonFlags.Protected) continue;
85
- if (member.decorators && node.decorators.some((v) => (v.name as IdentifierExpression).text == "omit")) continue;
89
+ if (type.startsWith("(") && type.includes("=>")) continue;
86
90
 
87
91
  const mem = new Property();
88
92
  mem.name = name.text;
89
93
  mem.type = type;
90
94
  mem.value = value;
91
95
  mem.node = member;
96
+ mem.byteSize = sizeof(mem.type);
92
97
 
93
- if (type.includes("JSON.Raw")) {
94
- mem.flags.set(PropertyFlags.JSON_Raw, []);
95
- }
98
+ this.schema.byteSize += mem.byteSize;
96
99
 
97
- if (member.type.isNullable) {
98
- mem.flags.set(PropertyFlags.Null, []);
99
- }
100
+ if (type.includes("JSON.Raw")) mem.flags.set(PropertyFlags.Raw, null);
100
101
 
101
102
  if (member.decorators) {
102
103
  for (const decorator of member.decorators) {
103
- const decoratorName = (decorator.name as IdentifierExpression).text;
104
-
105
- const args = getArgs(decorator.args);
106
-
104
+ const decoratorName = (decorator.name as IdentifierExpression).text.toLowerCase().trim();
107
105
  switch (decoratorName) {
108
106
  case "alias": {
109
- if (!args.length) throw new Error("Expected 1 argument but got zero at @alias in " + node.range.source.normalizedPath);
107
+ const args = getArgs(decorator.args);
108
+ if (!args.length) throwError("@alias must have an argument of type string or number", member.range);
110
109
  mem.alias = args[0]!;
111
- mem.flags.set(PropertyFlags.Alias, args);
112
- break;
113
- }
114
- case "omit": {
115
- mem.flags.set(PropertyFlags.Omit, args);
116
110
  break;
117
111
  }
118
112
  case "omitif": {
119
- if (!decorator.args?.length) throw new Error("Expected 1 argument but got zero at @omitif in " + node.range.source.normalizedPath);
120
- 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;
121
117
  break;
122
118
  }
123
119
  case "omitnull": {
124
- 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;
125
127
  break;
126
128
  }
127
129
  }
128
130
  }
129
131
  }
130
132
 
131
- mem.generate(false);
132
-
133
- if (this.schemasList.find((v) => v.name == type)) {
134
- mem.initialize = "this." + name.text + " = changetype<nonnull<" + mem.type + ">>(__new(offsetof<nonnull<" + mem.type + ">>(), idof<nonnull<" + mem.type + ">>()));\n changetype<nonnull<" + mem.type + ">>(this." + name.text + ").__INITIALIZE()";
135
- } else if (mem.value) {
136
- mem.initialize = "this." + name.text + " = " + mem.value;
137
- } else if (type === "Map") {
138
- mem.initialize = "this." + name.text + " = new " + mem.type + "()";
139
- } else if (type === "string") {
140
- mem.initialize = "this." + name.text + ' = ""';
141
- } else if (type === "Array") {
142
- mem.initialize = "this." + name.text + " = instantiate<" + mem.type + ">()";
143
- } else if (type === "bool" || type === "boolean") {
144
- mem.initialize = "this." + name.text + " = false";
145
- } else if (type === "JSON.Raw") {
146
- mem.initialize = "this." + name.text + ' = ""';
147
- } else if (type === "u8" || type === "u16" || type === "u32" || type === "u64" || type === "i8" || type === "i16" || type === "i32" || type === "i64") {
148
- mem.initialize = "this." + name.text + " = 0";
149
- } else if (type === "f32" || type === "f64") {
150
- mem.initialize = "this." + name.text + " = 0.0";
151
- }
152
-
153
- schema.members.push(mem);
133
+ this.schema.members.push(mem);
154
134
  }
155
135
 
156
- let SERIALIZE_RAW = "__SERIALIZE(): string {\n let out = `{";
157
- let SERIALIZE_PRETTY = "__SERIALIZE_PRETTY(): string {\n let out = `{";
136
+ if (!this.schema.static) this.schema.members = sortMembers(this.schema.members);
158
137
 
159
- let INITIALIZE = "__INITIALIZE(): this {\n";
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";
160
142
 
161
- let DESERIALIZE = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n const len = key_end - key_start;\n";
162
- let indent = " ";
143
+ indent = " ";
163
144
 
164
- if (!schema.members.length) return;
165
-
166
- found = false;
167
-
168
- if (schema.members[0]?.flags.has(PropertyFlags.OmitNull) || schema.members[0]?.flags.has(PropertyFlags.OmitIf)) {
169
- SERIALIZE_RAW += schema.members[0]?.serialize;
170
- SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize;
171
- } else {
172
- SERIALIZE_RAW += schema.members[0]?.serialize + ",";
173
- SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize + ",\\n";
174
- 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";
175
152
  }
176
153
 
177
- if (schema.members[0]?.initialize) INITIALIZE += " " + schema.members[0]?.initialize + ";\n";
178
-
179
- for (let i = 1; i < schema.members.length; i++) {
180
- const member = schema.members[i]!;
181
- if (member.initialize) INITIALIZE += " " + member.initialize + ";\n";
182
- if (member.flags.has(PropertyFlags.OmitNull) || member.flags.has(PropertyFlags.OmitIf)) {
183
- SERIALIZE_RAW += member.serialize;
184
- SERIALIZE_PRETTY += member.serialize;
185
- } else {
186
- SERIALIZE_RAW += member.serialize + ",";
187
- SERIALIZE_PRETTY += indent + member.serialize + ",\\n";
188
- 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`;
189
173
  }
190
- }
191
174
 
192
- if (found) {
193
- SERIALIZE_RAW += "`;\n store<u16>(changetype<usize>(out) + ((out.length - 1) << 1), 125);\n return out;\n}";
194
- SERIALIZE_PRETTY += "`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}";
195
- } else {
196
- SERIALIZE_RAW += "}`;\n return out;\n}";
197
- SERIALIZE_PRETTY += "}`;\n return out;\n}";
198
- }
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
+ }
199
208
 
200
- 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
+ }
201
232
 
202
- const sortedMembers: Property[][] = [];
203
- const _sorted = schema.members.sort((a, b) => (a.alias?.length! || a.name.length) - (b.alias?.length! || b.name.length));
204
- let len = -1;
205
- let offset = -1;
206
- for (let i = 0; i < _sorted.length; i++) {
207
- const member = _sorted[i]!;
208
- const _name = member.alias || member.name;
209
- if (_name.length === len) {
210
- sortedMembers[offset]?.push(member);
211
- } else {
212
- sortedMembers.push([member]);
213
- len = _name.length;
214
- offset++;
233
+ indentDec();
234
+ SERIALIZE += indent + `}\n`;
235
+ }
215
236
  }
216
237
  }
217
238
 
218
- let first = true;
219
- for (const memberSet of sortedMembers) {
220
- const firstMember = memberSet[0]!;
221
- const _name = encodeKey(firstMember.alias || firstMember.name);
222
- if (_name.length === 1) {
223
- if (first) {
224
- DESERIALIZE += " if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
225
- first = false;
226
- } else {
227
- DESERIALIZE += "else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n";
228
- }
229
- } else if (_name.length === 2) {
230
- if (first) {
231
- DESERIALIZE += " if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
232
- first = false;
233
- } else {
234
- DESERIALIZE += "else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n";
235
- }
236
- } else if (_name.length === 4) {
237
- if (first) {
238
- DESERIALIZE += " if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
239
- 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);
240
249
  } else {
241
- DESERIALIZE += "else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n";
250
+ sortedMembers.push([member]);
251
+ len = _nameLength;
242
252
  }
243
- } else {
244
- if (first) {
245
- DESERIALIZE += " if (" + _name.length + " === len) {\n";
246
- 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"}`;
247
285
  } else {
248
- 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"}`;
249
291
  }
250
292
  }
251
- let f = true;
252
- for (let i = 0; i < memberSet.length; i++) {
253
- const member = memberSet[i]!;
254
- if (!member.deserialize) continue;
255
- const _name = encodeKey(member.alias || member.name);
256
- if (_name.length === 1) {
257
- DESERIALIZE += ` case ${_name.charCodeAt(0)}: { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
258
- } else if (_name.length === 2) {
259
- DESERIALIZE += ` case ${charCodeAt32(_name, 0)}: { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
260
- } else if (_name.length === 4) {
261
- if (f) {
262
- f = false;
263
- DESERIALIZE += ` if (${charCodeAt64(_name, 0)} === code) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
264
- } else {
265
- DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + `else if (${charCodeAt64(_name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`;
266
- }
267
- } else {
268
- if (f) {
269
- f = false;
270
- 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`;
271
- } else {
272
- DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else if (0 === memory.compare(changetype<usize>("${escapeQuote(escapeSlash(_name))}"), changetype<usize>(data) + (key_start << 1), ${_name.length << 1})) { /* ${_name} */\n ${member.deserialize}\n return true;\n }\n`;
273
- }
274
- }
293
+ if (memberLen < 6) {
294
+ DESERIALIZE += `${indent}}\n`; // Close switch
275
295
  }
276
- if (_name.length < 3) {
277
- DESERIALIZE += ` default: {\n return false;\n }\n }\n`;
278
- } else if (_name.length == 4) {
279
- DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`;
280
- } else {
281
- DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`;
282
- }
283
- DESERIALIZE += " } ";
296
+ indentDec();
297
+ DESERIALIZE += `${indent} return;\n`; // Break switch
298
+ DESERIALIZE += `${indent}}\n`; // Close length switch
284
299
  }
285
300
 
286
- 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 = " ";
287
307
 
288
- //console.log(sortedMembers);
308
+ this.schema.byteSize += 2;
309
+ SERIALIZE += indent + "store<u16>(bs.offset, 125, 0); // }\n";
310
+ SERIALIZE += indent + "bs.offset += 2;\n";
311
+ SERIALIZE += "}";
312
+
313
+ ALLOCATE += indent + "bs.ensureSize(" + this.schema.byteSize + ");\n";
314
+ ALLOCATE += "}";
315
+
316
+ INITIALIZE += " return this;\n";
317
+ INITIALIZE += "}";
289
318
 
290
319
  if (process.env["JSON_DEBUG"]) {
291
- console.log(SERIALIZE_RAW);
292
- //console.log(SERIALIZE_PRETTY);
320
+ console.log(SERIALIZE);
293
321
  console.log(INITIALIZE);
294
322
  console.log(DESERIALIZE);
323
+ console.log(ALLOCATE);
295
324
  }
296
325
 
297
- const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(SERIALIZE_RAW, node);
298
-
299
- const DESERIALIZE_SAFE = DESERIALIZE.replaceAll("__DESERIALIZE", "__DESERIALIZE_SAFE");
300
- //const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node);
326
+ const SERIALIZE_METHOD = SimpleParser.parseClassMember(SERIALIZE, node);
301
327
  const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node);
302
328
  const DESERIALIZE_METHOD = SimpleParser.parseClassMember(DESERIALIZE, node);
303
- const DESERIALIZE_SAFE_METHOD = SimpleParser.parseClassMember(DESERIALIZE_SAFE, node);
329
+ const ALLOCATE_METHOD = SimpleParser.parseClassMember(ALLOCATE, node);
304
330
 
305
- if (!node.members.find((v) => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_RAW_METHOD);
331
+ if (!node.members.find((v) => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_METHOD);
306
332
  if (!node.members.find((v) => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD);
307
333
  if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD);
308
- if (!node.members.find((v) => v.name.text == "__DESERIALIZE_SAFE")) node.members.push(DESERIALIZE_SAFE_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}";
309
343
 
310
- this.schemasList.push(schema);
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);
311
395
  }
312
396
  visitSource(node: Source): void {
397
+ this.imports = [];
313
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
+ }
314
424
 
315
- // Only add the import statement to sources that have JSON decorated classes.
316
- if (!this.sources.has(node)) {
317
- 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
+ }
441
+ }
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
+ }
318
467
  }
468
+ out.push("bs.offset += " + offset + ";");
469
+ return out;
319
470
  }
320
471
  }
321
472
 
@@ -326,159 +477,231 @@ export default class Transformer extends Transform {
326
477
  const transformer = new JSONTransform();
327
478
 
328
479
  // Sort the sources so that user scripts are visited last
329
- const sources = parser.sources
330
- .filter((source) => !isStdlib(source))
331
- .sort((_a, _b) => {
332
- const a = _a.internalPath;
333
- const b = _b.internalPath;
334
- if (a[0] === "~" && b[0] !== "~") {
335
- return -1;
336
- } else if (a[0] !== "~" && b[0] === "~") {
337
- return 1;
338
- } else {
339
- return 0;
340
- }
341
- });
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
+ });
342
491
 
492
+ transformer.parser = parser;
343
493
  // Loop over every source
344
494
  for (const source of sources) {
495
+ transformer.imports = [];
496
+ transformer.currentSource = source;
345
497
  // Ignore all lib and std. Visit everything else.
346
- if (!isStdlib(source)) {
347
- 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;
348
522
  }
349
523
  }
350
524
  // Check that every parent and child class is hooked up correctly
351
- const schemas = transformer.schemasList;
525
+ const schemas = transformer.schemas;
352
526
  for (const schema of schemas) {
353
527
  if (schema.parent) {
354
- const parent = schemas.find((v) => v.name === schema.parent?.name);
355
- 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.`);
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);
356
530
  }
357
531
  }
358
532
  }
359
533
  }
360
534
 
361
- enum PropertyFlags {
362
- Null,
363
- Omit,
364
- OmitNull,
365
- OmitIf,
366
- Alias,
367
- JSON_Raw,
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);
539
+
540
+ if (aMove && !bMove) {
541
+ return -1;
542
+ } else if (!aMove && bMove) {
543
+ return 1;
544
+ } else {
545
+ return 0;
546
+ }
547
+ });
368
548
  }
369
549
 
370
- class Property {
371
- public name: string = "";
372
- public alias: string | null = null;
373
- public type: string = "";
374
- public value: string | null = null;
375
- public flags: Map<PropertyFlags, string[]> = new Map<PropertyFlags, string[]>();
550
+ function getArgs(args: Expression[] | null): string[] {
551
+ if (!args) return [];
552
+ let out: string[] = [];
553
+ for (const arg of args) {
554
+ if (arg instanceof StringLiteralExpression) {
555
+ out.push(arg.value);
556
+ } else if (arg instanceof IntegerLiteralExpression) {
557
+ out.push(i64_to_string(arg.value));
558
+ } else if (arg instanceof FloatLiteralExpression) {
559
+ out.push(arg.value.toString());
560
+ } else if (arg instanceof NullExpression) {
561
+ out.push(arg.text);
562
+ } else if (arg instanceof TrueExpression) {
563
+ out.push(arg.text);
564
+ } else if (arg instanceof FalseExpression) {
565
+ out.push(arg.text);
566
+ } else if (arg instanceof IdentifierExpression) {
567
+ out.push(arg.text);
568
+ }
569
+ }
570
+ return out;
571
+ }
376
572
 
377
- public serialize: string | null = null;
378
- public deserialize: string | null = null;
379
- public initialize: string | null = null;
573
+ function toU16(data: string, offset: number = 0): string {
574
+ return data.charCodeAt(offset + 0).toString();
575
+ }
380
576
 
381
- public node!: FieldDeclaration;
577
+ function toU32(data: string, offset: number = 0): string {
578
+ return ((data.charCodeAt(offset + 1) << 16) | data.charCodeAt(offset + 0)).toString();
579
+ }
382
580
 
383
- private right_s: string = "";
384
- private right_d: string = "";
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
+ }
385
584
 
386
- public generate(safe: boolean): void {
387
- const name = this.name;
388
- const escapedName = escapeString(JSON.stringify(this.alias || this.name));
389
- const type = this.type;
390
- if (this.flags.has(PropertyFlags.Omit)) return;
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
+ }
391
588
 
392
- if (this.flags.has(PropertyFlags.JSON_Raw)) {
393
- if (this.flags.has(PropertyFlags.Null)) {
394
- this.right_s = "(this." + name + ' || "null")';
395
- this.right_d = "value_start === value_end - 4 && 30399761348886638 === load<u64>(changetype<usize>(data) + (value_start << 1)) ? null : data.substring(value_start, value_end)";
396
- } else {
397
- this.right_s = "this." + name;
398
- this.right_d = "data.substring(value_start, value_end);";
399
- }
400
- } else {
401
- this.right_s = "__SERIALIZE<" + type + ">(this." + name + ")";
402
- this.right_d = (safe ? "__DESERIALIZE_SAFE" : "__DESERIALIZE") + "<" + type + ">(data.substring(value_start, value_end))";
403
- }
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
+ }
404
598
 
405
- if (this.flags.has(PropertyFlags.OmitIf)) {
406
- const condition = this.flags.get(PropertyFlags.OmitIf)![0];
407
- if (!condition) throw new Error("Could not find condition when using decorator @omitif! Provide at least one condition");
408
- this.serialize = "${" + condition + ' ? "" : \'' + escapedName + ":' + " + this.right_s + ' + ","}';
409
- this.deserialize = "this." + name + " = " + this.right_d + ";";
410
- } else if (this.flags.has(PropertyFlags.OmitNull)) {
411
- this.serialize = "${changetype<usize>(this." + name + ") == <usize>0" + ' ? "" : \'' + escapedName + ":' + " + this.right_s + ' + ","}';
412
- this.deserialize = "this." + name + " = " + this.right_d + ";";
413
- } else {
414
- this.serialize = escapedName + ":${" + this.right_s + "}";
415
- this.deserialize = "this." + name + " = " + this.right_d + ";";
416
- }
599
+ while (n >= 4) {
600
+ out += `${indent}const code${index++} = load<u32>(keyStart, ${offset});\n`;
601
+ offset += 4;
602
+ n -= 4;
417
603
  }
418
- }
419
604
 
420
- class SchemaData {
421
- public name: string = "";
422
- public members: Property[] = [];
423
- public parent: SchemaData | null = null;
424
- public node!: ClassDeclaration;
605
+ if (n == 1) out += `${indent}const code${index++} = load<u16>(keyStart, ${offset});\n`;
606
+
607
+ return out;
425
608
  }
426
609
 
427
- function charCodeAt32(data: string, offset: number): number {
428
- return (data.charCodeAt(offset + 1) << 16) | data.charCodeAt(offset);
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);
429
630
  }
430
631
 
431
- function charCodeAt64(data: string, offset: number): bigint {
432
- if (offset + 3 >= data.length) {
433
- throw new Error("The string must have at least 4 characters from the specified offset.");
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;
434
641
  }
435
642
 
436
- const firstCharCode = BigInt(data.charCodeAt(offset));
437
- const secondCharCode = BigInt(data.charCodeAt(offset + 1));
438
- const thirdCharCode = BigInt(data.charCodeAt(offset + 2));
439
- const fourthCharCode = BigInt(data.charCodeAt(offset + 3));
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
+ }
440
649
 
441
- const u64Value = (fourthCharCode << 48n) | (thirdCharCode << 32n) | (secondCharCode << 16n) | firstCharCode;
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
+ }
442
656
 
443
- return u64Value;
657
+ if (n === 1) {
658
+ const value = data.charCodeAt(offset + 0);
659
+ out.push(["u16", value.toString()]);
660
+ }
661
+
662
+ return out;
444
663
  }
445
664
 
446
- function encodeKey(key: string): string {
447
- const data = JSON.stringify(key);
448
- return data.slice(1, data.length - 1);
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));
449
668
  }
450
669
 
451
- function escapeString(data: string): string {
452
- return data.replace(/\\/g, "\\\\").replace(/\`/g, "\\`");
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;
453
674
  }
454
675
 
455
- function escapeSlash(data: string): string {
456
- return data.replace(/\\/g, "\\\\").replace(/\`/g, "\\`");
676
+ function indentInc(): void {
677
+ indent += " ";
457
678
  }
458
679
 
459
- function escapeQuote(data: string): string {
460
- return data.replace(/\"/g, '\\"');
680
+ function indentDec(): void {
681
+ indent = indent.slice(0, Math.max(0, indent.length - 2));
461
682
  }
462
683
 
463
- function getArgs(args: Expression[] | null): string[] {
464
- if (!args) return [];
465
- let out: string[] = [];
466
- for (const arg of args) {
467
- if (arg instanceof StringLiteralExpression) {
468
- out.push(arg.value);
469
- } else if (arg instanceof IntegerLiteralExpression) {
470
- out.push(i64_to_string(arg.value));
471
- } else if (arg instanceof FloatLiteralExpression) {
472
- out.push(arg.value.toString());
473
- } else if (arg instanceof NullExpression) {
474
- out.push(arg.text);
475
- } else if (arg instanceof TrueExpression) {
476
- out.push(arg.text);
477
- } else if (arg instanceof FalseExpression) {
478
- out.push(arg.text);
479
- } else if (arg instanceof IdentifierExpression) {
480
- out.push(arg.text);
481
- }
482
- }
483
- return out;
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);
484
707
  }