gofront 1.6.0 → 1.6.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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/backend/js/expressions.js +238 -109
  3. package/src/backend/js/index.js +38 -1
  4. package/src/backend/js/runtime.js +953 -53
  5. package/src/backend/js/statements.js +55 -67
  6. package/src/backend/js/stdlib/base64.js +12 -0
  7. package/src/backend/js/stdlib/binary.js +102 -0
  8. package/src/backend/js/stdlib/bits.js +15 -1
  9. package/src/backend/js/stdlib/builder.js +28 -9
  10. package/src/backend/js/stdlib/bytes.js +75 -18
  11. package/src/backend/js/stdlib/errors.js +20 -1
  12. package/src/backend/js/stdlib/fmt.js +70 -13
  13. package/src/backend/js/stdlib/hex.js +17 -0
  14. package/src/backend/js/stdlib/html.js +1 -1
  15. package/src/backend/js/stdlib/index.js +16 -0
  16. package/src/backend/js/stdlib/io.js +105 -3
  17. package/src/backend/js/stdlib/math.js +13 -1
  18. package/src/backend/js/stdlib/path.js +18 -4
  19. package/src/backend/js/stdlib/rand.js +10 -5
  20. package/src/backend/js/stdlib/sha256.js +12 -0
  21. package/src/backend/js/stdlib/slices.js +52 -26
  22. package/src/backend/js/stdlib/strconv.js +21 -5
  23. package/src/backend/js/stdlib/strings.js +148 -15
  24. package/src/backend/js/stdlib/sync.js +13 -0
  25. package/src/backend/js/stdlib/time.js +90 -19
  26. package/src/backend/js/stdlib/unicode.js +2 -2
  27. package/src/backend/wasm/boundary.js +1207 -41
  28. package/src/backend/wasm/const-fold.js +205 -0
  29. package/src/backend/wasm/emit-builtins.js +203 -76
  30. package/src/backend/wasm/emit-exprs.js +464 -276
  31. package/src/backend/wasm/emit-maps.js +144 -8
  32. package/src/backend/wasm/emit-stdlib.js +12 -3005
  33. package/src/backend/wasm/emit-stmts.js +264 -270
  34. package/src/backend/wasm/emit.js +115 -87
  35. package/src/backend/wasm/encode.js +55 -29
  36. package/src/backend/wasm/index.js +1001 -32
  37. package/src/backend/wasm/optimize.js +13 -4
  38. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  39. package/src/backend/wasm/stdlib/core.js +8903 -0
  40. package/src/backend/wasm/stdlib/index.js +40 -0
  41. package/src/backend/wasm/stdlib/io.js +2161 -0
  42. package/src/backend/wasm/stdlib/packages.js +2400 -0
  43. package/src/backend/wasm/stdlib/pure.js +720 -0
  44. package/src/backend/wasm/stdlib/time.js +1200 -0
  45. package/src/backend/wasm/types.js +153 -1
  46. package/src/cli-core.js +1 -1
  47. package/src/compiler.js +18 -4
  48. package/src/dts-parser.js +79 -126
  49. package/src/index.js +2 -6
  50. package/src/lexer.js +80 -29
  51. package/src/lower/boxing.js +1 -2
  52. package/src/lower/captures.js +2 -6
  53. package/src/lower/ownership.js +30 -9
  54. package/src/minifier.js +20 -68
  55. package/src/parser/declarations.js +3 -6
  56. package/src/parser/expressions.js +3 -8
  57. package/src/parser/index.js +18 -3
  58. package/src/parser/statements.js +25 -38
  59. package/src/parser/types.js +1 -2
  60. package/src/resolver.js +2 -27
  61. package/src/templ-lexer.js +29 -63
  62. package/src/templ-parser.js +22 -45
  63. package/src/typechecker/assignability.js +81 -13
  64. package/src/typechecker/expressions.js +102 -2
  65. package/src/typechecker/resolve.js +36 -6
  66. package/src/typechecker/stdlib/core.js +280 -89
  67. package/src/typechecker/stdlib/extended.js +453 -1
  68. package/src/typechecker/types.js +344 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gofront",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "author": "Luuk van Venrooij",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,6 +40,23 @@ const STRICT_OPS = new Set([
