Skip to content

Commit e000213

Browse files
fix(bindgen): regression in support for core wasm export initialize
This commit fixes a regression in Jco supporting imports that may be called from core wasm exports of a given component. Given a component that has an `_initialize` export that should be called at import time (when the transpiled component is `import`ed in a JS context), it may call imports, which would attempt to be lowered and use the import lowering machinery that is normally in place. Such calls do *not* go through either `Instruction::CallWasm` *nor* `Instruction::CallInterface` and as such, there is no current task. With this commit, we create a new task if necessary, but only in the backwards-compatible lower import version of lower import.
1 parent 49d5dec commit e000213

1 file changed

Lines changed: 63 additions & 4 deletions

File tree

crates/js-component-bindgen/src/intrinsics/p3/async_task.rs

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,10 +2185,14 @@ impl AsyncTaskIntrinsic {
21852185
let debug_log_fn = Intrinsic::DebugLog.name();
21862186
let lower_import_backwards_compat_fn = Self::LowerImportBackwardsCompat.name();
21872187
let current_task_get_fn = Self::GetCurrentTask.name();
2188+
let create_new_current_task_fn = Self::CreateNewCurrentTask.name();
21882189
let get_or_create_async_state_fn =
21892190
Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState).name();
21902191
let async_event_code_enum = Intrinsic::AsyncEventCodeEnum.name();
21912192
let get_global_current_task_meta_fn = Intrinsic::GetGlobalCurrentTaskMetaFn.name();
2193+
let set_global_current_task_meta_fn = Intrinsic::SetGlobalCurrentTaskMetaFn.name();
2194+
let clear_global_current_task_meta_fn =
2195+
Intrinsic::ClearGlobalCurrentTaskMetaFn.name();
21922196
let promise_with_resolvers_fn = Intrinsic::PromiseWithResolversPonyfill.name();
21932197

21942198
output.push_str(&format!(
@@ -2211,10 +2215,52 @@ impl AsyncTaskIntrinsic {
22112215
importFn,
22122216
}} = args;
22132217
2214-
const {{ taskID }} = {get_global_current_task_meta_fn}(componentIdx);
2218+
let meta = {get_global_current_task_meta_fn}(componentIdx);
2219+
let createdTask;
2220+
2221+
// Some components depend on initialization logic (i.e. `_initialize` or some such
2222+
// core wasm export) that is embedded in the component, but is not executed or wizer'd
2223+
// away before the transpiled component is attempted to be used.
2224+
//
2225+
// These components execut their initialization logic *when they are imported* in the
2226+
// transpiled context -- so we may get a call to an export that is lowered without going
2227+
// through `CallWasm` or `CallInterface`.
2228+
//
2229+
if (!meta) {{
2230+
if (funcTypeIsAsync || (isAsync && !isManualAsync)) {{
2231+
throw new Error('p3 async wasm exports cannot use backwards compat auto-task init');
2232+
}}
2233+
2234+
const [newTask, newTaskID] = {create_new_current_task_fn}({{
2235+
componentIdx,
2236+
isAsync,
2237+
isManualAsync,
2238+
callingWasmExport: false,
2239+
}});
2240+
createdTask = newTask;
2241+
2242+
// Since we're managing the task creation ourselves we must clear ourselves
2243+
createdTask.registerOnResolveHandler(() => {{
2244+
{clear_global_current_task_meta_fn}({{
2245+
taskID: task.id(),
2246+
componentIdx: task.componentIdx(),
2247+
}});
2248+
}});
2249+
2250+
{set_global_current_task_meta_fn}({{
2251+
componentIdx,
2252+
taskID: newTaskID,
2253+
}});
2254+
2255+
meta = {get_global_current_task_meta_fn}(componentIdx);
2256+
}}
2257+
2258+
const {{ taskID }} = meta;
22152259
22162260
const taskMeta = {current_task_get_fn}(componentIdx, taskID);
2217-
if (!taskMeta) {{ throw new Error('invalid/missing async task meta'); }}
2261+
if (!taskMeta) {{
2262+
throw new Error('invalid/missing async task meta');
2263+
}}
22182264
22192265
const task = taskMeta.task;
22202266
if (!task) {{ throw new Error('invalid/missing async task'); }}
@@ -2258,13 +2304,20 @@ impl AsyncTaskIntrinsic {
22582304
// TODO(breaking): remove once we get rid of manual async import specification,
22592305
// as func types cannot be detected in that case only (and we don't need that w/ p3)
22602306
if (!isManualAsync && !isAsync && !funcTypeIsAsync) {{
2307+
if (createdTask) {{ createdTask.enterSync(); }}
2308+
22612309
const res = importFn(...params);
2310+
22622311
// TODO(breaking): remove once we get rid of manual async import specification,
22632312
// as func types cannot be detected in that case only (and we don't need that w/ p3)
22642313
if (!funcTypeIsAsync && !subtask.isReturned()) {{
22652314
throw new Error('post-execution subtasks must either be async or returned');
22662315
}}
2267-
return subtask.getResult();
2316+
2317+
const syncRes = subtask.getResult();
2318+
if (createdTask) {{ createdTask.resolve([syncRes]); }}
2319+
2320+
return syncRes;
22682321
}}
22692322
22702323
// Sync-lowered async functions requires async behavior because the callee *can* block,
@@ -2333,10 +2386,16 @@ impl AsyncTaskIntrinsic {
23332386
queueMicrotask(async () => {{
23342387
try {{
23352388
{debug_log_fn}('[{lower_import_backwards_compat_fn}()] calling lowered import', {{ importFn, params }});
2336-
const res = await importFn(...params);
2389+
if (createdTask) {{ await createdTask.enter(); }}
2390+
2391+
const asyncRes = await importFn(...params);
23372392
if (requiresManualAsyncResult) {{
23382393
manualAsyncResult.resolve(subtask.getResult());
23392394
}}
2395+
2396+
if (createdTask) {{ createdTask.resolve([asyncRes]); }}
2397+
2398+
23402399
}} catch (err) {{
23412400
{debug_log_fn}("[{lower_import_backwards_compat_fn}()] import fn error:", err);
23422401
if (requiresManualAsyncResult) {{

0 commit comments

Comments
 (0)