gofront 1.6.0 → 1.7.0

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 (80) hide show
  1. package/README.md +35 -39
  2. package/package.json +37 -4
  3. package/src/backend/js/expressions.js +559 -144
  4. package/src/backend/js/index.js +174 -49
  5. package/src/backend/js/runtime.js +1518 -54
  6. package/src/backend/js/statements.js +91 -73
  7. package/src/backend/js/stdlib/base64.js +12 -0
  8. package/src/backend/js/stdlib/binary.js +102 -0
  9. package/src/backend/js/stdlib/bits.js +15 -1
  10. package/src/backend/js/stdlib/builder.js +28 -9
  11. package/src/backend/js/stdlib/bytes.js +75 -18
  12. package/src/backend/js/stdlib/errors.js +25 -1
  13. package/src/backend/js/stdlib/fmt.js +70 -13
  14. package/src/backend/js/stdlib/hex.js +17 -0
  15. package/src/backend/js/stdlib/html.js +1 -1
  16. package/src/backend/js/stdlib/index.js +28 -0
  17. package/src/backend/js/stdlib/io.js +105 -3
  18. package/src/backend/js/stdlib/json.js +972 -0
  19. package/src/backend/js/stdlib/math.js +13 -1
  20. package/src/backend/js/stdlib/path.js +18 -4
  21. package/src/backend/js/stdlib/rand.js +10 -5
  22. package/src/backend/js/stdlib/sha256.js +12 -0
  23. package/src/backend/js/stdlib/simd.js +24 -0
  24. package/src/backend/js/stdlib/slices.js +52 -26
  25. package/src/backend/js/stdlib/strconv.js +21 -5
  26. package/src/backend/js/stdlib/strings.js +148 -15
  27. package/src/backend/js/stdlib/sync.js +13 -0
  28. package/src/backend/js/stdlib/time.js +90 -19
  29. package/src/backend/js/stdlib/unicode.js +2 -2
  30. package/src/backend/wasm/boundary.js +1225 -41
  31. package/src/backend/wasm/const-fold.js +205 -0
  32. package/src/backend/wasm/emit-builtins.js +220 -76
  33. package/src/backend/wasm/emit-exprs.js +475 -276
  34. package/src/backend/wasm/emit-maps.js +144 -8
  35. package/src/backend/wasm/emit-stdlib.js +12 -3005
  36. package/src/backend/wasm/emit-stmts.js +264 -270
  37. package/src/backend/wasm/emit.js +124 -87
  38. package/src/backend/wasm/encode.js +140 -29
  39. package/src/backend/wasm/index.js +1001 -32
  40. package/src/backend/wasm/optimize.js +17 -6
  41. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  42. package/src/backend/wasm/stdlib/core.js +8903 -0
  43. package/src/backend/wasm/stdlib/index.js +43 -0
  44. package/src/backend/wasm/stdlib/io.js +2161 -0
  45. package/src/backend/wasm/stdlib/packages.js +2400 -0
  46. package/src/backend/wasm/stdlib/pure.js +720 -0
  47. package/src/backend/wasm/stdlib/simd.js +234 -0
  48. package/src/backend/wasm/stdlib/time.js +1200 -0
  49. package/src/backend/wasm/types.js +174 -1
  50. package/src/backend/wasm/wat.js +37 -0
  51. package/src/cli-core.js +1 -1
  52. package/src/compiler.js +18 -4
  53. package/src/dts-parser.js +150 -140
  54. package/src/index.js +2 -6
  55. package/src/lexer.js +80 -29
  56. package/src/lower/boxing.js +1 -7
  57. package/src/lower/captures.js +2 -6
  58. package/src/lower/embedding.js +2 -1
  59. package/src/lower/ownership.js +30 -9
  60. package/src/minifier.js +20 -68
  61. package/src/parser/declarations.js +3 -6
  62. package/src/parser/expressions.js +3 -8
  63. package/src/parser/index.js +18 -3
  64. package/src/parser/statements.js +25 -38
  65. package/src/parser/struct-tag.js +144 -0
  66. package/src/parser/types.js +64 -8
  67. package/src/resolver.js +2 -27
  68. package/src/templ-lexer.js +29 -63
  69. package/src/templ-parser.js +22 -45
  70. package/src/typechecker/assignability.js +126 -13
  71. package/src/typechecker/expressions.js +168 -16
  72. package/src/typechecker/index.js +35 -8
  73. package/src/typechecker/resolve.js +187 -8
  74. package/src/typechecker/statements.js +68 -9
  75. package/src/typechecker/stdlib/core.js +280 -89
  76. package/src/typechecker/stdlib/extended.js +490 -1
  77. package/src/typechecker/stdlib/simd.js +73 -0
  78. package/src/typechecker/stdlib.js +2 -0
  79. package/src/typechecker/types.js +362 -9
  80. package/src/vendor.js +86 -11
@@ -7,6 +7,7 @@ import {
7
7
  } from "../../lower/index.js";
