json-as 1.0.3 → 1.0.5

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 (36) hide show
  1. package/.github/dependabot.yml +11 -0
  2. package/CHANGELOG.md +14 -1
  3. package/README.md +27 -9
  4. package/SECURITY.md +32 -0
  5. package/assembly/__benches__/large.bench.ts +3 -0
  6. package/assembly/__benches__/vec3.bench.ts +3 -0
  7. package/assembly/__tests__/arbitrary.spec.ts +3 -3
  8. package/assembly/__tests__/array.spec.ts +5 -8
  9. package/assembly/__tests__/box.spec.ts +10 -20
  10. package/assembly/__tests__/custom.spec.ts +5 -6
  11. package/assembly/__tests__/date.spec.ts +4 -6
  12. package/assembly/__tests__/float.spec.ts +3 -3
  13. package/assembly/__tests__/map.spec.ts +1 -1
  14. package/assembly/__tests__/raw.spec.ts +3 -3
  15. package/assembly/__tests__/struct.spec.ts +24 -14
  16. package/assembly/deserialize/simd/string.ts +1 -2
  17. package/assembly/deserialize/simple/array/struct.ts +2 -3
  18. package/assembly/deserialize/simple/array.ts +1 -1
  19. package/assembly/deserialize/simple/bool.ts +1 -1
  20. package/assembly/deserialize/simple/map.ts +3 -4
  21. package/assembly/deserialize/simple/object.ts +2 -3
  22. package/assembly/deserialize/simple/raw.ts +1 -1
  23. package/assembly/deserialize/simple/struct.ts +2 -3
  24. package/assembly/index.ts +23 -7
  25. package/assembly/serialize/simd/string.ts +1 -0
  26. package/assembly/serialize/simple/integer.ts +1 -1
  27. package/assembly/serialize/simple/object.ts +7 -6
  28. package/assembly/test.ts +23 -24
  29. package/bench.js +2 -4
  30. package/index.ts +1 -1
  31. package/lib/as-bs.ts +4 -13
  32. package/package.json +6 -4
  33. package/run-tests.sh +1 -1
  34. package/transform/lib/index.js +65 -57
  35. package/transform/lib/index.js.map +1 -1
  36. package/transform/src/index.ts +80 -124
@@ -1,15 +1,18 @@
1
- import { ClassDeclaration, FieldDeclaration, IdentifierExpression, Parser, Source, NodeKind, CommonFlags, ImportStatement, Node, Tokenizer, SourceKind, NamedTypeNode, Range, FEATURE_SIMD, FunctionExpression, MethodDeclaration, Statement } from "assemblyscript/dist/assemblyscript.js";
1
+ import { ClassDeclaration, FieldDeclaration, IdentifierExpression, Parser, Source, NodeKind, CommonFlags, ImportStatement, Node, Tokenizer, SourceKind, NamedTypeNode, Range, FEATURE_SIMD, FunctionExpression, MethodDeclaration, Statement, Program } from "assemblyscript/dist/assemblyscript.js";
2
2
  import { Transform } from "assemblyscript/dist/transform.js";
3
3
  import { Visitor } from "./visitor.js";
4
- import { SimpleParser, toString } from "./util.js";
4
+ import { isStdlib, SimpleParser, toString } from "./util.js";
5
5
  import * as path from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import { Property, PropertyFlags, Schema } from "./types.js";
8
8
  import { getClasses, getImportedClass } from "./linker.js";
9
+ import { Feature } from "types:assemblyscript/src/common";
9
10
 
10
11
  let indent = " ";
11
12
 
