gofront 1.4.0 → 1.5.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 +72 -9
- package/package.json +2 -1
- package/src/{codegen → backend/js}/expressions.js +217 -60
- package/src/{codegen → backend/js}/index.js +21 -35
- package/src/{codegen → backend/js}/statements.js +43 -48
- package/src/backend/js/stdlib/bits.js +32 -0
- package/src/{codegen → backend/js}/stdlib/index.js +3 -0
- package/src/{codegen → backend/js}/templ.js +2 -2
- package/src/backend/wasm/boundary.js +1345 -0
- package/src/backend/wasm/emit.js +5779 -0
- package/src/backend/wasm/encode.js +789 -0
- package/src/backend/wasm/glue.js +130 -0
- package/src/backend/wasm/index.js +1413 -0
- package/src/backend/wasm/types.js +420 -0
- package/src/backend/wasm/wat.js +255 -0
- package/src/cli-core.js +52 -7
- package/src/compiler.js +322 -12
- package/src/dev-server.js +56 -15
- package/src/index.js +17 -9
- package/src/lexer.js +18 -0
- package/src/lower/boxing.js +36 -0
- package/src/lower/captures.js +179 -0
- package/src/lower/embedding.js +30 -0
- package/src/lower/escape.js +169 -0
- package/src/lower/functions.js +30 -0
- package/src/lower/globals.js +324 -0
- package/src/lower/index.js +115 -0
- package/src/lower/ownership.js +142 -0
- package/src/lower/range.js +70 -0
- package/src/minifier.js +2 -0
- package/src/parser/index.js +8 -3
- package/src/resolver.js +1 -0
- package/src/test-runner.js +74 -20
- package/src/typechecker/expressions.js +40 -1
- package/src/typechecker/index.js +234 -4
- package/src/typechecker/statements.js +7 -0
- package/src/typechecker/stdlib/core.js +17 -0
- package/src/typechecker/types.js +56 -2
- /package/src/{codegen → backend/js}/runtime.js +0 -0
- /package/src/{codegen → backend/js}/source-map.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/errors.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/fmt.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/maps.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/path.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/slices.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/sort.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/strconv.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/strings.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/testing.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/time.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
// src/backend/wasm/encode.js
|
|
2
|
+
// Dependency-free WebAssembly binary encoder (Wasm 2.0, WasmGC, Exception Handling).
|
|
3
|
+
|
|
4
|
+
// ── LEB128 & Primitive Encoders ──────────────────────────────
|
|
5
|
+
|
|
6
|
+
export function encodeU32LEB(val) {
|
|
7
|
+
let v = val >>> 0;
|
|
8
|
+
const bytes = [];
|
|
9
|
+
do {
|
|
10
|
+
let byte = v & 0x7f;
|
|
11
|
+
v >>>= 7;
|
|
12
|
+
if (v !== 0) byte |= 0x80;
|
|
13
|
+
bytes.push(byte);
|
|
14
|
+
} while (v !== 0);
|
|
15
|
+
return bytes;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function encodeI32LEB(val) {
|
|
19
|
+
let v = val | 0;
|
|
20
|
+
const bytes = [];
|
|
21
|
+
let more = true;
|
|
22
|
+
while (more) {
|
|
23
|
+
let byte = v & 0x7f;
|
|
24
|
+
v >>= 7;
|
|
25
|
+
if ((v === 0 && (byte & 0x40) === 0) || (v === -1 && (byte & 0x40) !== 0)) {
|
|
26
|
+
more = false;
|
|
27
|
+
} else {
|
|
28
|
+
byte |= 0x80;
|
|
29
|
+
}
|
|
30
|
+
bytes.push(byte);
|
|
31
|
+
}
|
|
32
|
+
return bytes;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function encodeI64LEB(val) {
|
|
36
|
+
let v = BigInt.asIntN(64, typeof val === "bigint" ? val : BigInt(val));
|
|
37
|
+
const bytes = [];
|
|
38
|
+
let more = true;
|
|
39
|
+
while (more) {
|
|
40
|
+
let byte = Number(v & 0x7fn);
|
|
41
|
+
v >>= 7n;
|
|
42
|
+
if (
|
|
43
|
+
(v === 0n && (byte & 0x40) === 0) ||
|
|
44
|
+
(v === -1n && (byte & 0x40) !== 0)
|
|
45
|
+
) {
|
|
46
|
+
more = false;
|
|
47
|
+
} else {
|
|
48
|
+
byte |= 0x80;
|
|
49
|
+
}
|
|
50
|
+
bytes.push(byte);
|
|
51
|
+
}
|
|
52
|
+
return bytes;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function encodeF32(val) {
|
|
56
|
+
const buf = new ArrayBuffer(4);
|
|
57
|
+
new DataView(buf).setFloat32(0, Number(val), true); // little-endian
|
|
58
|
+
return Array.from(new Uint8Array(buf));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function encodeF64(val) {
|
|
62
|
+
const buf = new ArrayBuffer(8);
|
|
63
|
+
new DataView(buf).setFloat64(0, Number(val), true); // little-endian
|
|
64
|
+
return Array.from(new Uint8Array(buf));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function encodeString(str) {
|
|
68
|
+
const utf8 = new TextEncoder().encode(str);
|
|
69
|
+
return [...encodeU32LEB(utf8.length), ...utf8];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function encodeVector(items, encodeItem) {
|
|
73
|
+
const count = encodeU32LEB(items.length);
|
|
74
|
+
const bytes = [];
|
|
75
|
+
for (const item of items) {
|
|
76
|
+
const chunk = encodeItem(item);
|
|
77
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
78
|
+
bytes.push(chunk[i]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return [...count, ...bytes];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Value Types & Heap Types ─────────────────────────────────
|
|
85
|
+
|
|
86
|
+
export const ValType = {
|
|
87
|
+
i32: 0x7f,
|
|
88
|
+
i64: 0x7e,
|
|
89
|
+
f32: 0x7d,
|
|
90
|
+
f64: 0x7c,
|
|
91
|
+
v128: 0x7b,
|
|
92
|
+
i8: 0x7a,
|
|
93
|
+
i16: 0x79,
|
|
94
|
+
funcref: 0x70,
|
|
95
|
+
externref: 0x6f,
|
|
96
|
+
anyref: 0x6e,
|
|
97
|
+
eqref: 0x6d,
|
|
98
|
+
i31ref: 0x6c,
|
|
99
|
+
structref: 0x6b,
|
|
100
|
+
arrayref: 0x6a,
|
|
101
|
+
nullref: 0x71,
|
|
102
|
+
nullfuncref: 0x73,
|
|
103
|
+
nullexternref: 0x72,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export function encodeValType(type) {
|
|
107
|
+
if (typeof type === "number") {
|
|
108
|
+
return [type];
|
|
109
|
+
}
|
|
110
|
+
if (typeof type === "string") {
|
|
111
|
+
if (ValType[type] !== undefined) return [ValType[type]];
|
|
112
|
+
throw new Error(`Unknown value type: ${type}`);
|
|
113
|
+
}
|
|
114
|
+
if (typeof type === "object" && type !== null) {
|
|
115
|
+
if (type.kind === "ref") {
|
|
116
|
+
const opcode = type.nullable ? 0x63 : 0x64;
|
|
117
|
+
if (typeof type.typeIndex === "number") {
|
|
118
|
+
return [opcode, ...encodeI32LEB(type.typeIndex)];
|
|
119
|
+
}
|
|
120
|
+
if (typeof type.heapType === "string") {
|
|
121
|
+
const ht = ValType[type.heapType] ?? ValType[`${type.heapType}ref`];
|
|
122
|
+
if (ht !== undefined) return [opcode, ht];
|
|
123
|
+
}
|
|
124
|
+
if (typeof type.heapType === "number") {
|
|
125
|
+
return [opcode, ...encodeI32LEB(type.heapType)];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
throw new Error(`Invalid valtype specification: ${JSON.stringify(type)}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function encodeBlockType(blockType) {
|
|
133
|
+
if (!blockType || blockType === "void") {
|
|
134
|
+
return [0x40];
|
|
135
|
+
}
|
|
136
|
+
if (typeof blockType === "string") {
|
|
137
|
+
return encodeValType(blockType);
|
|
138
|
+
}
|
|
139
|
+
if (typeof blockType === "number") {
|
|
140
|
+
// Type index for multi-value block: signed LEB128
|
|
141
|
+
return encodeI32LEB(blockType);
|
|
142
|
+
}
|
|
143
|
+
return encodeValType(blockType);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ── Instruction Encoders ─────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
const OPCODES = {
|
|
149
|
+
unreachable: 0x00,
|
|
150
|
+
nop: 0x01,
|
|
151
|
+
block: 0x02,
|
|
152
|
+
loop: 0x03,
|
|
153
|
+
if: 0x04,
|
|
154
|
+
else: 0x05,
|
|
155
|
+
throw: 0x08,
|
|
156
|
+
end: 0x0b,
|
|
157
|
+
br: 0x0c,
|
|
158
|
+
br_if: 0x0d,
|
|
159
|
+
br_table: 0x0e,
|
|
160
|
+
return: 0x0f,
|
|
161
|
+
call: 0x10,
|
|
162
|
+
call_indirect: 0x11,
|
|
163
|
+
return_call: 0x12,
|
|
164
|
+
return_call_indirect: 0x13,
|
|
165
|
+
call_ref: 0x14,
|
|
166
|
+
return_call_ref: 0x15,
|
|
167
|
+
drop: 0x1a,
|
|
168
|
+
select: 0x1b,
|
|
169
|
+
|
|
170
|
+
"local.get": 0x20,
|
|
171
|
+
"local.set": 0x21,
|
|
172
|
+
"local.tee": 0x22,
|
|
173
|
+
"global.get": 0x23,
|
|
174
|
+
"global.set": 0x24,
|
|
175
|
+
|
|
176
|
+
"i32.const": 0x41,
|
|
177
|
+
"i64.const": 0x42,
|
|
178
|
+
"f32.const": 0x43,
|
|
179
|
+
"f64.const": 0x44,
|
|
180
|
+
|
|
181
|
+
"i32.eqz": 0x45,
|
|
182
|
+
"i32.eq": 0x46,
|
|
183
|
+
"i32.ne": 0x47,
|
|
184
|
+
"i32.lt_s": 0x48,
|
|
185
|
+
"i32.lt_u": 0x49,
|
|
186
|
+
"i32.gt_s": 0x4a,
|
|
187
|
+
"i32.gt_u": 0x4b,
|
|
188
|
+
"i32.le_s": 0x4c,
|
|
189
|
+
"i32.le_u": 0x4d,
|
|
190
|
+
"i32.ge_s": 0x4e,
|
|
191
|
+
"i32.ge_u": 0x4f,
|
|
192
|
+
|
|
193
|
+
"i64.eqz": 0x50,
|
|
194
|
+
"i64.eq": 0x51,
|
|
195
|
+
"i64.ne": 0x52,
|
|
196
|
+
"i64.lt_s": 0x53,
|
|
197
|
+
"i64.lt_u": 0x54,
|
|
198
|
+
"i64.gt_s": 0x55,
|
|
199
|
+
"i64.gt_u": 0x56,
|
|
200
|
+
"i64.le_s": 0x57,
|
|
201
|
+
"i64.le_u": 0x58,
|
|
202
|
+
"i64.ge_s": 0x59,
|
|
203
|
+
"i64.ge_u": 0x5a,
|
|
204
|
+
|
|
205
|
+
"f32.eq": 0x5b,
|
|
206
|
+
"f32.ne": 0x5c,
|
|
207
|
+
"f32.lt": 0x5d,
|
|
208
|
+
"f32.gt": 0x5e,
|
|
209
|
+
"f32.le": 0x5f,
|
|
210
|
+
"f32.ge": 0x60,
|
|
211
|
+
|
|
212
|
+
"f64.eq": 0x61,
|
|
213
|
+
"f64.ne": 0x62,
|
|
214
|
+
"f64.lt": 0x63,
|
|
215
|
+
"f64.gt": 0x64,
|
|
216
|
+
"f64.le": 0x65,
|
|
217
|
+
"f64.ge": 0x66,
|
|
218
|
+
|
|
219
|
+
"i32.clz": 0x67,
|
|
220
|
+
"i32.ctz": 0x68,
|
|
221
|
+
"i32.popcnt": 0x69,
|
|
222
|
+
"i32.add": 0x6a,
|
|
223
|
+
"i32.sub": 0x6b,
|
|
224
|
+
"i32.mul": 0x6c,
|
|
225
|
+
"i32.div_s": 0x6d,
|
|
226
|
+
"i32.div_u": 0x6e,
|
|
227
|
+
"i32.rem_s": 0x6f,
|
|
228
|
+
"i32.rem_u": 0x70,
|
|
229
|
+
"i32.and": 0x71,
|
|
230
|
+
"i32.or": 0x72,
|
|
231
|
+
"i32.xor": 0x73,
|
|
232
|
+
"i32.shl": 0x74,
|
|
233
|
+
"i32.shr_s": 0x75,
|
|
234
|
+
"i32.shr_u": 0x76,
|
|
235
|
+
"i32.rotl": 0x77,
|
|
236
|
+
"i32.rotr": 0x78,
|
|
237
|
+
|
|
238
|
+
"i64.clz": 0x79,
|
|
239
|
+
"i64.ctz": 0x7a,
|
|
240
|
+
"i64.popcnt": 0x7b,
|
|
241
|
+
"i64.add": 0x7c,
|
|
242
|
+
"i64.sub": 0x7d,
|
|
243
|
+
"i64.mul": 0x7e,
|
|
244
|
+
"i64.div_s": 0x7f,
|
|
245
|
+
"i64.div_u": 0x80,
|
|
246
|
+
"i64.rem_s": 0x81,
|
|
247
|
+
"i64.rem_u": 0x82,
|
|
248
|
+
"i64.and": 0x83,
|
|
249
|
+
"i64.or": 0x84,
|
|
250
|
+
"i64.xor": 0x85,
|
|
251
|
+
"i64.shl": 0x86,
|
|
252
|
+
"i64.shr_s": 0x87,
|
|
253
|
+
"i64.shr_u": 0x88,
|
|
254
|
+
"i64.rotl": 0x89,
|
|
255
|
+
"i64.rotr": 0x8a,
|
|
256
|
+
|
|
257
|
+
"f32.abs": 0x8b,
|
|
258
|
+
"f32.neg": 0x8c,
|
|
259
|
+
"f32.ceil": 0x8d,
|
|
260
|
+
"f32.floor": 0x8e,
|
|
261
|
+
"f32.trunc": 0x8f,
|
|
262
|
+
"f32.nearest": 0x90,
|
|
263
|
+
"f32.sqrt": 0x91,
|
|
264
|
+
"f32.add": 0x92,
|
|
265
|
+
"f32.sub": 0x93,
|
|
266
|
+
"f32.mul": 0x94,
|
|
267
|
+
"f32.div": 0x95,
|
|
268
|
+
"f32.min": 0x96,
|
|
269
|
+
"f32.max": 0x97,
|
|
270
|
+
"f32.copysign": 0x98,
|
|
271
|
+
|
|
272
|
+
"f64.abs": 0x99,
|
|
273
|
+
"f64.neg": 0x9a,
|
|
274
|
+
"f64.ceil": 0x9b,
|
|
275
|
+
"f64.floor": 0x9c,
|
|
276
|
+
"f64.trunc": 0x9d,
|
|
277
|
+
"f64.nearest": 0x9e,
|
|
278
|
+
"f64.sqrt": 0x9f,
|
|
279
|
+
"f64.add": 0xa0,
|
|
280
|
+
"f64.sub": 0xa1,
|
|
281
|
+
"f64.mul": 0xa2,
|
|
282
|
+
"f64.div": 0xa3,
|
|
283
|
+
"f64.min": 0xa4,
|
|
284
|
+
"f64.max": 0xa5,
|
|
285
|
+
"f64.copysign": 0xa6,
|
|
286
|
+
|
|
287
|
+
"i32.wrap_i64": 0xa7,
|
|
288
|
+
"i32.trunc_f32_s": 0xa8,
|
|
289
|
+
"i32.trunc_f32_u": 0xa9,
|
|
290
|
+
"i32.trunc_f64_s": 0xaa,
|
|
291
|
+
"i32.trunc_f64_u": 0xab,
|
|
292
|
+
"i64.extend_i32_s": 0xac,
|
|
293
|
+
"i64.extend_i32_u": 0xad,
|
|
294
|
+
"i64.trunc_f32_s": 0xae,
|
|
295
|
+
"i64.trunc_f32_u": 0xaf,
|
|
296
|
+
"i64.trunc_f64_s": 0xb0,
|
|
297
|
+
"i64.trunc_f64_u": 0xb1,
|
|
298
|
+
"f32.convert_i32_s": 0xb2,
|
|
299
|
+
"f32.convert_i32_u": 0xb3,
|
|
300
|
+
"f32.convert_i64_s": 0xb4,
|
|
301
|
+
"f32.convert_i64_u": 0xb5,
|
|
302
|
+
"f32.demote_f64": 0xb6,
|
|
303
|
+
"f64.convert_i32_s": 0xb7,
|
|
304
|
+
"f64.convert_i32_u": 0xb8,
|
|
305
|
+
"f64.convert_i64_s": 0xb9,
|
|
306
|
+
"f64.convert_i64_u": 0xba,
|
|
307
|
+
"f64.promote_f32": 0xbb,
|
|
308
|
+
"i32.reinterpret_f32": 0xbc,
|
|
309
|
+
"i64.reinterpret_f64": 0xbd,
|
|
310
|
+
"f32.reinterpret_i32": 0xbe,
|
|
311
|
+
"f64.reinterpret_i64": 0xbf,
|
|
312
|
+
|
|
313
|
+
"i32.extend8_s": 0xc0,
|
|
314
|
+
"i32.extend16_s": 0xc1,
|
|
315
|
+
"i64.extend8_s": 0xc2,
|
|
316
|
+
"i64.extend16_s": 0xc3,
|
|
317
|
+
"i64.extend32_s": 0xc4,
|
|
318
|
+
|
|
319
|
+
"ref.null": 0xd0,
|
|
320
|
+
"ref.is_null": 0xd1,
|
|
321
|
+
"ref.func": 0xd2,
|
|
322
|
+
"ref.eq": 0xd3,
|
|
323
|
+
"ref.as_non_null": 0xd4,
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// 0xFC prefixed saturating truncations
|
|
327
|
+
const SAT_TRUNC_OPCODES = {
|
|
328
|
+
"i32.trunc_sat_f32_s": 0x00,
|
|
329
|
+
"i32.trunc_sat_f32_u": 0x01,
|
|
330
|
+
"i32.trunc_sat_f64_s": 0x02,
|
|
331
|
+
"i32.trunc_sat_f64_u": 0x03,
|
|
332
|
+
"i64.trunc_sat_f32_s": 0x04,
|
|
333
|
+
"i64.trunc_sat_f32_u": 0x05,
|
|
334
|
+
"i64.trunc_sat_f64_s": 0x06,
|
|
335
|
+
"i64.trunc_sat_f64_u": 0x07,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// 0xFB prefixed WasmGC instructions
|
|
339
|
+
const GC_OPCODES = {
|
|
340
|
+
"struct.new": 0x00,
|
|
341
|
+
"struct.new_default": 0x01,
|
|
342
|
+
"struct.get": 0x02,
|
|
343
|
+
"struct.get_s": 0x03,
|
|
344
|
+
"struct.get_u": 0x04,
|
|
345
|
+
"struct.set": 0x05,
|
|
346
|
+
"array.new": 0x06,
|
|
347
|
+
"array.new_default": 0x07,
|
|
348
|
+
"array.new_fixed": 0x08,
|
|
349
|
+
"array.get": 0x0b,
|
|
350
|
+
"array.get_s": 0x0c,
|
|
351
|
+
"array.get_u": 0x0d,
|
|
352
|
+
"array.set": 0x0e,
|
|
353
|
+
"array.len": 0x0f,
|
|
354
|
+
"array.copy": 0x11,
|
|
355
|
+
"ref.test": 0x14,
|
|
356
|
+
"ref.test_null": 0x15,
|
|
357
|
+
"ref.cast": 0x16,
|
|
358
|
+
"ref.cast_null": 0x17,
|
|
359
|
+
"any.convert_extern": 0x1a,
|
|
360
|
+
"extern.convert_any": 0x1b,
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
export function encodeInstruction(inst) {
|
|
364
|
+
if (typeof inst === "string") {
|
|
365
|
+
inst = { op: inst };
|
|
366
|
+
}
|
|
367
|
+
const { op } = inst;
|
|
368
|
+
|
|
369
|
+
// 1. Check saturating truncations (0xFC prefix)
|
|
370
|
+
if (SAT_TRUNC_OPCODES[op] !== undefined) {
|
|
371
|
+
return [0xfc, ...encodeU32LEB(SAT_TRUNC_OPCODES[op])];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 2. Check WasmGC opcodes (0xFB prefix)
|
|
375
|
+
if (GC_OPCODES[op] !== undefined) {
|
|
376
|
+
const subOp = GC_OPCODES[op];
|
|
377
|
+
const bytes = [0xfb, ...encodeU32LEB(subOp)];
|
|
378
|
+
if (op === "array.copy") {
|
|
379
|
+
bytes.push(...encodeU32LEB(inst.typeIndexDst ?? inst.typeIndex ?? 0));
|
|
380
|
+
bytes.push(...encodeU32LEB(inst.typeIndexSrc ?? inst.typeIndex ?? 0));
|
|
381
|
+
return bytes;
|
|
382
|
+
}
|
|
383
|
+
if (op === "array.new_fixed") {
|
|
384
|
+
bytes.push(...encodeU32LEB(inst.typeIndex ?? 0));
|
|
385
|
+
bytes.push(...encodeU32LEB(inst.size ?? inst.count ?? 0));
|
|
386
|
+
return bytes;
|
|
387
|
+
}
|
|
388
|
+
if (
|
|
389
|
+
op === "array.len" ||
|
|
390
|
+
op === "any.convert_extern" ||
|
|
391
|
+
op === "extern.convert_any"
|
|
392
|
+
) {
|
|
393
|
+
return bytes;
|
|
394
|
+
}
|
|
395
|
+
if (inst.typeIndex !== undefined) {
|
|
396
|
+
bytes.push(...encodeU32LEB(inst.typeIndex));
|
|
397
|
+
}
|
|
398
|
+
if (inst.fieldIndex !== undefined) {
|
|
399
|
+
bytes.push(...encodeU32LEB(inst.fieldIndex));
|
|
400
|
+
}
|
|
401
|
+
return bytes;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 3. Standard opcodes
|
|
405
|
+
if (OPCODES[op] !== undefined) {
|
|
406
|
+
const byte = OPCODES[op];
|
|
407
|
+
switch (op) {
|
|
408
|
+
case "block":
|
|
409
|
+
case "loop":
|
|
410
|
+
case "if":
|
|
411
|
+
return [byte, ...encodeBlockType(inst.blockType ?? inst.resultType)];
|
|
412
|
+
|
|
413
|
+
case "br":
|
|
414
|
+
case "br_if":
|
|
415
|
+
return [byte, ...encodeU32LEB(inst.depth ?? inst.index ?? 0)];
|
|
416
|
+
|
|
417
|
+
case "br_table": {
|
|
418
|
+
const targets = inst.targets ?? [];
|
|
419
|
+
const defaultTarget = inst.defaultTarget ?? 0;
|
|
420
|
+
const count = encodeU32LEB(targets.length);
|
|
421
|
+
const targetBytes = targets.flatMap((t) => encodeU32LEB(t));
|
|
422
|
+
return [byte, ...count, ...targetBytes, ...encodeU32LEB(defaultTarget)];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
case "throw":
|
|
426
|
+
return [byte, ...encodeU32LEB(inst.tagIndex ?? inst.index ?? 0)];
|
|
427
|
+
|
|
428
|
+
case "call":
|
|
429
|
+
case "return_call":
|
|
430
|
+
return [byte, ...encodeU32LEB(inst.funcIndex ?? inst.index ?? 0)];
|
|
431
|
+
|
|
432
|
+
case "call_indirect":
|
|
433
|
+
case "return_call_indirect":
|
|
434
|
+
return [
|
|
435
|
+
byte,
|
|
436
|
+
...encodeU32LEB(inst.typeIndex ?? 0),
|
|
437
|
+
...encodeU32LEB(inst.tableIndex ?? 0),
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
case "call_ref":
|
|
441
|
+
case "return_call_ref":
|
|
442
|
+
return [byte, ...encodeU32LEB(inst.typeIndex ?? 0)];
|
|
443
|
+
|
|
444
|
+
case "ref.func":
|
|
445
|
+
return [byte, ...encodeU32LEB(inst.funcIndex ?? inst.index ?? 0)];
|
|
446
|
+
|
|
447
|
+
case "local.get":
|
|
448
|
+
case "local.set":
|
|
449
|
+
case "local.tee":
|
|
450
|
+
return [byte, ...encodeU32LEB(inst.localIndex ?? inst.index ?? 0)];
|
|
451
|
+
|
|
452
|
+
case "global.get":
|
|
453
|
+
case "global.set":
|
|
454
|
+
return [byte, ...encodeU32LEB(inst.globalIndex ?? inst.index ?? 0)];
|
|
455
|
+
|
|
456
|
+
case "i32.const":
|
|
457
|
+
return [byte, ...encodeI32LEB(inst.value ?? 0)];
|
|
458
|
+
|
|
459
|
+
case "i64.const":
|
|
460
|
+
return [byte, ...encodeI64LEB(inst.value ?? 0n)];
|
|
461
|
+
|
|
462
|
+
case "f32.const":
|
|
463
|
+
return [byte, ...encodeF32(inst.value ?? 0)];
|
|
464
|
+
|
|
465
|
+
case "f64.const":
|
|
466
|
+
return [byte, ...encodeF64(inst.value ?? 0)];
|
|
467
|
+
|
|
468
|
+
case "ref.null": {
|
|
469
|
+
const ht =
|
|
470
|
+
inst.heapType !== undefined
|
|
471
|
+
? inst.heapType
|
|
472
|
+
: inst.typeIndex !== undefined
|
|
473
|
+
? inst.typeIndex
|
|
474
|
+
: "any";
|
|
475
|
+
if (typeof ht === "number") {
|
|
476
|
+
return [byte, ...encodeI32LEB(ht)];
|
|
477
|
+
}
|
|
478
|
+
const code = ValType[ht] ?? ValType[`${ht}ref`] ?? 0x6e;
|
|
479
|
+
return [byte, code];
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
default:
|
|
483
|
+
return [byte];
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
throw new Error(`Unsupported WASM instruction opcode: '${op}'`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ── Section Encoders ─────────────────────────────────────────
|
|
491
|
+
|
|
492
|
+
function encodeSection(id, payload) {
|
|
493
|
+
if (payload.length === 0) return [];
|
|
494
|
+
return [id, ...encodeU32LEB(payload.length), ...payload];
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
export function encodeTypeEntry(t) {
|
|
498
|
+
if (t.form === "rec") {
|
|
499
|
+
// Recursive type group: 0x4E, count, types
|
|
500
|
+
const count = encodeU32LEB(t.types.length);
|
|
501
|
+
const innerBytes = t.types.flatMap((sub) => encodeTypeEntry(sub));
|
|
502
|
+
return [0x4e, ...count, ...innerBytes];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (t.form === "struct") {
|
|
506
|
+
// Struct type: 0x5F, vector of fields [valtype, mutability]
|
|
507
|
+
const fields = t.fields ?? [];
|
|
508
|
+
return [
|
|
509
|
+
0x5f,
|
|
510
|
+
...encodeVector(fields, (f) => [
|
|
511
|
+
...encodeValType(f.type),
|
|
512
|
+
f.mutable ? 0x01 : 0x00,
|
|
513
|
+
]),
|
|
514
|
+
];
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (t.form === "array") {
|
|
518
|
+
// Array type: 0x5E, field [valtype, mutability]
|
|
519
|
+
return [
|
|
520
|
+
0x5e,
|
|
521
|
+
...encodeValType(t.elemType ?? t.type),
|
|
522
|
+
t.mutable ? 0x01 : 0x00,
|
|
523
|
+
];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Function type: 0x60, params vector, results vector
|
|
527
|
+
const params = t.params ?? [];
|
|
528
|
+
const results = t.results ?? [];
|
|
529
|
+
return [
|
|
530
|
+
0x60,
|
|
531
|
+
...encodeVector(params, encodeValType),
|
|
532
|
+
...encodeVector(results, encodeValType),
|
|
533
|
+
];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export function encodeTypeSection(types) {
|
|
537
|
+
if (!types || types.length === 0) return [];
|
|
538
|
+
return encodeSection(1, encodeVector(types, encodeTypeEntry));
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export function encodeImportSection(imports) {
|
|
542
|
+
if (!imports || imports.length === 0) return [];
|
|
543
|
+
return encodeSection(
|
|
544
|
+
2,
|
|
545
|
+
encodeVector(imports, (imp) => {
|
|
546
|
+
const mod = encodeString(imp.module);
|
|
547
|
+
const name = encodeString(imp.name);
|
|
548
|
+
let desc = [];
|
|
549
|
+
switch (imp.kind) {
|
|
550
|
+
case "func":
|
|
551
|
+
desc = [0x00, ...encodeU32LEB(imp.typeIndex ?? 0)];
|
|
552
|
+
break;
|
|
553
|
+
case "table":
|
|
554
|
+
desc = [
|
|
555
|
+
0x01,
|
|
556
|
+
...encodeValType(imp.elemType ?? "funcref"),
|
|
557
|
+
imp.max !== undefined ? 0x01 : 0x00,
|
|
558
|
+
...encodeU32LEB(imp.min ?? 0),
|
|
559
|
+
...(imp.max !== undefined ? encodeU32LEB(imp.max) : []),
|
|
560
|
+
];
|
|
561
|
+
break;
|
|
562
|
+
case "memory":
|
|
563
|
+
desc = [
|
|
564
|
+
0x02,
|
|
565
|
+
imp.max !== undefined ? 0x01 : 0x00,
|
|
566
|
+
...encodeU32LEB(imp.min ?? 1),
|
|
567
|
+
...(imp.max !== undefined ? encodeU32LEB(imp.max) : []),
|
|
568
|
+
];
|
|
569
|
+
break;
|
|
570
|
+
case "global":
|
|
571
|
+
desc = [0x03, ...encodeValType(imp.type), imp.mutable ? 0x01 : 0x00];
|
|
572
|
+
break;
|
|
573
|
+
case "tag":
|
|
574
|
+
desc = [0x04, 0x00, ...encodeU32LEB(imp.typeIndex ?? 0)];
|
|
575
|
+
break;
|
|
576
|
+
default:
|
|
577
|
+
throw new Error(`Unknown import kind: ${imp.kind}`);
|
|
578
|
+
}
|
|
579
|
+
return [...mod, ...name, ...desc];
|
|
580
|
+
}),
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export function encodeFunctionSection(funcs) {
|
|
585
|
+
if (!funcs || funcs.length === 0) return [];
|
|
586
|
+
return encodeSection(
|
|
587
|
+
3,
|
|
588
|
+
encodeVector(funcs, (fn) => encodeU32LEB(fn.typeIndex ?? 0)),
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export function encodeTagSection(tags) {
|
|
593
|
+
if (!tags || tags.length === 0) return [];
|
|
594
|
+
return encodeSection(
|
|
595
|
+
13,
|
|
596
|
+
encodeVector(tags, (tag) => [0x00, ...encodeU32LEB(tag.typeIndex ?? 0)]),
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export function encodeGlobalSection(globals) {
|
|
601
|
+
if (!globals || globals.length === 0) return [];
|
|
602
|
+
return encodeSection(
|
|
603
|
+
6,
|
|
604
|
+
encodeVector(globals, (g) => {
|
|
605
|
+
const typeBytes = encodeValType(g.type);
|
|
606
|
+
const mutByte = g.mutable ? 0x01 : 0x00;
|
|
607
|
+
const initInsts = g.init ?? [{ op: "i32.const", value: 0 }];
|
|
608
|
+
const exprBytes = [];
|
|
609
|
+
for (const inst of initInsts) {
|
|
610
|
+
const chunk = encodeInstruction(inst);
|
|
611
|
+
for (let i = 0; i < chunk.length; i++) exprBytes.push(chunk[i]);
|
|
612
|
+
}
|
|
613
|
+
if (
|
|
614
|
+
initInsts.length === 0 ||
|
|
615
|
+
initInsts[initInsts.length - 1].op !== "end"
|
|
616
|
+
) {
|
|
617
|
+
exprBytes.push(0x0b);
|
|
618
|
+
}
|
|
619
|
+
return [...typeBytes, mutByte, ...exprBytes];
|
|
620
|
+
}),
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export function encodeExportSection(exports) {
|
|
625
|
+
if (!exports || exports.length === 0) return [];
|
|
626
|
+
return encodeSection(
|
|
627
|
+
7,
|
|
628
|
+
encodeVector(exports, (exp) => {
|
|
629
|
+
const name = encodeString(exp.name);
|
|
630
|
+
let kindByte = 0x00;
|
|
631
|
+
switch (exp.kind) {
|
|
632
|
+
case "func":
|
|
633
|
+
kindByte = 0x00;
|
|
634
|
+
break;
|
|
635
|
+
case "table":
|
|
636
|
+
kindByte = 0x01;
|
|
637
|
+
break;
|
|
638
|
+
case "memory":
|
|
639
|
+
kindByte = 0x02;
|
|
640
|
+
break;
|
|
641
|
+
case "global":
|
|
642
|
+
kindByte = 0x03;
|
|
643
|
+
break;
|
|
644
|
+
case "tag":
|
|
645
|
+
kindByte = 0x04;
|
|
646
|
+
break;
|
|
647
|
+
default:
|
|
648
|
+
throw new Error(`Unknown export kind: ${exp.kind}`);
|
|
649
|
+
}
|
|
650
|
+
return [...name, kindByte, ...encodeU32LEB(exp.index ?? 0)];
|
|
651
|
+
}),
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function sameValType(a, b) {
|
|
656
|
+
if (a === b) return true;
|
|
657
|
+
if (
|
|
658
|
+
typeof a === "object" &&
|
|
659
|
+
typeof b === "object" &&
|
|
660
|
+
a !== null &&
|
|
661
|
+
b !== null
|
|
662
|
+
) {
|
|
663
|
+
return (
|
|
664
|
+
a.kind === b.kind &&
|
|
665
|
+
a.nullable === b.nullable &&
|
|
666
|
+
a.typeIndex === b.typeIndex &&
|
|
667
|
+
a.heapType === b.heapType
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
export function encodeElementSection(elements) {
|
|
674
|
+
if (!elements || elements.length === 0) return [];
|
|
675
|
+
const segment = [0x03, 0x00, ...encodeVector(elements, encodeU32LEB)];
|
|
676
|
+
return encodeSection(
|
|
677
|
+
9,
|
|
678
|
+
encodeVector([segment], (bytes) => bytes),
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
export function encodeCodeSection(funcs) {
|
|
683
|
+
if (!funcs || funcs.length === 0) return [];
|
|
684
|
+
return encodeSection(
|
|
685
|
+
10,
|
|
686
|
+
encodeVector(funcs, (fn) => {
|
|
687
|
+
// Compress locals: [type1, type1, type2] -> [{ count: 2, type: type1 }, { count: 1, type: type2 }]
|
|
688
|
+
const rawLocals = fn.locals ?? [];
|
|
689
|
+
const compressedLocals = [];
|
|
690
|
+
for (const loc of rawLocals) {
|
|
691
|
+
const locType =
|
|
692
|
+
typeof loc === "string"
|
|
693
|
+
? loc
|
|
694
|
+
: loc.type !== undefined
|
|
695
|
+
? loc.type
|
|
696
|
+
: loc;
|
|
697
|
+
if (
|
|
698
|
+
compressedLocals.length > 0 &&
|
|
699
|
+
sameValType(
|
|
700
|
+
compressedLocals[compressedLocals.length - 1].type,
|
|
701
|
+
locType,
|
|
702
|
+
)
|
|
703
|
+
) {
|
|
704
|
+
compressedLocals[compressedLocals.length - 1].count++;
|
|
705
|
+
} else {
|
|
706
|
+
compressedLocals.push({ count: 1, type: locType });
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const localsBytes = encodeVector(compressedLocals, (group) => [
|
|
711
|
+
...encodeU32LEB(group.count),
|
|
712
|
+
...encodeValType(group.type),
|
|
713
|
+
]);
|
|
714
|
+
|
|
715
|
+
const bodyInsts = fn.body ?? [];
|
|
716
|
+
const bodyBytes = [];
|
|
717
|
+
for (const inst of bodyInsts) {
|
|
718
|
+
const chunk = encodeInstruction(inst);
|
|
719
|
+
for (let i = 0; i < chunk.length; i++) bodyBytes.push(chunk[i]);
|
|
720
|
+
}
|
|
721
|
+
// Ensure terminating end opcode (0x0B)
|
|
722
|
+
if (
|
|
723
|
+
bodyInsts.length === 0 ||
|
|
724
|
+
bodyInsts[bodyInsts.length - 1].op !== "end"
|
|
725
|
+
) {
|
|
726
|
+
bodyBytes.push(0x0b);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const fullBody = [...localsBytes, ...bodyBytes];
|
|
730
|
+
return [...encodeU32LEB(fullBody.length), ...fullBody];
|
|
731
|
+
}),
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// ── Top-level Module Encoder ─────────────────────────────────
|
|
736
|
+
|
|
737
|
+
// Custom section placed right after the header; `isGoFrontWasm` keys off it.
|
|
738
|
+
export const GOFRONT_SECTION_NAME = "gofront";
|
|
739
|
+
const GOFRONT_SECTION_BYTES = [
|
|
740
|
+
0x00,
|
|
741
|
+
...encodeU32LEB(1 + GOFRONT_SECTION_NAME.length),
|
|
742
|
+
...encodeString(GOFRONT_SECTION_NAME),
|
|
743
|
+
];
|
|
744
|
+
|
|
745
|
+
// True when `bytes` start with the wasm header followed by our custom section.
|
|
746
|
+
export function isGoFrontWasm(bytes) {
|
|
747
|
+
const expected = [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00];
|
|
748
|
+
expected.push(...GOFRONT_SECTION_BYTES);
|
|
749
|
+
if (!bytes || bytes.length < expected.length) return false;
|
|
750
|
+
for (let i = 0; i < expected.length; i++) {
|
|
751
|
+
if (bytes[i] !== expected[i]) return false;
|
|
752
|
+
}
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function encodeModule(mod) {
|
|
757
|
+
const magic = [0x00, 0x61, 0x73, 0x6d]; // \0asm
|
|
758
|
+
const version = [0x01, 0x00, 0x00, 0x00]; // version 1
|
|
759
|
+
|
|
760
|
+
const typeSec = encodeTypeSection(mod.types);
|
|
761
|
+
const importSec = encodeImportSection(mod.imports);
|
|
762
|
+
const funcSec = encodeFunctionSection(mod.funcs);
|
|
763
|
+
const tagSec = encodeTagSection(mod.tags);
|
|
764
|
+
const globalSec = encodeGlobalSection(mod.globals);
|
|
765
|
+
const exportSec = encodeExportSection(mod.exports);
|
|
766
|
+
const startSec =
|
|
767
|
+
typeof mod.start === "number"
|
|
768
|
+
? encodeSection(8, encodeU32LEB(mod.start))
|
|
769
|
+
: [];
|
|
770
|
+
const elemSec = encodeElementSection(mod.elements);
|
|
771
|
+
const codeSec = encodeCodeSection(mod.funcs);
|
|
772
|
+
|
|
773
|
+
const allBytes = [
|
|
774
|
+
...magic,
|
|
775
|
+
...version,
|
|
776
|
+
...GOFRONT_SECTION_BYTES,
|
|
777
|
+
...typeSec,
|
|
778
|
+
...importSec,
|
|
779
|
+
...funcSec,
|
|
780
|
+
...tagSec,
|
|
781
|
+
...globalSec,
|
|
782
|
+
...exportSec,
|
|
783
|
+
...startSec,
|
|
784
|
+
...elemSec,
|
|
785
|
+
...codeSec,
|
|
786
|
+
];
|
|
787
|
+
|
|
788
|
+
return new Uint8Array(allBytes);
|
|
789
|
+
}
|