gofront 1.5.1 → 1.6.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 +174 -466
- package/package.json +16 -20
- package/src/backend/js/expressions.js +50 -19
- package/src/backend/js/index.js +100 -55
- package/src/backend/js/runtime.js +102 -0
- package/src/backend/js/source-map.js +32 -0
- package/src/backend/js/statements.js +96 -16
- package/src/backend/js/stdlib/errors.js +2 -2
- package/src/backend/js/stdlib/fmt.js +26 -8
- package/src/backend/js/stdlib/index.js +3 -0
- package/src/backend/js/stdlib/maps.js +1 -1
- package/src/backend/js/stdlib/path.js +2 -2
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/backend/js/stdlib/slices.js +1 -1
- package/src/backend/js/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +34 -9
- package/src/backend/js/stdlib/strings.js +4 -1
- package/src/backend/js/stdlib/testing.js +2 -2
- package/src/backend/js/stdlib/time.js +3 -3
- package/src/backend/js/templ.js +1 -1
- package/src/backend/wasm/boundary.js +486 -33
- package/src/backend/wasm/emit-builtins.js +1103 -0
- package/src/backend/wasm/emit-exprs.js +1908 -0
- package/src/backend/wasm/emit-maps.js +1863 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +3006 -0
- package/src/backend/wasm/emit-stmts.js +2472 -0
- package/src/backend/wasm/emit.js +606 -5009
- package/src/backend/wasm/encode.js +212 -64
- package/src/backend/wasm/glue.js +19 -5
- package/src/backend/wasm/index.js +1426 -49
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +227 -0
- package/src/backend/wasm/types.js +159 -35
- package/src/backend/wasm/wat.js +49 -2
- package/src/cli-core.js +275 -72
- package/src/compiler.js +144 -21
- package/src/index.js +86 -115
- package/src/lower/boxing.js +3 -4
- package/src/lower/captures.js +10 -17
- 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 +5 -10
- package/src/lower/range.js +2 -5
- package/src/lower/walk.js +25 -0
- package/src/resolver.js +2 -1
- package/src/test-runner.js +20 -0
- package/src/typechecker/assignability.js +42 -1
- package/src/typechecker/expressions.js +142 -2
- package/src/typechecker/index.js +27 -1
- package/src/typechecker/resolve.js +35 -6
- package/src/typechecker/statements.js +0 -7
- package/src/typechecker/stdlib/core.js +17 -19
- package/src/typechecker/stdlib/extended.js +18 -3
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +206 -7
- package/src/vendor.js +2 -2
|
@@ -64,12 +64,12 @@ export function encodeF64(val) {
|
|
|
64
64
|
return Array.from(new Uint8Array(buf));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
function encodeString(str) {
|
|
68
68
|
const utf8 = new TextEncoder().encode(str);
|
|
69
69
|
return [...encodeU32LEB(utf8.length), ...utf8];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
function encodeVector(items, encodeItem) {
|
|
73
73
|
const count = encodeU32LEB(items.length);
|
|
74
74
|
const bytes = [];
|
|
75
75
|
for (const item of items) {
|
|
@@ -83,7 +83,7 @@ export function encodeVector(items, encodeItem) {
|
|
|
83
83
|
|
|
84
84
|
// ── Value Types & Heap Types ─────────────────────────────────
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
const ValType = {
|
|
87
87
|
i32: 0x7f,
|
|
88
88
|
i64: 0x7e,
|
|
89
89
|
f32: 0x7d,
|
|
@@ -101,9 +101,11 @@ export const ValType = {
|
|
|
101
101
|
nullref: 0x71,
|
|
102
102
|
nullfuncref: 0x73,
|
|
103
103
|
nullexternref: 0x72,
|
|
104
|
+
exnref: 0x69,
|
|
105
|
+
nullexnref: 0x75,
|
|
104
106
|
};
|
|
105
107
|
|
|
106
|
-
|
|
108
|
+
function encodeValType(type) {
|
|
107
109
|
if (typeof type === "number") {
|
|
108
110
|
return [type];
|
|
109
111
|
}
|
|
@@ -129,7 +131,7 @@ export function encodeValType(type) {
|
|
|
129
131
|
throw new Error(`Invalid valtype specification: ${JSON.stringify(type)}`);
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
|
|
134
|
+
function encodeBlockType(blockType) {
|
|
133
135
|
if (!blockType || blockType === "void") {
|
|
134
136
|
return [0x40];
|
|
135
137
|
}
|
|
@@ -153,6 +155,7 @@ const OPCODES = {
|
|
|
153
155
|
if: 0x04,
|
|
154
156
|
else: 0x05,
|
|
155
157
|
throw: 0x08,
|
|
158
|
+
throw_ref: 0x0a,
|
|
156
159
|
end: 0x0b,
|
|
157
160
|
br: 0x0c,
|
|
158
161
|
br_if: 0x0d,
|
|
@@ -164,6 +167,7 @@ const OPCODES = {
|
|
|
164
167
|
return_call_indirect: 0x13,
|
|
165
168
|
call_ref: 0x14,
|
|
166
169
|
return_call_ref: 0x15,
|
|
170
|
+
try_table: 0x1f,
|
|
167
171
|
drop: 0x1a,
|
|
168
172
|
select: 0x1b,
|
|
169
173
|
|
|
@@ -173,6 +177,23 @@ const OPCODES = {
|
|
|
173
177
|
"global.get": 0x23,
|
|
174
178
|
"global.set": 0x24,
|
|
175
179
|
|
|
180
|
+
"i32.load": 0x28,
|
|
181
|
+
"i64.load": 0x29,
|
|
182
|
+
"f32.load": 0x2a,
|
|
183
|
+
"f64.load": 0x2b,
|
|
184
|
+
"i32.load8_s": 0x2c,
|
|
185
|
+
"i32.load8_u": 0x2d,
|
|
186
|
+
"i32.load16_s": 0x2e,
|
|
187
|
+
"i32.load16_u": 0x2f,
|
|
188
|
+
"i32.store": 0x36,
|
|
189
|
+
"i64.store": 0x37,
|
|
190
|
+
"f32.store": 0x38,
|
|
191
|
+
"f64.store": 0x39,
|
|
192
|
+
"i32.store8": 0x3a,
|
|
193
|
+
"i32.store16": 0x3b,
|
|
194
|
+
"memory.size": 0x3f,
|
|
195
|
+
"memory.grow": 0x40,
|
|
196
|
+
|
|
176
197
|
"i32.const": 0x41,
|
|
177
198
|
"i64.const": 0x42,
|
|
178
199
|
"f32.const": 0x43,
|
|
@@ -360,12 +381,41 @@ const GC_OPCODES = {
|
|
|
360
381
|
"extern.convert_any": 0x1b,
|
|
361
382
|
};
|
|
362
383
|
|
|
363
|
-
|
|
384
|
+
// Natural alignment (log2) per load/store opcode; memarg = align, offset.
|
|
385
|
+
const MEM_ALIGN = {
|
|
386
|
+
"i32.load": 2,
|
|
387
|
+
"i64.load": 3,
|
|
388
|
+
"f32.load": 2,
|
|
389
|
+
"f64.load": 3,
|
|
390
|
+
"i32.load8_s": 0,
|
|
391
|
+
"i32.load8_u": 0,
|
|
392
|
+
"i32.load16_s": 1,
|
|
393
|
+
"i32.load16_u": 1,
|
|
394
|
+
"i32.store": 2,
|
|
395
|
+
"i64.store": 3,
|
|
396
|
+
"f32.store": 2,
|
|
397
|
+
"f64.store": 3,
|
|
398
|
+
"i32.store8": 0,
|
|
399
|
+
"i32.store16": 1,
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
function encodeInstruction(inst) {
|
|
364
403
|
if (typeof inst === "string") {
|
|
365
404
|
inst = { op: inst };
|
|
366
405
|
}
|
|
367
406
|
const { op } = inst;
|
|
368
407
|
|
|
408
|
+
if (MEM_ALIGN[op] !== undefined) {
|
|
409
|
+
return [
|
|
410
|
+
OPCODES[op],
|
|
411
|
+
...encodeU32LEB(inst.align ?? MEM_ALIGN[op]),
|
|
412
|
+
...encodeU32LEB(inst.offset ?? 0),
|
|
413
|
+
];
|
|
414
|
+
}
|
|
415
|
+
if (op === "memory.size" || op === "memory.grow") {
|
|
416
|
+
return [OPCODES[op], 0x00];
|
|
417
|
+
}
|
|
418
|
+
|
|
369
419
|
// 1. Check saturating truncations (0xFC prefix)
|
|
370
420
|
if (SAT_TRUNC_OPCODES[op] !== undefined) {
|
|
371
421
|
return [0xfc, ...encodeU32LEB(SAT_TRUNC_OPCODES[op])];
|
|
@@ -392,6 +442,26 @@ export function encodeInstruction(inst) {
|
|
|
392
442
|
) {
|
|
393
443
|
return bytes;
|
|
394
444
|
}
|
|
445
|
+
if (
|
|
446
|
+
op === "ref.cast" ||
|
|
447
|
+
op === "ref.cast_null" ||
|
|
448
|
+
op === "ref.test" ||
|
|
449
|
+
op === "ref.test_null"
|
|
450
|
+
) {
|
|
451
|
+
const ht =
|
|
452
|
+
inst.heapType !== undefined
|
|
453
|
+
? inst.heapType
|
|
454
|
+
: inst.typeIndex !== undefined
|
|
455
|
+
? inst.typeIndex
|
|
456
|
+
: "any";
|
|
457
|
+
if (typeof ht === "number") {
|
|
458
|
+
bytes.push(...encodeI32LEB(ht));
|
|
459
|
+
} else {
|
|
460
|
+
const code = ValType[ht] ?? ValType[`${ht}ref`] ?? 0x6e;
|
|
461
|
+
bytes.push(code);
|
|
462
|
+
}
|
|
463
|
+
return bytes;
|
|
464
|
+
}
|
|
395
465
|
if (inst.typeIndex !== undefined) {
|
|
396
466
|
bytes.push(...encodeU32LEB(inst.typeIndex));
|
|
397
467
|
}
|
|
@@ -425,6 +495,40 @@ export function encodeInstruction(inst) {
|
|
|
425
495
|
case "throw":
|
|
426
496
|
return [byte, ...encodeU32LEB(inst.tagIndex ?? inst.index ?? 0)];
|
|
427
497
|
|
|
498
|
+
case "throw_ref":
|
|
499
|
+
return [byte];
|
|
500
|
+
|
|
501
|
+
case "try_table": {
|
|
502
|
+
const blockTypeBytes = encodeBlockType(
|
|
503
|
+
inst.blockType ?? inst.resultType,
|
|
504
|
+
);
|
|
505
|
+
const catches = inst.catches ?? [];
|
|
506
|
+
const catchBytes = encodeVector(catches, (c) => {
|
|
507
|
+
const kind = c.kind ?? c.op;
|
|
508
|
+
switch (kind) {
|
|
509
|
+
case "catch":
|
|
510
|
+
return [
|
|
511
|
+
0x00,
|
|
512
|
+
...encodeU32LEB(c.tagIndex ?? c.tag ?? 0),
|
|
513
|
+
...encodeU32LEB(c.label ?? c.depth ?? 0),
|
|
514
|
+
];
|
|
515
|
+
case "catch_ref":
|
|
516
|
+
return [
|
|
517
|
+
0x01,
|
|
518
|
+
...encodeU32LEB(c.tagIndex ?? c.tag ?? 0),
|
|
519
|
+
...encodeU32LEB(c.label ?? c.depth ?? 0),
|
|
520
|
+
];
|
|
521
|
+
case "catch_all":
|
|
522
|
+
return [0x02, ...encodeU32LEB(c.label ?? c.depth ?? 0)];
|
|
523
|
+
case "catch_all_ref":
|
|
524
|
+
return [0x03, ...encodeU32LEB(c.label ?? c.depth ?? 0)];
|
|
525
|
+
default:
|
|
526
|
+
throw new Error(`Unknown catch kind: ${kind}`);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
return [byte, ...blockTypeBytes, ...catchBytes];
|
|
530
|
+
}
|
|
531
|
+
|
|
428
532
|
case "call":
|
|
429
533
|
case "return_call":
|
|
430
534
|
return [byte, ...encodeU32LEB(inst.funcIndex ?? inst.index ?? 0)];
|
|
@@ -494,7 +598,7 @@ function encodeSection(id, payload) {
|
|
|
494
598
|
return [id, ...encodeU32LEB(payload.length), ...payload];
|
|
495
599
|
}
|
|
496
600
|
|
|
497
|
-
|
|
601
|
+
function encodeTypeEntry(t) {
|
|
498
602
|
if (t.form === "rec") {
|
|
499
603
|
// Recursive type group: 0x4E, count, types
|
|
500
604
|
const count = encodeU32LEB(t.types.length);
|
|
@@ -533,12 +637,12 @@ export function encodeTypeEntry(t) {
|
|
|
533
637
|
];
|
|
534
638
|
}
|
|
535
639
|
|
|
536
|
-
|
|
640
|
+
function encodeTypeSection(types) {
|
|
537
641
|
if (!types || types.length === 0) return [];
|
|
538
642
|
return encodeSection(1, encodeVector(types, encodeTypeEntry));
|
|
539
643
|
}
|
|
540
644
|
|
|
541
|
-
|
|
645
|
+
function encodeImportSection(imports) {
|
|
542
646
|
if (!imports || imports.length === 0) return [];
|
|
543
647
|
return encodeSection(
|
|
544
648
|
2,
|
|
@@ -581,7 +685,7 @@ export function encodeImportSection(imports) {
|
|
|
581
685
|
);
|
|
582
686
|
}
|
|
583
687
|
|
|
584
|
-
|
|
688
|
+
function encodeFunctionSection(funcs) {
|
|
585
689
|
if (!funcs || funcs.length === 0) return [];
|
|
586
690
|
return encodeSection(
|
|
587
691
|
3,
|
|
@@ -589,7 +693,7 @@ export function encodeFunctionSection(funcs) {
|
|
|
589
693
|
);
|
|
590
694
|
}
|
|
591
695
|
|
|
592
|
-
|
|
696
|
+
function encodeTagSection(tags) {
|
|
593
697
|
if (!tags || tags.length === 0) return [];
|
|
594
698
|
return encodeSection(
|
|
595
699
|
13,
|
|
@@ -597,7 +701,22 @@ export function encodeTagSection(tags) {
|
|
|
597
701
|
);
|
|
598
702
|
}
|
|
599
703
|
|
|
600
|
-
|
|
704
|
+
// Single linear memory: `{ min, max }` in 64 KiB pages. `max` is optional;
|
|
705
|
+
// the shared-buffer runtime only grows the memory inside the start function,
|
|
706
|
+
// before any JS TypedArray view exists.
|
|
707
|
+
function encodeMemorySection(memory) {
|
|
708
|
+
if (!memory) return [];
|
|
709
|
+
const limits =
|
|
710
|
+
memory.max != null
|
|
711
|
+
? [0x01, ...encodeU32LEB(memory.min), ...encodeU32LEB(memory.max)]
|
|
712
|
+
: [0x00, ...encodeU32LEB(memory.min)];
|
|
713
|
+
return encodeSection(
|
|
714
|
+
5,
|
|
715
|
+
encodeVector([limits], (b) => b),
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function encodeGlobalSection(globals) {
|
|
601
720
|
if (!globals || globals.length === 0) return [];
|
|
602
721
|
return encodeSection(
|
|
603
722
|
6,
|
|
@@ -621,7 +740,7 @@ export function encodeGlobalSection(globals) {
|
|
|
621
740
|
);
|
|
622
741
|
}
|
|
623
742
|
|
|
624
|
-
|
|
743
|
+
function encodeExportSection(exports) {
|
|
625
744
|
if (!exports || exports.length === 0) return [];
|
|
626
745
|
return encodeSection(
|
|
627
746
|
7,
|
|
@@ -670,7 +789,7 @@ function sameValType(a, b) {
|
|
|
670
789
|
return false;
|
|
671
790
|
}
|
|
672
791
|
|
|
673
|
-
|
|
792
|
+
function encodeElementSection(elements) {
|
|
674
793
|
if (!elements || elements.length === 0) return [];
|
|
675
794
|
const segment = [0x03, 0x00, ...encodeVector(elements, encodeU32LEB)];
|
|
676
795
|
return encodeSection(
|
|
@@ -679,64 +798,76 @@ export function encodeElementSection(elements) {
|
|
|
679
798
|
);
|
|
680
799
|
}
|
|
681
800
|
|
|
682
|
-
|
|
801
|
+
// Encodes the code section. `bodyOffsets` (when given) receives, per
|
|
802
|
+
// function, the offset of the body's first instruction relative to the start
|
|
803
|
+
// of the section bytes returned.
|
|
804
|
+
function encodeCodeSection(funcs, bodyOffsets = null) {
|
|
683
805
|
if (!funcs || funcs.length === 0) return [];
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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)
|
|
806
|
+
const relOffsets = [];
|
|
807
|
+
const payload = encodeVector(funcs, (fn) => {
|
|
808
|
+
// Compress locals: [type1, type1, type2] -> [{ count: 2, type: type1 }, { count: 1, type: type2 }]
|
|
809
|
+
const rawLocals = fn.locals ?? [];
|
|
810
|
+
const compressedLocals = [];
|
|
811
|
+
for (const loc of rawLocals) {
|
|
812
|
+
const locType =
|
|
813
|
+
typeof loc === "string" ? loc : loc.type !== undefined ? loc.type : loc;
|
|
722
814
|
if (
|
|
723
|
-
|
|
724
|
-
|
|
815
|
+
compressedLocals.length > 0 &&
|
|
816
|
+
sameValType(compressedLocals[compressedLocals.length - 1].type, locType)
|
|
725
817
|
) {
|
|
726
|
-
|
|
818
|
+
compressedLocals[compressedLocals.length - 1].count++;
|
|
819
|
+
} else {
|
|
820
|
+
compressedLocals.push({ count: 1, type: locType });
|
|
727
821
|
}
|
|
822
|
+
}
|
|
728
823
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
824
|
+
const localsBytes = encodeVector(compressedLocals, (group) => [
|
|
825
|
+
...encodeU32LEB(group.count),
|
|
826
|
+
...encodeValType(group.type),
|
|
827
|
+
]);
|
|
828
|
+
|
|
829
|
+
const bodyInsts = fn.body ?? [];
|
|
830
|
+
const bodyBytes = [];
|
|
831
|
+
for (const inst of bodyInsts) {
|
|
832
|
+
const chunk = encodeInstruction(inst);
|
|
833
|
+
for (let i = 0; i < chunk.length; i++) bodyBytes.push(chunk[i]);
|
|
834
|
+
}
|
|
835
|
+
// Ensure terminating end opcode (0x0B)
|
|
836
|
+
if (
|
|
837
|
+
bodyInsts.length === 0 ||
|
|
838
|
+
bodyInsts[bodyInsts.length - 1].op !== "end"
|
|
839
|
+
) {
|
|
840
|
+
bodyBytes.push(0x0b);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const fullBody = [...localsBytes, ...bodyBytes];
|
|
844
|
+
const sizeBytes = encodeU32LEB(fullBody.length);
|
|
845
|
+
// Offset of the first instruction within this entry + entry length.
|
|
846
|
+
relOffsets.push([
|
|
847
|
+
sizeBytes.length + localsBytes.length,
|
|
848
|
+
sizeBytes.length + fullBody.length,
|
|
849
|
+
]);
|
|
850
|
+
return [...sizeBytes, ...fullBody];
|
|
851
|
+
});
|
|
852
|
+
if (bodyOffsets) {
|
|
853
|
+
// Section-relative: id + size LEB + count LEB + preceding entries.
|
|
854
|
+
let cursor =
|
|
855
|
+
1 +
|
|
856
|
+
encodeU32LEB(payload.length).length +
|
|
857
|
+
encodeU32LEB(funcs.length).length;
|
|
858
|
+
for (const [first, len] of relOffsets) {
|
|
859
|
+
bodyOffsets.push(cursor + first);
|
|
860
|
+
cursor += len;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return encodeSection(10, payload);
|
|
733
864
|
}
|
|
734
865
|
|
|
735
866
|
// ── Top-level Module Encoder ─────────────────────────────────
|
|
736
867
|
|
|
737
868
|
// Custom section placed right after the header; `isGoFrontWasm` keys off it.
|
|
738
|
-
|
|
739
|
-
const GOFRONT_SECTION_BYTES = [
|
|
869
|
+
const GOFRONT_SECTION_NAME = "gofront";
|
|
870
|
+
export const GOFRONT_SECTION_BYTES = [
|
|
740
871
|
0x00,
|
|
741
872
|
...encodeU32LEB(1 + GOFRONT_SECTION_NAME.length),
|
|
742
873
|
...encodeString(GOFRONT_SECTION_NAME),
|
|
@@ -753,6 +884,10 @@ export function isGoFrontWasm(bytes) {
|
|
|
753
884
|
return true;
|
|
754
885
|
}
|
|
755
886
|
|
|
887
|
+
// Encodes the module. When `mod.sourceMappingURL` is set a trailing
|
|
888
|
+
// `sourceMappingURL` custom section is appended, and `mod.codeOffsets` is
|
|
889
|
+
// filled with the absolute byte offset of every function body (aligned with
|
|
890
|
+
// `mod.funcs`) so a function-level source map can be built.
|
|
756
891
|
export function encodeModule(mod) {
|
|
757
892
|
const magic = [0x00, 0x61, 0x73, 0x6d]; // \0asm
|
|
758
893
|
const version = [0x01, 0x00, 0x00, 0x00]; // version 1
|
|
@@ -760,6 +895,7 @@ export function encodeModule(mod) {
|
|
|
760
895
|
const typeSec = encodeTypeSection(mod.types);
|
|
761
896
|
const importSec = encodeImportSection(mod.imports);
|
|
762
897
|
const funcSec = encodeFunctionSection(mod.funcs);
|
|
898
|
+
const memorySec = encodeMemorySection(mod.memory);
|
|
763
899
|
const tagSec = encodeTagSection(mod.tags);
|
|
764
900
|
const globalSec = encodeGlobalSection(mod.globals);
|
|
765
901
|
const exportSec = encodeExportSection(mod.exports);
|
|
@@ -768,7 +904,8 @@ export function encodeModule(mod) {
|
|
|
768
904
|
? encodeSection(8, encodeU32LEB(mod.start))
|
|
769
905
|
: [];
|
|
770
906
|
const elemSec = encodeElementSection(mod.elements);
|
|
771
|
-
const
|
|
907
|
+
const sectionOffsets = [];
|
|
908
|
+
const codeSec = encodeCodeSection(mod.funcs, sectionOffsets);
|
|
772
909
|
|
|
773
910
|
const allBytes = [
|
|
774
911
|
...magic,
|
|
@@ -777,13 +914,24 @@ export function encodeModule(mod) {
|
|
|
777
914
|
...typeSec,
|
|
778
915
|
...importSec,
|
|
779
916
|
...funcSec,
|
|
917
|
+
...memorySec,
|
|
780
918
|
...tagSec,
|
|
781
919
|
...globalSec,
|
|
782
920
|
...exportSec,
|
|
783
921
|
...startSec,
|
|
784
922
|
...elemSec,
|
|
785
|
-
...codeSec,
|
|
786
923
|
];
|
|
924
|
+
const codeStart = allBytes.length;
|
|
925
|
+
mod.codeOffsets = sectionOffsets.map((o) => codeStart + o);
|
|
926
|
+
allBytes.push(...codeSec);
|
|
927
|
+
if (mod.sourceMappingURL) {
|
|
928
|
+
allBytes.push(
|
|
929
|
+
...encodeSection(0, [
|
|
930
|
+
...encodeString("sourceMappingURL"),
|
|
931
|
+
...encodeString(mod.sourceMappingURL),
|
|
932
|
+
]),
|
|
933
|
+
);
|
|
934
|
+
}
|
|
787
935
|
|
|
788
936
|
return new Uint8Array(allBytes);
|
|
789
937
|
}
|
package/src/backend/wasm/glue.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// src/backend/wasm/glue.js
|
|
2
2
|
// Runtime imports and JS glue for executing GoFront WASM modules.
|
|
3
3
|
|
|
4
|
-
import { WASM_IMPORTS_JS } from "./boundary.js";
|
|
4
|
+
import { WASM_IMPORTS_JS, WASM_STDLIB_JS } from "./boundary.js";
|
|
5
5
|
|
|
6
6
|
// The import table is authored once as source text (it is spliced into the
|
|
7
7
|
// production bundle by the facade); evaluate that same text here so tests
|
|
8
8
|
// exercise exactly what ships. The text is a compile-time constant.
|
|
9
|
-
const makeImports = new Function(
|
|
9
|
+
const makeImports = new Function(
|
|
10
|
+
`${WASM_IMPORTS_JS}\n${WASM_STDLIB_JS}\nreturn __gfw_imports;`,
|
|
11
|
+
)();
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
function createWasmImports({
|
|
12
14
|
stringTable = [],
|
|
13
15
|
panicTag = null,
|
|
14
16
|
stdout = null,
|
|
@@ -30,6 +32,14 @@ const NIL_DEREF_PATTERNS = [
|
|
|
30
32
|
"null dereference", // JavaScriptCore
|
|
31
33
|
];
|
|
32
34
|
|
|
35
|
+
function safeString(v) {
|
|
36
|
+
try {
|
|
37
|
+
return String(v);
|
|
38
|
+
} catch {
|
|
39
|
+
return "[panic object]";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
function isNilDerefTrap(e) {
|
|
34
44
|
if (
|
|
35
45
|
typeof WebAssembly !== "undefined" &&
|
|
@@ -53,6 +63,7 @@ export function instantiateWasm(
|
|
|
53
63
|
|
|
54
64
|
const module = new WebAssembly.Module(wasmBytes);
|
|
55
65
|
const instance = new WebAssembly.Instance(module, imports);
|
|
66
|
+
imports.__bind?.(instance.exports);
|
|
56
67
|
|
|
57
68
|
// The module exports its panic tag if defined internally, or uses the imported one
|
|
58
69
|
const tag = instance.exports.panicTag ?? defaultPanicTag;
|
|
@@ -67,9 +78,12 @@ export function instantiateWasm(
|
|
|
67
78
|
try {
|
|
68
79
|
return val(...cur);
|
|
69
80
|
} catch (e) {
|
|
81
|
+
if (instance.exports.__panic) {
|
|
82
|
+
instance.exports.__panic.value = null;
|
|
83
|
+
}
|
|
70
84
|
if (tag && e instanceof WebAssembly.Exception && e.is(tag)) {
|
|
71
85
|
const msg = e.getArg(tag, 0);
|
|
72
|
-
throw new Error(
|
|
86
|
+
throw new Error(safeString(msg));
|
|
73
87
|
}
|
|
74
88
|
if (isNilDerefTrap(e)) {
|
|
75
89
|
throw new Error(
|
|
@@ -91,7 +105,7 @@ export function instantiateWasm(
|
|
|
91
105
|
inner.is(tag)
|
|
92
106
|
) {
|
|
93
107
|
const msg = inner.getArg(tag, 0);
|
|
94
|
-
throw new Error(
|
|
108
|
+
throw new Error(safeString(msg));
|
|
95
109
|
}
|
|
96
110
|
if (isNilDerefTrap(inner)) {
|
|
97
111
|
throw new Error(
|