40
40
  ">>",
41
41
  ]);
42
42
 
43
+ const NAMED_ZERO_TABLE = {
44
+ "time.Time": [null, "{ _d: new Date(0), _isZero: true }"],
45
+ "strings.Builder": [null, '{ _buf: "" }'],
46
+ "bytes.Buffer": [null, "{ _buf: [] }"],
47
+ "strings.Replacer": [null, "{ _p: [], Replace(s) { return s; } }"],
48
+ "sync.Mutex": ["sync", "__sync_mutex()"],
49
+ "sync.RWMutex": ["sync", "__sync_rwmutex()"],
50
+ "sync.Once": ["sync", "__sync_once()"],
51
+ "sync.WaitGroup": ["sync", "__sync_waitgroup()"],
52
+ "binary.LittleEndian": ["binary", "__binary_le"],
53
+ "encoding/binary.LittleEndian": ["binary", "__binary_le"],
54
+ "binary.BigEndian": ["binary", "__binary_be"],
55
+ "encoding/binary.BigEndian": ["binary", "__binary_be"],
56
+ "base64.Encoding": ["base64", "__base64_std"],
57
+ "encoding/base64.Encoding": ["base64", "__base64_std"],
58
+ };
59
+
43
60
  // Splits a generated JS lvalue at its outermost trailing access:
44
61
  // `a.b[i]` → { obj: "a.b", key: "i" }, `a.b.c` → { obj: "a.b", prop: "c" }.
