Unify wasm-bindgen output under -sWASM_BINDGEN, add -sWASM_BINDGEN=auto - #27208
Open
guybedford wants to merge 1 commit into
Open
Unify wasm-bindgen output under -sWASM_BINDGEN, add -sWASM_BINDGEN=auto#27208guybedford wants to merge 1 commit into
guybedford wants to merge 1 commit into
Conversation
guybedford
force-pushed
the
wasm-bindgen-unified-flow
branch
from
June 26, 2026 23:24
cddd117 to
b7e9291
Compare
guybedford
force-pushed
the
wasm-bindgen-unified-flow
branch
from
June 26, 2026 23:32
b7e9291 to
c10cb62
Compare
guybedford
force-pushed
the
wasm-bindgen-unified-flow
branch
from
June 29, 2026 23:54
c10cb62 to
918ef09
Compare
Collaborator
Author
|
@walkingeyerobot has now also confirmed that this unification approach works in his testing too. |
guybedford
force-pushed
the
wasm-bindgen-unified-flow
branch
2 times, most recently
from
July 1, 2026 22:48
cc70816 to
cc29009
Compare
guybedford
added a commit
to guybedford/emscripten
that referenced
this pull request
Jul 24, 2026
walkingeyerobot
approved these changes
Jul 27, 2026
guybedford
added a commit
to guybedford/emscripten
that referenced
this pull request
Jul 28, 2026
Collaborator
|
@sbc100 Sam, this looks good to me, but I'd appreciate it if you could give it a quick look before we hit the merge button. Thanks! |
sbc100
reviewed
Jul 28, 2026
guybedford
force-pushed
the
wasm-bindgen-unified-flow
branch
from
July 28, 2026 21:16
cc29009 to
b9865b9
Compare
sbc100
pushed a commit
that referenced
this pull request
Aug 5, 2026
…ttributes (#27436) This implements support for JS libraries declaring their own public exports using per-symbol attributes, split out from #27208 as requested in review there. The attributes separate symbol inclusion from export visibility: * `__export: true` exports the symbol if it is otherwise included. * `__force: true` includes the symbol even when nothing references it, without exporting it, analogous to `DEFAULT_LIBRARY_FUNCS_TO_INCLUDE`. * Using both unconditionally includes and exports the symbol. This allows binding layers such as wasm-bindgen to define a public JS API surface distinct from the wasm export names, including exported classes and namespaces: ```js addToLibrary({ $Counter__export: true, $Counter__force: true, $Counter: class Counter { /* ... */ }, }); ``` The attributes are consumed as JS library compile-time metadata and do not appear in generated output. The JS compiler forwards only the symbols actually emitted through `extraExports`, allowing the `WASM_ESM_INTEGRATION` wrapper to re-export them without mutating or returning the global `EXPORTED_FUNCTIONS` setting. Decorated exports are also retained through meta-DCE. Test coverage verifies the independent attribute semantics under legacy modularized output, `MODULARIZE=instance`, `WASM_ESM_INTEGRATION`, and optimized `-O3` builds, together with decorator validation. _Made with AI assistance under my review_
guybedford
force-pushed
the
wasm-bindgen-unified-flow
branch
3 times, most recently
from
August 8, 2026 00:57
3aee74b to
2d5101f
Compare
Collaborator
Author
|
This is now ready for final review. |
guybedford
force-pushed
the
wasm-bindgen-unified-flow
branch
from
August 8, 2026 01:01
2d5101f to
538e1fb
Compare
wasm-bindgen (wasm-bindgen/wasm-bindgen#5210, updated for the __export/ __force symbol attributes from emscripten-core#27436 in wasm-bindgen/wasm-bindgen#5253) hoists the clean exported API (functions, classes, enums) into top-level library symbols that self-register as exports via its `--js-library`. The user-facing API now comes from wasm-bindgen's library in every flow, so emscripten no longer needs to guess or own the export set. This collapses the previous staticlib-only handling into a single -sWASM_BINDGEN path that works whether cargo/rustc drives the link (bin crate, emcc as the linker, rustc supplies -sEXPORTED_FUNCTIONS) or emcc drives it (staticlib, exports discovered locally): - The exports the wasm-bindgen expansion reaches by name - the supplied EXPORTED_FUNCTIONS (method shims, the __wbindgen_* runtime, the marker, main) plus anything its expansion adds - are internal glue, not a user API. They are captured and kept off every export layer: the ESM wrapper (user_requested_exports), the factory Module attachment (EXPORTED_FUNCTIONS, via should_export), and the keepalive pass in finalize_wasm. `main` still runs automatically on init; `_main` isn't surfaced. - A genuine EMSCRIPTEN_KEEPALIVE C/C++ export is not in that internal set and remains surfaced, so a hand-written native export composes with wasm-bindgen's API in the same module. Human-supplied EXPORTED_FUNCTIONS are not preserved through wasm-bindgen linkage yet (the rustc-supplied set is indistinguishable from glue); that can be revisited later. - Strip the placeholder symbols wasm-bindgen consumes (__wbindgen_describe*, __externref_*, ...) so they aren't reported as undefined exports. - Only run nm-based export discovery for explicit -sWASM_BINDGEN when no driver supplied EXPORTED_FUNCTIONS; the rustc-driven link already lists them exactly. - Wire imported JS: feed library_bindgen.extern-pre.js as extern-pre-js and copy the snippets/ dir next to the output so relative imports resolve. - The WASM_ESM_INTEGRATION wrapper re-exports the JS library symbols that were exported (MODULARIZE=instance), and provides wasmExports via a namespace import of the wasm so by-name export access works. Add -sWASM_BINDGEN=auto: run wasm-bindgen only when the linked wasm carries wasm-bindgen's __wasm_bindgen_emscripten_marker custom section, which is how cargo/rustc opts in when driving emcc as the linker (addressing the request to replace implicit marker detection with an explicit flag); otherwise it is a no-op and wasm-bindgen need not be installed. Both output modes then expose only the clean API (e.g. a `Greeter` class). Add an end-to-end test parameterized over the ESM and factory output modes (built via cargo with -sWASM_BINDGEN=auto), a no-marker test asserting auto is a no-op for an ordinary build, extend the staticlib integration test to assert an EMSCRIPTEN_KEEPALIVE export survives alongside the wasm-bindgen API, and install a pinned wasm-bindgen-cli alongside rust in CI so the flow is always exercised. The wasm-bindgen library and CLI are pinned to the same wasm-bindgen main rev (the attribute support is not yet in a release); they must match exactly, and can move to a version pin once released.
guybedford
force-pushed
the
wasm-bindgen-unified-flow
branch
from
August 8, 2026 01:03
538e1fb to
5d82a9d
Compare
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.
This unifies wasm-bindgen output generation under a single
-sWASM_BINDGENpath and adds-sWASM_BINDGEN=auto, building on the wasm-bindgen side change in wasm-bindgen/wasm-bindgen#5210. It supersedes #27179.Previously
-sWASM_BINDGENwas effectively a staticlib-only flow where emcc had to discover and own the export set itself. With #5210, wasm-bindgen hoists its clean exported API (functions, classes, enums) into top-level library symbols that self-register as public exports via the__export/__forcesymbol attributes (#27436, wasm-bindgen/wasm-bindgen#5253) in its--js-library. The user-facing API now comes from wasm-bindgen's own library in every flow, so emscripten no longer needs to guess or own exports — it just surfaces what wasm-bindgen registered. That lets one-sWASM_BINDGENpath serve both whether cargo/rustc drives the link (bin crate, emcc as the linker, rustc supplies-sEXPORTED_FUNCTIONS) or emcc drives it (staticlib, exports discovered locally).The model for a public export is the union across both systems: wasm-bindgen's self-registered API, plus emscripten's own
EMSCRIPTEN_KEEPALIVEexports — minus wasm-bindgen's internal expansion glue, which must not spill into the public surface.What's implemented:
EXPORTED_FUNCTIONS(method shims, the__wbindgen_*runtime, the marker,main) plus anything its expansion adds — are captured as internal glue and kept off every export layer: the ESM wrapper (user_requested_exports), the factoryModuleattachment (EXPORTED_FUNCTIONS, viashould_export), and the keepalive pass infinalize_wasm.mainstill runs automatically on init;_mainisn't surfaced.EMSCRIPTEN_KEEPALIVEC/C++ export is not in that internal set and remains surfaced, so a hand-written native export composes with wasm-bindgen's API in the same module.__wbindgen_describe*,__externref_*, ...) are stripped so they aren't reported as undefined exports.-sWASM_BINDGENwhen no driver suppliedEXPORTED_FUNCTIONS; the rustc-driven link already lists them exactly.library_bindgen.extern-pre.jsis fed as extern-pre-js and thesnippets/dir is copied next to the output so relative imports resolve.WASM_ESM_INTEGRATION,wasmExportsis provided via a namespace import of the wasm so wasm-bindgen's by-name export access works. This is gated behindWASM_BINDGENso plain ESM integration keeps per-symbol named imports and their tree-shakability. (The wrapper re-export of self-registered JS library symbols itself landed in Support JS libraries self-registering their exports with visibility attributes #27436.)-sWASM_BINDGEN=auto(replacing #27179's implicit marker detection, per review feedback there): runs wasm-bindgen only when the linked wasm carries wasm-bindgen's__wasm_bindgen_emscripten_markercustom section — how cargo/rustc opts in when driving emcc as the linker. Otherwise it's a no-op and wasm-bindgen need not be installed, so rustc can always pass it via-Clink-argwithout affecting ordinary builds.Both output modes then expose only the clean API (e.g. a
Greeterclass).Testing:
-sWASM_ESM_INTEGRATION) and factory (-sMODULARIZE -sEXPORT_ES6) output modes, built viacargo buildwith-sWASM_BINDGEN=auto(marker auto-detected), asserting the cleanGreeterAPI works,mainruns on init, and none of the raw wasm exports leak. (The test crate passes-sDEFAULT_TO_CXXas a link arg since rust's precompiled std requires emscripten's C++ runtime libraries but rustc links via emcc, needed since Disable C++ linking (DEFAULT_TO_CXX) by default #27469.)-sWASM_BINDGEN=autois a no-op for an ordinaryhello_world.cbuild (doesn't require wasm-bindgen installed).EMSCRIPTEN_KEEPALIVEnative export survives alongside the wasm-bindgen API (fails under a blanket export wipe).wasm-bindgen-clialongside rust so the flow is always exercised.Not in scope (follow-up): preserving human-supplied
EXPORTED_FUNCTIONSthrough wasm-bindgen linkage (the rustc-supplied set is currently indistinguishable from glue, so explicit exports aren't surfaced).