gofront 1.5.1 → 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 (91) hide show
  1. package/README.md +174 -466
  2. package/package.json +16 -20
  3. package/src/backend/js/expressions.js +288 -128
  4. package/src/backend/js/index.js +138 -56
  5. package/src/backend/js/runtime.js +1055 -53
  6. package/src/backend/js/source-map.js +32 -0
  7. package/src/backend/js/statements.js +151 -83
  8. package/src/backend/js/stdlib/base64.js +12 -0
  9. package/src/backend/js/stdlib/binary.js +102 -0
  10. package/src/backend/js/stdlib/bits.js +15 -1
  11. package/src/backend/js/stdlib/builder.js +28 -9
  12. package/src/backend/js/stdlib/bytes.js +75 -18
  13. package/src/backend/js/stdlib/errors.js +22 -3
  14. package/src/backend/js/stdlib/fmt.js +95 -20
  15. package/src/backend/js/stdlib/hex.js +17 -0
  16. package/src/backend/js/stdlib/html.js +1 -1
  17. package/src/backend/js/stdlib/index.js +19 -0
  18. package/src/backend/js/stdlib/io.js +105 -3
  19. package/src/backend/js/stdlib/maps.js +1 -1
  20. package/src/backend/js/stdlib/math.js +13 -1
  21. package/src/backend/js/stdlib/path.js +20 -6
  22. package/src/backend/js/stdlib/rand.js +10 -5
  23. package/src/backend/js/stdlib/sha256.js +12 -0
  24. package/src/backend/js/stdlib/shared.js +17 -0
  25. package/src/backend/js/stdlib/slices.js +53 -27
  26. package/src/backend/js/stdlib/sort.js +1 -1
  27. package/src/backend/js/stdlib/strconv.js +52 -11
  28. package/src/backend/js/stdlib/strings.js +152 -16
  29. package/src/backend/js/stdlib/sync.js +13 -0
  30. package/src/backend/js/stdlib/testing.js +2 -2
  31. package/src/backend/js/stdlib/time.js +93 -22
  32. package/src/backend/js/stdlib/unicode.js +2 -2
  33. package/src/backend/js/templ.js +1 -1
  34. package/src/backend/wasm/boundary.js +1677 -58
  35. package/src/backend/wasm/const-fold.js +205 -0
  36. package/src/backend/wasm/emit-builtins.js +1230 -0
  37. package/src/backend/wasm/emit-exprs.js +2096 -0
  38. package/src/backend/wasm/emit-maps.js +1999 -0
  39. package/src/backend/wasm/emit-shared.js +519 -0
  40. package/src/backend/wasm/emit-stdlib.js +13 -0
  41. package/src/backend/wasm/emit-stmts.js +2466 -0
  42. package/src/backend/wasm/emit.js +656 -5031
  43. package/src/backend/wasm/encode.js +259 -85
  44. package/src/backend/wasm/glue.js +19 -5
  45. package/src/backend/wasm/index.js +2432 -86
  46. package/src/backend/wasm/monomorph.js +711 -0
  47. package/src/backend/wasm/optimize.js +236 -0
  48. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  49. package/src/backend/wasm/stdlib/core.js +8903 -0
  50. package/src/backend/wasm/stdlib/index.js +40 -0
  51. package/src/backend/wasm/stdlib/io.js +2161 -0
  52. package/src/backend/wasm/stdlib/packages.js +2400 -0
  53. package/src/backend/wasm/stdlib/pure.js +720 -0
  54. package/src/backend/wasm/stdlib/time.js +1200 -0
  55. package/src/backend/wasm/types.js +312 -36
  56. package/src/backend/wasm/wat.js +49 -2
  57. package/src/cli-core.js +276 -73
  58. package/src/compiler.js +162 -25
  59. package/src/dts-parser.js +79 -126
  60. package/src/index.js +88 -121
  61. package/src/lexer.js +80 -29
  62. package/src/lower/boxing.js +4 -6
  63. package/src/lower/captures.js +12 -23
  64. package/src/lower/escape.js +4 -9
  65. package/src/lower/functions.js +277 -5
  66. package/src/lower/globals.js +7 -15
  67. package/src/lower/index.js +5 -2
  68. package/src/lower/ownership.js +35 -19
  69. package/src/lower/range.js +2 -5
  70. package/src/lower/walk.js +25 -0
  71. package/src/minifier.js +20 -68
  72. package/src/parser/declarations.js +3 -6
  73. package/src/parser/expressions.js +3 -8
  74. package/src/parser/index.js +18 -3
  75. package/src/parser/statements.js +25 -38
  76. package/src/parser/types.js +1 -2
  77. package/src/resolver.js +3 -27
  78. package/src/templ-lexer.js +29 -63
  79. package/src/templ-parser.js +22 -45
  80. package/src/test-runner.js +20 -0
  81. package/src/typechecker/assignability.js +123 -14
  82. package/src/typechecker/expressions.js +244 -4
  83. package/src/typechecker/index.js +27 -1
  84. package/src/typechecker/resolve.js +71 -12
  85. package/src/typechecker/statements.js +0 -7
  86. package/src/typechecker/stdlib/core.js +292 -103
  87. package/src/typechecker/stdlib/extended.js +471 -4
  88. package/src/typechecker/stdlib/shared.js +53 -0
  89. package/src/typechecker/stdlib.js +2 -0
  90. package/src/typechecker/types.js +545 -8
  91. package/src/vendor.js +2 -2
