Skip to content

Commit 2347cb6

Browse files
GamePad64vados-cosmonic
authored andcommitted
fix(bindgen): register p3 global table-map intrinsics before per-table assignments
When `Instantiator::instantiate` emits `STREAM_TABLES[N] = …`, `FUTURE_TABLES[N] = …`, and `ERR_CTX_TABLES[N] = …` lines, it currently calls `Intrinsic::…::Global…TableMap.name()` directly to get the identifier string. That bypasses `Bindgen::intrinsic()` and therefore never inserts the intrinsic into `all_intrinsics`, so the corresponding render branch (which emits the `const STREAM_TABLES = {};` etc. declarations) never runs. For any wasip3-async component with stream/future/err-ctx tables, the transpiled module then references undeclared identifiers and fails at module evaluation time: ReferenceError: STREAM_TABLES is not defined at line 4554, ‹entry›.js Route the name lookup through `self.bindgen.intrinsic(…)` so the declaration is emitted alongside the assignments, and guard each block on `is_empty()` so we don't register intrinsics nobody uses. Reproducer: any act:tools/tool-provider component built with wasip3-async (e.g. `ghcr.io/actpkg/time:0.2.0`) transpiled via `jco transpile … --async-mode jspi`. Signed-off-by: Alexander Shishenko <alex@shishenko.com>
1 parent c472afd commit 2347cb6

1 file changed

Lines changed: 43 additions & 28 deletions

File tree

crates/js-component-bindgen/src/transpile_bindgen.rs

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -806,40 +806,55 @@ impl<'a> Instantiator<'a, '_> {
806806
}
807807
}
808808

809-
// Set up global stream map, which is used by intrinsics like stream.transfer
810-
let global_stream_table_map =
811-
Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamTableMap).name();
812-
let rep_table_class = Intrinsic::RepTableClass.name();
813-
for (table_idx, component_idx) in self.stream_tables.iter() {
814-
self.src.js.push_str(&format!(
815-
"{global_stream_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
816-
table_idx.as_u32(),
817-
component_idx.as_u32(),
809+
// Set up global stream map, which is used by intrinsics like stream.transfer.
810+
// Register the intrinsic so the `const STREAM_TABLES = {};` declaration
811+
// is emitted before these per-table assignments — without that, the
812+
// generated module references an undeclared identifier and fails to
813+
// load (ReferenceError: STREAM_TABLES is not defined).
814+
if !self.stream_tables.is_empty() {
815+
let global_stream_table_map = self.bindgen.intrinsic(Intrinsic::AsyncStream(
816+
AsyncStreamIntrinsic::GlobalStreamTableMap,
818817
));
818+
let rep_table_class = Intrinsic::RepTableClass.name();
819+
for (table_idx, component_idx) in self.stream_tables.iter() {
820+
self.src.js.push_str(&format!(
821+
"{global_stream_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
822+
table_idx.as_u32(),
823+
component_idx.as_u32(),
824+
));
825+
}
819826
}
820827

821-
// Set up global future map, which is used by intrinsics like future.transfer
822-
let global_future_table_map =
823-
Intrinsic::AsyncFuture(AsyncFutureIntrinsic::GlobalFutureTableMap).name();
824-
let rep_table_class = Intrinsic::RepTableClass.name();
825-
for (table_idx, component_idx) in self.future_tables.iter() {
826-
self.src.js.push_str(&format!(
827-
"{global_future_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
828-
table_idx.as_u32(),
829-
component_idx.as_u32(),
828+
// Set up global future map, which is used by intrinsics like future.transfer.
829+
// Same registration fix as above for FUTURE_TABLES.
830+
if !self.future_tables.is_empty() {
831+
let global_future_table_map = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
832+
AsyncFutureIntrinsic::GlobalFutureTableMap,
830833
));
834+
let rep_table_class = Intrinsic::RepTableClass.name();
835+
for (table_idx, component_idx) in self.future_tables.iter() {
836+
self.src.js.push_str(&format!(
837+
"{global_future_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
838+
table_idx.as_u32(),
839+
component_idx.as_u32(),
840+
));
841+
}
831842
}
832843

833-
// Set up global error context map, which is used by intrinsics like err_ctx.transfer
834-
let global_err_ctx_table_map =
835-
Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalErrCtxTableMap).name();
836-
let rep_table_class = Intrinsic::RepTableClass.name();
837-
for (table_idx, component_idx) in self.err_ctx_tables.iter() {
838-
self.src.js.push_str(&format!(
839-
"{global_err_ctx_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
840-
table_idx.as_u32(),
841-
component_idx.as_u32(),
842-
));
844+
// Set up global error context map, which is used by intrinsics like err_ctx.transfer.
845+
// Same registration fix as above for ERR_CTX_TABLES.
846+
if !self.err_ctx_tables.is_empty() {
847+
let global_err_ctx_table_map = self
848+
.bindgen
849+
.intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalErrCtxTableMap));
850+
let rep_table_class = Intrinsic::RepTableClass.name();
851+
for (table_idx, component_idx) in self.err_ctx_tables.iter() {
852+
self.src.js.push_str(&format!(
853+
"{global_err_ctx_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
854+
table_idx.as_u32(),
855+
component_idx.as_u32(),
856+
));
857+
}
843858
}
844859

845860
// Process global initializers

0 commit comments

Comments
 (0)