gofront 1.8.0 → 1.9.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.
- package/README.md +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +228 -247
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +203 -573
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
package/src/backend/js/index.js
CHANGED
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
import {
|
|
21
21
|
computeEmbeddedStubs,
|
|
22
22
|
hasDirectRecover,
|
|
23
|
-
isReferenceType,
|
|
24
23
|
markInBoundsIndexes,
|
|
24
|
+
markRedundantNilChecks,
|
|
25
25
|
normalizeDefers,
|
|
26
26
|
scanAddressTaken,
|
|
27
|
+
takesElementAddress,
|
|
27
28
|
} from "../../lower/index.js";
|
|
28
29
|
import { orderPackageInits } from "../../typechecker/init-order.js";
|
|
29
30
|
import {
|
|
@@ -31,56 +32,44 @@ import {
|
|
|
31
32
|
isComplex,
|
|
32
33
|
isNumeric,
|
|
33
34
|
} from "../../typechecker/types.js";
|
|
35
|
+
import {
|
|
36
|
+
unicodeIsJs,
|
|
37
|
+
unicodeToLowerJs,
|
|
38
|
+
unicodeToUpperJs,
|
|
39
|
+
} from "../shared/host-unicode.js";
|
|
34
40
|
import { comparesToNil, expressionGenMethods } from "./expressions.js";
|
|
35
41
|
import {
|
|
36
42
|
HELPER_APPEND,
|
|
37
|
-
HELPER_BASE64,
|
|
38
|
-
HELPER_BINARY,
|
|
39
43
|
HELPER_BOUNDS,
|
|
40
44
|
HELPER_CDIV,
|
|
41
45
|
HELPER_CMUL,
|
|
42
|
-
HELPER_CRYPTO_RAND,
|
|
43
|
-
HELPER_DURATION,
|
|
44
46
|
HELPER_EQUAL,
|
|
45
47
|
HELPER_ERROR,
|
|
46
|
-
HELPER_ERROR_AS,
|
|
47
|
-
HELPER_ERROR_IS,
|
|
48
|
-
HELPER_ERROR_JOIN,
|
|
49
48
|
HELPER_GENERICS,
|
|
50
49
|
HELPER_GEQ,
|
|
51
|
-
HELPER_HEX,
|
|
52
50
|
HELPER_IFACE_BOX,
|
|
53
|
-
HELPER_IFD,
|
|
54
51
|
HELPER_IFEQ,
|
|
55
52
|
HELPER_INJECT_STYLES,
|
|
56
|
-
|
|
57
|
-
HELPER_JSON,
|
|
53
|
+
HELPER_JSONRT,
|
|
58
54
|
HELPER_LEN,
|
|
55
|
+
HELPER_MKLEN,
|
|
59
56
|
HELPER_NIL_CALL,
|
|
60
57
|
HELPER_NIL_MAP,
|
|
61
58
|
HELPER_NM,
|
|
62
|
-
HELPER_OS,
|
|
63
59
|
HELPER_PANIC,
|
|
64
|
-
HELPER_PATH_CLEAN,
|
|
65
|
-
HELPER_PATH_ERROR,
|
|
66
60
|
HELPER_PTREQ,
|
|
61
|
+
HELPER_PUSHS,
|
|
62
|
+
HELPER_RUNESTR,
|
|
67
63
|
HELPER_S,
|
|
68
64
|
HELPER_SCLONE,
|
|
69
|
-
HELPER_SHA256,
|
|
70
65
|
HELPER_SHIFT,
|
|
71
66
|
HELPER_SIMD,
|
|
72
67
|
HELPER_SLICE,
|
|
73
|
-
HELPER_SORT_SLICE,
|
|
74
|
-
HELPER_SPRINT,
|
|
75
|
-
HELPER_SPRINTF,
|
|
76
|
-
HELPER_STRCONV,
|
|
77
|
-
HELPER_STRCONV_ERROR,
|
|
78
68
|
HELPER_STRUCT_KEY,
|
|
79
69
|
HELPER_SV,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
HELPER_TIME_PARSE,
|
|
70
|
+
HELPER_TO64,
|
|
71
|
+
HELPER_U8DEC,
|
|
72
|
+
HELPER_U8ENC,
|
|
84
73
|
HELPER_VA,
|
|
85
74
|
HELPER_VCLONE,
|
|
86
75
|
} from "./runtime.js";
|
|
@@ -142,15 +131,19 @@ const jsBindingName = (n) =>
|
|
|
142
131
|
// `this.useHelper(name)`; `_prependHelpers` emits every marked entry.
|
|
143
132
|
const HELPER_MAP = [
|
|
144
133
|
["len", HELPER_LEN],
|
|
134
|
+
["u8enc", HELPER_U8ENC],
|
|
135
|
+
["u8dec", HELPER_U8DEC],
|
|
145
136
|
["sv", HELPER_SV],
|
|
146
137
|
["slice", HELPER_SLICE],
|
|
138
|
+
["mklen", HELPER_MKLEN],
|
|
139
|
+
["runestr", HELPER_RUNESTR],
|
|
147
140
|
["append", HELPER_APPEND],
|
|
148
141
|
["sliceGuard", HELPER_S],
|
|
142
|
+
["pushs", HELPER_PUSHS],
|
|
149
143
|
["sclone", HELPER_SCLONE],
|
|
150
144
|
["va", HELPER_VA],
|
|
151
145
|
["vclone", HELPER_VCLONE],
|
|
152
146
|
["ifaceBox", HELPER_IFACE_BOX],
|
|
153
|
-
["ifd", HELPER_IFD],
|
|
154
147
|
["equal", HELPER_EQUAL],
|
|
155
148
|
["geq", HELPER_GEQ],
|
|
156
149
|
["ptreq", HELPER_PTREQ],
|
|
@@ -159,42 +152,87 @@ const HELPER_MAP = [
|
|
|
159
152
|
["cdiv", HELPER_CDIV],
|
|
160
153
|
["panicRt", HELPER_PANIC],
|
|
161
154
|
["shift", HELPER_SHIFT],
|
|
155
|
+
["to64", HELPER_TO64],
|
|
162
156
|
["nilCall", HELPER_NIL_CALL],
|
|
163
157
|
["bounds", HELPER_BOUNDS],
|
|
164
158
|
["nilMap", HELPER_NIL_MAP],
|
|
165
159
|
["nm", HELPER_NM],
|
|
166
160
|
["structKey", HELPER_STRUCT_KEY],
|
|
167
161
|
["generics", HELPER_GENERICS],
|
|
168
|
-
["sprintf", HELPER_SPRINTF],
|
|
169
|
-
["sprint", HELPER_SPRINT],
|
|
170
162
|
["error", HELPER_ERROR],
|
|
171
|
-
["
|
|
172
|
-
["errorAs", HELPER_ERROR_AS],
|
|
173
|
-
["errorJoin", HELPER_ERROR_JOIN],
|
|
174
|
-
["strconvError", HELPER_STRCONV_ERROR],
|
|
175
|
-
["pathError", HELPER_PATH_ERROR],
|
|
176
|
-
["ioEOF", HELPER_IO_EOF],
|
|
177
|
-
["os", HELPER_OS],
|
|
178
|
-
["strconv", HELPER_STRCONV],
|
|
179
|
-
["pathClean", HELPER_PATH_CLEAN],
|
|
180
|
-
["sortSlice", HELPER_SORT_SLICE],
|
|
181
|
-
["timeFmt", HELPER_TIME_FMT],
|
|
182
|
-
["timeParse", HELPER_TIME_PARSE],
|
|
183
|
-
["duration", HELPER_DURATION],
|
|
184
|
-
["testing", HELPER_TESTING],
|
|
185
|
-
["sync", HELPER_SYNC],
|
|
186
|
-
["cryptoRand", HELPER_CRYPTO_RAND],
|
|
187
|
-
["sha256", HELPER_SHA256],
|
|
188
|
-
["binary", HELPER_BINARY],
|
|
189
|
-
["hex", HELPER_HEX],
|
|
190
|
-
["base64", HELPER_BASE64],
|
|
191
|
-
["json", HELPER_JSON],
|
|
163
|
+
["jsonrt", HELPER_JSONRT],
|
|
192
164
|
["simd", HELPER_SIMD],
|
|
193
165
|
];
|
|
194
166
|
|
|
195
167
|
// JS bodies of `//gofront:intrinsic` functions (names: INTRINSICS in
|
|
196
168
|
// typechecker/types.js). The WASM backend keeps their empty body.
|
|
197
|
-
const JS_INTRINSICS = {
|
|
169
|
+
const JS_INTRINSICS = {
|
|
170
|
+
rand_source: () => "return Math.random();",
|
|
171
|
+
time_now: () =>
|
|
172
|
+
'return BigInt(Math.floor(typeof globalThis.__GOFRONT_CLOCK === "function" ? globalThis.__GOFRONT_CLOCK() : Date.now())) * 1000000n;',
|
|
173
|
+
time_sleep: ([ns]) =>
|
|
174
|
+
`await new Promise((r) => setTimeout(r, Number(${ns}) / 1000000));`,
|
|
175
|
+
time_zone_offset: ([unix]) =>
|
|
176
|
+
`return (-new Date(Number(${unix}) * 1000).getTimezoneOffset() * 60) | 0;`,
|
|
177
|
+
// os: node writes straight to the stream; other hosts print whole lines
|
|
178
|
+
// (console.log adds the newline), buffering the partial line.
|
|
179
|
+
os_write: ([fd, s]) =>
|
|
180
|
+
`if (typeof process !== "undefined" && process?.stdout?.write) { (${fd} === 2 && process.stderr?.write ? process.stderr : process.stdout).write(${s}); return; } let b = (globalThis.__os_buf ?? "") + ${s}; const nl = b.lastIndexOf("\\n"); if (nl >= 0) { (${fd} === 2 && typeof console.error === "function" ? console.error : console.log)(b.slice(0, nl)); b = b.slice(nl + 1); } globalThis.__os_buf = b;`,
|
|
181
|
+
os_exit: ([code]) =>
|
|
182
|
+
`if (typeof process !== "undefined" && process?.exit) process.exit(${code}); throw new Error("os.Exit(" + ${code} + ")");`,
|
|
183
|
+
os_getenv: ([key]) =>
|
|
184
|
+
`return (typeof process !== "undefined" && process?.env ? process.env : (globalThis.__os_env ??= {}))[${key}] ?? "";`,
|
|
185
|
+
os_setenv: ([key, value]) =>
|
|
186
|
+
`(typeof process !== "undefined" && process?.env ? process.env : (globalThis.__os_env ??= {}))[${key}] = ${value};`,
|
|
187
|
+
unicode_is: ([r, class_]) => unicodeIsJs(r, class_),
|
|
188
|
+
unicode_to_upper: ([r]) => unicodeToUpperJs(r),
|
|
189
|
+
unicode_to_lower: ([r]) => unicodeToLowerJs(r),
|
|
190
|
+
crypto_rand_u32: () =>
|
|
191
|
+
'const c = typeof crypto !== "undefined" ? crypto : globalThis.crypto; if (c?.getRandomValues) return c.getRandomValues(new Uint32Array(1))[0]; return Math.floor(Math.random() * 4294967296);',
|
|
192
|
+
// A number per object for the whole run, from 1 (pointers print as 0x<id>).
|
|
193
|
+
testing_nanotime: () => "return BigInt(Math.round(performance.now() * 1e6));",
|
|
194
|
+
testing_match: ([pattern, s]) =>
|
|
195
|
+
`const c = (globalThis.__testRe ??= new Map()); let re = c.get(${pattern}); if (!re) { re = new RegExp(${pattern}); c.set(${pattern}, re); } return re.test(${s});`,
|
|
196
|
+
testing_call: ([f, t]) =>
|
|
197
|
+
`const r = ${f}(${t}); return r && typeof r.then === "function" ? r : null;`,
|
|
198
|
+
fmt_ptr_id: ([v]) =>
|
|
199
|
+
`if (${v} === null || ${v} === undefined || (typeof ${v} !== "object" && typeof ${v} !== "function")) return 0n; const t = (globalThis.__fmtptr ??= { m: new WeakMap(), n: 1 }); let id = t.m.get(${v}); if (!id) { id = t.n++; t.m.set(${v}, id); } return BigInt(id);`,
|
|
200
|
+
math_float64bits: ([f]) =>
|
|
201
|
+
`const $b = new ArrayBuffer(8); new Float64Array($b)[0] = ${f}; return new BigUint64Array($b)[0];`,
|
|
202
|
+
math_float64frombits: ([u]) =>
|
|
203
|
+
`const $b = new ArrayBuffer(8); new BigUint64Array($b)[0] = BigInt(${u}); return new Float64Array($b)[0];`,
|
|
204
|
+
math_float32bits: ([f]) =>
|
|
205
|
+
`const $b = new ArrayBuffer(4); new Float32Array($b)[0] = ${f}; return new Uint32Array($b)[0];`,
|
|
206
|
+
math_float32frombits: ([u]) =>
|
|
207
|
+
`const $b = new ArrayBuffer(4); new Uint32Array($b)[0] = ${u} >>> 0; return new Float32Array($b)[0];`,
|
|
208
|
+
math_mod: ([x, y]) => `return ${x} % ${y};`,
|
|
209
|
+
...Object.fromEntries(
|
|
210
|
+
[
|
|
211
|
+
"sin",
|
|
212
|
+
"cos",
|
|
213
|
+
"tan",
|
|
214
|
+
"asin",
|
|
215
|
+
"acos",
|
|
216
|
+
"atan",
|
|
217
|
+
"sinh",
|
|
218
|
+
"cosh",
|
|
219
|
+
"tanh",
|
|
220
|
+
"exp",
|
|
221
|
+
"expm1",
|
|
222
|
+
"log",
|
|
223
|
+
"log1p",
|
|
224
|
+
"log2",
|
|
225
|
+
"log10",
|
|
226
|
+
"cbrt",
|
|
227
|
+
].map((f) => [`math_${f}`, ([x]) => `return Math.${f}(${x});`]),
|
|
228
|
+
),
|
|
229
|
+
...Object.fromEntries(
|
|
230
|
+
["atan2", "pow", "hypot"].map((f) => [
|
|
231
|
+
`math_${f}`,
|
|
232
|
+
([x, y]) => `return Math.${f}(${x}, ${y});`,
|
|
233
|
+
]),
|
|
234
|
+
),
|
|
235
|
+
};
|
|
198
236
|
|
|
199
237
|
export class CodeGen {
|
|
200
238
|
// jsImports: Map<importPath, string[]> — npm package imports to emit at top of file
|
|
@@ -209,6 +247,7 @@ export class CodeGen {
|
|
|
209
247
|
this.checker = checker;
|
|
210
248
|
this.target = options.target ?? "js";
|
|
211
249
|
this.strict = Boolean(options.strict || this.target === "both");
|
|
250
|
+
this.natives = options.natives !== false; // replace native calls with host code
|
|
212
251
|
this.out = [];
|
|
213
252
|
this.indent = 0;
|
|
214
253
|
this.structNames = new Set();
|
|
@@ -224,9 +263,9 @@ export class CodeGen {
|
|
|
224
263
|
this._srcMappings = []; // { genLine, srcLine, srcFileIdx } for source map
|
|
225
264
|
this._currentSrcFileIdx = 0; // updated as each top-level decl is generated
|
|
226
265
|
this._boxedVars = new Set(); // address-taken scalar variables that need boxing
|
|
227
|
-
this.
|
|
228
|
-
this.
|
|
229
|
-
this.
|
|
266
|
+
this._hoistedLocals = new Set(); // function-local classes the json codecs name
|
|
267
|
+
this._hasJsonCodecs = false;
|
|
268
|
+
this._jsonCodecFileIdx = -1;
|
|
230
269
|
// Runtime helper usage tracking — only emit helpers that are actually used
|
|
231
270
|
this._helpers = new Set();
|
|
232
271
|
// Per-function context for Go value semantics (see _withFnCtx)
|
|
@@ -331,7 +370,10 @@ export class CodeGen {
|
|
|
331
370
|
// `lower()`, the JS path here.
|
|
332
371
|
normalizeDefers(program);
|
|
333
372
|
// Strict mode: index checks the loop shape proves unnecessary are elided.
|
|
334
|
-
if (this.strict)
|
|
373
|
+
if (this.strict) {
|
|
374
|
+
markInBoundsIndexes(program);
|
|
375
|
+
markRedundantNilChecks(program, (d) => comparesToNil(d.body, d.recvName));
|
|
376
|
+
}
|
|
335
377
|
if (program._filename) this._srcFileNames = [program._filename];
|
|
336
378
|
// Ensure _pkgName is set on all decls (generateAll sets it for
|
|
337
379
|
// multi-file; for single-file compiles we derive it from the AST).
|
|
@@ -343,16 +385,22 @@ export class CodeGen {
|
|
|
343
385
|
}
|
|
344
386
|
}
|
|
345
387
|
this._renameReservedIdents(program.decls);
|
|
388
|
+
this._elemAddrTaken =
|
|
389
|
+
takesElementAddress(program.decls) ||
|
|
390
|
+
this.checker?.depElemAddrTaken === true;
|
|
346
391
|
this._boxedGlobals = this._collectBoxedGlobals(program);
|
|
347
392
|
this._boxedVars = new Set(this._boxedGlobals);
|
|
348
393
|
this._collectResliceNames(program);
|
|
349
|
-
this._jsonAnyUsed = this._usesJsonAnyMarshal(program.decls);
|
|
350
394
|
const methods = this._collectDecls(program);
|
|
351
395
|
this._emitJsImports();
|
|
352
396
|
this._emitTypeDecls(program, methods);
|
|
397
|
+
// Package variable initialisers may call functions that declare local
|
|
398
|
+
// structs, so the hoisted classes are declared before them.
|
|
399
|
+
const hoistedAt = this.out.length;
|
|
400
|
+
if (this._hasJsonCodecs) this.line("");
|
|
353
401
|
this._emitVarConstDecls(program);
|
|
354
402
|
const initNames = this._emitFuncDecls(program);
|
|
355
|
-
this.
|
|
403
|
+
this._emitHoistedLocals(hoistedAt);
|
|
356
404
|
this._callInitAndMain(initNames, program, isTest);
|
|
357
405
|
const prependedLines = this._prependHelpers(isTest);
|
|
358
406
|
return this._finalizeOutput(prependedLines);
|
|
@@ -467,6 +515,7 @@ export class CodeGen {
|
|
|
467
515
|
FuncLit: "_renameSignature",
|
|
468
516
|
TemplDecl: "_renameSignature",
|
|
469
517
|
TemplExpr: "_renameTemplExprNode",
|
|
518
|
+
TemplChildren: "_renameTemplExprNode",
|
|
470
519
|
TemplComponent: "_renameTemplExprNode",
|
|
471
520
|
TemplIf: "_renameTemplExprNode",
|
|
472
521
|
TemplSwitch: "_renameTemplExprNode",
|
|
@@ -567,37 +616,15 @@ export class CodeGen {
|
|
|
567
616
|
this._helpers.add(name);
|
|
568
617
|
if (name === "ifeq" || name === "geq") this._helpers.add("equal");
|
|
569
618
|
if (name === "geq") this._helpers.add("ifeq");
|
|
570
|
-
if (
|
|
571
|
-
name === "strconvError" ||
|
|
572
|
-
name === "pathError" ||
|
|
573
|
-
name === "ioEOF" ||
|
|
574
|
-
name === "base64" ||
|
|
575
|
-
name === "hex" ||
|
|
576
|
-
name === "json"
|
|
577
|
-
) {
|
|
578
|
-
this._helpers.add("error");
|
|
579
|
-
}
|
|
580
619
|
if (name === "json") {
|
|
581
|
-
this.useHelper("base64");
|
|
582
|
-
}
|
|
583
|
-
if (name === "os") {
|
|
584
|
-
this.useHelper("ioEOF");
|
|
585
620
|
this._helpers.add("error");
|
|
586
621
|
}
|
|
622
|
+
if (name === "panicRt") this._helpers.add("error");
|
|
587
623
|
if (name === "append") this.useHelper("slice");
|
|
588
|
-
if (name === "sprint") this._helpers.add("sprintf");
|
|
589
624
|
if (name === "slice") this._helpers.add("sv");
|
|
590
|
-
if (name === "binary") {
|
|
591
|
-
this.useHelper("ioEOF");
|
|
592
|
-
this._helpers.add("error");
|
|
593
|
-
}
|
|
594
625
|
}
|
|
595
626
|
|
|
596
|
-
_prependHelpers(
|
|
597
|
-
if (isTest || this._helpers.has("testing")) {
|
|
598
|
-
this.useHelper("sprintf");
|
|
599
|
-
this.useHelper("testing");
|
|
600
|
-
}
|
|
627
|
+
_prependHelpers(_isTest = false) {
|
|
601
628
|
const helpers = [];
|
|
602
629
|
if (this.collectedCss.length > 0) helpers.push(HELPER_INJECT_STYLES);
|
|
603
630
|
for (const [name, src] of HELPER_MAP) {
|
|
@@ -616,6 +643,8 @@ export class CodeGen {
|
|
|
616
643
|
// Annotates each decl with its source file index before merging.
|
|
617
644
|
generateAll(programs, options = {}) {
|
|
618
645
|
this._srcFileNames = programs.map((p) => p._filename ?? null);
|
|
646
|
+
this._jsonCodecFileIdx = programs.findIndex((p) => p._generated);
|
|
647
|
+
this._hasJsonCodecs = this._jsonCodecFileIdx >= 0;
|
|
619
648
|
for (let i = 0; i < programs.length; i++) {
|
|
620
649
|
for (const decl of programs[i].decls) {
|
|
621
650
|
decl._srcFileIdx = i;
|
|
@@ -632,8 +661,13 @@ export class CodeGen {
|
|
|
632
661
|
// Records a non-fatal diagnostic at `node` ("main.go at line 3:5: msg").
|
|
633
662
|
_warn(msg, node) {
|
|
634
663
|
const file = this._srcFileNames[this._currentSrcFileIdx ?? 0];
|
|
635
|
-
|
|
636
|
-
|
|
664
|
+
// Binary and unary expressions carry no position of their own: use the
|
|
665
|
+
// leftmost operand that does.
|
|
666
|
+
let pos = node;
|
|
667
|
+
while (pos && !(pos.line ?? pos._line))
|
|
668
|
+
pos = pos.left ?? pos.operand ?? pos.expr;
|
|
669
|
+
const line = pos?.line ?? pos?._line;
|
|
670
|
+
const at = line ? `at line ${line}:${pos.col ?? pos._col ?? 1}` : "";
|
|
637
671
|
const loc = [file, at].filter(Boolean).join(" ");
|
|
638
672
|
this.warnings.push(loc ? `${loc}: ${msg}` : msg);
|
|
639
673
|
}
|
|
@@ -646,14 +680,6 @@ export class CodeGen {
|
|
|
646
680
|
return this._srcMappings;
|
|
647
681
|
}
|
|
648
682
|
|
|
649
|
-
// Returns a source map JSON string for the last generate() call.
|
|
650
|
-
// sources: string[] of source filenames (relative to the output file).
|
|
651
|
-
// sourcesContent: string[] of original file contents (embedded for DevTools breakpoints).
|
|
652
|
-
getSourceMap(sources, sourcesContent) {
|
|
653
|
-
const srcArray = Array.isArray(sources) ? sources : [sources];
|
|
654
|
-
return buildSourceMap(srcArray, this._srcMappings, sourcesContent);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
683
|
// ── Type declarations ────────────────────────────────────────
|
|
658
684
|
|
|
659
685
|
// `type S Base` with Base a struct declared in this package: the struct
|
|
@@ -665,9 +691,12 @@ export class CodeGen {
|
|
|
665
691
|
let t = decl.type;
|
|
666
692
|
for (let depth = 0; depth < 16 && t; depth++) {
|
|
667
693
|
if (t.kind === "StructType") return t;
|
|
694
|
+
// `type S Base[int]`: the fields come from the checked type.
|
|
695
|
+
if (t.kind === "GenericTypeName" || t.kind === "InstantiationExpr")
|
|
696
|
+
return { kind: "StructType", fields: [] };
|
|
668
697
|
if (t.kind !== "TypeName" && t.kind !== "Ident") return null;
|
|
669
698
|
const next = this._typeDeclsByName?.get(t.name);
|
|
670
|
-
if (!next
|
|
699
|
+
if (!next) return null;
|
|
671
700
|
t = next.type;
|
|
672
701
|
}
|
|
673
702
|
return null;
|
|
@@ -715,6 +744,7 @@ export class CodeGen {
|
|
|
715
744
|
);
|
|
716
745
|
}
|
|
717
746
|
}
|
|
747
|
+
this._tagMultiUnwrap(methodDecls);
|
|
718
748
|
});
|
|
719
749
|
this.line("}");
|
|
720
750
|
// Box for *T in an interface. It also proxies `.value` to the pointer, so
|
|
@@ -732,10 +762,20 @@ export class CodeGen {
|
|
|
732
762
|
`${m.name}(...__a) { return ${name}.$${m.name}(${recv}, ...__a); }`,
|
|
733
763
|
);
|
|
734
764
|
}
|
|
765
|
+
this._tagMultiUnwrap(methodDecls);
|
|
735
766
|
});
|
|
736
767
|
this.line("}");
|
|
737
768
|
}
|
|
738
769
|
|
|
770
|
+
// `Unwrap() []error` on a box class is told apart from `Unwrap() error`
|
|
771
|
+
// in type tests, as on struct classes.
|
|
772
|
+
_tagMultiUnwrap(methodDecls) {
|
|
773
|
+
const unwrap = methodDecls.find(
|
|
774
|
+
(m) => m.name === "Unwrap" && m.returnType?.kind === "SliceType",
|
|
775
|
+
);
|
|
776
|
+
if (unwrap) this.line("static { this.prototype.Unwrap.__multi = true; }");
|
|
777
|
+
}
|
|
778
|
+
|
|
739
779
|
_namedBoxField(underlying) {
|
|
740
780
|
if (underlying?.kind === "func") return "_fn";
|
|
741
781
|
if (underlying?.kind === "map") return "_map";
|
|
@@ -948,46 +988,82 @@ export class CodeGen {
|
|
|
948
988
|
`Object.defineProperty(${name}.prototype, "value", { get() { return this; }, set(v) { Object.assign(this, v); }, configurable: true });`,
|
|
949
989
|
);
|
|
950
990
|
}
|
|
951
|
-
this.
|
|
991
|
+
this._genValueMethodMeta(name, methodDecls);
|
|
952
992
|
}
|
|
953
993
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
this.
|
|
960
|
-
|
|
994
|
+
// Names of the value-receiver methods: a typed nil pointer in an interface
|
|
995
|
+
// panics when one is called (see __mknil).
|
|
996
|
+
_genValueMethodMeta(name, methodDecls) {
|
|
997
|
+
const vm = methodDecls.filter((m) => !m.recvPointer).map((m) => m.name);
|
|
998
|
+
if (vm.length > 0)
|
|
999
|
+
this.line(
|
|
1000
|
+
`Object.defineProperty(${name}, "__vm", { value: ${JSON.stringify(vm)} });`,
|
|
1001
|
+
);
|
|
1002
|
+
// Pointer-receiver methods are outside the method set of a struct value
|
|
1003
|
+
// held in an interface (see _ifaceMethodCheck).
|
|
1004
|
+
const pm = methodDecls.filter((m) => m.recvPointer).map((m) => m.name);
|
|
1005
|
+
// ...and so are those promoted from an embedded struct value.
|
|
1006
|
+
for (const stub of computeEmbeddedStubs(name, methodDecls, this.checker))
|
|
1007
|
+
if (stub.ptrOnly) pm.push(stub.methodName);
|
|
1008
|
+
if (pm.length > 0)
|
|
1009
|
+
this.line(
|
|
1010
|
+
`Object.defineProperty(${name}, "__pm", { value: ${JSON.stringify(pm)} });`,
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// The generated json codecs live at package level and name function-local
|
|
1015
|
+
// struct classes: those are declared once at module level and assigned
|
|
1016
|
+
// where the Go source declares them.
|
|
1017
|
+
_hoistLocalClass(stmt, start) {
|
|
1018
|
+
if (!this._hasJsonCodecs || stmt.type.kind !== "StructType") return;
|
|
1019
|
+
const open = this.out.findIndex(
|
|
1020
|
+
(l, i) => i >= start && l.trimStart().startsWith(`class ${stmt.name} `),
|
|
1021
|
+
);
|
|
1022
|
+
if (open < 0) return;
|
|
1023
|
+
const indent = this.out[open].match(/^\s*/)[0];
|
|
1024
|
+
const close = this.out.findIndex((l, i) => i > open && l === `${indent}}`);
|
|
1025
|
+
if (close < 0) return;
|
|
1026
|
+
this.out[open] =
|
|
1027
|
+
`${indent}${stmt.name} = ${stmt.name}.__ph ? ${this.out[open].trimStart()}`;
|
|
1028
|
+
this.out[close] = `${indent}} : ${stmt.name};`;
|
|
1029
|
+
this._hoistedLocals.add(stmt.name);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
_emitHoistedLocals(at) {
|
|
1033
|
+
if (this._hoistedLocals.size > 0 && this._hasJsonCodecs)
|
|
1034
|
+
this.out[at] =
|
|
1035
|
+
`var ${[...this._hoistedLocals].map((n) => `${n} = class { static __ph = true; }`).join(", ")};`;
|
|
961
1036
|
}
|
|
962
1037
|
|
|
963
1038
|
// A promoted method runs on the embedded value (`this.In`), so it and the
|
|
964
1039
|
// outer struct's promoted-field accesses see the same storage. Embedded
|
|
965
1040
|
// types that are not compiled Go structs keep the borrowed-`this` form.
|
|
966
|
-
_embedStub(method, embedName) {
|
|
1041
|
+
_embedStub(method, embedName, valueEmbed = false, fieldName = null) {
|
|
1042
|
+
// The field is named after the type without its package suffix
|
|
1043
|
+
// (`Mutex$$sync` is the field `Mutex`), or after the alias it embeds.
|
|
1044
|
+
const field = fieldName || embedName.replace(/\$\$.*$/, "");
|
|
1045
|
+
// A named non-struct type is boxed: its methods are static, taking
|
|
1046
|
+
// the value as the receiver.
|
|
1047
|
+
if (valueEmbed)
|
|
1048
|
+
return `${method}(...__a) { return ${embedName}.$${method}(this.${field}, ...__a); }`;
|
|
967
1049
|
const call = this.structNames.has(embedName)
|
|
968
|
-
? `this.${
|
|
1050
|
+
? `this.${field}.${method}(...__a)`
|
|
969
1051
|
: `${embedName}.prototype.${method}.call(this, ...__a)`;
|
|
970
1052
|
return `${method}(...__a) { return ${call}; }`;
|
|
971
1053
|
}
|
|
972
1054
|
|
|
973
|
-
_genSingleEmbedStubs(embed, declared) {
|
|
974
|
-
const embedName = embed.kind === "named" ? embed.name : null;
|
|
975
|
-
if (!embedName) return;
|
|
976
|
-
const embedBase = embed.kind === "named" ? embed.underlying : embed;
|
|
977
|
-
if (embedBase?.kind !== "struct" || !embedBase.methods) return;
|
|
978
|
-
for (const [mName] of embedBase.methods.entries()) {
|
|
979
|
-
if (!declared.has(mName)) {
|
|
980
|
-
this.blank();
|
|
981
|
-
this.line(this._embedStub(mName, embedName));
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
1055
|
_genEmbeddedMethodStubs(name, methodDecls) {
|
|
987
1056
|
const stubs = computeEmbeddedStubs(name, methodDecls, this.checker);
|
|
988
1057
|
for (const stub of stubs) {
|
|
989
1058
|
this.blank();
|
|
990
|
-
this.line(
|
|
1059
|
+
this.line(
|
|
1060
|
+
this._embedStub(
|
|
1061
|
+
stub.methodName,
|
|
1062
|
+
stub.embedName,
|
|
1063
|
+
stub.valueEmbed,
|
|
1064
|
+
stub.fieldName,
|
|
1065
|
+
),
|
|
1066
|
+
);
|
|
991
1067
|
}
|
|
992
1068
|
}
|
|
993
1069
|
|
|
@@ -1252,47 +1328,65 @@ export class CodeGen {
|
|
|
1252
1328
|
return Boolean(info?.methods.get(method)?._ptrRecv);
|
|
1253
1329
|
}
|
|
1254
1330
|
|
|
1255
|
-
_isReferenceType(t) {
|
|
1256
|
-
return isReferenceType(t);
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
1331
|
// ── Variable / const declarations ────────────────────────────
|
|
1260
1332
|
|
|
1261
1333
|
genVarDecl(decl) {
|
|
1262
1334
|
for (const spec of decl.decls) {
|
|
1263
|
-
if (spec.
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1335
|
+
if (spec._commaOk) this._genDefineStmt(this._varSpecAsDefine(spec));
|
|
1336
|
+
else if (spec.value) this._genVarSpecValues(spec);
|
|
1337
|
+
else this._genVarSpecZero(spec);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
_genVarSpecValues(spec) {
|
|
1342
|
+
const paired = spec.value.length === spec.names.length;
|
|
1343
|
+
const vals = spec.value.map((v, i) => {
|
|
1344
|
+
const js = paired
|
|
1345
|
+
? this._genDeclValue(spec.names[i], v)
|
|
1346
|
+
: this.genExpr(v);
|
|
1347
|
+
// Wrap numeric values assigned to complex-typed vars
|
|
1348
|
+
if (spec.type?.name === "complex128" || spec.type?.name === "complex64") {
|
|
1349
|
+
if (
|
|
1350
|
+
!isComplex(v._type) &&
|
|
1351
|
+
!v._asComplex &&
|
|
1352
|
+
(isNumeric(v._type) || v._type?.kind === "untyped")
|
|
1353
|
+
) {
|
|
1354
|
+
return `{ re: ${js}, im: 0 }`;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
if (spec.type?.name === "int64" || spec.type?.name === "uint64") {
|
|
1358
|
+
if (
|
|
1359
|
+
v._type?.kind === "untyped" &&
|
|
1360
|
+
!js.endsWith("n") &&
|
|
1361
|
+
/^-?\d+$/.test(js)
|
|
1362
|
+
) {
|
|
1363
|
+
return `${js}n`;
|
|
1291
1364
|
}
|
|
1292
1365
|
}
|
|
1366
|
+
return js;
|
|
1367
|
+
});
|
|
1368
|
+
this._emitVarBindings(spec.names, vals, paired);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
_genVarSpecZero(spec) {
|
|
1372
|
+
const zero = spec.type ? this.zeroValueForTypeNode(spec.type) : "null";
|
|
1373
|
+
for (const name of spec.names) {
|
|
1374
|
+
if (name === "_") continue;
|
|
1375
|
+
this._markOwnership(name, true);
|
|
1376
|
+
const val = this._boxedVars.has(name) ? `{ value: ${zero} }` : zero;
|
|
1377
|
+
this.line(`let ${name} = ${val};`);
|
|
1293
1378
|
}
|
|
1294
1379
|
}
|
|
1295
1380
|
|
|
1381
|
+
// `var v, ok = m[k]` generates like `v, ok := m[k]`.
|
|
1382
|
+
_varSpecAsDefine(spec) {
|
|
1383
|
+
return {
|
|
1384
|
+
kind: "DefineStmt",
|
|
1385
|
+
lhs: spec.names.map((name) => ({ kind: "Ident", name })),
|
|
1386
|
+
rhs: spec.value,
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1296
1390
|
// `var a = v` / `var a, b = v1, v2` / `var a, b = f()`, boxing address-taken names.
|
|
1297
1391
|
_emitVarBindings(names, vals, paired) {
|
|
1298
1392
|
if (names.length === 1 && names[0] === "_") {
|