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
@@ -57,3 +57,35 @@ export function buildSourceMap(sources, mappings, sourcesContent) {
57
57
  if (sourcesContent) map.sourcesContent = sourcesContent;
58
58
  return JSON.stringify(map);
59
59
  }
60
+
61
+ // WASM source maps use the convention "generated line 0, generated column =
62
+ // byte offset into the module". `mappings` is Array<{ offset, srcFileIdx,
63
+ // srcLine }> (0-based line), one segment per function body (function-level
64
+ // granularity); DevTools shows the Go file/line for each wasm function.
65
+ export function buildWasmSourceMap(sources, mappings, sourcesContent) {
66
+ const sorted = [...mappings].sort((a, b) => a.offset - b.offset);
67
+ const segments = [];
68
+ let prevOffset = 0;
69
+ let prevSrcFile = 0;
70
+ let prevSrcLine = 0;
71
+ for (const m of sorted) {
72
+ const fileIdx = m.srcFileIdx ?? 0;
73
+ segments.push(
74
+ vlqEncode(m.offset - prevOffset) +
75
+ vlqEncode(fileIdx - prevSrcFile) +
76
+ vlqEncode(m.srcLine - prevSrcLine) +
77
+ vlqEncode(0),
78
+ );
79
+ prevOffset = m.offset;
80
+ prevSrcFile = fileIdx;
81
+ prevSrcLine = m.srcLine;
82
+ }
83
+ const map = {
84
+ version: 3,
85
+ sources: sources.map((s) => s.replace(/\\/g, "/")),
86
+ names: [],
87
+ mappings: segments.join(","),
88
+ };
89
+ if (sourcesContent) map.sourcesContent = sourcesContent;
90
+ return JSON.stringify(map);
91
+ }
@@ -31,27 +31,35 @@ export const statementGenMethods = {
31
31
  },
32
32
 
33
33
  // Shared helper for comma-ok map index in both DefineStmt and AssignStmt.
