Can we combine multiple (more than 10) .wasm files into a single .wasm file?
3 Answers
This might not be the answer you're looking for, but here it is.
I do not know of a tool that combines wasm files into one. However, it is possibly to do this manually (and possibly automate it).
You will need wasm2wat
to convert .wasm
files to .wat
files, which are an intermediary representation of wasm (like assembly is for machine code).
Each .wat
file has the following strucure:
(module
;; contains code (functions, types, etc.)
)
So your output .wat
file should look like this:
(module
;; code from the other .wat files, excluding the `(module )`
)
So, you strip the (module
at the beginning of your input files and the )
at the end of those files. Then, you paste that code into the output file.
This has one downside, however; you will also copy duplicate functions, so you'll have to filter those out.
Hope this at least helps a little.

- 514
- 6
- 22
There was a project for that, but apparently, it was considered as not used enough.
You cannot "just" copy/paste the WebAssembly textual serialization of two or more modules and expect that to work. The different objects inside (functions, globals, etc.) are accessed (called/get/set) by indexes not just by names. You would have to fully process the modules and merge them IF the multiple memories proposal was operational. But it is not yet ready.

- 1,360
- 1
- 6
- 20
As Nikolay mentioned, the lack of 'multiple memories' support is the main hurdle in doing that reliably.
The rest of the wasm module "primitives" -- types, funcs, tables, globals, elems, datas, imports, exports -- can be merged by avoiding conflicts using a relatively simple, but fully static renaming/renumbering scheme. I'm saying relatively simple, because this entails inspecting and modifying the func byte code so it refers to the new indices after renumbering.
One of the reasons this works is there's already support for multiple tables, so the code in "module A" can use the original table indices, the code in "module B", when merged, can be patched to use "original table index + (count of tables in mod A)", the code in mod C will use "original table index + (count of tables in mod A + count of tables in mod C)", etc. (The other reason this works is because it's not possible to define new tables at run-time -- that is, the count of tables in each module is static and known in advance)
'start' (entry point) is a bit different -- you'll have to pick one.
'memory' is a problem though. The merged module will still only have just a single 'memory' that needs to somehow be shared between all the pre-merge modules. Generally, this is not possible, because the func logic in each module assumes it has full control over the entire memory space.
That being said, it's possible to come up with a scheme for merging 'memory' that might work for most situation. (let's ignore memory64 proposal for now) There are several constraints though:
- the module being merged should not contain the
memory.grow
instruction, or if it contains it then the memory definition should also specify a max size, e.g.(memory 1 16)
. - the combined total memory usage of all modules being merged should not overflow the memory32 size limit, i.e. 4 GiB.
Given these constraints, the 'trick' relies on the fact that memory load
and store
instructions allow for a const immediate offset arg, e.g. (i32.load8_u offset=25)
. So, if memory use of each module is known (the constraints ensure that), the byte code of load/store instruction of each module being merged could be modified by adding a synthetic offset=
to each instruction so that it doesn't overlap with the memory 'address space' of all modules merged so far (existing offset=
will simply be increased by that amount)

- 246
- 3
- 8