gofront 1.7.0 → 1.8.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 (128) hide show
  1. package/README.md +13 -13
  2. package/package.json +8 -37
  3. package/src/backend/js/expressions.js +1341 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +375 -57
  6. package/src/backend/js/runtime.js +1460 -133
  7. package/src/backend/js/statements.js +900 -86
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +2 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +8 -3
  14. package/src/backend/js/stdlib/json.js +247 -52
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +10 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +436 -80
  29. package/src/backend/wasm/emit-builtins.js +303 -13
  30. package/src/backend/wasm/emit-exprs.js +1397 -203
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +512 -77
  34. package/src/backend/wasm/encode.js +43 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/prescan.js +397 -0
  41. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  42. package/src/backend/wasm/stdlib/core.js +1574 -199
  43. package/src/backend/wasm/stdlib/index.js +0 -5
  44. package/src/backend/wasm/stdlib/io.js +1567 -150
  45. package/src/backend/wasm/stdlib/packages.js +2 -0
  46. package/src/backend/wasm/stdlib/simd.js +293 -28
  47. package/src/backend/wasm/stdlib/time.js +1041 -12
  48. package/src/backend/wasm/types.js +200 -15
  49. package/src/backend/wasm/variadic.js +93 -0
  50. package/src/backend/wasm/wat.js +47 -20
  51. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  52. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  53. package/src/{vendor.js → cli/vendor.js} +3 -4
  54. package/src/compiler.js +107 -269
  55. package/src/index.js +15 -169
  56. package/src/{lexer.js → lexer/lexer.js} +102 -55
  57. package/src/{tokens.js → lexer/tokens.js} +1 -0
  58. package/src/link/sourcemap.js +97 -0
  59. package/src/link/stdlib.js +515 -0
  60. package/src/link/wasm.js +294 -0
  61. package/src/lower/bounds.js +394 -0
  62. package/src/lower/boxing.js +1 -1
  63. package/src/lower/captures.js +176 -32
  64. package/src/lower/index.js +26 -18
  65. package/src/lower/json-codecs.js +1398 -0
  66. package/src/lower/ownership.js +22 -1
  67. package/src/lower/range.js +86 -1
  68. package/src/lower/reachability.js +132 -0
  69. package/src/parser/declarations.js +58 -13
  70. package/src/parser/expressions.js +29 -7
  71. package/src/parser/index.js +2 -1
  72. package/src/parser/statements.js +37 -22
  73. package/src/{templ-parser.js → parser/templ.js} +138 -15
  74. package/src/parser/types.js +69 -9
  75. package/src/resolver.js +22 -1
  76. package/src/typechecker/assignability.js +176 -17
  77. package/src/typechecker/const-fold.js +356 -0
  78. package/src/typechecker/constants.js +256 -0
  79. package/src/typechecker/expressions.js +931 -122
  80. package/src/typechecker/index.js +459 -67
  81. package/src/typechecker/init-order.js +330 -0
  82. package/src/typechecker/resolve.js +451 -56
  83. package/src/typechecker/statements.js +90 -9
  84. package/src/typechecker/stdlib/core.js +333 -55
  85. package/src/typechecker/stdlib/excluded.js +898 -0
  86. package/src/typechecker/stdlib/extended.js +322 -26
  87. package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
  88. package/src/typechecker/stdlib/simd.js +65 -15
  89. package/src/typechecker/termination.js +1 -1
  90. package/src/typechecker/types.js +149 -25
  91. package/stdlib/bytes/iter.go +192 -0
  92. package/stdlib/bytes/search.go +240 -0
  93. package/stdlib/bytes/split.go +218 -0
  94. package/stdlib/cmp/cmp.go +65 -0
  95. package/stdlib/encoding/json/errors.go +31 -0
  96. package/stdlib/encoding/json/indent.go +71 -0
  97. package/stdlib/encoding/json/number.go +12 -0
  98. package/stdlib/encoding/json/stream.go +173 -0
  99. package/stdlib/errors/overlay.go +12 -0
  100. package/stdlib/hash/hash.go +53 -0
  101. package/stdlib/internal/jsonrt/dec.go +738 -0
  102. package/stdlib/internal/jsonrt/enc.go +366 -0
  103. package/stdlib/internal/jsonrt/indent.go +135 -0
  104. package/stdlib/internal/jsonrt/number.go +27 -0
  105. package/stdlib/internal/jsonrt/reader.go +73 -0
  106. package/stdlib/internal/jsonrt/scanner.go +649 -0
  107. package/stdlib/io/io.go +346 -0
  108. package/stdlib/iter/iter.go +18 -0
  109. package/stdlib/maps/iter.go +65 -0
  110. package/stdlib/math/extra.go +270 -0
  111. package/stdlib/slices/extra.go +73 -0
  112. package/stdlib/slices/iter.go +113 -0
  113. package/stdlib/sort/sort.go +561 -0
  114. package/stdlib/strconv/quote.go +341 -0
  115. package/stdlib/strings/extra.go +35 -0
  116. package/stdlib/strings/iter.go +122 -0
  117. package/stdlib/sync/extra.go +105 -0
  118. package/stdlib/unicode/unicode.go +112 -0
  119. package/stdlib/unicode/utf8/bytes.go +239 -0
  120. package/src/backend/wasm/const-fold.js +0 -205
  121. package/src/backend/wasm/emit-stdlib.js +0 -13
  122. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  123. /package/src/{colors.js → cli/colors.js} +0 -0
  124. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  125. /package/src/{minifier.js → cli/minifier.js} +0 -0
  126. /package/src/{project-config.js → cli/project-config.js} +0 -0
  127. /package/src/{pwa.js → cli/pwa.js} +0 -0
  128. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -3,6 +3,7 @@
