@Victor Adossi
I'm working on fixing #1393.
Things are going relatively smoothly, but I do have one question: where can I put some "global" state?
asm.js returns the high 32-bits of i64 by making a call to an import of env.setTempRet0 before the actual return of the low bits
I need some safe place to store the data so I can retrieve it immediately after the call finishes.
So far I rewrite the export invocation to look like this:
const started = task.enterSync();
let ret = _withGlobalCurrentTaskMeta({
taskID: task.id(),
componentIdx: task.componentIdx(),
fn: () => mergeInt64(exports0Add(...(splitInt64(toUint64(arg0))), ...(splitInt64(toUint64(arg1)))), /* <some way to get the tmp high bits> */),
});
I added mergeInt64 and splitInt64 ConversionIntrinsics and inserted them when a core Wasm signature for a call takes or yields i64
Where would be the correct spot to handle this temporary global state? There's AsyncTask, AsyncSubtask, and ComponentAsyncState at least. And then I saw something about "trampolines", which may be relavent here? Any ideas?
hey @QuantumSegfault thanks for the hard work on this (diving into the js-component-bindgen codebase isn't easy, I think!).
If I'm understanding correctly, storing on the task itself seems like a good way to go, especially since this is tied to a particular call (and at this point due to the p3 changes underneath, every call should result in a somewhat-misnamed AsyncTask being created).
Where would be the correct spot to handle this temporary global state? There's AsyncTask, AsyncSubtask, and ComponentAsyncState at least. And then I saw something about "trampolines", which may be relavent here? Any ideas?
Looks like we need some docs in this area, so I've taken a note to try and add some. For now though:
AsyncTask is misnamed -- every call should result in an AsyncTask (it was introduced as part of P3 which is how it got that naming, but tasks are created for sync calls too)AsyncSubtasks are generated when imports are loweredComponentAsyncState is really component state (and behavior) that is shared cross-task and strongly tied to the component. For example cstate.createStream(...)Trampolines (e.g. Trampoline::CallWasm) are actually the pieces of code that get called during wasm execution when either a canonical built in (e.g. resource.new) is called, or when a FACT module special operation (e.g. Trampoline::StreamTransfer) which is injected by toolchains like wasmtime are called.
I think the right place to add this extra state is on the AsynctTask since this is call-related -- you can of course gate the functionality by whether we are transpiling to JS or not (please let me know if that option isn't available -- I just actually made a change to the codebase to enable transpile options to be accessed during Intrinsic::render)!
I added
mergeInt64andsplitInt64ConversionIntrinsicsand inserted them when a core Wasm signature for a call takes or yieldsi64
As a separate note -- this sounds like you're doing it exactly the right way with respect to how the codebase is written right now.
That said, I would appreciate any feedback on things you found weird/annoying about the current organization of the codebase -- there is a lot of cleanup/simplification that needs to be done, and breaking out the intrinsics was a step I did a long time ago to attempt to add order into the chaos.
For example, on the horizon I want to break out all the Intrinsics to an external package and use them so that we can finally just write regular TS/JS code against some interface that lists out all the required functions.
Last updated: May 03 2026 at 22:13 UTC