@@ -14,7 +14,7 @@ import {
14
14
 
15
15
  /** @typedef {import('./index.js').CodeGen} CodeGen */
16
16
 
17
- export const STRICT_NUMERIC_TYPES = new Set([
17
+ const STRICT_NUMERIC_TYPES = new Set([
18
18
  "float32",
19
19
  "int8",
20
20
  "int16",
@@ -26,7 +26,7 @@ export const STRICT_NUMERIC_TYPES = new Set([
26
26
  "uint32",
27
27
  ]);
28
28
 
29
- export const STRICT_OPS = new Set([
29
+ const STRICT_OPS = new Set([
30
30
  "+",
31
31
  "-",
32
32
  "*",
@@ -40,6 +40,23 @@ export 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",
@@ -233,14 +308,19 @@ const BUILTIN_GEN = {
233
308
  }
234
309
  return `{ value: ${s.zeroValueForExpr(arg)} }`;
235
310
  },
236
- print: (s, e) => `console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
311
+ // `print` writes without a trailing newline (Go semantics); the wasm glue
312
+ // buffers the same way, so output parity holds across backends.
313
+ print: (s, e) =>
314
+ `((__s) => (typeof process !== "undefined" && process?.stdout?.write ? process.stdout.write(__s) : console.log(__s)))([${e.args.map((a) => s.genExpr(a)).join(", ")}].map(String).join(" "))`,
237
315
  println: (s, e) =>
238
316
  `console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
239
317
  panic: (s, e) => `(() => { throw new Error(${s.genExpr(e.args[0])}); })()`,
240
- recover: () =>
241
- `(typeof __panic !== "undefined" && __panic !== null ? (() => { const __r = __panic.message ?? String(__panic); __panic = null; return __r; })() : null)`,
318
+ recover: (s) => {
319
+ s.useHelper("panicRt");
320
+ return `__recover(${s._recoverOkInScope ? "__recoverOk" : "false"})`;
321
+ },
242
322
  error: (s, e) => {
243
- s._usesError = true;
323
+ s.useHelper("error");
244
324
  return `__error(${s.genExpr(e.args[0])})`;
245
325
  },
246
326
  min: (s, e) => `Math.min(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
@@ -272,9 +352,23 @@ export const expressionGenMethods = {
272
352
  if (expr._ifaceBox && !expr._ifaceBoxing) {
273
353
  expr._ifaceBoxing = true;
274
354
  try {
275
- this._usesIfaceBox = true;
276
- const fn = expr._ifaceBox === "ptr" ? "__ifp" : "__ifv";
277
- return `${fn}(${this.genExpr(expr)})`;
355
+ this.useHelper("ifaceBox");
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})`;
278
372
  } finally {
279
373
  expr._ifaceBoxing = false;
280
374
  }
@@ -365,7 +459,7 @@ export const expressionGenMethods = {
365
459
  if (base.kind === "struct") {
366
460
  if (this._structClassName(t) || this._structClassName(base))
367
461
  return `${js}.__clone()`;
368
- this._usesSClone = true;
462
+ this.useHelper("sclone");
369
463
  return `__sclone(${js})`;
370
464
  }
371
465
  const elemClone = this._cloneJs(base.elem, "__e");
@@ -495,6 +589,13 @@ export const expressionGenMethods = {
495
589
  ) {
496
590
  return this.genExpr(expr.operand);
497
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
+ }
498
599
  // For non-boxed vars (primitives, etc.), wrap in { value: x }
499
600
  return `{ value: ${this.genExpr(expr.operand)} }`;
500
601
  }
@@ -524,7 +625,27 @@ export const expressionGenMethods = {
524
625
  }
525
626
  if (expr.expr.kind === "Ident") {
526
627
  const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
527
- 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
+ }
528
649
  }
529
650
  const base = this.genExpr(expr.expr);
530
651
  if (this.bundledPackages.has(base)) return expr.field;
@@ -613,7 +734,7 @@ export const expressionGenMethods = {
613
734
  this._withFnCtx(expr.body, () =>
614
735
  this.indented(() => {
615
736
  this._emitParamCopies(expr.params);
616
- this._genBody(expr.body);
737
+ this._genBody(expr.body, expr);
617
738
  }),
618
739
  );
619
740
  const body = this.out.join("\n");
@@ -700,14 +821,71 @@ export const expressionGenMethods = {
700
821
  return this._genRegexpMethodCall(expr.func.field, expr);
701
822
  if (recvName === "time.Time")
702
823
  return this._genTimeMethodCall(expr.func.field, expr);
824
+ if (recvName === "time.Duration")
825
+ return this._genDurationMethodCall(expr.func.field, expr);
703
826
  if (recvName === "testing.T")
704
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
+ }
833
+ if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
834
+ const args = expr.args.map((a) => this.genExpr(a)).join(", ");
835
+ return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
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
+ }
705
879
  return undefined;
706
880
  },
707
881
 
708
882
  _genSelectorCall(expr) {
709
- if (this._isErrorMethodCall(expr))
710
- return `${this.genExpr(expr.func.expr)}.Error()`;
883
+ if (this._isErrorMethodCall(expr)) {
884
+ const recv = this.genExpr(expr.func.expr);
885
+ if (!this.strict) return `${recv}.Error()`;
886
+ this.useHelper("nilCall");
887
+ return `__nilcall(${recv}).Error()`;
888
+ }
711
889
  if (expr.func.expr.kind === "Ident") {
712
890
  const ns = expr.func.expr.name;
713
891
  const fn = expr.func.field;
@@ -716,7 +894,25 @@ export const expressionGenMethods = {
716
894
  }
717
895
  const recvType = expr.func.expr._type;
718
896
  const recvName = this._resolveRecvName(recvType);
719
- return this._genReceiverTypeCall(recvName, expr);
897
+ const typed = this._genReceiverTypeCall(recvName, expr);
898
+ if (typed !== undefined) return typed;
899
+ if (this.strict && this._isInterfaceType(recvType)) {
900
+ // Go panics with a nil-dereference runtime error; JS would throw a
901
+ // TypeError with a different message.
902
+ this.useHelper("nilCall");
903
+ const recv = this.genExpr(expr.func.expr);
904
+ const args = expr.args
905
+ .map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
906
+ .join(", ");
907
+ return `__nilcall(${recv}).${expr.func.field}(${args})`;
908
+ }
909
+ return undefined;
910
+ },
911
+
912
+ _isInterfaceType(t) {
913
+ if (!t) return false;
914
+ if (t.kind === "interface") return true;
915
+ return t.kind === "named" && t.underlying?.kind === "interface";
720
916
  },
721
917
 
722
918
  _genBuiltinLen(expr) {
@@ -727,7 +923,7 @@ export const expressionGenMethods = {
727
923
  if (wrapField) return `${this.genExpr(arg)}.${wrapField}.length`;
728
924
  const js = this.genExpr(arg);
729
925
  if (t?.kind === "map") return `Object.keys(${js}).length`;
730
- this._usesLen = true;
926
+ this.useHelper("len");
731
927
  return `__len(${js})`;
732
928
  },
733
929
 
@@ -760,7 +956,7 @@ export const expressionGenMethods = {
760
956
  a._spread ? `...${this.genExpr(a)}` : this.genValueExpr(a),
761
957
  );
762
958
  if (elems.length === 0) return this.genExpr(expr.args[0]);
763
- this._usesAppend = true;
959
+ this.useHelper("append");
764
960
  return `new ${retType.name}(__append(${sliceJS}, ${elems.join(", ")}))`;
765
961
  }
766
962
  const slice = this.genExpr(expr.args[0]);
@@ -768,7 +964,7 @@ export const expressionGenMethods = {
768
964
  .slice(1)
769
965
  .map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genValueExpr(a)));
770
966
  if (elems.length === 0) return slice;
771
- this._usesAppend = true;
967
+ this.useHelper("append");
772
968
  const sliceType = expr._type ?? expr.args[0]._type;
773
969
  const typedCtor = isTypedArraySlice(sliceType)
774
970
  ? typedArrayConstructorForElem(
@@ -950,10 +1146,38 @@ export const expressionGenMethods = {
950
1146
  return `new ${typeName}(${args.join(", ")})`;
951
1147
  },
952
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
+
953
1173
  genCompositeLit(expr) {
954
1174
  const t = expr.typeExpr;
955
1175
  if (t === null) return this._genImplicitCompositeLit(expr);
956
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: "" }';
957
1181
  if (typeName && this.structNames.has(typeName))
958
1182
  return this._genPositionalStructLit(typeName, expr);
959
1183
  if (typeName && this.namedWrapperNames.has(typeName))
@@ -961,29 +1185,7 @@ export const expressionGenMethods = {
961
1185
  if (expr.elems.some((e) => e._positionalField)) {
962
1186
  if (typeName) return this._genPositionalStructLit(typeName, expr);
963
1187
  }
964
- if (this._isSliceOrArrayType(t)) {
965
- const elems = expr.elems
966
- .map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
967
- .join(", ");
968
- const elemNode =
969
- t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
970
- const elemResolved =
971
- expr._type?.kind === "slice" || expr._type?.kind === "array"
972
- ? expr._type.elem
973
- : null;
974
- const typedCtor =
975
- typedArrayConstructorForElem(elemResolved) ??
976
- (elemNode?.name
977
- ? typedArrayConstructorForElem({
978
- kind: "basic",
979
- name: elemNode.name,
980
- })
981
- : null);
982
- if (typedCtor) {
983
- return `new ${typedCtor}([${elems}])`;
984
- }
985
- return `[${elems}]`;
986
- }
1188
+ if (this._isSliceOrArrayType(t)) return this._genSliceOrArrayLit(t, expr);
987
1189
  if (this._isMapTypeNode(t))
988
1190
  return `{ ${this._genMapLitEntries(expr.elems)} }`;
989
1191
  return this._genFallbackCompositeLit(expr);
@@ -1018,19 +1220,7 @@ export const expressionGenMethods = {
1018
1220
  const namedType = this.checker?.types.get(typeName);
1019
1221
  const u = namedType?.underlying;
1020
1222
  if (u?.kind === "map") {
1021
- const entries = expr.elems
1022
- .map((e) => {
1023
- if (e.kind === "KeyValueExpr") {
1024
- const k =
1025
- e.key.litKind === "STRING"
1026
- ? JSON.stringify(e.key.value)
1027
- : `[${this.genExpr(e.key)}]`;
1028
- return `${k}: ${this.genValueExpr(e.value)}`;
1029
- }
1030
- return this.genValueExpr(e);
1031
- })
1032
- .join(", ");
1033
- return `new ${typeName}({ ${entries} })`;
1223
+ return `new ${typeName}({ ${this._genMapLitEntries(expr.elems)} })`;
1034
1224
  }
1035
1225
  // Default: slice
1036
1226
  const elems = expr.elems
@@ -1093,8 +1283,11 @@ export const expressionGenMethods = {
1093
1283
  },
1094
1284
 
1095
1285
  _zeroForNamedType(name) {
1096
- if (name === "strings.Builder") return '{ _buf: "" }';
1097
- 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
+ }
1098
1291
  name = this._dequalify(name);
1099
1292
  if (this.structNames.has(name)) return `new ${name}()`;
1100
1293
  return "null";
@@ -1240,10 +1433,10 @@ export const expressionGenMethods = {
1240
1433
  case "-":
1241
1434
  return `{ re: ${l}.re - ${r}.re, im: ${l}.im - ${r}.im }`;
1242
1435
  case "*":
1243
- this._usesCmul = true;
1436
+ this.useHelper("cmul");
1244
1437
  return `__cmul(${l}, ${r})`;
1245
1438
  case "/":
1246
- this._usesCdiv = true;
1439
+ this.useHelper("cdiv");
1247
1440
  return `__cdiv(${l}, ${r})`;
1248
1441
  case "==":
1249
1442
  return `(${l}.re === ${r}.re && ${l}.im === ${r}.im)`;
@@ -1261,7 +1454,7 @@ export const expressionGenMethods = {
1261
1454
  return this._genComplexBinary(expr);
1262
1455
 
1263
1456
  if (this._isStructEquality(expr)) {
1264
- this._usesEqual = true;
1457
+ this.useHelper("equal");
1265
1458
  const cmp = `__equal(${this.genExpr(expr.left)}, ${this.genExpr(expr.right)})`;
1266
1459
  return expr.op === "==" ? cmp : `!${cmp}`;
1267
1460
  }
@@ -1346,7 +1539,7 @@ export const expressionGenMethods = {
1346
1539
  return this._genSliceTypeConversion(expr, inner, t);
1347
1540
  const target = t?.name;
1348
1541
  if (target === "error") {
1349
- this._usesError = true;
1542
+ this.useHelper("error");
1350
1543
  return `__error(${inner})`;
1351
1544
  }
1352
1545
  if (target && this.namedWrapperNames.has(target))
@@ -1366,24 +1559,17 @@ export const expressionGenMethods = {
1366
1559
  return inner;
1367
1560
  },
1368
1561
 
1369
- _isStringSource(srcType) {
1370
- return (
1371
- (srcType?.kind === "basic" && srcType?.name === "string") ||
1372
- (srcType?.kind === "untyped" && srcType?.base === "string")
1373
- );
1374
- },
1375
-
1376
1562
  _genSliceTypeConversion(expr, inner, t) {
1377
1563
  const elem = t.elem?.name;
1378
1564
  if (elem === "byte" || elem === "uint8") {
1379
- if (this._isStringSource(expr.expr._type))
1565
+ if (this._isStringExprType(expr.expr._type))
1380
1566
  return `new TextEncoder().encode(${inner})`;
1381
1567
  return `new Uint8Array(${inner})`;
1382
1568
  }
1383
1569
  const typedCtor = typedArrayConstructorForElem(
1384
1570
  t.elem ? { kind: "basic", name: t.elem.name } : null,
1385
1571
  );
1386
- const fromString = this._isStringSource(expr.expr._type);
1572
+ const fromString = this._isStringExprType(expr.expr._type);
1387
1573
  if (typedCtor) {
1388
1574
  return fromString
1389
1575
  ? `${typedCtor}.from(${inner}, __c => __c.codePointAt(0))`
@@ -1501,81 +1687,55 @@ export const expressionGenMethods = {
1501
1687
  if (tName === "float32") {
1502
1688
  return `Math.fround(${l} ${op} ${r})`;
1503
1689
  }
1504
- if (tName === "int32" || tName === "rune") {
1505
- if (op === "*") return `Math.imul(${l}, ${r})`;
1506
- if (op === "/")
1507
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) | 0))`;
1508
- if (op === "%")
1509
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) | 0))`;
1510
- if (op === "<<" || op === ">>") {
1511
- return `((${r}) >= 32 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : ((${l}) ${op} (${r}))) | 0`;
1512
- }
1513
- if (op === "&^") return `((${l}) & ~(${r})) | 0`;
1514
- return `((${l} ${op} ${r}) | 0)`;
1515
- }
1516
- 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})`;
1517
1696
  if (op === "/")
1518
- 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}))`;
1519
1698
  if (op === "%")
1520
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) >>> 0))`;
1699
+ return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) ${cast}))`;
1521
1700
  if (op === "<<" || op === ">>") {
1522
- const jsOp = op === ">>" ? ">>>" : "<<";
1523
- 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}`;
1524
1704
  }
1525
- if (op === "&^") return `((${l}) & ~(${r})) >>> 0`;
1526
- return `((${l} ${op} ${r}) >>> 0)`;
1705
+ if (op === "&^") return `((${l}) & ~(${r})) ${cast}`;
1706
+ return `((${l} ${op} ${r}) ${cast})`;
1527
1707
  }
1528
- if (tName === "int8") {
1708
+ if (tName === "int8" || tName === "int16") {
1709
+ const bits = tName === "int8" ? 8 : 16;
1710
+ const sh = 32 - bits;
1529
1711
  if (op === "/")
1530
- 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}))`;
1531
1713
  if (op === "%")
1532
- 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})))`;
1533
1715
  if (op === "<<" || op === ">>") {
1534
- 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}))`;
1535
1717
  }
1536
- if (op === "&^") return `((((${l}) & ~(${r})) << 24) >> 24)`;
1537
- return `((((${l} ${op} ${r})) << 24) >> 24)`;
1718
+ if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
1719
+ return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
1538
1720
  }