3
3
  // are mutated and thus require heap-allocated environment cells for WASM closures.
4
4
 
5
5
  import { collectMutatedVars } from "./ownership.js";
6
+ import { getOrCreateIteratorFuncLit, isIteratorRangeFor } from "./range.js";
6
7
  import { forEachChild } from "./walk.js";
7
8
 
8
9
  /**
@@ -40,7 +41,7 @@ export function collectDeclaredNames(node, names = new Set()) {
40
41
  }
41
42
 
42
43
  // Do not descend into nested functions when collecting local declarations
43
- if (node.kind === "FuncLit") return names;
44
+ if (node.kind === "FuncLit" || isIteratorRangeFor(node)) return names;
44
45
 
45
46
  forEachChild(node, (child) => collectDeclaredNames(child, names));
46
47
  return names;
@@ -92,6 +93,53 @@ function collectReferencedNames(node, localDecls, refs = new Set()) {
92
93
  return refs;
93
94
  }
94
95
 
96
+ function hasReturnInNode(n) {
97
+ let found = false;
98
+ const check = (x) => {
99
+ if (!x || typeof x !== "object" || found) return;
100
+ if (Array.isArray(x)) {
101
+ for (const elem of x) check(elem);
102
+ return;
103
+ }
104
+ if (x.kind === "ReturnStmt") {
105
+ found = true;
106
+ return;
107
+ }
108
+ if (x.kind === "FuncLit" && !x._isYieldClosure) return;
109
+ for (const k of Object.keys(x)) {
110
+ if (k.startsWith("_")) continue;
111
+ check(x[k]);
112
+ }
113
+ };
114
+ check(n);
115
+ return found;
116
+ }
117
+
118
+ function hasIteratorWithReturn(body) {
119
+ let found = false;
120
+ const check = (x) => {
121
+ if (!x || typeof x !== "object" || found) return;
122
+ if (Array.isArray(x)) {
123
+ for (const elem of x) check(elem);
124
+ return;
125
+ }
126
+ if (x.kind === "FuncLit") return;
127
+ if (isIteratorRangeFor(x)) {
128
+ const lit = getOrCreateIteratorFuncLit(x);
129
+ if (hasReturnInNode(lit?.body)) {
130
+ found = true;
131
+ return;
132
+ }
133
+ }
134
+ for (const k of Object.keys(x)) {
135
+ if (k.startsWith("_")) continue;
136
+ check(x[k]);
137
+ }
138
+ };
139
+ check(body);
140
+ return found;
141
+ }
142
+
95
143
  /**
96
144
  * Analyzes a function or method declaration for closures and their captured variables.
97
145
  * @param {Object} fnDecl AST FuncDecl or FuncLit node
@@ -101,6 +149,26 @@ export function analyzeCaptures(fnDecl) {
101
149
  const result = new CaptureAnalysis();
102
150
  if (!fnDecl?.body) return result;
103
151
 
152
+ // Synthesize named returns if not already present, so iterator returns can share boxed slots
153
+ if (
154
+ fnDecl.returnType &&
155
+ !fnDecl.returnType._namedReturns &&
156
+ hasIteratorWithReturn(fnDecl.body)
157
+ ) {
158
+ const ret = fnDecl.returnType;
159
+ if (ret.name !== "void") {
160
+ const types =
161
+ ret.kind === "TupleType" || ret.kind === "tuple" ? ret.types : [ret];
162
+ if (types && types.length > 0) {
163
+ fnDecl.returnType._namedReturns = types.map((t, i) => ({
164
+ name: `__retVal$${i}`,
165
+ type: { ...t, _namedReturns: undefined },
166
+ _synthesized: true,
167
+ }));
168
+ }
169
+ }
170
+ }
171
+
104
172
  // Collect parameters, receiver and named returns of the root function
105
173
  const rootScope = new Set();
106
174
  if (fnDecl.recvName && fnDecl.recvName !== "_") {
@@ -117,46 +185,87 @@ export function analyzeCaptures(fnDecl) {
117
185
  // Fast pre-collected set of all mutated variables in the entire function
118
186
  const mutatedVars = collectMutatedVars(fnDecl.body);
119
187
 
120
- // Traverse closures with lexical scope stack to properly handle arbitrary nesting
121
- const walkClosures = (node, scopeStack) => {
122
- if (!node || typeof node !== "object") return;
123
- if (Array.isArray(node)) {
124
- for (const item of node) walkClosures(item, scopeStack);
125
- return;
188
+ const handleIteratorRange = (node, scopeStack) => {
189
+ const funcLit = getOrCreateIteratorFuncLit(node);
190
+ if (node._iterStateName) {
191
+ rootScope.add(node._iterStateName);
192
+ result.mutatedCaptures.add(node._iterStateName);
126
193
  }
127
-
128
- if (node.kind === "FuncLit") {
129
- const closureLocals = new Set();
130
- for (const p of node.params ?? []) {
131
- if (p.name && p.name !== "_") closureLocals.add(p.name);
194
+ const rootNamed = fnDecl.returnType?._namedReturns ?? [];
195
+ const hasRet = hasReturnInNode(funcLit.body);
196
+ if (hasRet && rootNamed.length > 0) {
197
+ for (const r of rootNamed) {
198
+ if (r.name && r.name !== "_") {
199
+ rootScope.add(r.name);
200
+ result.mutatedCaptures.add(r.name);
201
+ }
132
202
  }
133
- for (const r of node.returnType?._namedReturns ?? []) {
134
- if (r.name && r.name !== "_") closureLocals.add(r.name);
203
+ }
204
+ walkClosures(funcLit, scopeStack);
205
+ // A func literal ranged over directly is a closure of its own.
206
+ walkClosures(node.cond?.expr ?? node.init?.rhs?.[0]?.expr, scopeStack);
207
+ const captured = result.capturesByClosure.get(funcLit) ?? new Set();
208
+ if (node._iterStateName) {
209
+ captured.add(node._iterStateName);
210
+ }
211
+ if (hasRet && rootNamed.length > 0) {
212
+ for (const r of rootNamed) {
213
+ if (r.name && r.name !== "_") {
214
+ captured.add(r.name);
215
+ }
135
216
  }
136
- collectDeclaredNames(node.body, closureLocals);
217
+ }
218
+ result.capturesByClosure.set(funcLit, captured);
219
+ };
137
220
 
138
- // Visible outer variables from all enclosing ancestor scopes
139
- const allOuterScope = new Set();
140
- for (const scope of scopeStack) {
141
- for (const name of scope) allOuterScope.add(name);
142
- }
221
+ const handleFuncLit = (node, scopeStack) => {
222
+ const closureLocals = new Set();
223
+ for (const p of node.params ?? []) {
224
+ if (p.name && p.name !== "_") closureLocals.add(p.name);
225
+ }
226
+ for (const r of node.returnType?._namedReturns ?? []) {
227
+ if (r.name && r.name !== "_") closureLocals.add(r.name);
228
+ }
229
+ collectDeclaredNames(node.body, closureLocals);
143
230
 
144
- const allRefs = collectReferencedNames(node.body, closureLocals);
145
- const captured = new Set();
146
- for (const ref of allRefs) {
147
- if (allOuterScope.has(ref)) {
148
- captured.add(ref);
149
- }
231
+ const allOuterScope = new Set();
232
+ for (const scope of scopeStack) {
233
+ for (const name of scope) allOuterScope.add(name);
234
+ }
235
+
236
+ const allRefs = collectReferencedNames(node.body, closureLocals);
237
+ const captured = new Set();
238
+ for (const ref of allRefs) {
239
+ if (allOuterScope.has(ref)) {
240
+ captured.add(ref);
150
241
  }
151
- result.capturesByClosure.set(node, captured);
242
+ }
243
+ result.capturesByClosure.set(node, captured);
152
244
 
153
- for (const varName of captured) {
154
- if (mutatedVars.has(varName)) {
155
- result.mutatedCaptures.add(varName);
156
- }
245
+ for (const varName of captured) {
246
+ if (mutatedVars.has(varName)) {
247
+ result.mutatedCaptures.add(varName);
157
248
  }
249
+ }
158
250
 
159
- walkClosures(node.body, [...scopeStack, closureLocals]);
251
+ walkClosures(node.body, [...scopeStack, closureLocals]);
252
+ };
253
+
254
+ // Traverse closures with lexical scope stack to properly handle arbitrary nesting
255
+ const walkClosures = (node, scopeStack) => {
256
+ if (!node || typeof node !== "object") return;
257
+ if (Array.isArray(node)) {
258
+ for (const item of node) walkClosures(item, scopeStack);
259
+ return;
260
+ }
261
+
262
+ if (isIteratorRangeFor(node)) {
263
+ handleIteratorRange(node, scopeStack);
264
+ return;
265
+ }
266
+
267
+ if (node.kind === "FuncLit") {
268
+ handleFuncLit(node, scopeStack);
160
269
  return;
161
270
  }
162
271
 
@@ -166,3 +275,38 @@ export function analyzeCaptures(fnDecl) {
166
275
  walkClosures(fnDecl.body, [rootScope]);
167
276
  return result;
168
277
  }
278
+
279
+ /**
280
+ * Go 1.22 per-iteration loop variables: the names declared by a three-clause
281
+ * `for` init (`for i := …`) that a closure inside the loop (body, cond or
282
+ * post) refers to, or whose address is taken there (`&i`, `&p.X`): a pointer
283
+ * from one iteration must keep pointing at that iteration's variable.
284
+ * Conservative by name. Only these need a fresh copy per iteration; the
285
+ * others stay plain locals.
286
+ */
287
+ export function loopVarsCapturedInLoop(stmt) {
288
+ const out = new Set();
289
+ if (stmt?.init?.kind !== "DefineStmt") return out;
290
+ const names = new Set(
291
+ (stmt.init.lhs ?? [])
292
+ .filter((l) => l.kind === "Ident" && l.name !== "_")
293
+ .map((l) => l.name),
294
+ );
295
+ if (names.size === 0) return out;
296
+ const walk = (node, inClosure) => {
297
+ if (!node || typeof node !== "object") return;
298
+ if (Array.isArray(node)) {
299
+ for (const n of node) walk(n, inClosure);
300
+ return;
301
+ }
302
+ if (inClosure && node.kind === "Ident" && names.has(node.name))
303
+ out.add(node.name);
304
+ const nowIn = inClosure || node.kind === "FuncLit";
305
+ forEachChild(node, (child) => walk(child, nowIn));
306
+ };
307
+ const loop = [stmt.cond, stmt.post, stmt.body];
308
+ walk(loop, false);
309
+ for (const name of collectMutatedVars(loop, new Set(), true))
310
+ if (names.has(name)) out.add(name);
311
+ return out;
312
+ }
@@ -1,6 +1,7 @@
1
1
  // src/lower/index.js