45
62
  function splitLastAccess(js) {
@@ -78,18 +95,76 @@ const NS_CONSTANTS = {
78
95
  math: {
79
96
  Pi: "Math.PI",
80
97
  E: "Math.E",
98
+ Phi: "1.618033988749895",
99
+ Sqrt2: "Math.SQRT2",
100
+ SqrtE: "1.6487212707001282",
101
+ SqrtPi: "1.7724538509055159",
102
+ SqrtPhi: "1.272019649514069",
103
+ Ln2: "Math.LN2",
104
+ Log2E: "Math.LOG2E",
105
+ Ln10: "Math.LN10",
106
+ Log10E: "Math.LOG10E",
107
+ MaxFloat32: "3.4028234663852886e38",
108
+ SmallestNonzeroFloat32: "1.401298464324817e-45",
81
109
  MaxFloat64: "Number.MAX_VALUE",
82
110
  SmallestNonzeroFloat64: "Number.MIN_VALUE",
83
111
  MaxInt: "Number.MAX_SAFE_INTEGER",
84
112
  MinInt: "Number.MIN_SAFE_INTEGER",
113
+ MaxInt8: "127",
114
+ MinInt8: "-128",
115
+ MaxInt16: "32767",
116
+ MinInt16: "-32768",
117
+ MaxInt32: "2147483647",
118
+ MinInt32: "-2147483648",
119
+ MaxInt64: "9223372036854775807",
120
+ MinInt64: "-9223372036854775808",
121
+ MaxUint8: "255",
122
+ MaxUint16: "65535",
123
+ MaxUint32: "4294967295",
124
+ MaxUint: "18446744073709551615",
125
+ MaxUint64: "18446744073709551615",
126
+ },
127
+ strconv: {
128
+ ErrSyntax: "__strconv_err_syntax",
129
+ ErrRange: "__strconv_err_range",
130
+ },
131
+ path: {
132
+ ErrBadPattern: "__path_err_bad_pattern",
133
+ },
134
+ filepath: {
135
+ ErrBadPattern: "__path_err_bad_pattern",
85
136
  },
86
137
  io: {
87
- EOF: '"EOF"',
138
+ EOF: "__io_eof",
88
139
  Discard:
89
- "{ WriteString(s) { return s.length; }, Write(b) { return b.length; } }",
140
+ "{ WriteString(s) { return [s.length, null]; }, Write(b) { return [b.length, null]; } }",
141
+ },
142
+ sha256: {
143
+ Size: "32",
144
+ BlockSize: "64",
145
+ },
146
+ hex: {
147
+ ErrLength: '__error("encoding/hex: odd length hex string")',
148
+ },
149
+ base64: {
150
+ StdPadding: "61",
151
+ NoPadding: "-1",
152
+ StdEncoding: "__base64_std",
153
+ URLEncoding: "__base64_url",
154
+ RawStdEncoding: "__base64_raw_std",
155
+ RawURLEncoding: "__base64_raw_url",
156
+ },
157
+ binary: {
158
+ LittleEndian: "__binary_le",
159
+ BigEndian: "__binary_be",
160
+ },
161
+ rand: {
162
+ Reader: "__crypto_rand_reader",
90
163
  },
91
164
  os: { Args: "process.argv" },
92
165
  time: {
166
+ Nanosecond: "1",
167
+ Microsecond: "1000",
93
168
  Millisecond: "1000000",
94
169
  Second: "1000000000",
95
170
  Minute: "60000000000",
@@ -278,8 +353,22 @@ export const expressionGenMethods = {
278
353
  expr._ifaceBoxing = true;
279
354
  try {
280
355
  this.useHelper("ifaceBox");
281
- const fn = expr._ifaceBox === "ptr" ? "__ifp" : "__ifv";
282
- return `${fn}(${this.genExpr(expr)})`;
356
+ const fn =
357
+ expr._ifaceBox === "ptr"
358
+ ? "__ifp"
359
+ : expr._ifaceBox === "ptr_slice"
360
+ ? "__ifps"
361
+ : expr._ifaceBox === "ptr_map"
362
+ ? "__ifpm"
363
+ : expr._ifaceBox === "map"
364
+ ? "__ifm"
365
+ : expr._ifaceBox === "slice"
366
+ ? "__ifs"
367
+ : "__ifv";
368
+ const extra = expr._ifacePtrFields?.length
369
+ ? `, ${JSON.stringify(expr._ifacePtrFields)}`
370
+ : "";
371
+ return `${fn}(${this.genExpr(expr)}${extra})`;
283
372
  } finally {
284
373
  expr._ifaceBoxing = false;
285
374
  }
@@ -500,6 +589,13 @@ export const expressionGenMethods = {
500
589
  ) {
501
590
  return this.genExpr(expr.operand);
502
591
  }
592
+ if (
593
+ expr.operand.kind === "SelectorExpr" ||
594
+ expr.operand.kind === "IndexExpr"
595
+ ) {
596
+ const inner = this.genExpr(expr.operand);
597
+ return `{ get value() { return ${inner}; }, set value(v) { ${inner} = v; } }`;
598
+ }
503
599
  // For non-boxed vars (primitives, etc.), wrap in { value: x }
504
600
  return `{ value: ${this.genExpr(expr.operand)} }`;
505
601
  }
@@ -529,7 +625,27 @@ export const expressionGenMethods = {
529
625
  }
530
626
  if (expr.expr.kind === "Ident") {
531
627
  const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
532
- if (nsConst !== undefined) return nsConst;
628
+ if (nsConst !== undefined) {
629
+ if (expr.expr.name === "strconv") {
630
+ this.useHelper("strconvError");
631
+ } else if (expr.expr.name === "path" || expr.expr.name === "filepath") {
632
+ if (expr.field === "ErrBadPattern") this.useHelper("pathError");
633
+ } else if (expr.expr.name === "io" && expr.field === "EOF") {
634
+ this.useHelper("ioEOF");
635
+ } else if (expr.expr.name === "binary") {
636
+ this.useHelper("binary");
637
+ } else if (expr.expr.name === "hex") {
638
+ this.useHelper("hex");
639
+ this.useHelper("error");
640
+ } else if (expr.expr.name === "base64") {
641
+ this.useHelper("base64");
642
+ } else if (expr.expr.name === "rand" && expr.field === "Reader") {
643
+ this.useHelper("cryptoRand");
644
+ } else if (expr.expr.name === "sha256") {
645
+ this.useHelper("sha256");
646
+ }
647
+ return nsConst;
648
+ }
533
649
  }
534
650
  const base = this.genExpr(expr.expr);
535
651
  if (this.bundledPackages.has(base)) return expr.field;
@@ -705,12 +821,61 @@ export const expressionGenMethods = {
705
821
  return this._genRegexpMethodCall(expr.func.field, expr);
706
822
  if (recvName === "time.Time")
707
823
  return this._genTimeMethodCall(expr.func.field, expr);
824
+ if (recvName === "time.Duration")
825
+ return this._genDurationMethodCall(expr.func.field, expr);
708
826
  if (recvName === "testing.T")
709
827
  return this._genTestingMethodCall(expr.func.field, expr);
828
+ if (recvName === "strings.Replacer") {
829
+ const recv = this.genExpr(expr.func.expr);
830
+ const arg = this.genExpr(expr.args[0]);
831
+ return `(${recv} ? ${recv}.Replace(${arg}) : ${arg})`;
832
+ }
710
833
  if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
711
834
  const args = expr.args.map((a) => this.genExpr(a)).join(", ");
712
835
  return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
713
836
  }
837
+ if (recvName === "sync.Mutex" || recvName === "sync.RWMutex") {
838
+ if (expr.func.field === "TryLock" || expr.func.field === "TryRLock")
839
+ return "true";
840
+ return "(void 0)";
841
+ }
842
+ if (recvName === "sync.Once" && expr.func.field === "Do") {
843
+ const recv = this.genExpr(expr.func.expr);
844
+ const f = this.genExpr(expr.args[0]);
845
+ return `((o, fn) => { if (!o.done) { o.done = 1; fn(); } })(${recv}, ${f})`;
846
+ }
847
+ if (recvName === "sync.WaitGroup") {
848
+ const recv = this.genExpr(expr.func.expr);
849
+ if (expr.func.field === "Add") {
850
+ return `((wg, d) => { wg.counter = (wg.counter || 0) + d; })(${recv}, ${this.genExpr(expr.args[0])})`;
851
+ }
852
+ if (expr.func.field === "Done") {
853
+ return `((wg) => { wg.counter = (wg.counter || 0) - 1; })(${recv})`;
854
+ }
855
+ if (expr.func.field === "Wait") return "(void 0)";
856
+ }
857
+ if (
858
+ recvName === "binary.LittleEndian" ||
859
+ recvName === "binary.BigEndian" ||
860
+ recvName === "encoding/binary.LittleEndian" ||
861
+ recvName === "encoding/binary.BigEndian" ||
862
+ recvName === "binary.ByteOrder" ||
863
+ recvName === "encoding/binary.ByteOrder"
864
+ ) {
865
+ this.useHelper("binary");
866
+ const recv = this.genExpr(expr.func.expr);
867
+ const args = expr.args.map((a) => this.genExpr(a)).join(", ");
868
+ return `${recv}.${expr.func.field}(${args})`;
869
+ }
870
+ if (
871
+ recvName === "base64.Encoding" ||
872
+ recvName === "encoding/base64.Encoding"
873
+ ) {
874
+ this.useHelper("base64");
875
+ const recv = this.genExpr(expr.func.expr);
876
+ const args = expr.args.map((a) => this.genExpr(a)).join(", ");
877
+ return `${recv}.${expr.func.field}(${args})`;
878
+ }
714
879
  return undefined;
715
880
  },
716
881
 
@@ -981,10 +1146,38 @@ export const expressionGenMethods = {
981
1146
  return `new ${typeName}(${args.join(", ")})`;
982
1147
  },
983
1148
 
1149
+ _genSliceOrArrayLit(t, expr) {
1150
+ const elems = expr.elems
1151
+ .map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
1152
+ .join(", ");
1153
+ const elemNode =
1154
+ t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
1155
+ const elemResolved =
1156
+ expr._type?.kind === "slice" || expr._type?.kind === "array"
1157
+ ? expr._type.elem
1158
+ : null;
1159
+ const typedCtor =
1160
+ typedArrayConstructorForElem(elemResolved) ??
1161
+ (elemNode?.name
1162
+ ? typedArrayConstructorForElem({
1163
+ kind: "basic",
1164
+ name: elemNode.name,
1165
+ })
1166
+ : null);
1167
+ if (typedCtor) {
1168
+ return `new ${typedCtor}([${elems}])`;
1169
+ }
1170
+ return `[${elems}]`;
1171
+ },
1172
+
984
1173
  genCompositeLit(expr) {
985
1174
  const t = expr.typeExpr;
986
1175
  if (t === null) return this._genImplicitCompositeLit(expr);
987
1176
  const typeName = this.getTypeName(t);
1177
+ if (typeName === "bytes.Buffer" || typeName === "Buffer")
1178
+ return "{ _buf: [] }";
1179
+ if (typeName === "strings.Builder" || typeName === "Builder")
1180
+ return '{ _buf: "" }';
988
1181
  if (typeName && this.structNames.has(typeName))
989
1182
  return this._genPositionalStructLit(typeName, expr);
990
1183
  if (typeName && this.namedWrapperNames.has(typeName))
@@ -992,29 +1185,7 @@ export const expressionGenMethods = {
992
1185
  if (expr.elems.some((e) => e._positionalField)) {
993
1186
  if (typeName) return this._genPositionalStructLit(typeName, expr);
994
1187
  }
995
- if (this._isSliceOrArrayType(t)) {
996
- const elems = expr.elems
997
- .map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
998
- .join(", ");
999
- const elemNode =
1000
- t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
1001
- const elemResolved =
1002
- expr._type?.kind === "slice" || expr._type?.kind === "array"
1003
- ? expr._type.elem
1004
- : null;
1005
- const typedCtor =
1006
- typedArrayConstructorForElem(elemResolved) ??
1007
- (elemNode?.name
1008
- ? typedArrayConstructorForElem({
1009
- kind: "basic",
1010
- name: elemNode.name,
1011
- })
1012
- : null);
1013
- if (typedCtor) {
1014
- return `new ${typedCtor}([${elems}])`;
1015
- }
1016
- return `[${elems}]`;
1017
- }
1188
+ if (this._isSliceOrArrayType(t)) return this._genSliceOrArrayLit(t, expr);
1018
1189
  if (this._isMapTypeNode(t))
1019
1190
  return `{ ${this._genMapLitEntries(expr.elems)} }`;
1020
1191
  return this._genFallbackCompositeLit(expr);
@@ -1049,19 +1220,7 @@ export const expressionGenMethods = {
1049
1220
  const namedType = this.checker?.types.get(typeName);
1050
1221
  const u = namedType?.underlying;
1051
1222
  if (u?.kind === "map") {
1052
- const entries = expr.elems
1053
- .map((e) => {
1054
- if (e.kind === "KeyValueExpr") {
1055
- const k =
1056
- e.key.litKind === "STRING"
1057
- ? JSON.stringify(e.key.value)
1058
- : `[${this.genExpr(e.key)}]`;
1059
- return `${k}: ${this.genValueExpr(e.value)}`;
1060
- }
1061
- return this.genValueExpr(e);
1062
- })
1063
- .join(", ");
1064
- return `new ${typeName}({ ${entries} })`;
1223
+ return `new ${typeName}({ ${this._genMapLitEntries(expr.elems)} })`;
1065
1224
  }
1066
1225
  // Default: slice
1067
1226
  const elems = expr.elems
@@ -1124,8 +1283,11 @@ export const expressionGenMethods = {
1124
1283
  },
1125
1284
 
1126
1285
  _zeroForNamedType(name) {
1127
- if (name === "strings.Builder") return '{ _buf: "" }';
1128
- if (name === "bytes.Buffer") return "{ _buf: [] }";
1286
+ const entry = NAMED_ZERO_TABLE[name];
1287
+ if (entry) {
1288
+ if (entry[0]) this.useHelper(entry[0]);
1289
+ return entry[1];
1290
+ }
1129
1291
  name = this._dequalify(name);
1130
1292
  if (this.structNames.has(name)) return `new ${name}()`;
1131
1293
  return "null";
@@ -1397,24 +1559,17 @@ export const expressionGenMethods = {
1397
1559
  return inner;
1398
1560
  },
1399
1561
 
1400
- _isStringSource(srcType) {
1401
- return (
1402
- (srcType?.kind === "basic" && srcType?.name === "string") ||
1403
- (srcType?.kind === "untyped" && srcType?.base === "string")
1404
- );
1405
- },
1406
-
1407
1562
  _genSliceTypeConversion(expr, inner, t) {
1408
1563
  const elem = t.elem?.name;
1409
1564
  if (elem === "byte" || elem === "uint8") {
1410
- if (this._isStringSource(expr.expr._type))
1565
+ if (this._isStringExprType(expr.expr._type))
1411
1566
  return `new TextEncoder().encode(${inner})`;
1412
1567
  return `new Uint8Array(${inner})`;
1413
1568
  }
1414
1569
  const typedCtor = typedArrayConstructorForElem(
1415
1570
  t.elem ? { kind: "basic", name: t.elem.name } : null,
1416
1571
  );
1417
- const fromString = this._isStringSource(expr.expr._type);
1572
+ const fromString = this._isStringExprType(expr.expr._type);
1418
1573
  if (typedCtor) {
1419
1574
  return fromString
1420
1575
  ? `${typedCtor}.from(${inner}, __c => __c.codePointAt(0))`
@@ -1532,81 +1687,55 @@ export const expressionGenMethods = {
1532
1687
  if (tName === "float32") {
1533
1688
  return `Math.fround(${l} ${op} ${r})`;
1534
1689
  }
1535
- if (tName === "int32" || tName === "rune") {
1536
- if (op === "*") return `Math.imul(${l}, ${r})`;
1537
- if (op === "/")
1538
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) | 0))`;
1539
- if (op === "%")
1540
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) | 0))`;
1541
- if (op === "<<" || op === ">>") {
1542
- return `((${r}) >= 32 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : ((${l}) ${op} (${r}))) | 0`;
1543
- }
1544
- if (op === "&^") return `((${l}) & ~(${r})) | 0`;
1545
- return `((${l} ${op} ${r}) | 0)`;
1546
- }
1547
- if (tName === "uint32") {
1690
+ const divZero =
1691
+ '(() => { throw new Error("runtime error: integer divide by zero"); })()';
1692
+ if (tName === "int32" || tName === "rune" || tName === "uint32") {
1693
+ const signed = tName !== "uint32";
1694
+ const cast = signed ? "| 0" : ">>> 0";
1695
+ if (signed && op === "*") return `Math.imul(${l}, ${r})`;
1548
1696
  if (op === "/")
1549
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) >>> 0))`;
1697
+ return `((${r}) === 0 ? ${divZero} : (Math.trunc((${l}) / (${r})) ${cast}))`;
1550
1698
  if (op === "%")
1551
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) >>> 0))`;
1699
+ return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) ${cast}))`;
1552
1700
  if (op === "<<" || op === ">>") {
1553
- const jsOp = op === ">>" ? ">>>" : "<<";
1554
- return `((${r}) >= 32 ? 0 : ((${l}) ${jsOp} (${r}))) >>> 0`;
1701
+ const ovf = signed && op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0";
1702
+ const jsOp = !signed && op === ">>" ? ">>>" : op;
1703
+ return `((${r}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast}`;
1555
1704
  }
1556
- if (op === "&^") return `((${l}) & ~(${r})) >>> 0`;
1557
- return `((${l} ${op} ${r}) >>> 0)`;
1705
+ if (op === "&^") return `((${l}) & ~(${r})) ${cast}`;
1706
+ return `((${l} ${op} ${r}) ${cast})`;
1558
1707
  }
1559
- if (tName === "int8") {
1708
+ if (tName === "int8" || tName === "int16") {
1709
+ const bits = tName === "int8" ? 8 : 16;
1710
+ const sh = 32 - bits;
1560
1711
  if (op === "/")
1561
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : ((Math.trunc((${l}) / (${r})) << 24) >> 24))`;
1712
+ return `((${r}) === 0 ? ${divZero} : ((Math.trunc((${l}) / (${r})) << ${sh}) >> ${sh}))`;
1562
1713
  if (op === "%")
1563
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((((${l}) % (${r})) << 24) >> 24)))`;
1714
+ return `((${r}) === 0 ? ${divZero} : (((((${l}) % (${r})) << ${sh}) >> ${sh})))`;
1564
1715
  if (op === "<<" || op === ">>") {
1565
- return `((${r}) >= 8 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << 24 >> 24))`;
1716
+ return `((${r}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
1566
1717
  }
1567
- if (op === "&^") return `((((${l}) & ~(${r})) << 24) >> 24)`;
1568
- return `((((${l} ${op} ${r})) << 24) >> 24)`;
1718
+ if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
1719
+ return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
1569
1720
  }
