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.
- package/README.md +174 -466
- package/package.json +16 -20
- package/src/backend/js/expressions.js +288 -128
- package/src/backend/js/index.js +138 -56
- package/src/backend/js/runtime.js +1055 -53
- package/src/backend/js/source-map.js +32 -0
- package/src/backend/js/statements.js +151 -83
- package/src/backend/js/stdlib/base64.js +12 -0
- package/src/backend/js/stdlib/binary.js +102 -0
- package/src/backend/js/stdlib/bits.js +15 -1
- package/src/backend/js/stdlib/builder.js +28 -9
- package/src/backend/js/stdlib/bytes.js +75 -18
- package/src/backend/js/stdlib/errors.js +22 -3
- package/src/backend/js/stdlib/fmt.js +95 -20
- package/src/backend/js/stdlib/hex.js +17 -0
- package/src/backend/js/stdlib/html.js +1 -1
- package/src/backend/js/stdlib/index.js +19 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/maps.js +1 -1
- package/src/backend/js/stdlib/math.js +13 -1
- package/src/backend/js/stdlib/path.js +20 -6
- package/src/backend/js/stdlib/rand.js +10 -5
- package/src/backend/js/stdlib/sha256.js +12 -0
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/backend/js/stdlib/slices.js +53 -27
- package/src/backend/js/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +52 -11
- package/src/backend/js/stdlib/strings.js +152 -16
- package/src/backend/js/stdlib/sync.js +13 -0
- package/src/backend/js/stdlib/testing.js +2 -2
- package/src/backend/js/stdlib/time.js +93 -22
- package/src/backend/js/stdlib/unicode.js +2 -2
- package/src/backend/js/templ.js +1 -1
- package/src/backend/wasm/boundary.js +1677 -58
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +1230 -0
- package/src/backend/wasm/emit-exprs.js +2096 -0
- package/src/backend/wasm/emit-maps.js +1999 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +13 -0
- package/src/backend/wasm/emit-stmts.js +2466 -0
- package/src/backend/wasm/emit.js +656 -5031
- package/src/backend/wasm/encode.js +259 -85
- package/src/backend/wasm/glue.js +19 -5
- package/src/backend/wasm/index.js +2432 -86
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +236 -0
- package/src/backend/wasm/stdlib/bytes.js +5039 -0
- package/src/backend/wasm/stdlib/core.js +8903 -0
- package/src/backend/wasm/stdlib/index.js +40 -0
- package/src/backend/wasm/stdlib/io.js +2161 -0
- package/src/backend/wasm/stdlib/packages.js +2400 -0
- package/src/backend/wasm/stdlib/pure.js +720 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +312 -36
- package/src/backend/wasm/wat.js +49 -2
- package/src/cli-core.js +276 -73
- package/src/compiler.js +162 -25
- package/src/dts-parser.js +79 -126
- package/src/index.js +88 -121
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +4 -6
- package/src/lower/captures.js +12 -23
- package/src/lower/escape.js +4 -9
- package/src/lower/functions.js +277 -5
- package/src/lower/globals.js +7 -15
- package/src/lower/index.js +5 -2
- package/src/lower/ownership.js +35 -19
- package/src/lower/range.js +2 -5
- package/src/lower/walk.js +25 -0
- package/src/minifier.js +20 -68
- package/src/parser/declarations.js +3 -6
- package/src/parser/expressions.js +3 -8
- package/src/parser/index.js +18 -3
- package/src/parser/statements.js +25 -38
- package/src/parser/types.js +1 -2
- package/src/resolver.js +3 -27
- package/src/templ-lexer.js +29 -63
- package/src/templ-parser.js +22 -45
- package/src/test-runner.js +20 -0
- package/src/typechecker/assignability.js +123 -14
- package/src/typechecker/expressions.js +244 -4
- package/src/typechecker/index.js +27 -1
- package/src/typechecker/resolve.js +71 -12
- package/src/typechecker/statements.js +0 -7
- package/src/typechecker/stdlib/core.js +292 -103
- package/src/typechecker/stdlib/extended.js +471 -4
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +545 -8
- package/src/vendor.js +2 -2
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// src/backend/wasm/optimize.js
|
|
2
|
+
// Binaryen optimization pipeline for GoFront WebAssembly modules.
|
|
3
|
+
//
|
|
4
|
+
// Supports:
|
|
5
|
+
// - Bundled Binaryen optimizer (binaryen npm package)
|
|
6
|
+
// - Native wasm-opt binary auto-detection with graceful fallback
|
|
7
|
+
// - WasmGC feature flag configuration (GC, ReferenceTypes, BulkMemory, Multivalue, ExceptionHandling, etc.)
|
|
8
|
+
// - Optimization levels (-O3 by default) + GUFA (Grand Unified Flow Analysis)
|
|
9
|
+
// - Post-optimization module validation (WebAssembly.validate)
|
|
10
|
+
// - WASM source map passthrough (`-ism` the encoder's function-level map,
|
|
11
|
+
// `-osm` the remapped one) and GoFront custom section preservation
|
|
12
|
+
|
|
13
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
14
|
+
import {
|
|
15
|
+
existsSync,
|
|
16
|
+
mkdtempSync,
|
|
17
|
+
readFileSync,
|
|
18
|
+
rmSync,
|
|
19
|
+
writeFileSync,
|
|
20
|
+
} from "node:fs";
|
|
21
|
+
import { createRequire } from "node:module";
|
|
22
|
+
import { tmpdir } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { GOFRONT_SECTION_BYTES, isGoFrontWasm } from "./encode.js";
|
|
25
|
+
|
|
26
|
+
// Binaryen feature bitmask: GC (1024) | ReferenceTypes (256) | BulkMemory (16) | Multivalue (512) |
|
|
27
|
+
// ExceptionHandling (64) | MutableGlobals (2) | NontrappingFPToInt (4) | TailCall (128) = 2006
|
|
28
|
+
export const DEFAULT_WASM_FEATURES = 2006;
|
|
29
|
+
|
|
30
|
+
let _nativeWasmOptAvailable = null;
|
|
31
|
+
let _npmWasmOptPath = null;
|
|
32
|
+
|
|
33
|
+
const _require = createRequire(import.meta.url);
|
|
34
|
+
|
|
35
|
+
function getNpmWasmOptPath() {
|
|
36
|
+
if (_npmWasmOptPath === null) {
|
|
37
|
+
try {
|
|
38
|
+
_npmWasmOptPath = _require.resolve("binaryen/bin/wasm-opt");
|
|
39
|
+
} catch {
|
|
40
|
+
_npmWasmOptPath = "";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return _npmWasmOptPath;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function hasNativeWasmOpt() {
|
|
47
|
+
if (_nativeWasmOptAvailable === null) {
|
|
48
|
+
try {
|
|
49
|
+
const res = spawnSync("wasm-opt", ["--version"], { stdio: "ignore" });
|
|
50
|
+
_nativeWasmOptAvailable = res.status === 0;
|
|
51
|
+
} catch {
|
|
52
|
+
_nativeWasmOptAvailable = false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return _nativeWasmOptAvailable;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Injects GoFront custom section right after the 8-byte WASM header so that
|
|
59
|
+
// isGoFrontWasm recognises the module for clean artifact cleanup.
|
|
60
|
+
export function injectGoFrontSection(bytes) {
|
|
61
|
+
if (isGoFrontWasm(bytes)) return bytes;
|
|
62
|
+
if (!bytes || bytes.length < 8) return bytes;
|
|
63
|
+
const header = bytes.subarray(0, 8);
|
|
64
|
+
const rest = bytes.subarray(8);
|
|
65
|
+
const tagged = new Uint8Array(
|
|
66
|
+
header.length + GOFRONT_SECTION_BYTES.length + rest.length,
|
|
67
|
+
);
|
|
68
|
+
tagged.set(header, 0);
|
|
69
|
+
tagged.set(GOFRONT_SECTION_BYTES, header.length);
|
|
70
|
+
tagged.set(rest, header.length + GOFRONT_SECTION_BYTES.length);
|
|
71
|
+
return tagged;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function runWasmOpt(
|
|
75
|
+
command,
|
|
76
|
+
cmdArgs,
|
|
77
|
+
wasmBytes,
|
|
78
|
+
options = {},
|
|
79
|
+
engine = "native",
|
|
80
|
+
) {
|
|
81
|
+
const dir = mkdtempSync(join(tmpdir(), "gofront-wasmopt-"));
|
|
82
|
+
const input = join(dir, "in.wasm");
|
|
83
|
+
const output = join(dir, "out.wasm");
|
|
84
|
+
const mapOutput = join(dir, "out.wasm.map");
|
|
85
|
+
const watOutput = join(dir, "out.wat");
|
|
86
|
+
try {
|
|
87
|
+
writeFileSync(input, wasmBytes);
|
|
88
|
+
const level = options.level ?? 3;
|
|
89
|
+
const shrinkLevel = options.shrinkLevel ?? 0;
|
|
90
|
+
const args = [
|
|
91
|
+
`-O${level}`,
|
|
92
|
+
`--shrink-level=${shrinkLevel}`,
|
|
93
|
+
"--enable-gc",
|
|
94
|
+
"--enable-reference-types",
|
|
95
|
+
"--enable-exception-handling",
|
|
96
|
+
"--enable-multivalue",
|
|
97
|
+
"--enable-bulk-memory",
|
|
98
|
+
"--enable-mutable-globals",
|
|
99
|
+
"--enable-nontrapping-float-to-int",
|
|
100
|
+
"--enable-tail-call",
|
|
101
|
+
];
|
|
102
|
+
if (options.gufa !== false) {
|
|
103
|
+
args.push("--gufa");
|
|
104
|
+
}
|
|
105
|
+
if (options.closedWorld !== false) {
|
|
106
|
+
args.push("--closed-world");
|
|
107
|
+
}
|
|
108
|
+
if (options.trapsNeverHappen === true) {
|
|
109
|
+
args.push("--traps-never-happen");
|
|
110
|
+
}
|
|
111
|
+
if (options.converge) {
|
|
112
|
+
args.push("--converge");
|
|
113
|
+
}
|
|
114
|
+
if (options.sourceMap) {
|
|
115
|
+
if (options.inputSourceMap) {
|
|
116
|
+
const mapInput = join(dir, "in.wasm.map");
|
|
117
|
+
writeFileSync(mapInput, options.inputSourceMap);
|
|
118
|
+
args.push("-ism", mapInput);
|
|
119
|
+
}
|
|
120
|
+
args.push("-osm", mapOutput);
|
|
121
|
+
if (options.sourceMapUrl) {
|
|
122
|
+
args.push("-osu", options.sourceMapUrl);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
args.push(input, "-o", output);
|
|
126
|
+
execFileSync(command, [...cmdArgs, ...args], { stdio: "pipe" });
|
|
127
|
+
const optimizedBytes = new Uint8Array(readFileSync(output));
|
|
128
|
+
let sourceMap = null;
|
|
129
|
+
if (options.sourceMap && existsSync(mapOutput)) {
|
|
130
|
+
sourceMap = readFileSync(mapOutput, "utf8");
|
|
131
|
+
}
|
|
132
|
+
let wat = null;
|
|
133
|
+
if (options.emitWat) {
|
|
134
|
+
try {
|
|
135
|
+
const watArgs = [
|
|
136
|
+
"--enable-gc",
|
|
137
|
+
"--enable-reference-types",
|
|
138
|
+
"--enable-exception-handling",
|
|
139
|
+
"--enable-multivalue",
|
|
140
|
+
"--enable-bulk-memory",
|
|
141
|
+
"--enable-mutable-globals",
|
|
142
|
+
"--enable-nontrapping-float-to-int",
|
|
143
|
+
"--enable-tail-call",
|
|
144
|
+
output,
|
|
145
|
+
"-S",
|
|
146
|
+
"-o",
|
|
147
|
+
watOutput,
|
|
148
|
+
];
|
|
149
|
+
execFileSync(command, [...cmdArgs, ...watArgs], { stdio: "pipe" });
|
|
150
|
+
if (existsSync(watOutput)) {
|
|
151
|
+
wat = readFileSync(watOutput, "utf8");
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// Continue if WAT generation fails
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const tagged = injectGoFrontSection(optimizedBytes);
|
|
158
|
+
if (typeof WebAssembly !== "undefined" && !WebAssembly.validate(tagged)) {
|
|
159
|
+
throw new Error("WASM module validation failed after optimization");
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
wasm: tagged,
|
|
163
|
+
wat,
|
|
164
|
+
sourceMap,
|
|
165
|
+
engine,
|
|
166
|
+
};
|
|
167
|
+
} catch (e) {
|
|
168
|
+
const stderr = e.stderr ? e.stderr.toString() : e.message;
|
|
169
|
+
throw new Error(`WASM optimization failed: ${stderr}`);
|
|
170
|
+
} finally {
|
|
171
|
+
rmSync(dir, { recursive: true, force: true });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function optimizeWithNative(wasmBytes, options = {}) {
|
|
176
|
+
return runWasmOpt("wasm-opt", [], wasmBytes, options, "native");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function optimizeWithBinaryen(wasmBytes, options = {}) {
|
|
180
|
+
const npmPath = getNpmWasmOptPath();
|
|
181
|
+
if (!npmPath) {
|
|
182
|
+
throw new Error("Binaryen npm package wasm-opt binary not found");
|
|
183
|
+
}
|
|
184
|
+
return runWasmOpt(
|
|
185
|
+
process.execPath,
|
|
186
|
+
[npmPath],
|
|
187
|
+
wasmBytes,
|
|
188
|
+
options,
|
|
189
|
+
"binaryen",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function optimizeWasm(wasmBytes, options = {}) {
|
|
194
|
+
if (!wasmBytes || wasmBytes.length === 0) {
|
|
195
|
+
return {
|
|
196
|
+
wasm: wasmBytes,
|
|
197
|
+
wat: null,
|
|
198
|
+
sourceMap: null,
|
|
199
|
+
originalSize: 0,
|
|
200
|
+
optimizedSize: 0,
|
|
201
|
+
savedBytes: 0,
|
|
202
|
+
percentSaved: 0,
|
|
203
|
+
engine: "none",
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const originalSize = wasmBytes.length;
|
|
208
|
+
let res;
|
|
209
|
+
if (options.preferNative !== false && hasNativeWasmOpt()) {
|
|
210
|
+
try {
|
|
211
|
+
res = optimizeWithNative(wasmBytes, options);
|
|
212
|
+
} catch {
|
|
213
|
+
res = optimizeWithBinaryen(wasmBytes, options);
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
res = optimizeWithBinaryen(wasmBytes, options);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const optimizedSize = res.wasm.length;
|
|
220
|
+
const savedBytes = originalSize - optimizedSize;
|
|
221
|
+
const percentSaved =
|
|
222
|
+
originalSize > 0
|
|
223
|
+
? Number(((savedBytes / originalSize) * 100).toFixed(1))
|
|
224
|
+
: 0;
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
wasm: res.wasm,
|
|
228
|
+
wat: res.wat ?? null,
|
|
229
|
+
sourceMap: res.sourceMap ?? null,
|
|
230
|
+
originalSize,
|
|
231
|
+
optimizedSize,
|
|
232
|
+
savedBytes,
|
|
233
|
+
percentSaved,
|
|
234
|
+
engine: res.engine,
|
|
235
|
+
};
|
|
236
|
+
}
|