12
13
  class JSONTransform extends Visitor {
14
+ public program!: Program;
15
+ public baseDir!: string;
13
16
  public parser!: Parser;
14
17
  public schemas: Schema[] = [];
15
18
  public schema!: Schema;
@@ -17,6 +20,7 @@ class JSONTransform extends Visitor {
17
20
  public imports: ImportStatement[] = [];
18
21
 
19
22
  public topStatements: Statement[] = [];
23
+ public simdStatements: string[] = [];
20
24
 
21
25
  visitClassDeclaration(node: ClassDeclaration): void {
22
26
  if (!node.decorators?.length) return;
@@ -34,11 +38,11 @@ class JSONTransform extends Visitor {
34
38
  this.schema.name = node.name.text;
35
39
 
36
40
  this.schemas.push(this.schema);
37
- if (process.env["JSON_DEBUG"]) console.log("Created schema: " + this.schema.name);
41
+ if (process.env["JSON_DEBUG"]) console.log("Created schema: " + this.schema.name + " in file " + node.range.source.normalizedPath);
38
42
 
39
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[])];
40
- const serializers: MethodDeclaration[] = [...(node.members.filter((v) => v.kind === NodeKind.MethodDeclaration && v.decorators && v.decorators.some((e) => (<IdentifierExpression>e.name).text.toLowerCase() === "serializer")))] as MethodDeclaration[];
41
- const deserializers: MethodDeclaration[] = [...(node.members.filter((v) => v.kind === NodeKind.MethodDeclaration && v.decorators && v.decorators.some((e) => (<IdentifierExpression>e.name).text.toLowerCase() === "deserializer")))] as MethodDeclaration[];
44
+ const serializers: MethodDeclaration[] = [...node.members.filter((v) => v.kind === NodeKind.MethodDeclaration && v.decorators && v.decorators.some((e) => (<IdentifierExpression>e.name).text.toLowerCase() === "serializer"))] as MethodDeclaration[];
45
+ const deserializers: MethodDeclaration[] = [...node.members.filter((v) => v.kind === NodeKind.MethodDeclaration && v.decorators && v.decorators.some((e) => (<IdentifierExpression>e.name).text.toLowerCase() === "deserializer"))] as MethodDeclaration[];
42
46
 
43
47
  if (serializers.length > 1) throwError("Multiple serializers detected for class " + node.name.text + " but schemas can only have one serializer!", serializers[1].range);
44
48
  if (deserializers.length > 1) throwError("Multiple deserializers detected for class " + node.name.text + " but schemas can only have one deserializer!", deserializers[1].range);
@@ -51,21 +55,12 @@ class JSONTransform extends Visitor {
51
55
  if (!serializer.signature.returnType || !(<NamedTypeNode>serializer.signature.returnType).name.identifier.text.includes("string")) throwError("Could not find valid return type for serializer in " + this.schema.name + "!. Set the return type to type 'string' and try again", serializer.signature.returnType.range);
52
56
 
53
57
  if (!serializer.decorators.some((v) => (<IdentifierExpression>v.name).text == "inline")) {
54
- serializer.decorators.push(
55
- Node.createDecorator(
56
- Node.createIdentifierExpression(
57
- "inline",
58
- serializer.range
59
- ),
60
- null,
61
- serializer.range
62
- )
63
- );
58
+ serializer.decorators.push(Node.createDecorator(Node.createIdentifierExpression("inline", serializer.range), null, serializer.range));
64
59
  }
65
60
  let SERIALIZER = "";
66
61
  SERIALIZER += " __SERIALIZE_CUSTOM(ptr: usize): void {\n";
67
62
  SERIALIZER += " const data = this." + serializer.name.text + "(changetype<" + this.schema.name + ">(ptr));\n";
68
- SERIALIZER += " if (isNullable(data) && changetype<usize>(data) == <usize>0) throw new Error(\"Could not serialize data using custom serializer!\");\n";
63
+ SERIALIZER += ' if (isNullable(data) && changetype<usize>(data) == <usize>0) throw new Error("Could not serialize data using custom serializer!");\n';
69
64
  SERIALIZER += " const dataSize = data.length << 1;\n";
70
65
  SERIALIZER += " memory.copy(bs.offset, changetype<usize>(data), dataSize);\n";
71
66
  SERIALIZER += " bs.offset += dataSize;\n";
@@ -86,21 +81,12 @@ class JSONTransform extends Visitor {
86
81
  if (!deserializer.signature.returnType || !((<NamedTypeNode>deserializer.signature.returnType).name.identifier.text.includes(this.schema.name) || (<NamedTypeNode>deserializer.signature.returnType).name.identifier.text.includes("this"))) throwError("Could not find valid return type for deserializer in " + this.schema.name + "!. Set the return type to type '" + this.schema.name + "' or 'this' and try again", deserializer.signature.returnType.range);
87
82
 
88
83
  if (!deserializer.decorators.some((v) => (<IdentifierExpression>v.name).text == "inline")) {
89
- deserializer.decorators.push(
90
- Node.createDecorator(
91
- Node.createIdentifierExpression(
92
- "inline",
93
- deserializer.range
94
- ),
95
- null,
96
- deserializer.range
97
- )
98
- );
84
+ deserializer.decorators.push(Node.createDecorator(Node.createIdentifierExpression("inline", deserializer.range), null, deserializer.range));
99
85
  }
100
86
  let DESERIALIZER = "";
101
87
  DESERIALIZER += " __DESERIALIZE_CUSTOM(data: string): " + this.schema.name + " {\n";
102
88
  DESERIALIZER += " const d = this." + deserializer.name.text + "(data)";
103
- DESERIALIZER += " if (isNullable(d) && changetype<usize>(d) == <usize>0) throw new Error(\"Could not deserialize data using custom deserializer!\");\n";
89
+ DESERIALIZER += ' if (isNullable(d) && changetype<usize>(d) == <usize>0) throw new Error("Could not deserialize data using custom deserializer!");\n';
104
90
  DESERIALIZER += " return d;\n";
105
91
  DESERIALIZER += " }\n";
106
92
 
@@ -251,11 +237,12 @@ class JSONTransform extends Visitor {
251
237
  INITIALIZE += ` this.${member.name} = "";\n`;
252
238
  }
253
239
 
240
+ const SIMD_ENABLED = this.program.options.hasFeature(Feature.Simd);
254
241
  if (!isRegular && !member.flags.has(PropertyFlags.OmitIf) && !member.flags.has(PropertyFlags.OmitNull)) isRegular = true;
255
242
  if (isRegular && isPure) {
256
243
  const keyPart = (isFirst ? "{" : ",") + aliasName + ":";
257
244
  this.schema.byteSize += keyPart.length << 1;
258
- SERIALIZE += this.getStores(keyPart)
245
+ SERIALIZE += this.getStores(keyPart, SIMD_ENABLED)
259
246
  .map((v) => indent + v + "\n")
260
247
  .join("");
261
248
  SERIALIZE += indent + `JSON.__serialize<${member.type}>(load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
@@ -263,7 +250,7 @@ class JSONTransform extends Visitor {
263
250
  } else if (isRegular && !isPure) {
264
251
  const keyPart = (isFirst ? "" : ",") + aliasName + ":";
265
252
  this.schema.byteSize += keyPart.length << 1;
266
- SERIALIZE += this.getStores(keyPart)
253
+ SERIALIZE += this.getStores(keyPart, SIMD_ENABLED)
267
254
  .map((v) => indent + v + "\n")
268
255
  .join("");
269
256
  SERIALIZE += indent + `JSON.__serialize<${member.type}>(load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
@@ -274,7 +261,7 @@ class JSONTransform extends Visitor {
274
261
  indentInc();
275
262
  const keyPart = aliasName + ":";
276
263
  this.schema.byteSize += keyPart.length << 1;
277
- SERIALIZE += this.getStores(keyPart)
264
+ SERIALIZE += this.getStores(keyPart, SIMD_ENABLED)
278
265
  .map((v) => indent + v + "\n")
279
266
  .join("");
280
267
  SERIALIZE += indent + `JSON.__serialize<${member.type}>(load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
@@ -292,12 +279,7 @@ class JSONTransform extends Visitor {
292
279
  if (member.flags.get(PropertyFlags.OmitIf).kind == NodeKind.Function) {
293
280
  const arg = member.flags.get(PropertyFlags.OmitIf) as FunctionExpression;
294
281
  // @ts-ignore: type
295
- arg.declaration.signature.parameters[0].type = Node.createNamedType(
296
- Node.createSimpleTypeName("this", node.range),
297
- null,
298
- false,
299
- node.range
300
- );
282
+ arg.declaration.signature.parameters[0].type = Node.createNamedType(Node.createSimpleTypeName("this", node.range), null, false, node.range);
301
283
  // @ts-ignore: type
302
284
  arg.declaration.signature.returnType.name = Node.createSimpleTypeName("boolean", arg.declaration.signature.returnType.name.range);
303
285
  SERIALIZE += indent + `if (!(${toString(member.flags.get(PropertyFlags.OmitIf))})(this)) {\n`;
@@ -305,7 +287,7 @@ class JSONTransform extends Visitor {
305
287
  SERIALIZE += indent + `if (${toString(member.flags.get(PropertyFlags.OmitIf))}) {\n`;
306
288
  }
307
289
  indentInc();
308
- SERIALIZE += this.getStores(aliasName + ":")
290
+ SERIALIZE += this.getStores(aliasName + ":", SIMD_ENABLED)
309
291
  .map((v) => indent + v + "\n")
310
292
  .join("");
311
293
  SERIALIZE += indent + `JSON.__serialize<${member.type}>(load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
@@ -353,7 +335,9 @@ class JSONTransform extends Visitor {
353
335
  for (let i = 0; i < memberGroup.length; i++) {
354
336
  const member = memberGroup[i];
355
337
  const memberName = member.alias || member.name;
356
- const dst = this.schemas.find(v => v.name == member.type) ? "ptr + offsetof<this>(\"" + member.name + "\") + 12" : "0";
338
+ const dst = this.schemas.find(v => v.name == member.type)
339
+ ? "load<usize>(ptr + offsetof<this>(\"" + member.name + "\"))"
340
+ : "0";
357
341
  if (memberLen == 2) {
358
342
  DESERIALIZE += `${indent} case ${memberName.charCodeAt(0)}: { // ${memberName}\n`;
359
343
  DESERIALIZE += `${indent} store<${member.type}>(ptr, JSON.__deserialize<${member.type}>(valStart, valEnd, ${dst}), offsetof<this>(${JSON.stringify(member.name)}));\n`;
@@ -478,46 +462,34 @@ class JSONTransform extends Visitor {
478
462
  }
479
463
  addRequiredImports(node: Source): void {
480
464
  const filePath = fileURLToPath(import.meta.url);
481
- const fileDir = path.posix.dirname(filePath);
465
+ const baseDir = path.resolve(filePath, "..", "..", "..");
466
+ const nodePath = path.resolve(this.baseDir, node.range.source.normalizedPath);
482
467
 
483
468
  const bsImport = this.imports.find((i) => i.declarations?.find((d) => d.foreignName.text == "bs" || d.name.text == "bs"));
484
469
  const jsonImport = this.imports.find((i) => i.declarations?.find((d) => d.foreignName.text == "JSON" || d.name.text == "JSON"));
485
470
 
486
- let pkgRel = path.posix.relative(
487
- path.posix.dirname(node.range.source.normalizedPath),
488
- path.posix.resolve(fileDir, "../../")
489
- );
471
+ let bsPath = path.posix.join(...path.relative(path.dirname(nodePath), path.join(baseDir, "lib", "as-bs")).split(path.sep)).replace(/^.*node_modules\/json-as/, "json-as");
490
472
 
491
- if (!pkgRel.startsWith(".") && !pkgRel.startsWith("/")) pkgRel = "./" + pkgRel;
492
- pkgRel = pkgRel.replace(/^.*json-as/, "json-as");
473
+ let jsonPath = path.posix.join(...path.relative(path.dirname(nodePath), path.join(baseDir, "assembly", "index.ts")).split(path.sep)).replace(/^.*node_modules\/json-as/, "json-as");
493
474
 
494
475
  if (!bsImport) {
495
- const replaceNode = Node.createImportStatement(
496
- [
497
- Node.createImportDeclaration(
498
- Node.createIdentifierExpression("bs", node.range, false),
499
- null,
500
- node.range
501
- )
502
- ],
503
- Node.createStringLiteralExpression(path.posix.join(pkgRel, "./lib/as-bs"), node.range),
504
- node.range
505
- );
476
+ if (node.normalizedPath.startsWith("~")) {
477
+ bsPath = "json-as/lib/as-bs";
478
+ }
479
+
480
+ const replaceNode = Node.createImportStatement([Node.createImportDeclaration(Node.createIdentifierExpression("bs", node.range, false), null, node.range)], Node.createStringLiteralExpression(bsPath, node.range), node.range);
506
481
  this.topStatements.push(replaceNode);
507
482
  if (process.env["JSON_DEBUG"]) console.log("Added as-bs import: " + toString(replaceNode) + "\n");
508
483
  }
509
484
 
510
485
  if (!jsonImport) {
486
+ if (node.normalizedPath.startsWith("~")) {
487
+ jsonPath = "json-as/assembly/index.ts";
488
+ }
511
489
  const replaceNode = Node.createImportStatement(
512
- [
513
- Node.createImportDeclaration(
514
- Node.createIdentifierExpression("JSON", node.range, false),
515
- null,
516
- node.range
517
- )
518
- ],
519
- Node.createStringLiteralExpression(path.posix.join(pkgRel, "./assembly"), node.range),
520
- node.range
490
+ [Node.createImportDeclaration(Node.createIdentifierExpression("JSON", node.range, false), null, node.range)],
491
+ Node.createStringLiteralExpression(jsonPath, node.range), // Ensure POSIX-style path for 'assembly'
492
+ node.range,
521
493
  );
522
494
  this.topStatements.push(replaceNode);
523
495
  if (process.env["JSON_DEBUG"]) console.log("Added json-as import: " + toString(replaceNode) + "\n");
@@ -529,14 +501,14 @@ class JSONTransform extends Visitor {
529
501
  const sizes = strToNum(data, simd);
530
502
  let offset = 0;
531
503
  for (const [size, num] of sizes) {
532
- // if (size == "v128") {
533
- // // This could be put in its own file
534
- // let index = this.newStmts.simd.findIndex((v) => v.includes(num));
535
- // let name = "SIMD_" + (index == -1 ? this.newStmts.simd.length : index);
536
- // if (index && !this.newStmts.simd.includes(`const ${name} = ${num};`)) this.newStmts.simd.push(`const ${name} = ${num};`);
537
- // out.push("store<v128>(bs.offset, " + name + ", " + offset + "); // " + data.slice(offset >> 1, (offset >> 1) + 8));
538
- // offset += 16;
539
- // }
504
+ if (size == "v128" && simd) {
505
+ // This could be put in its own file
506
+ let index = this.simdStatements.findIndex((v) => v.includes(num));
507
+ let name = "SIMD_" + (index == -1 ? this.simdStatements.length : index);
508
+ if (index && !this.simdStatements.includes(`const ${name} = ${num};`)) this.simdStatements.push(`const ${name} = ${num};`);
509
+ out.push("store<v128>(bs.offset, " + name + ", " + offset + "); // " + data.slice(offset >> 1, (offset >> 1) + 8));
510
+ offset += 16;
511
+ }
540
512
  if (size == "u64") {
541
513
  out.push("store<u64>(bs.offset, " + num + ", " + offset + "); // " + data.slice(offset >> 1, (offset >> 1) + 4));
542
514
  offset += 8;
@@ -552,37 +524,9 @@ class JSONTransform extends Visitor {
552
524
  return out;
553
525
  }
554
526
  isValidType(type: string, node: ClassDeclaration): boolean {
555
- const validTypes = [
556
- "string",
557
- "u8",
558
- "i8",
559
- "u16",
560
- "i16",
561
- "u32",
562
- "i32",
563
- "u64",
564
- "i64",
565
- "f32",
566
- "f64",
567
- "bool",
568
- "boolean",
569
- "Date",
570
- "JSON.Value",
571
- "JSON.Obj",
572
- "JSON.Raw",
573
- "Value",
574
- "Obj",
575
- "Raw",
576
- ...this.schemas.map((v) => v.name)
577
- ];
578
-
579
- const baseTypes = [
580
- "Array",
581
- "Map",
582
- "Set",
583
- "JSON.Box",
584
- "Box"
585
- ]
527
+ const validTypes = ["string", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f32", "f64", "bool", "boolean", "Date", "JSON.Value", "JSON.Obj", "JSON.Raw", "Value", "Obj", "Raw", ...this.schemas.map((v) => v.name)];
528
+
529
+ const baseTypes = ["Array", "Map", "Set", "JSON.Box", "Box"];
586
530
 
587
531
  if (node && node.isGeneric && node.typeParameters) validTypes.push(...node.typeParameters.map((v) => v.name.text));
588
532
  if (type.endsWith("| null")) {
@@ -596,39 +540,52 @@ class JSONTransform extends Visitor {
596
540
  }
597
541
 
598
542
  export default class Transformer extends Transform {
599
- // Trigger the transform after parse.
600
543
  afterParse(parser: Parser): void {
601
- // Create new transform
602
544
  const transformer = new JSONTransform();
545
+ const sources = parser.sources
546
+ .filter((source) => {
547
+ const p = source.internalPath;
548
+ if (p.startsWith("~lib/rt") || p.startsWith("~lib/performance") || p.startsWith("~lib/wasi_") || p.startsWith("~lib/shared/")) {
549
+ return false;
550
+ }
551
+ return !isStdlib(source);
552
+ })
553
+ .sort((a, b) => {
554
+ if (a.sourceKind >= 2 && b.sourceKind <= 1) {
555
+ return -1;
556
+ } else if (a.sourceKind <= 1 && b.sourceKind >= 2) {
557
+ return 1;
558
+ } else {
559
+ return 0;
560
+ }
561
+ })
562
+ .sort((a, b) => {
563
+ if (a.sourceKind === SourceKind.UserEntry) {
564
+ return 1;
565
+ } else {
566
+ return 0;
567
+ }
568
+ });
603
569
 
604
- // Sort the sources so that user scripts are visited last
605
- const sources = parser.sources.sort((_a, _b) => {
606
- const a = _a.internalPath;
607
- const b = _b.internalPath;
608
- if (a[0] == "~" && b[0] !== "~") {
609
- return -1;
610
- } else if (a[0] !== "~" && b[0] == "~") {
611
- return 1;
612
- } else {
613
- return 0;
614
- }
615
- });
616
-
570
+ transformer.baseDir = path.join(process.cwd(), this.baseDir);
571
+ transformer.program = this.program;
617
572
  transformer.parser = parser;
618
- // Loop over every source
619
573
  for (const source of sources) {
620
- // console.log("Source: " + source.normalizedPath);
621
574
  transformer.imports = [];
622
575
  transformer.currentSource = source;
623
- // Ignore all lib and std. Visit everything else.
624
576
  transformer.visit(source);
625
577
 
626
578
  if (transformer.topStatements.length) {
627
579
  source.statements.unshift(...transformer.topStatements);
628
580
  transformer.topStatements = [];
629
581
  }
582
+ if (transformer.simdStatements.length) {
583
+ for (const simd of transformer.simdStatements)
584
+ source.statements.unshift(SimpleParser.parseTopLevelStatement(simd));
585
+ }
586
+ transformer.simdStatements = [];
630
587
  }
631
- // Check that every parent and child class is hooked up correctly
588
+
632
589
  const schemas = transformer.schemas;
633
590
  for (const schema of schemas) {
634
591
  if (schema.parent) {
@@ -719,7 +676,6 @@ function strToNum(data: string, simd: boolean = false, offset: number = 0): stri
719
676
 
720
677
  while (n >= 8 && simd) {
721
678
  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) + ")"]);
722
-
723
679
  offset += 8;
724
680
  n -= 8;
725
681
  }