8
8
  import {
9
9
  ERROR,
10
+ isAny,
10
11
  isComplex,
11
12
  isTypedArraySlice,
12
13
  typedArrayConstructorForElem,
@@ -40,6 +41,25 @@ const STRICT_OPS = new Set([
40
41
  ">>",
41
42
  ]);
42
43
 
44
+ const NAMED_ZERO_TABLE = {
45
+ "time.Time": [null, "{ _d: new Date(0), _isZero: true }"],
46
+ "strings.Builder": [null, '{ _buf: "" }'],
47
+ "bytes.Buffer": [null, "{ _buf: [] }"],
48
+ "strings.Replacer": [null, "{ _p: [], Replace(s) { return s; } }"],
49
+ "sync.Mutex": ["sync", "__sync_mutex()"],
50
+ "sync.RWMutex": ["sync", "__sync_rwmutex()"],
51
+ "sync.Once": ["sync", "__sync_once()"],
52
+ "sync.WaitGroup": ["sync", "__sync_waitgroup()"],
53
+ "binary.LittleEndian": ["binary", "__binary_le"],
54
+ "encoding/binary.LittleEndian": ["binary", "__binary_le"],
55
+ "binary.BigEndian": ["binary", "__binary_be"],
56
+ "encoding/binary.BigEndian": ["binary", "__binary_be"],
57
+ "base64.Encoding": ["base64", "__base64_std"],
58
+ "encoding/base64.Encoding": ["base64", "__base64_std"],
59
+ "simd.F32x4": [null, "Object.freeze([0, 0, 0, 0])"],
60
+ F32x4: [null, "Object.freeze([0, 0, 0, 0])"],
61
+ };
62
+
43
63
  // Splits a generated JS lvalue at its outermost trailing access:
44
64
  // `a.b[i]` → { obj: "a.b", key: "i" }, `a.b.c` → { obj: "a.b", prop: "c" }.
45
65
  function splitLastAccess(js) {
@@ -78,18 +98,76 @@ const NS_CONSTANTS = {
78
98
  math: {
79
99
  Pi: "Math.PI",
80
100
  E: "Math.E",
101
+ Phi: "1.618033988749895",
102
+ Sqrt2: "Math.SQRT2",
103
+ SqrtE: "1.6487212707001282",
104
+ SqrtPi: "1.7724538509055159",
105
+ SqrtPhi: "1.272019649514069",
106
+ Ln2: "Math.LN2",
107
+ Log2E: "Math.LOG2E",
108
+ Ln10: "Math.LN10",
109
+ Log10E: "Math.LOG10E",
110
+ MaxFloat32: "3.4028234663852886e38",
111
+ SmallestNonzeroFloat32: "1.401298464324817e-45",
81
112
  MaxFloat64: "Number.MAX_VALUE",
82
113
  SmallestNonzeroFloat64: "Number.MIN_VALUE",
83
114
  MaxInt: "Number.MAX_SAFE_INTEGER",
84
115
  MinInt: "Number.MIN_SAFE_INTEGER",
116
+ MaxInt8: "127",
117
+ MinInt8: "-128",
118
+ MaxInt16: "32767",
119
+ MinInt16: "-32768",
120
+ MaxInt32: "2147483647",
121
+ MinInt32: "-2147483648",
122
+ MaxInt64: "9223372036854775807",
123
+ MinInt64: "-9223372036854775808",
124
+ MaxUint8: "255",
125
+ MaxUint16: "65535",
126
+ MaxUint32: "4294967295",
127
+ MaxUint: "18446744073709551615",
128
+ MaxUint64: "18446744073709551615",
129
+ },
130
+ strconv: {
131
+ ErrSyntax: "__strconv_err_syntax",
132
+ ErrRange: "__strconv_err_range",
133
+ },
134
+ path: {
135
+ ErrBadPattern: "__path_err_bad_pattern",
136
+ },
137
+ filepath: {
138
+ ErrBadPattern: "__path_err_bad_pattern",
85
139
  },
86
140
  io: {
87
- EOF: '"EOF"',
141
+ EOF: "__io_eof",
88
142
  Discard:
89
- "{ WriteString(s) { return s.length; }, Write(b) { return b.length; } }",
143
+ "{ WriteString(s) { return [s.length, null]; }, Write(b) { return [b.length, null]; } }",
144
+ },
145
+ sha256: {
146
+ Size: "32",
147
+ BlockSize: "64",
148
+ },
149
+ hex: {
150
+ ErrLength: '__error("encoding/hex: odd length hex string")',
151
+ },
152
+ base64: {
153
+ StdPadding: "61",
154
+ NoPadding: "-1",
155
+ StdEncoding: "__base64_std",
156
+ URLEncoding: "__base64_url",
157
+ RawStdEncoding: "__base64_raw_std",
158
+ RawURLEncoding: "__base64_raw_url",
159
+ },
160
+ binary: {
161
+ LittleEndian: "__binary_le",
162
+ BigEndian: "__binary_be",
163
+ },
164
+ rand: {
165
+ Reader: "__crypto_rand_reader",
90
166
  },
91
167
  os: { Args: "process.argv" },
92
168
  time: {
169
+ Nanosecond: "1",
170
+ Microsecond: "1000",
93
171
  Millisecond: "1000000",
94
172
  Second: "1000000000",
95
173
  Minute: "60000000000",
@@ -274,12 +352,27 @@ const EXPR_GEN_DELEGATE = {
274
352
  /** @type {ThisType<CodeGen>} */
275
353
  export const expressionGenMethods = {
276
354
  genExpr(expr) {
277
- if (expr._ifaceBox && !expr._ifaceBoxing) {
355
+ if (this._isNamedIfaceBox(expr)) return this._genNamedIfaceBoxExpr(expr);
356
+ if (expr._ifaceBox && !expr._ifaceBoxing && !this._isNamedBoxKind(expr)) {
278
357
  expr._ifaceBoxing = true;
279
358
  try {
280
359
  this.useHelper("ifaceBox");
281
- const fn = expr._ifaceBox === "ptr" ? "__ifp" : "__ifv";
282
- return `${fn}(${this.genExpr(expr)})`;
360
+ const fn =
361
+ expr._ifaceBox === "ptr"
362
+ ? "__ifp"
363
+ : expr._ifaceBox === "ptr_slice"
364
+ ? "__ifps"
365
+ : expr._ifaceBox === "ptr_map"
366
+ ? "__ifpm"
367
+ : expr._ifaceBox === "map"
368
+ ? "__ifm"
369
+ : expr._ifaceBox === "slice"
370
+ ? "__ifs"
371
+ : "__ifv";
372
+ const extra = expr._ifacePtrFields?.length
373
+ ? `, ${JSON.stringify(expr._ifacePtrFields)}`
374
+ : "";
375
+ return `${fn}(${this.genExpr(expr)}${extra})`;
283
376
  } finally {
284
377
  expr._ifaceBoxing = false;
285
378
  }
@@ -307,6 +400,29 @@ export const expressionGenMethods = {
307
400
  }
308
401
  },
309
402
 
403
+ _isNamedBoxKind(expr) {
404
+ return expr._ifaceBox === "named" || expr._ifaceBox === "named_ptr";
405
+ },
406
+
407
+ _isNamedIfaceBox(expr) {
408
+ return (
409
+ this._isNamedBoxKind(expr) && !expr._ifaceBoxing && !expr._noNamedBox
410
+ );
411
+ },
412
+
413
+ _genNamedIfaceBoxExpr(expr) {
414
+ expr._ifaceBoxing = true;
415
+ try {
416
+ return this._genNamedIfaceBox(
417
+ this.genExpr(expr),
418
+ expr._ifaceNamedType,
419
+ expr._ifaceBox === "named_ptr",
420
+ );
421
+ } finally {
422
+ expr._ifaceBoxing = false;
423
+ }
424
+ },
425
+
310
426
  _isStructType(t) {
311
427
  if (!t) return false;
312
428
  if (t.name && this.structNames.has(t.name)) return true;
@@ -453,8 +569,13 @@ export const expressionGenMethods = {
453
569
  _emitParamCopies(params) {
454
570
  for (const p of params) {
455
571
  if (p.variadic || !p.name || p.name === "_") continue;
572
+ if (this._boxedVars.has(p.name)) {
573
+ // Address-taken parameter: rebind it to a { value } box.
574
+ this.line(`${p.name} = { value: ${p.name} };`);
575
+ continue;
576
+ }
456
577
  const t = this._typeFromNode(p.type);
457
- if (!t || this._boxedVars.has(p.name)) continue;
578
+ if (!t) continue;
458
579
  if (this._fnMutates(p.name)) {
459
580
  this.line(`${p.name} = ${this._cloneJs(t, p.name)};`);
460
581
  this._markOwnership(p.name, true);
@@ -500,6 +621,13 @@ export const expressionGenMethods = {
500
621
  ) {
501
622
  return this.genExpr(expr.operand);
502
623
  }
624
+ if (
625
+ expr.operand.kind === "SelectorExpr" ||
626
+ expr.operand.kind === "IndexExpr"
627
+ ) {
628
+ const inner = this.genExpr(expr.operand);
629
+ return `{ get value() { return ${inner}; }, set value(v) { ${inner} = v; } }`;
630
+ }
503
631
  // For non-boxed vars (primitives, etc.), wrap in { value: x }
504
632
  return `{ value: ${this.genExpr(expr.operand)} }`;
505
633
  }
@@ -525,11 +653,35 @@ export const expressionGenMethods = {
525
653
 
526
654
  _genSelectorExpr(expr) {
527
655
  if (expr._isMethodExpr) {
656
+ const namedExpr = this._namedMethodExprInfo(expr);
657
+ if (namedExpr) return `${namedExpr.cls}.$${expr.field}`;
528
658
  return `((recv, ...args) => recv.${expr.field}(...args))`;
529
659
  }
660
+ const namedValue = this._genNamedMethodValue(expr);
661
+ if (namedValue !== undefined) return namedValue;
530
662
  if (expr.expr.kind === "Ident") {
531
663
  const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
532
- if (nsConst !== undefined) return nsConst;
664
+ if (nsConst !== undefined) {
665
+ if (expr.expr.name === "strconv") {
666
+ this.useHelper("strconvError");
667
+ } else if (expr.expr.name === "path" || expr.expr.name === "filepath") {
668
+ if (expr.field === "ErrBadPattern") this.useHelper("pathError");
669
+ } else if (expr.expr.name === "io" && expr.field === "EOF") {
670
+ this.useHelper("ioEOF");
671
+ } else if (expr.expr.name === "binary") {
672
+ this.useHelper("binary");
673
+ } else if (expr.expr.name === "hex") {
674
+ this.useHelper("hex");
675
+ this.useHelper("error");
676
+ } else if (expr.expr.name === "base64") {
677
+ this.useHelper("base64");
678
+ } else if (expr.expr.name === "rand" && expr.field === "Reader") {
679
+ this.useHelper("cryptoRand");
680
+ } else if (expr.expr.name === "sha256") {
681
+ this.useHelper("sha256");
682
+ }
683
+ return nsConst;
684
+ }
533
685
  }
534
686
  const base = this.genExpr(expr.expr);
535
687
  if (this.bundledPackages.has(base)) return expr.field;
@@ -556,6 +708,81 @@ export const expressionGenMethods = {
556
708
  return f;
557
709
  },
558
710
 
711
+ // ── Named non-struct types with methods: raw values, static dispatch ──
712
+
713
+ // Boxes a raw named value (or *T) entering an interface so dynamic dispatch works.
714
+ _genNamedIfaceBox(js, type, isPtr) {
715
+ const info = this._namedBoxInfo(type);
716
+ if (!info) return js;
717
+ return isPtr ? `new ${info.cls}$Ptr(${js})` : `new ${info.cls}(${js})`;
718
+ },
719
+
720
+ // Static target of `x.M` on a named non-struct type: its class and the
721
+ // receiver argument (the { value } box for pointer receivers).
722
+ _namedMethodTarget(sel) {
723
+ const recvType = sel.expr?._type;
724
+ const viaPtr = recvType?.kind === "pointer";
725
+ const info = this._namedBoxInfo(viaPtr ? recvType.base : recvType);
726
+ const method = info?.methods.get(sel.field);
727
+ if (!method) return null;
728
+ let recv;
729
+ if (method._ptrRecv) {
730
+ recv = viaPtr ? this.genExpr(sel.expr) : this._genAddressOfExpr(sel.expr);
731
+ } else {
732
+ recv = viaPtr
733
+ ? `${this.genExpr(sel.expr)}.value`
734
+ : this.genExpr(sel.expr);
735
+ }
736
+ return { cls: info.cls, recv };
737
+ },
738
+
739
+ // `x.M` used as a value: binds the receiver now, as Go does.
740
+ _genNamedMethodValue(expr) {
741
+ if (!expr._isMethodValue || expr._callee) return undefined;
742
+ const target = this._namedMethodTarget(expr);
743
+ if (!target) return undefined;
744
+ return `((__r) => (...__a) => ${target.cls}.$${expr.field}(__r, ...__a))(${target.recv})`;
745
+ },
746
+
747
+ _genAddressOfExpr(operand) {
748
+ return this._genUnaryExpr({
749
+ kind: "UnaryExpr",
750
+ op: "&",
751
+ operand,
752
+ _type: { kind: "pointer", base: operand._type },
753
+ });
754
+ },
755
+
756
+ // `T.M` method expression on a named non-struct type.
757
+ _namedMethodExprInfo(sel) {
758
+ const t =
759
+ sel.expr?._type?.kind === "named"
760
+ ? sel.expr._type
761
+ : this.checker?.types.get(sel.expr?.name);
762
+ const info = this._namedBoxInfo(t);
763
+ return info?.methods.has(sel.field) ? info : null;
764
+ },
765
+
766
+ _genNamedMethodCall(expr) {
767
+ const sel = expr.func;
768
+ if (sel._isMethodExpr) {
769
+ const info = this._namedMethodExprInfo(sel);
770
+ if (!info) return undefined;
771
+ return `${info.cls}.$${sel.field}(${this._genCallArgs(expr)})`;
772
+ }
773
+ const target = this._namedMethodTarget(sel);
774
+ if (!target) return undefined;
775
+ const args = this._genCallArgs(expr);
776
+ return `${target.cls}.$${sel.field}(${args ? `${target.recv}, ${args}` : target.recv})`;
777
+ },
778
+
779
+ _genCallArgs(expr) {
780
+ if (expr._multiForward) return `...${this.genExpr(expr.args[0])}`;
781
+ return expr.args
782
+ .map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
783
+ .join(", ");
784
+ },
785
+
559
786
  _genPointerSelectorField(expr, base) {
560
787
  if (this._isStructPointerType(expr.expr._type)) {
561
788
  const sel = `${base}.${expr.field}`;
@@ -590,9 +817,10 @@ export const expressionGenMethods = {
590
817
  },
591
818
 
592
819
  _isStringExprType(exprType) {
820
+ const t = exprType?.kind === "named" ? exprType.underlying : exprType;
593
821
  return (
594
- (exprType?.kind === "basic" && exprType.name === "string") ||
595
- (exprType?.kind === "untyped" && exprType.base === "string")
822
+ (t?.kind === "basic" && t.name === "string") ||
823
+ (t?.kind === "untyped" && t.base === "string")
596
824
  );
597
825
  },
598
826
 
@@ -635,22 +863,104 @@ export const expressionGenMethods = {
635
863
  const val = hoist ? "__v" : src;
636
864
  const check = this._typeCheckExpr(expr.type, val);
637
865
  const wrap = (body) => (hoist ? `((__v) => ${body})(${src})` : body);
866
+ // Named non-struct values sit in interfaces as boxes; the asserted value is raw.
867
+ const out = this._isNamedBoxTypeNode(expr.type)
868
+ ? `${val}.__gfUnbox()`
869
+ : val;
638
870
  if (!expr._commaOk) {
639
871
  // plain assertion: panic if check fails (matches Go behavior)
640
872
  if (check === "true") return src; // can't check at runtime — pass through
641
873
  return wrap(
642
- `(${check} ? ${val} : (() => { throw new Error("interface conversion: type assertion failed"); })())`,
874
+ `(${check} ? ${out} : (() => { throw new Error("interface conversion: type assertion failed"); })())`,
643
875
  );
644
876
  }
645
877
  // comma-ok: emit [value-or-zero, runtimeTypeCheck]
646
878
  const zero = this.zeroValueForTypeNode(expr.type);
647
- return wrap(`(${check} ? [${val}, true] : [${zero}, false])`);
879
+ return wrap(`(${check} ? [${out}, true] : [${zero}, false])`);
880
+ },
881
+
882
+ _typeNameCheckExpr(name, val) {
883
+ if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
884
+ if (name === "string") return `typeof ${val} === "string"`;
885
+ if (name === "bool") return `typeof ${val} === "boolean"`;
886
+ if (name === "nil") return `${val} === null`;
887
+ if (name === "error")
888
+ return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
889
+ if (this.structNames.has(name))
890
+ return `(${val} instanceof ${name} && ${val}.__p !== true)`;
891
+ const named = this.checker?.types.get(name);
892
+ const namedInfo = this._namedBoxInfo(named);
893
+ if (namedInfo) return `(${val} instanceof ${namedInfo.cls})`;
894
+ // User-declared interfaces: check the dynamic value has every method.
895
+ if (!name.includes(".") && named?.underlying?.kind === "interface")
896
+ return this._ifaceMethodCheck(val, [...named.underlying.methods.keys()]);
897
+ return "true"; // unknown type — can't check at runtime
898
+ },
899
+
900
+ // Anonymous interfaces and pointers to named non-struct types; null otherwise.
901
+ _namedOrIfaceCheckExpr(typeNode, val) {
902
+ if (typeNode.kind === "InterfaceType" && !(typeNode.embeds?.length > 0))
903
+ return this._ifaceMethodCheck(
904
+ val,
905
+ (typeNode.methods ?? []).map((m) => m.name),
906
+ );
907
+ if (typeNode.kind === "PointerType" && typeNode.base?.kind === "TypeName") {
908
+ const info = this._namedBoxInfo(
909
+ this.checker?.types.get(typeNode.base.name),
910
+ );
911
+ if (info) return `(${val} instanceof ${info.cls}$Ptr)`;
912
+ }
913
+ return null;
914
+ },
915
+
916
+ _ifaceMethodCheck(val, methodNames) {
917
+ const checks = methodNames.map((m) => `typeof ${val}.${m} === "function"`);
918
+ return `(${[`${val} != null`, ...checks].join(" && ")})`;
919
+ },
920
+
921
+ _isNamedBoxTypeNode(typeNode) {
922
+ if (typeNode?.kind === "TypeName")
923
+ return Boolean(
924
+ this._namedBoxInfo(this.checker?.types.get(typeNode.name)),
925
+ );
926
+ if (typeNode?.kind === "PointerType" && typeNode.base?.kind === "TypeName")
927
+ return Boolean(
928
+ this._namedBoxInfo(this.checker?.types.get(typeNode.base.name)),
929
+ );
930
+ return false;
648
931
  },
649
932
 
650
933
  _genTypeConversionCall(expr) {
651
- const name = expr._conversionTargetType?.name;
934
+ const targetType = expr._conversionTargetType;
652
935
  const arg = this.genExpr(expr.args[0]);
653
- if (name && this.namedWrapperNames.has(name)) return `new ${name}(${arg})`;
936
+ const isRaw =
937
+ targetType?.name === "json.RawMessage" ||
938
+ targetType?.name === "encoding/json.RawMessage";
939
+ const base =
940
+ targetType?.kind === "named" ? targetType.underlying : targetType;
941
+ if (
942
+ targetType?.kind === "named" &&
943
+ base?.kind === "basic" &&
944
+ this._namedBoxInfo(targetType)
945
+ )
946
+ return this._genPrimitiveConversion(
947
+ { expr: expr.args[0] },
948
+ arg,
949
+ base.name,
950
+ );
951
+ if (base?.kind === "slice") {
952
+ const elem = base.elem?.name;
953
+ if (
954
+ (elem === "byte" || elem === "uint8") &&
955
+ this._isStringExprType(expr.args[0]?._type)
956
+ ) {
957
+ const enc = `new TextEncoder().encode(${arg})`;
958
+ return isRaw ? `((u) => { u.__raw = true; return u; })(${enc})` : enc;
959
+ }
960
+ if (isRaw) {
961
+ return `((u) => { u.__raw = true; return u; })(${arg})`;
962
+ }
963
+ }
654
964
  return arg;
655
965
  },
656
966
 
@@ -705,12 +1015,93 @@ export const expressionGenMethods = {
705
1015
  return this._genRegexpMethodCall(expr.func.field, expr);
706
1016
  if (recvName === "time.Time")
707
1017
  return this._genTimeMethodCall(expr.func.field, expr);
1018
+ if (recvName === "time.Duration")
1019
+ return this._genDurationMethodCall(expr.func.field, expr);
708
1020
  if (recvName === "testing.T")
709
1021
  return this._genTestingMethodCall(expr.func.field, expr);
1022
+ if (recvName === "strings.Replacer") {
1023
+ const recv = this.genExpr(expr.func.expr);
1024
+ const arg = this.genExpr(expr.args[0]);
1025
+ return `(${recv} ? ${recv}.Replace(${arg}) : ${arg})`;
1026
+ }
710
1027
  if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
711
1028
  const args = expr.args.map((a) => this.genExpr(a)).join(", ");
712
1029
  return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
713
1030
  }
1031
+ if (recvName === "simd.F32x4" || recvName === "F32x4") {
1032
+ return this._genSimdReceiverCall(expr);
1033
+ }
1034
+ if (recvName === "sync.Mutex" || recvName === "sync.RWMutex") {
1035
+ if (expr.func.field === "TryLock" || expr.func.field === "TryRLock")
1036
+ return "true";
1037
+ return "(void 0)";
1038
+ }
1039
+ if (recvName === "sync.Once" && expr.func.field === "Do") {
1040
+ const recv = this.genExpr(expr.func.expr);
1041
+ const f = this.genExpr(expr.args[0]);
1042
+ return `((o, fn) => { if (!o.done) { o.done = 1; fn(); } })(${recv}, ${f})`;
1043
+ }
1044
+ if (recvName === "sync.WaitGroup") {
1045
+ const recv = this.genExpr(expr.func.expr);
1046
+ if (expr.func.field === "Add") {
1047
+ return `((wg, d) => { wg.counter = (wg.counter || 0) + d; })(${recv}, ${this.genExpr(expr.args[0])})`;
1048
+ }
1049
+ if (expr.func.field === "Done") {
1050
+ return `((wg) => { wg.counter = (wg.counter || 0) - 1; })(${recv})`;
1051
+ }
1052
+ if (expr.func.field === "Wait") return "(void 0)";
1053
+ }
1054
+ if (
1055
+ recvName === "binary.LittleEndian" ||
1056
+ recvName === "binary.BigEndian" ||
1057
+ recvName === "encoding/binary.LittleEndian" ||
1058
+ recvName === "encoding/binary.BigEndian" ||
1059
+ recvName === "binary.ByteOrder" ||
1060
+ recvName === "encoding/binary.ByteOrder"
1061
+ ) {
1062
+ this.useHelper("binary");
1063
+ const recv = this.genExpr(expr.func.expr);
1064
+ const args = expr.args.map((a) => this.genExpr(a)).join(", ");
1065
+ return `${recv}.${expr.func.field}(${args})`;
1066
+ }
1067
+ if (
1068
+ recvName === "base64.Encoding" ||
1069
+ recvName === "encoding/base64.Encoding"
1070
+ ) {
1071
+ this.useHelper("base64");
1072
+ const recv = this.genExpr(expr.func.expr);
1073
+ const args = expr.args.map((a) => this.genExpr(a)).join(", ");
1074
+ return `${recv}.${expr.func.field}(${args})`;
1075
+ }
1076
+ return undefined;
1077
+ },
1078
+
1079
+ _genSimdReceiverCall(expr) {
1080
+ this.useHelper("simd");
1081
+ const method = expr.func.field;
1082
+ const recv = this.genExpr(expr.func.expr);
1083
+ const helpers = {
1084
+ Add: "__simd_add",
1085
+ Sub: "__simd_sub",
1086
+ Mul: "__simd_mul",
1087
+ Div: "__simd_div",
1088
+ Min: "__simd_min",
1089
+ Max: "__simd_max",
1090
+ Dot3: "__simd_dot3",
1091
+ Dot4: "__simd_dot4",
1092
+ };
1093
+ if (helpers[method]) {
1094
+ return `${helpers[method]}(${recv}, ${this.genExpr(expr.args[0])})`;
1095
+ }
1096
+ if (method === "Lane") {
1097
+ return `${recv}[${this.genExpr(expr.args[0])}]`;
1098
+ }
1099
+ if (method === "WithLane") {
1100
+ return `__simd_with_lane(${recv}, ${this.genExpr(expr.args[0])}, ${this.genExpr(expr.args[1])})`;
1101
+ }
1102
+ if (method === "Splat") {
1103
+ return `__simd_splat(${this.genExpr(expr.args[0])})`;
1104
+ }
714
1105
  return undefined;
715
1106
  },
716
1107
 
@@ -731,6 +1122,8 @@ export const expressionGenMethods = {
731
1122
  const recvName = this._resolveRecvName(recvType);
732
1123
  const typed = this._genReceiverTypeCall(recvName, expr);
733
1124
  if (typed !== undefined) return typed;
1125
+ const namedCall = this._genNamedMethodCall(expr);
1126
+ if (namedCall !== undefined) return namedCall;
734
1127
  if (this.strict && this._isInterfaceType(recvType)) {
735
1128
  // Go panics with a nil-dereference runtime error; JS would throw a
736
1129
  // TypeError with a different message.
@@ -933,7 +1326,9 @@ export const expressionGenMethods = {
933
1326
  ? this.genValueExpr(match.value ?? match)
934
1327
  : fields[idx].zero;
935
1328
  } else {
936
- values[idx] = `${this.genExpr(e.value)}.${ef}`;
1329
+ const valExpr = this.genExpr(e.value);
1330
+ values[idx] =
1331
+ `(${valExpr} != null ? ${valExpr}.${ef} : ${fields[idx].zero})`;
937
1332
  }
938
1333
  return idx;
939
1334
  },
@@ -951,6 +1346,11 @@ export const expressionGenMethods = {
951
1346
  const idx = this._fillEmbedField(fields, ef, e, values);
952
1347
  if (idx > maxIdx) maxIdx = idx;
953
1348
  }
1349
+ const embedIdx = fields.findIndex((f) => f.name === k);
1350
+ if (embedIdx >= 0) {
1351
+ values[embedIdx] = this.genValueExpr(e.value);
1352
+ if (embedIdx > maxIdx) maxIdx = embedIdx;
1353
+ }
954
1354
  } else {
955
1355
  const idx = fields.findIndex((f) => f.name === k);
956
1356
  if (idx >= 0) {
@@ -974,17 +1374,53 @@ export const expressionGenMethods = {
974
1374
  const maxExplicitIdx = hasPositional
975
1375
  ? this._fillPositionalStructElems(fields, expr, values)
976
1376
  : this._fillKeyedStructElems(fields, expr, values);
977
- if (maxExplicitIdx < 0) {
978
- return `new ${typeName}()`;
1377
+ const inst =
1378
+ maxExplicitIdx < 0
1379
+ ? `new ${typeName}()`
1380
+ : `new ${typeName}(${values.slice(0, maxExplicitIdx + 1).join(", ")})`;
1381
+ return inst;
1382
+ },
1383
+
1384
+ _genSliceOrArrayLit(t, expr) {
1385
+ const elems = expr.elems
1386
+ .map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
1387
+ .join(", ");
1388
+ const elemNode =
1389
+ t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
1390
+ const elemResolved =
1391
+ expr._type?.kind === "slice" || expr._type?.kind === "array"
1392
+ ? expr._type.elem
1393
+ : null;
1394
+ const typedCtor =
1395
+ typedArrayConstructorForElem(elemResolved) ??
1396
+ (elemNode?.name
1397
+ ? typedArrayConstructorForElem({
1398
+ kind: "basic",
1399
+ name: elemNode.name,
1400
+ })
1401
+ : null);
1402
+ if (typedCtor) {
1403
+ return `new ${typedCtor}([${elems}])`;
979
1404
  }
980
- const args = values.slice(0, maxExplicitIdx + 1);
981
- return `new ${typeName}(${args.join(", ")})`;
1405
+ return `[${elems}]`;
982
1406
  },
983
1407
 
984
1408
  genCompositeLit(expr) {
985
1409
  const t = expr.typeExpr;
986
1410
  if (t === null) return this._genImplicitCompositeLit(expr);
987
1411
  const typeName = this.getTypeName(t);
1412
+ if (typeName === "bytes.Buffer" || typeName === "Buffer")
1413
+ return "{ _buf: [] }";
1414
+ if (typeName === "strings.Builder" || typeName === "Builder")
1415
+ return '{ _buf: "" }';
1416
+ if (typeName === "simd.F32x4" || typeName === "F32x4") {
1417
+ this.useHelper("simd");
1418
+ const a = expr.elems[0] ? this.genExpr(expr.elems[0]) : "0";
1419
+ const b = expr.elems[1] ? this.genExpr(expr.elems[1]) : "0";
1420
+ const c = expr.elems[2] ? this.genExpr(expr.elems[2]) : "0";
1421
+ const d = expr.elems[3] ? this.genExpr(expr.elems[3]) : "0";
1422
+ return `__simd_f32x4(${a}, ${b}, ${c}, ${d})`;
1423
+ }
988
1424
  if (typeName && this.structNames.has(typeName))
989
1425
  return this._genPositionalStructLit(typeName, expr);
990
1426
  if (typeName && this.namedWrapperNames.has(typeName))
@@ -992,29 +1428,7 @@ export const expressionGenMethods = {
992
1428
  if (expr.elems.some((e) => e._positionalField)) {
993
1429
  if (typeName) return this._genPositionalStructLit(typeName, expr);
994
1430
  }
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
- }
1431
+ if (this._isSliceOrArrayType(t)) return this._genSliceOrArrayLit(t, expr);
1018
1432
  if (this._isMapTypeNode(t))
1019
1433
  return `{ ${this._genMapLitEntries(expr.elems)} }`;
1020
1434
  return this._genFallbackCompositeLit(expr);
@@ -1049,25 +1463,13 @@ export const expressionGenMethods = {
1049
1463
  const namedType = this.checker?.types.get(typeName);
1050
1464
  const u = namedType?.underlying;
1051
1465
  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} })`;
1466
+ return `{ ${this._genMapLitEntries(expr.elems)} }`;
1065
1467
  }
1066
1468
  // Default: slice
1067
1469
  const elems = expr.elems
1068
1470
  .map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
1069
1471
  .join(", ");
1070
- return `new ${typeName}([${elems}])`;
1472
+ return `[${elems}]`;
1071
1473
  },
1072
1474
 
1073
1475
  // ── Helpers ───────────────────────────────────────────────────
@@ -1109,11 +1511,14 @@ export const expressionGenMethods = {
1109
1511
  },
1110
1512
 
1111
1513
  // Returns true if the AST node contains a function call (side-effect risk).
1112
- _hasCallExpr(node) {
1514
+ _hasCallExpr(node, seen = new Set()) {
1113
1515
  if (!node || typeof node !== "object") return false;
1516
+ if (seen.has(node)) return false;
1517
+ seen.add(node);
1114
1518
  if (node.kind === "CallExpr") return true;
1115
- for (const v of Object.values(node)) {
1116
- if (v && typeof v === "object" && this._hasCallExpr(v)) return true;
1519
+ for (const [k, v] of Object.entries(node)) {
1520
+ if (k.startsWith("_") || k === "scope") continue;
1521
+ if (v && typeof v === "object" && this._hasCallExpr(v, seen)) return true;
1117
1522
  }
1118
1523
  return false;
1119
1524
  },
@@ -1124,8 +1529,20 @@ export const expressionGenMethods = {
1124
1529
  },
1125
1530
 
1126
1531
  _zeroForNamedType(name) {
1127
- if (name === "strings.Builder") return '{ _buf: "" }';
1128
- if (name === "bytes.Buffer") return "{ _buf: [] }";
1532
+ const entry = NAMED_ZERO_TABLE[name];
1533
+ if (entry) {
1534
+ if (entry[0]) this.useHelper(entry[0]);
1535
+ return entry[1];
1536
+ }
1537
+ const named = this.checker?.types.get(name);
1538
+ const u = named?.kind === "named" ? named.underlying : null;
1539
+ if (
1540
+ u &&
1541
+ u.kind !== "struct" &&
1542
+ u.kind !== "interface" &&
1543
+ u.kind !== "named"
1544
+ )
1545
+ return this.zeroValueForType(u);
1129
1546
  name = this._dequalify(name);
1130
1547
  if (this.structNames.has(name)) return `new ${name}()`;
1131
1548
  return "null";
@@ -1214,18 +1631,10 @@ export const expressionGenMethods = {
1214
1631
  // Emit a JS boolean expression that checks whether `val` matches type node `t`.
1215
1632
  _typeCheckExpr(typeNode, val) {
1216
1633
  if (!typeNode) return "true";
1217
- if (typeNode.kind === "TypeName") {
1218
- const name = typeNode.name;
1219
- if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
1220
- if (name === "string") return `typeof ${val} === "string"`;
1221
- if (name === "bool") return `typeof ${val} === "boolean"`;
1222
- if (name === "nil") return `${val} === null`;
1223
- if (name === "error")
1224
- return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
1225
- if (this.structNames.has(name))
1226
- return `(${val} instanceof ${name} && ${val}.__p !== true)`;
1227
- return "true"; // unknown type — can't check at runtime
1228
- }
1634
+ if (typeNode.kind === "TypeName")
1635
+ return this._typeNameCheckExpr(typeNode.name, val);
1636
+ const extended = this._namedOrIfaceCheckExpr(typeNode, val);
1637
+ if (extended) return extended;
1229
1638
  if (
1230
1639
  typeNode.kind === "PointerType" &&
1231
1640
  typeNode.base?.kind === "TypeName" &&
@@ -1297,6 +1706,12 @@ export const expressionGenMethods = {
1297
1706
  return expr.op === "==" ? cmp : `!${cmp}`;
1298
1707
  }
1299
1708
 
1709
+ if (this._isIfaceEquality(expr)) {
1710
+ this.useHelper("ifeq");
1711
+ const cmp = `__ifeq(${this._genIfaceOperand(expr.left)}, ${this._genIfaceOperand(expr.right)})`;
1712
+ return expr.op === "==" ? cmp : `!${cmp}`;
1713
+ }
1714
+
1300
1715
  if (this.strict && this._needsStrictOp(expr.op, expr._type)) {
1301
1716
  const l = this.genExpr(expr.left);
1302
1717
  const r = this.genExpr(expr.right);
@@ -1331,6 +1746,31 @@ export const expressionGenMethods = {
1331
1746
  );
1332
1747
  },
1333
1748
 
1749
+ _isIfaceType(t) {
1750
+ return isAny(t) || this._isInterfaceType(t);
1751
+ },
1752
+
1753
+ // Interface comparisons go through __ifeq so boxed named values compare by
1754
+ // dynamic type and value, as in Go (`err == ErrNotFound`).
1755
+ _isIfaceEquality(expr) {
1756
+ if (expr.op !== "==" && expr.op !== "!=") return false;
1757
+ if (this._isNilLiteral(expr.left) || this._isNilLiteral(expr.right))
1758
+ return false;
1759
+ return (
1760
+ this._isIfaceType(expr.left._type) || this._isIfaceType(expr.right._type)
1761
+ );
1762
+ },
1763
+
1764
+ // A concrete operand compared with an interface is converted to an interface first.
1765
+ _genIfaceOperand(e) {
1766
+ if (e._ifaceBox || this._isIfaceType(e._type)) return this.genExpr(e);
1767
+ const isPtr = e._type?.kind === "pointer";
1768
+ const named = isPtr ? e._type.base : e._type;
1769
+ if (this._namedBoxInfo(named))
1770
+ return this._genNamedIfaceBox(this.genExpr(e), named, isPtr);
1771
+ return this.genExpr(e);
1772
+ },
1773
+
1334
1774
  _isIntDivision(expr) {
1335
1775
  return (
1336
1776
  expr.op === "/" &&
@@ -1356,6 +1796,7 @@ export const expressionGenMethods = {
1356
1796
  )
1357
1797
  return null;
1358
1798
  if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
1799
+ if (this._isIfaceEquality(expr)) return null;
1359
1800
  if (expr.op === "&^") return "&";
1360
1801
  if (expr.op === "==" || expr.op === "!=") {
1361
1802
  if (this._isNilLiteral(expr.left) || this._isNilLiteral(expr.right))
@@ -1380,8 +1821,13 @@ export const expressionGenMethods = {
1380
1821
  this.useHelper("error");
1381
1822
  return `__error(${inner})`;
1382
1823
  }
1383
- if (target && this.namedWrapperNames.has(target))
1384
- return `new ${target}(${inner})`;
1824
+ if (target && this.namedWrapperNames.has(target)) {
1825
+ const named = this.checker?.types.get(target);
1826
+ const u = named?.underlying;
1827
+ return u?.kind === "basic"
1828
+ ? this._genPrimitiveConversion(expr, inner, u.name)
1829
+ : inner;
1830
+ }
1385
1831
  return this._genPrimitiveConversion(expr, inner, target);
1386
1832
  },
1387
1833
 
@@ -1397,24 +1843,19 @@ export const expressionGenMethods = {
1397
1843
  return inner;
1398
1844
  },
1399
1845
 
1400
- _isStringSource(srcType) {
1401
- return (
1402
- (srcType?.kind === "basic" && srcType?.name === "string") ||
1403
- (srcType?.kind === "untyped" && srcType?.base === "string")
1404
- );
1405
- },
1406
-
1407
1846
  _genSliceTypeConversion(expr, inner, t) {
1408
1847
  const elem = t.elem?.name;
1409
1848
  if (elem === "byte" || elem === "uint8") {
1410
- if (this._isStringSource(expr.expr._type))
1849
+ if (this._isStringExprType(expr.expr._type))
1411
1850
  return `new TextEncoder().encode(${inner})`;
1851
+ if (isAny(expr.expr._type) || !expr.expr._type)
1852
+ return `(typeof ${inner} === "string" ? new TextEncoder().encode(${inner}) : new Uint8Array(${inner}))`;
1412
1853
  return `new Uint8Array(${inner})`;
1413
1854
  }
1414
1855
  const typedCtor = typedArrayConstructorForElem(
1415
1856
  t.elem ? { kind: "basic", name: t.elem.name } : null,
1416
1857
  );
1417
- const fromString = this._isStringSource(expr.expr._type);
1858
+ const fromString = this._isStringExprType(expr.expr._type);
1418
1859
  if (typedCtor) {
1419
1860
  return fromString
1420
1861
  ? `${typedCtor}.from(${inner}, __c => __c.codePointAt(0))`
@@ -1426,10 +1867,10 @@ export const expressionGenMethods = {
1426
1867
  },
1427
1868
 
1428
1869
  _genStringConversion(srcType, inner) {
1429
- if (srcType && this.isIntType(srcType))
1430
- return `String.fromCodePoint(${inner})`;
1431
- if (srcType?.kind === "slice") {
1432
- const elem = srcType.elem?.name;
1870
+ const base = srcType?.kind === "named" ? srcType.underlying : srcType;
1871
+ if (base && this.isIntType(base)) return `String.fromCodePoint(${inner})`;
1872
+ if (base?.kind === "slice") {
1873
+ const elem = base.elem?.name;
1433
1874
  if (elem === "byte" || elem === "uint8")
1434
1875
  return `new TextDecoder().decode(new Uint8Array(${inner}))`;
1435
1876
  if (elem === "rune" || elem === "int" || elem === "int32")
@@ -1532,81 +1973,55 @@ export const expressionGenMethods = {
1532
1973
  if (tName === "float32") {
1533
1974
  return `Math.fround(${l} ${op} ${r})`;
1534
1975
  }
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") {
1548
- if (op === "/")
1549
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) >>> 0))`;
1550
- if (op === "%")
1551
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) >>> 0))`;
1552
- if (op === "<<" || op === ">>") {
1553
- const jsOp = op === ">>" ? ">>>" : "<<";
1554
- return `((${r}) >= 32 ? 0 : ((${l}) ${jsOp} (${r}))) >>> 0`;
1555
- }
1556
- if (op === "&^") return `((${l}) & ~(${r})) >>> 0`;
1557
- return `((${l} ${op} ${r}) >>> 0)`;
1558
- }
1559
- if (tName === "int8") {
1560
- if (op === "/")
1561
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : ((Math.trunc((${l}) / (${r})) << 24) >> 24))`;
1562
- if (op === "%")
1563
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((((${l}) % (${r})) << 24) >> 24)))`;
1564
- if (op === "<<" || op === ">>") {
1565
- return `((${r}) >= 8 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << 24 >> 24))`;
1566
- }
1567
- if (op === "&^") return `((((${l}) & ~(${r})) << 24) >> 24)`;
1568
- return `((((${l} ${op} ${r})) << 24) >> 24)`;
1569
- }
1570
- if (tName === "int16") {
1976
+ const divZero =
1977
+ '(() => { throw new Error("runtime error: integer divide by zero"); })()';
1978
+ if (tName === "int32" || tName === "rune" || tName === "uint32") {
1979
+ const signed = tName !== "uint32";
1980
+ const cast = signed ? "| 0" : ">>> 0";
1981
+ if (signed && op === "*") return `Math.imul(${l}, ${r})`;
1571
1982
  if (op === "/")
1572
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : ((Math.trunc((${l}) / (${r})) << 16) >> 16))`;
1983
+ return `((${r}) === 0 ? ${divZero} : (Math.trunc((${l}) / (${r})) ${cast}))`;
1573
1984
  if (op === "%")
1574
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((((${l}) % (${r})) << 16) >> 16)))`;
1985
+ return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) ${cast}))`;
1575
1986
  if (op === "<<" || op === ">>") {
1576
- return `((${r}) >= 16 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << 16 >> 16))`;
1987
+ const ovf = signed && op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0";
1988
+ const jsOp = !signed && op === ">>" ? ">>>" : op;
1989
+ return `((${r}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast}`;
1577
1990
  }
1578
- if (op === "&^") return `((((${l}) & ~(${r})) << 16) >> 16)`;
1579
- return `((((${l} ${op} ${r})) << 16) >> 16)`;
1991
+ if (op === "&^") return `((${l}) & ~(${r})) ${cast}`;
1992
+ return `((${l} ${op} ${r}) ${cast})`;
1580
1993
  }
1581
- if (tName === "uint8" || tName === "byte") {
1994
+ if (tName === "int8" || tName === "int16") {
1995
+ const bits = tName === "int8" ? 8 : 16;
1996
+ const sh = 32 - bits;
1582
1997
  if (op === "/")
1583
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) & 0xFF))`;
1998
+ return `((${r}) === 0 ? ${divZero} : ((Math.trunc((${l}) / (${r})) << ${sh}) >> ${sh}))`;
1584
1999
  if (op === "%")
1585
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) & 0xFF))`;
2000
+ return `((${r}) === 0 ? ${divZero} : (((((${l}) % (${r})) << ${sh}) >> ${sh})))`;
1586
2001
  if (op === "<<" || op === ">>") {
1587
- const jsOp = op === ">>" ? ">>>" : "<<";
1588
- return `((${r}) >= 8 ? 0 : (((${l}) ${jsOp} (${r})) & 0xFF))`;
2002
+ return `((${r}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
1589
2003
  }
1590
- if (op === "&^") return `(((${l}) & ~(${r})) & 0xFF)`;
1591
- return `((${l} ${op} ${r}) & 0xFF)`;
2004
+ if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
2005
+ return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
1592
2006
  }
1593
- if (tName === "uint16") {
2007
+ if (tName === "uint8" || tName === "byte" || tName === "uint16") {
2008
+ const bits = tName === "uint16" ? 16 : 8;
2009
+ const mask = bits === 16 ? "0xFFFF" : "0xFF";
1594
2010
  if (op === "/")
1595
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) & 0xFFFF))`;
2011
+ return `((${r}) === 0 ? ${divZero} : (Math.trunc((${l}) / (${r})) & ${mask}))`;
1596
2012
  if (op === "%")
1597
- return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) & 0xFFFF))`;
2013
+ return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
1598
2014
  if (op === "<<" || op === ">>") {
1599
2015
  const jsOp = op === ">>" ? ">>>" : "<<";
1600
- return `((${r}) >= 16 ? 0 : (((${l}) ${jsOp} (${r})) & 0xFFFF))`;
2016
+ return `((${r}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
1601
2017
  }
1602
- if (op === "&^") return `(((${l}) & ~(${r})) & 0xFFFF)`;
1603
- return `((${l} ${op} ${r}) & 0xFFFF)`;
2018
+ if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
2019
+ return `((${l} ${op} ${r}) & ${mask})`;
1604
2020
  }
1605
2021
  if (this.isIntType(type)) {
1606
2022
  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})))`;
2023
+ return `((${r}) === 0 ? ${divZero} : Math.trunc((${l}) / (${r})))`;
2024
+ if (op === "%") return `((${r}) === 0 ? ${divZero} : ((${l}) % (${r})))`;
1610
2025
  }
1611
2026
  return `${l} ${op} ${r}`;
1612
2027
  },