34
- // isDefine=true → emit `let v = m[k]; let ok = k in m;`
35
- // isDefine=false → emit `v = k in m ? m[k] : zero; ok = k in m;`
36
34
  _genCommaOkMapIndex(stmt, rhsNode, isDefine) {
37
35
  const mapExpr = this.genExpr(rhsNode.expr);
38
36
  const keyExpr = this.genExpr(rhsNode.index);
39
37
  const [vName, okName] = stmt.lhs.map((e) => e.name ?? this.genExpr(e));
40
38
  const decl = isDefine ? "let " : "";
41
- if (vName !== "_") {
42
- if (isDefine) {
39
+ if (isDefine && !this.strict) {
40
+ if (vName !== "_") {
43
41
  this.line(`let ${vName} = ${mapExpr}[${keyExpr}];`);
44
- } else {
45
- const zero = rhsNode._mapValueType
46
- ? this.zeroValueForType(rhsNode._mapValueType)
47
- : "undefined";
48
- this.line(
49
- `${vName} = (${keyExpr}) in ${mapExpr} ? ${mapExpr}[${keyExpr}] : ${zero};`,
50
- );
51
42
  }
43
+ if (okName !== "_") {
44
+ this.line(`let ${okName} = (${keyExpr}) in ${mapExpr};`);
45
+ }
46
+ return;
47
+ }
48
+ const zero = rhsNode._mapValueType
49
+ ? this.zeroValueForType(rhsNode._mapValueType)
50
+ : "undefined";
51
+ this._tmpCounter = (this._tmpCounter ?? 0) + 1;
52
+ const mTmp = `__m${this._tmpCounter}`;
53
+ const kTmp = `__k${this._tmpCounter}`;
54
+ this.line(`const ${mTmp} = ${mapExpr}, ${kTmp} = ${keyExpr};`);
55
+ if (vName !== "_") {
56
+ this.line(
57
+ `${decl}${vName} = (${mTmp} && (${kTmp} in ${mTmp})) ? ${mTmp}[${kTmp}] : ${zero};`,
58
+ );
59
+ }
60
+ if (okName !== "_") {
61
+ this.line(`${decl}${okName} = Boolean(${mTmp} && (${kTmp} in ${mTmp}));`);
52
62
  }
53
- if (okName !== "_")
54
- this.line(`${decl}${okName} = (${keyExpr}) in ${mapExpr};`);
55
63
  },
56
64
 
57
65
  genStmt(stmt) {
@@ -63,6 +71,33 @@ export const statementGenMethods = {
63
71
  this.genTypeDeclWithMethods(stmt, []);
64
72
  break;
65
73
  case "IncDecStmt": {
74
+ if (stmt.expr.kind === "IndexExpr" && stmt.expr._mapValueType) {
75
+ const zero = this.zeroValueForType(stmt.expr._mapValueType);
76
+ const wrapField = this._namedWrapperField(
77
+ stmt.expr.expr._type,
78
+ stmt.expr.expr,
79
+ );
80
+ const base = wrapField
81
+ ? `${this.genExpr(stmt.expr.expr)}.${wrapField}`
82
+ : this.genExpr(stmt.expr.expr);
83
+ const idx = this.genExpr(stmt.expr.index);
84
+ const op = stmt.op === "++" ? "+" : "-";
85
+ if (
86
+ this._hasCallExpr(stmt.expr.expr) ||
87
+ this._hasCallExpr(stmt.expr.index)
88
+ ) {
89
+ this.line(
90
+ `((__m, __k) => { __m[__k] = (__m[__k] ?? ${zero}) ${op} 1; })(${base}, ${idx});`,
91
+ srcLine,
92
+ );
93
+ } else {
94
+ this.line(
95
+ `${base}[${idx}] = (${base}[${idx}] ?? ${zero}) ${op} 1;`,
96
+ srcLine,
97
+ );
98
+ }
99
+ break;
100
+ }
66
101
  if (
67
102
  this.strict &&
68
103
  this._genStrictCompound(
@@ -266,6 +301,35 @@ export const statementGenMethods = {
266
301
  if (active.length === 0) {
267
302
  if (rhs.length > 0) this.line(`${rhs[0]};`);
268
303
  } else if (lhs.length === 1) {
304
+ if (
305
+ stmt.op !== "=" &&
306
+ stmt.lhs[0].kind === "IndexExpr" &&
307
+ stmt.lhs[0]._mapValueType
308
+ ) {
309
+ const zero = this.zeroValueForType(stmt.lhs[0]._mapValueType);
310
+ const wrapField = this._namedWrapperField(
311
+ stmt.lhs[0].expr._type,
312
+ stmt.lhs[0].expr,
313
+ );
314
+ const base = wrapField
315
+ ? `${this.genExpr(stmt.lhs[0].expr)}.${wrapField}`
316
+ : this.genExpr(stmt.lhs[0].expr);
317
+ const idx = this.genExpr(stmt.lhs[0].index);
318
+ const binOp = stmt.op.slice(0, -1);
319
+ if (
320
+ this._hasCallExpr(stmt.lhs[0].expr) ||
321
+ this._hasCallExpr(stmt.lhs[0].index)
322
+ ) {
323
+ this.line(
324
+ `((__m, __k) => { __m[__k] = (__m[__k] ?? ${zero}) ${binOp} (${active[0].r}); })(${base}, ${idx});`,
325
+ );
326
+ } else {
327
+ this.line(
328
+ `${base}[${idx}] = (${base}[${idx}] ?? ${zero}) ${binOp} (${active[0].r});`,
329
+ );
330
+ }
331
+ return;
332
+ }
269
333
  if (
270
334
  this.strict &&
271
335
  stmt.op !== "=" &&
@@ -333,11 +397,11 @@ export const statementGenMethods = {
333
397
  result = `{ re: ${lhsStr}.re - ${rhsExpr}.re, im: ${lhsStr}.im - ${rhsExpr}.im }`;
334
398
  break;
335
399
  case "*":
336
- this._usesCmul = true;
400
+ this.useHelper("cmul");
337
401
  result = `__cmul(${lhsStr}, ${rhsExpr})`;
338
402
  break;
339
403
  case "/":
340
- this._usesCdiv = true;
404
+ this.useHelper("cdiv");
341
405
  result = `__cdiv(${lhsStr}, ${rhsExpr})`;
342
406
  break;
343
407
  default:
@@ -379,6 +443,22 @@ export const statementGenMethods = {
379
443
  } else {
380
444
  this.line("return;");
381
445
  }
446
+ } else if (this.namedReturnVars?.length > 0) {
447
+ const vars = this.namedReturnVars;
448
+ if (stmt.values.length === 1 && vars.length === 1) {
449
+ this.line(`${vars[0]} = ${this._genReturnValue(stmt.values[0])};`);
450
+ } else if (stmt.values.length === 1 && vars.length > 1) {
451
+ const val = this._genReturnValue(stmt.values[0]);
452
+ this.line(`[${vars.join(", ")}] = ${val};`);
453
+ } else if (stmt.values.length > 1 && stmt.values.length === vars.length) {
454
+ const temps = stmt.values.map((v) => this._genReturnValue(v));
455
+ this.line(`[${vars.join(", ")}] = [${temps.join(", ")}];`);
456
+ }
457
+ this.line(
458
+ vars.length === 1
459
+ ? `return ${vars[0]};`
460
+ : `return [${vars.join(", ")}];`,
461
+ );
382
462
  } else if (stmt.values.length === 1) {
383
463
  this.line(`return ${this._genReturnValue(stmt.values[0])};`);
384
464
  } else {
@@ -616,6 +696,39 @@ export const statementGenMethods = {
616
696
  return nodeAssigns(node, name);
617
697
  },
618
698
 
699
+ _genStringRangeFor(stmt, iteree, isAssign) {
700
+ this._loopDepth = this._loopDepth ?? 0;
701
+ const d = this._loopDepth++;
702
+ const [idxNode, valNode] = stmt.init.lhs;
703
+ const s = `__s${d}`;
704
+ const i = `__i${d}`;
705
+ const cp = `__cp${d}`;
706
+ this.line(
707
+ `for (let ${i} = 0, ${s} = Array.from(${iteree} ?? ""); ${i} < ${s}.length; ${i}++) {`,
708
+ );
709
+ this.indented(() => {
710
+ this._emitRangeVar(idxNode, i, isAssign);
711
+ if (valNode && valNode.name !== "_") {
712
+ this.line(`const ${cp} = ${s}[${i}].codePointAt(0);`);
713
+ this._emitRangeVar(valNode, cp, isAssign);
714
+ }
715
+ const declared = isAssign
716
+ ? []
717
+ : [idxNode, valNode]
718
+ .filter((e) => e?.kind === "Ident" && e.name !== "_")
719
+ .map((e) => e.name);
720
+ if (this._bodyRedeclares(stmt.body, declared)) {
721
+ this.line("{");
722
+ this.indented(() => this.genBlock(stmt.body));
723
+ this.line("}");
724
+ } else {
725
+ this.genBlock(stmt.body);
726
+ }
727
+ });
728
+ this.line("}");
729
+ this._loopDepth--;
730
+ },
731
+
619
732
  _genIterFor(init, iterType, iteree, lhs, body) {
620
733
  const iterExpr = this._genRangeIterExpr(iterType, iteree, lhs);
621
734
  const binding =
@@ -645,12 +758,17 @@ export const statementGenMethods = {
645
758
  return;
646
759
  }
647
760
 
648
- if (!this._isMapRangeType(iterType) && !this._isStringRangeType(iterType)) {
649
- this._genSliceRangeFor(stmt, iteree, init.kind === "AssignStmt");
761
+ if (this._isStringRangeType(iterType)) {
762
+ this._genStringRangeFor(stmt, iteree, init.kind === "AssignStmt");
763
+ return;
764
+ }
765
+
766
+ if (this._isMapRangeType(iterType)) {
767
+ this._genIterFor(init, iterType, iteree, lhs, stmt.body);
650
768
  return;
651
769
  }
652
770
 
653
- this._genIterFor(init, iterType, iteree, lhs, stmt.body);
771
+ this._genSliceRangeFor(stmt, iteree, init.kind === "AssignStmt");
654
772
  },
655
773
 
656
774
  _genRangeIterExpr(iterType, iteree, lhs) {
@@ -658,23 +776,14 @@ export const statementGenMethods = {
658
776
  if (lhs.length === 1) return `Object.keys(${iteree})`;
659
777
  return `Object.entries(${iteree})`;
660
778
  }
661
- if (this._isStringRangeType(iterType)) {
662
- if (lhs.length === 1) return `Array.from(${iteree}).keys()`;
663
- return `Array.from(${iteree}, (__c, __i) => [__i, __c.codePointAt(0)])`;
664
- }
665
- this._usesSliceGuard = true;
779
+ this.useHelper("sliceGuard");
666
780
  if (lhs.length === 1) return `__s(${iteree}).keys()`;
667
781
  return `__s(${iteree}).entries()`;
668
782
  },
669
783
 
670
- genIteratorFor(stmt) {
671
- const range = stmt.init.rhs[0];
672
- const lhs = stmt.init.lhs.map((e) => e.name ?? this.genExpr(e));
673
- let iteree = this.genExpr(range.expr);
674
- const yieldParams = range._yieldParams;
675
-
676
- // Wrap bare function expressions to avoid "Function statements require a name"
677
- if (range.expr.kind === "FuncLit") {
784
+ _genIteratorLoop(itereeExpr, paramsStr, body) {
785
+ let iteree = this.genExpr(itereeExpr);
786
+ if (itereeExpr.kind === "FuncLit") {
678
787
  iteree = `(${iteree})`;
679
788
  }
680
789
 
@@ -683,17 +792,13 @@ export const statementGenMethods = {
683
792
  const retFlag = `__returned${d}`;
684
793
  const retVar = `__retVal${d}`;
685
794
 
686
- // Yield callback param names (blank vars get _$N to avoid JS syntax error)
687
- const cbParams = lhs.map((n, i) => (n === "_" ? `_$${i}` : n));
688
-
689
795
  this.line("{");
690
796
  this.indented(() => {
691
797
  this.line(`let ${breakFlag} = false;`);
692
798
  this.line(`let ${retFlag} = false;`);
693
799
  this.line(`let ${retVar};`);
694
800
 
695
- const params = yieldParams.length === 0 ? "" : cbParams.join(", ");
696
- this.line(`${iteree}(function(${params}) {`);
801
+ this.line(`${iteree}(function(${paramsStr}) {`);
697
802
  this.indented(() => {
698
803
  this.line(`if (${breakFlag}) return false;`);
699
804
  const prev = {
@@ -707,7 +812,7 @@ export const statementGenMethods = {
707
812
  this._iterReturnFlag = retFlag;
708
813
  this._iterReturnVar = retVar;
709
814
 
710
- this.genBlock(stmt.body);
815
+ this.genBlock(body);
711
816
 
712
817
  this._inIteratorBody = prev.in;
713
818
  this._iterBreakFlag = prev.break;
@@ -723,54 +828,17 @@ export const statementGenMethods = {
723
828
  this._iterDepth--;
724
829
  },
725
830
 
831
+ genIteratorFor(stmt) {
832
+ const range = stmt.init.rhs[0];
833
+ const lhs = stmt.init.lhs.map((e) => e.name ?? this.genExpr(e));
834
+ const cbParams = lhs.map((n, i) => (n === "_" ? `_$${i}` : n));
835
+ const params = range._yieldParams.length === 0 ? "" : cbParams.join(", ");
836
+ this._genIteratorLoop(range.expr, params, stmt.body);
837
+ },
838
+
726
839
  // 0-param iterator: for range iter { body }
727
840
  genIteratorForCond(stmt) {
728
- const range = stmt.cond;
729
- let iteree = this.genExpr(range.expr);
730
-
731
- if (range.expr.kind === "FuncLit") {
732
- iteree = `(${iteree})`;
733
- }
734
-
735
- const d = this._iterDepth++;
736
- const breakFlag = `__broke${d}`;
737
- const retFlag = `__returned${d}`;
738
- const retVar = `__retVal${d}`;
739
-
740
- this.line("{");
741
- this.indented(() => {
742
- this.line(`let ${breakFlag} = false;`);
743
- this.line(`let ${retFlag} = false;`);
744
- this.line(`let ${retVar};`);
745
-
746
- this.line(`${iteree}(function() {`);
747
- this.indented(() => {
748
- this.line(`if (${breakFlag}) return false;`);
749
- const prev = {
750
- in: this._inIteratorBody,
751
- break: this._iterBreakFlag,
752
- ret: this._iterReturnFlag,
753
- retVar: this._iterReturnVar,
754
- };
755
- this._inIteratorBody = true;
756
- this._iterBreakFlag = breakFlag;
757
- this._iterReturnFlag = retFlag;
758
- this._iterReturnVar = retVar;
759
-
760
- this.genBlock(stmt.body);
761
-
762
- this._inIteratorBody = prev.in;
763
- this._iterBreakFlag = prev.break;
764
- this._iterReturnFlag = prev.ret;
765
- this._iterReturnVar = prev.retVar;
766
-
767
- this.line("return true;");
768
- });
769
- this.line("});");
770
- this.line(`if (${retFlag}) return ${retVar};`);
771
- });
772
- this.line("}");
773
- this._iterDepth--;
841
+ this._genIteratorLoop(stmt.cond.expr, "", stmt.body);
774
842
  },
775
843
 
776
844
  // Inline a statement as a for-init/post string (no semicolon)
@@ -0,0 +1,12 @@
1
+ // src/backend/js/stdlib/base64.js
2
+ // CodeGen for Go encoding/base64 package.
3
+
4
+ export const base64Methods = {
5
+ _genBase64(fn, a) {
6
+ this.useHelper("base64");
7
+ this.useHelper("error");
8
+ const args = a();
9
+ if (fn === "NewEncoding") return `__base64_make(${args[0]}, "=")`;
10
+ return undefined;
11
+ },
12
+ };
@@ -0,0 +1,102 @@
1
+ // src/backend/js/stdlib/binary.js
2
+ // CodeGen for Go encoding/binary package.
3
+
4
+ function sizeOfGoType(gt) {
5
+ if (!gt) return -1;
6
+ if (gt.kind === "basic") {
7
+ switch (gt.name) {
8
+ case "bool":
9
+ case "int8":
10
+ case "uint8":
11
+ case "byte":
12
+ return 1;
13
+ case "int16":
14
+ case "uint16":
15
+ return 2;
16
+ case "int32":
17
+ case "uint32":
18
+ case "float32":
19
+ case "rune":
20
+ return 4;
21
+ case "int64":
22
+ case "uint64":
23
+ case "float64":
24
+ case "int":
25
+ case "uint":
26
+ case "uintptr":
27
+ return 8;
28
+ default:
29
+ return -1;
30
+ }
31
+ }
32
+ if (gt.kind === "array" || gt.kind === "ArrayType") {
33
+ const es = sizeOfGoType(gt.elem);
34
+ const len = gt.len ?? gt.size ?? 0;
35
+ return es > 0 ? es * len : -1;
36
+ }
37
+ if (
38
+ gt.kind === "pointer" ||
39
+ gt.kind === "PointerType" ||
40
+ gt.kind === "StarExpr"
41
+ ) {
42
+ return sizeOfGoType(gt.base ?? gt.expr ?? gt.operand);
43
+ }
44
+ if (gt.kind === "struct" || gt.kind === "StructType") {
45
+ let total = 0;
46
+ if (gt.fields instanceof Map) {
47
+ for (const fType of gt.fields.values()) {
48
+ const s = sizeOfGoType(fType);
49
+ if (s < 0) return -1;
50
+ total += s;
51
+ }
52
+ return total;
53
+ }
54
+ }
55
+ return -1;
56
+ }
57
+
58
+ export const binaryMethods = {
59
+ _genBinary(fn, a, expr) {
60
+ this.useHelper("binary");
61
+ this.useHelper("error");
62
+ const args = a();
63
+ if (fn === "Read") {
64
+ const targetType = expr?.args?.[2]?._type;
65
+ const baseType = targetType?.base ?? targetType;
66
+ const resolved =
67
+ baseType?.kind === "named"
68
+ ? (baseType.underlying ?? baseType)
69
+ : baseType;
70
+ const sz = sizeOfGoType(resolved);
71
+ const typeTag =
72
+ resolved?.name ??
73
+ (resolved?.kind === "basic" ? resolved.name : (baseType?.name ?? ""));
74
+ return `__binary_read(${args[0]}, ${args[1]}, ${args[2]}${sz > 0 ? `, ${sz}` : ", 0"}, "${typeTag}")`;
75
+ }
76
+ if (fn === "Write") {
77
+ const targetType = expr?.args?.[2]?._type;
78
+ const baseType = targetType?.base ?? targetType;
79
+ const resolved =
80
+ baseType?.kind === "named"
81
+ ? (baseType.underlying ?? baseType)
82
+ : baseType;
83
+ const sz = sizeOfGoType(resolved);
84
+ const typeTag =
85
+ resolved?.name ??
86
+ (resolved?.kind === "basic" ? resolved.name : (baseType?.name ?? ""));
87
+ return `__binary_write(${args[0]}, ${args[1]}, ${args[2]}${sz > 0 ? `, ${sz}` : ", 0"}, "${typeTag}")`;
88
+ }
89
+ if (fn === "Size") {
90
+ const sz = sizeOfGoType(expr?.args?.[0]?._type);
91
+ if (sz >= 0) return `${sz}`;
92
+ return `__binary_size(${args[0]})`;
93
+ }
94
+ if (fn === "AppendUint16")
95
+ return `__binary_le.AppendUint16(${args[0]}, ${args[1]})`;
96
+ if (fn === "AppendUint32")
97
+ return `__binary_le.AppendUint32(${args[0]}, ${args[1]})`;
98
+ if (fn === "AppendUint64")
99
+ return `__binary_le.AppendUint64(${args[0]}, ${args[1]})`;
100
+ return undefined;
101
+ },
102
+ };
@@ -19,7 +19,21 @@ const BITS_MAP = {
19
19
  OnesCount64: ([x]) =>
20
20
  `((() => { let v = BigInt.asUintN(64, BigInt(${x})); let c = 0; while (v > 0n) { if (v & 1n) c++; v >>= 1n; } return c; })())`,
21
21
  RotateLeft64: ([x, k]) =>
22
- `((() => { const s = BigInt(${k}) & 63n; const v = BigInt.asUintN(64, BigInt(${x})); return BigInt.asUintN(64, (v << s) | (v >> ((64n - s) & 63n))); })())`,
22
+ `((() => { const s = BigInt(${k}) & 63n; const v = BigInt.asUintN(64, BigInt(${x})); return Number(BigInt.asUintN(64, (v << s) | (v >> ((64n - s) & 63n)))); })())`,
23
+
24
+ Len32: ([x]) => `((v => 32 - Math.clz32(v >>> 0))(${x}))`,
25
+ Len64: ([x]) =>
26
+ `((() => { const v = BigInt.asUintN(64, BigInt(${x})); if (v === 0n) return 0; const hi = Number(v >> 32n); return hi !== 0 ? 64 - Math.clz32(hi) : 32 - Math.clz32(Number(v & 0xffffffffn)); })())`,
27
+ ReverseBytes16: ([x]) =>
28
+ `((v => ((v & 0xff) << 8) | ((v >>> 8) & 0xff))((${x}) & 0xffff))`,
29
+ ReverseBytes32: ([x]) =>
30
+ `((v => (((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v >>> 8) & 0xff00) | ((v >>> 24) & 0xff)) >>> 0)((${x}) >>> 0))`,
31
+ ReverseBytes64: ([x]) =>
32
+ `((() => { const v = BigInt.asUintN(64, BigInt(${x})); let r = 0n; for (let i = 0n; i < 8n; i++) r = (r << 8n) | ((v >> (i * 8n)) & 0xffn); return Number(r); })())`,
33
+ Reverse32: ([x]) =>
34
+ `((() => { let v = (${x}) >>> 0; v = ((v >>> 1) & 0x55555555) | ((v & 0x55555555) << 1); v = ((v >>> 2) & 0x33333333) | ((v & 0x33333333) << 2); v = ((v >>> 4) & 0x0f0f0f0f) | ((v & 0x0f0f0f0f) << 4); v = ((v >>> 8) & 0x00ff00ff) | ((v & 0x00ff00ff) << 8); return ((v >>> 16) | (v << 16)) >>> 0; })())`,
35
+ Reverse64: ([x]) =>
36
+ `((() => { const rev32 = (v) => { v = ((v >>> 1) & 0x55555555) | ((v & 0x55555555) << 1); v = ((v >>> 2) & 0x33333333) | ((v & 0x33333333) << 2); v = ((v >>> 4) & 0x0f0f0f0f) | ((v & 0x0f0f0f0f) << 4); v = ((v >>> 8) & 0x00ff00ff) | ((v & 0x00ff00ff) << 8); return ((v >>> 16) | (v << 16)) >>> 0; }; const v = BigInt.asUintN(64, BigInt(${x})); const lo = Number(v & 0xffffffffn); const hi = Number((v >> 32n) & 0xffffffffn); return Number((BigInt(rev32(lo)) << 32n) | BigInt(rev32(hi))); })())`,
23
37
  };
24
38
 
25
39
  /** @type {ThisType<CodeGen>} */
@@ -3,25 +3,40 @@
3
3
  /** @typedef {import('../index.js').CodeGen} CodeGen */
4
4
 
5
5
  const BUILDER_STR = {
6
- WriteString: (b, a) => `(${b}._buf += ${a[0]}, [${a[0]}.length, null])`,
7
- WriteByte: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}))`,
8
- Write: (b, a) => `(${b}._buf += String.fromCharCode(...${a[0]}))`,
6
+ WriteString: (b, a) =>
7
+ `((__s) => { ${b}._buf += __s; return [new TextEncoder().encode(__s).length, null]; })(${a[0]})`,
8
+ WriteByte: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}), null)`,
9
+ WriteRune: (b, a) =>
10
+ `((cp) => { const r = (cp < 0 || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff)) ? 0xfffd : cp; const s = String.fromCodePoint(r); ${b}._buf += s; return [new TextEncoder().encode(s).length, null]; })(${a[0]})`,
11
+ Write: (b, a) =>
12
+ `(${b}._buf += new TextDecoder().decode(new Uint8Array(${a[0]})), [${a[0]}.length, null])`,
9
13
  String: (b) => `${b}._buf`,
10
14
  Reset: (b) => `(${b}._buf = "")`,
15
+ Len: (b) => `new TextEncoder().encode(${b}._buf).length`,
16
+ Cap: (b) =>
17
+ `Math.max(${b}._cap ?? 0, new TextEncoder().encode(${b}._buf).length)`,
18
+ Grow: (b, a) =>
19
+ `((n) => { if (n < 0) throw new Error("strings.Builder.Grow: negative count"); ${b}._cap = Math.max(${b}._cap ?? 0, new TextEncoder().encode(${b}._buf).length) + n; })(${a[0]})`,
11
20
  };
12
21
  const BUILDER_BYTES = {
13
22
  WriteString: (b, a) =>
14
- `(${b}._buf.push(...new TextEncoder().encode(${a[0]})), [${a[0]}.length, null])`,
15
- WriteByte: (b, a) => `${b}._buf.push(${a[0]})`,
16
- Write: (b, a) => `(${b}._buf.push(...${a[0]}), [${a[0]}.length, null])`,
23
+ `((buf, s) => { const bytes = new TextEncoder().encode(s); for (let i = 0; i < bytes.length; i++) buf._buf.push(bytes[i]); return [bytes.length, null]; })(${b}, ${a[0]})`,
24
+ WriteByte: (b, a) => `(${b}._buf.push(${a[0]}), null)`,
25
+ WriteRune: (b, a) =>
26
+ `((buf, cp) => { const r = (cp < 0 || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff)) ? 0xfffd : cp; const bytes = new TextEncoder().encode(String.fromCodePoint(r)); for (let i = 0; i < bytes.length; i++) buf._buf.push(bytes[i]); return [bytes.length, null]; })(${b}, ${a[0]})`,
27
+ Write: (b, a) =>
28
+ `((buf, d) => { for (let i = 0; i < d.length; i++) buf._buf.push(d[i]); return [d.length, null]; })(${b}, ${a[0]})`,
17
29
  String: (b) => `new TextDecoder().decode(new Uint8Array(${b}._buf))`,
18
30
  Reset: (b) => `(${b}._buf = [])`,
31
+ Read: (b, a) =>
32
+ `((buf, p) => { if (buf._buf.length === 0) return [0, __io_eof]; const n = Math.min(p.length, buf._buf.length); for (let i = 0; i < n; i++) p[i] = buf._buf.shift(); return [n, null]; })(${b}, ${a[0]})`,
19
33
  };
20
34
  const BUILDER_COMMON = {
21
- WriteRune: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}))`,
22
- Bytes: (b) => `${b}._buf.slice()`,
35
+ Bytes: (b) => `new Uint8Array(${b}._buf)`,
23
36
  Len: (b) => `${b}._buf.length`,
24
- Grow: () => "undefined",
37
+ Cap: (b) => `Math.max(${b}._cap ?? 0, ${b}._buf.length)`,
38
+ Grow: (b, a) =>
39
+ `((n) => { if (n < 0) throw new Error("bytes.Buffer.Grow: negative count"); ${b}._cap = Math.max(${b}._cap ?? 0, ${b}._buf.length) + n; })(${a[0]})`,
25
40
  };
26
41
 
27
42
  /** @type {ThisType<CodeGen>} */
@@ -30,6 +45,10 @@ export const builderMethods = {
30
45
  const recv = expr.func.expr;
31
46
  const base = this._unboxWriter(this.genExpr(recv));
32
47
  const args = expr.args.map((a) => this.genExpr(a));
48
+ if (method === "Read") {
49
+ this.useHelper("ioEOF");
50
+ this.useHelper("error");
51
+ }
33
52
  const isStr = typeName === "strings.Builder";
34
53
  const table = isStr ? BUILDER_STR : BUILDER_BYTES;
35
54
  const gen = table[method] ?? BUILDER_COMMON[method];