1539
- 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";
1540
1724
  if (op === "/")
1541
- 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}))`;
1542
1726
  if (op === "%")
1543
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((((${l}) % (${r})) << 16) >> 16)))`;
1544
- if (op === "<<" || op === ">>") {
1545
- return `((${r}) >= 16 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << 16 >> 16))`;
1546
- }
1547
- if (op === "&^") return `((((${l}) & ~(${r})) << 16) >> 16)`;
1548
- return `((((${l} ${op} ${r})) << 16) >> 16)`;
1549
- }
1550
- if (tName === "uint8" || tName === "byte") {
1551
- if (op === "/")
1552
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) & 0xFF))`;
1553
- if (op === "%")
1554
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) & 0xFF))`;
1727
+ return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
1555
1728
  if (op === "<<" || op === ">>") {
1556
1729
  const jsOp = op === ">>" ? ">>>" : "<<";
1557
- return `((${r}) >= 8 ? 0 : (((${l}) ${jsOp} (${r})) & 0xFF))`;
1730
+ return `((${r}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
1558
1731
  }
1559
- if (op === "&^") return `(((${l}) & ~(${r})) & 0xFF)`;
1560
- return `((${l} ${op} ${r}) & 0xFF)`;
1561
- }
1562
- if (tName === "uint16") {
1563
- if (op === "/")
1564
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) & 0xFFFF))`;
1565
- if (op === "%")
1566
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) & 0xFFFF))`;
1567
- if (op === "<<" || op === ">>") {
1568
- const jsOp = op === ">>" ? ">>>" : "<<";
1569
- return `((${r}) >= 16 ? 0 : (((${l}) ${jsOp} (${r})) & 0xFFFF))`;
1570
- }
1571
- if (op === "&^") return `(((${l}) & ~(${r})) & 0xFFFF)`;
1572
- return `((${l} ${op} ${r}) & 0xFFFF)`;
1732
+ if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
1733
+ return `((${l} ${op} ${r}) & ${mask})`;
1573
1734
  }
1574
1735
  if (this.isIntType(type)) {
1575
1736
  if (op === "/")
1576
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : Math.trunc((${l}) / (${r})))`;
1577
- if (op === "%")
1578
- 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})))`;
1579
1739
  }
1580
1740
  return `${l} ${op} ${r}`;
1581
1741
  },