1570
- if (tName === "int16") {
1721
+ if (tName === "uint8" || tName === "byte" || tName === "uint16") {
1722
+ const bits = tName === "uint16" ? 16 : 8;
1723
+ const mask = bits === 16 ? "0xFFFF" : "0xFF";
1571
1724
  if (op === "/")
1572
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : ((Math.trunc((${l}) / (${r})) << 16) >> 16))`;
1725
+ return `((${r}) === 0 ? ${divZero} : (Math.trunc((${l}) / (${r})) & ${mask}))`;
1573
1726
  if (op === "%")
1574
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((((${l}) % (${r})) << 16) >> 16)))`;
1575
- if (op === "<<" || op === ">>") {
1576
- return `((${r}) >= 16 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << 16 >> 16))`;
1577
- }
1578
- if (op === "&^") return `((((${l}) & ~(${r})) << 16) >> 16)`;
1579
- return `((((${l} ${op} ${r})) << 16) >> 16)`;
1580
- }
1581
- if (tName === "uint8" || tName === "byte") {
1582
- if (op === "/")
1583
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) & 0xFF))`;
1584
- if (op === "%")
1585
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) & 0xFF))`;
1727
+ return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
1586
1728
  if (op === "<<" || op === ">>") {
1587
1729
  const jsOp = op === ">>" ? ">>>" : "<<";
1588
- return `((${r}) >= 8 ? 0 : (((${l}) ${jsOp} (${r})) & 0xFF))`;
1730
+ return `((${r}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
1589
1731
  }
1590
- if (op === "&^") return `(((${l}) & ~(${r})) & 0xFF)`;
1591
- return `((${l} ${op} ${r}) & 0xFF)`;
1592
- }
1593
- if (tName === "uint16") {
1594
- if (op === "/")
1595
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) & 0xFFFF))`;
1596
- if (op === "%")
1597
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) & 0xFFFF))`;
1598
- if (op === "<<" || op === ">>") {
1599
- const jsOp = op === ">>" ? ">>>" : "<<";
1600
- return `((${r}) >= 16 ? 0 : (((${l}) ${jsOp} (${r})) & 0xFFFF))`;
1601
- }
1602
- if (op === "&^") return `(((${l}) & ~(${r})) & 0xFFFF)`;
1603
- return `((${l} ${op} ${r}) & 0xFFFF)`;
1732
+ if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
1733
+ return `((${l} ${op} ${r}) & ${mask})`;
1604
1734
  }
1605
1735
  if (this.isIntType(type)) {
1606
1736
  if (op === "/")
1607
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : Math.trunc((${l}) / (${r})))`;
1608
- if (op === "%")
1609
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : ((${l}) % (${r})))`;
1737
+ return `((${r}) === 0 ? ${divZero} : Math.trunc((${l}) / (${r})))`;
1738
+ if (op === "%") return `((${r}) === 0 ? ${divZero} : ((${l}) % (${r})))`;
1610
1739
  }
1611
1740
  return `${l} ${op} ${r}`;
1612
1741
  },
@@ -28,22 +28,34 @@ import { isComplex, isNumeric } from "../../typechecker/types.js";
28
28
  import { expressionGenMethods } from "./expressions.js";
29
29
  import {
30
30
  HELPER_APPEND,
31
+ HELPER_BASE64,
32
+ HELPER_BINARY,
31
33
  HELPER_CDIV,
32
34
  HELPER_CMUL,
35
+ HELPER_CRYPTO_RAND,
36
+ HELPER_DURATION,
33
37
  HELPER_EQUAL,
34
38
  HELPER_ERROR,
39
+ HELPER_ERROR_AS,
35
40
  HELPER_ERROR_IS,
41
+ HELPER_ERROR_JOIN,
42
+ HELPER_HEX,
36
43
  HELPER_IFACE_BOX,
37
44
  HELPER_INJECT_STYLES,
45
+ HELPER_IO_EOF,
38
46
  HELPER_LEN,
39
47
  HELPER_NIL_CALL,
40
48
  HELPER_PANIC,
41
49
  HELPER_PATH_CLEAN,
50
+ HELPER_PATH_ERROR,
42
51
  HELPER_S,
43
52
  HELPER_SCLONE,
53
+ HELPER_SHA256,
44
54
  HELPER_SORT_SLICE,
45
55
  HELPER_SPRINTF,
46
56
  HELPER_STRCONV,
57
+ HELPER_STRCONV_ERROR,
58
+ HELPER_SYNC,
47
59
  HELPER_TESTING,
48
60
  HELPER_TIME_FMT,
49
61
  HELPER_TIME_PARSE,
@@ -111,12 +123,24 @@ const HELPER_MAP = [
111
123
  ["sprintf", HELPER_SPRINTF],
112
124
  ["error", HELPER_ERROR],
113
125
  ["errorIs", HELPER_ERROR_IS],
126
+ ["errorAs", HELPER_ERROR_AS],
127
+ ["errorJoin", HELPER_ERROR_JOIN],
128
+ ["strconvError", HELPER_STRCONV_ERROR],
129
+ ["pathError", HELPER_PATH_ERROR],
130
+ ["ioEOF", HELPER_IO_EOF],
114
131
  ["strconv", HELPER_STRCONV],
115
132
  ["pathClean", HELPER_PATH_CLEAN],
116
133
  ["sortSlice", HELPER_SORT_SLICE],
117
134
  ["timeFmt", HELPER_TIME_FMT],
118
135
  ["timeParse", HELPER_TIME_PARSE],
136
+ ["duration", HELPER_DURATION],
119
137
  ["testing", HELPER_TESTING],
138
+ ["sync", HELPER_SYNC],
139
+ ["cryptoRand", HELPER_CRYPTO_RAND],
140
+ ["sha256", HELPER_SHA256],
141
+ ["binary", HELPER_BINARY],
142
+ ["hex", HELPER_HEX],
143
+ ["base64", HELPER_BASE64],
120
144
  ];
121
145
 
122
146
  export class CodeGen {
@@ -428,6 +452,19 @@ export class CodeGen {
428
452
  /** Marks a runtime helper (key of HELPER_MAP) as needed by the output. */
429
453
  useHelper(name) {
430
454
  this._helpers.add(name);
455
+ if (
456
+ name === "strconvError" ||
457
+ name === "pathError" ||
458
+ name === "ioEOF" ||
459
+ name === "base64" ||
460
+ name === "hex"
461
+ ) {
462
+ this._helpers.add("error");
463
+ }
464
+ if (name === "binary") {
465
+ this.useHelper("ioEOF");
466
+ this._helpers.add("error");
467
+ }
431
468
  }
432
469
 
433
470
  _prependHelpers(isTest = false) {
@@ -570,7 +607,7 @@ export class CodeGen {
570
607
  .map((f) => `${f.name}: ${binding(f)} = ${f.zero}`)
571
608
  .join(", ");
572
609
  this.line(
573
- `if (typeof ${first} === "object" && ${first} !== null && ${first}.constructor === Object) ({ ${pattern} } = ${first});`,
610
+ `if (arguments.length === 1 && typeof ${first} === "object" && ${first} !== null && ${first}.constructor === Object) ({ ${pattern} } = ${first});`,
574
611
  );
575
612
  }
576
613
  for (const f of fields) {