json-as 0.9.29 → 1.0.0-alpha.2

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