Fix instantiation failure with wazero >= v1.12 (cross-runtime CompiledModule reuse) - #50
Open
elkhantar wants to merge 1 commit into
Open
Fix instantiation failure with wazero >= v1.12 (cross-runtime CompiledModule reuse)#50elkhantar wants to merge 1 commit into
elkhantar wants to merge 1 commit into
Conversation
Runtime.CompileModule bakes the compiling runtime's store-assigned function type IDs into the CompiledModule. Since wazero v1.12, import resolution compares those numeric IDs (required for the GC proposal's concrete ref types) rather than structural signatures, so a module compiled on the global throwaway runtime fails to instantiate on the per-New runtime with 'signature mismatch' on identical-looking signatures (wazero <= v1.11 compared structurally and tolerated this). Compiling on the instantiating runtime assigns consistent type IDs. The machine code is shared through the CompilationCache held in cachedRuntimeConfig, so the per-runtime compile is a cache hit.
|
This PR is no longer needed because the issue was actually a regression caused by wazero, and it has already been fixed upstream: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With wazero v1.12.0, every
qjs.New()fails:wazero v1.9.0 through v1.11.0 work fine; v1.12.0 is the first breaking release (bisected). This currently breaks qjs for any application whose dependency graph pulls wazero >= v1.12, and it will block Dependabot bumps like #38 once they reach v1.12.
Minimal reproduction — just
qjs.New()with wazero pinned to v1.12.0:Root cause
createGlobalCompiledModulecompiles the QuickJS module once on a throwawaywazero.Runtimeand caches theCompiledModulein a package global;New()then instantiates that global module on a different, per-instanceRuntime.The hidden coupling:
Runtime.CompileModuledoesn't only produce machine code — it also resolves every function type to a numericFunctionTypeIDfrom the compiling runtime's store and bakes that array into theCompiledModule(wazeroruntime.go: "typeIDs are static and compile-time known"). Type IDs are assigned sequentially per store, in order of first registration.Up to v1.11, import resolution compared signatures structurally (
bytes.Equalon params/results), so the baked IDs were never consulted and the cross-runtime reuse worked by accident. v1.12 implemented the WebAssembly GC proposal, where structural comparison is no longer sufficient (concrete ref types), soresolveImportsnow compares the numeric type IDs when both modules carry them:The qjs module instance arrives with the throwaway runtime's numbering while the
envhost module was registered in the new runtime's store, so the same signature gets different IDs — and the error prints the structural signatures, which is why the message shows two identical strings. Instrumenting wazero confirms both sides areparams=[0x7f 0x7e 0x7f 0x7f] results=[0x7e]; only the store numbering differs.Fix
Compile the module on the runtime that instantiates it, so the baked type IDs come from the correct store. This does not reintroduce the compilation cost the global module was avoiding: all runtimes share one
wazero.CompilationCacheviacachedRuntimeConfig, and wazero shares the engine (and its compiled machine code) through that cache, so the per-New()CompileModuleis a cache hit — only per-store type-ID resolution and module metadata work run per call.createGlobalCompiledModuleis kept as-is: it still validates the bytes, primes the shared cache, and maintains the existing hash/recompile semantics for customQuickJSWasmBytes.Testing
TestEvalOptions/CWDOption/deleted_working_directory, fails identically on untouched v0.0.6.)New()stress (pool of runtimes created in parallel) works under both versions.Disclosure
This contribution was developed with AI assistance (Claude). The root-cause analysis (version bisect, wazero instrumentation) and the change itself were reviewed by me, and all testing described above was run and verified on real hardware.