2
2
  // Lowering step: analyzes AST and types to populate side-tables for backends (JS and WASM).
3
3
 
4
+ export * from "./bounds.js";
4
5
  export * from "./boxing.js";
5
6
  export * from "./captures.js";
6
7
  export * from "./embedding.js";
@@ -42,6 +43,30 @@ class LowerResult {
42
43
  }
43
44
  }
44
45
 
46
+ /**
47
+ * Runs the per-function analyses for `d` and records them in `res`. Also
48
+ * used for synthesized functions (the package-level initializer).
49
+ */
50
+ export function lowerFunction(res, d) {
51
+ if (!d.body) return;
52
+ const boxed = scanAddressTaken(d.body, new Set());
53
+ res.boxedVars.set(d, boxed);
54
+ res.ownership.set(d, new OwnershipContext(d.body));
55
+ res.functions.set(d, {
56
+ hasDefer: hasDefer(d.body),
57
+ namedReturns: extractNamedReturns(d),
58
+ });
59
+ res.captures.set(d, analyzeCaptures(d));
60
+
61
+ const escapingParams = new Set();
62
+ for (const p of d.params ?? []) {
63
+ if (p.name && p.name !== "_" && paramEscapes(d.body, p.name)) {
64
+ escapingParams.add(p.name);
65
+ }
66
+ }
67
+ res.escapes.set(d, escapingParams);
68
+ }
69
+
45
70
  /**
46
71
  * Runs lowering analyses across an AST or collection of programs, returning side-tables.
47
72
  * @param {Object} programOrPrograms
@@ -74,24 +99,7 @@ export function lower(programOrPrograms, checker = null) {
74
99
 
75
100
  for (const d of program.decls) {
76
101
  if (d.kind === "FuncDecl" || d.kind === "MethodDecl") {
77
- if (d.body) {
78
- const boxed = scanAddressTaken(d.body, new Set());
79
- res.boxedVars.set(d, boxed);
80
- res.ownership.set(d, new OwnershipContext(d.body));
81
- res.functions.set(d, {
82
- hasDefer: hasDefer(d.body),
83
- namedReturns: extractNamedReturns(d),
84
- });
85
- res.captures.set(d, analyzeCaptures(d));
86
-
87
- const escapingParams = new Set();
88
- for (const p of d.params ?? []) {
89
- if (p.name && p.name !== "_" && paramEscapes(d.body, p.name)) {
90
- escapingParams.add(p.name);
91
- }
92
- }
93
- res.escapes.set(d, escapingParams);
94
- }
102
+ lowerFunction(res, d);
95
103
  }
96
104
  }
97
105