wasm-bindgen
wasm-bindgen copied to clipboard
Incompatibility when using web_sys along with asyncWebAssembly in webpack 5
Describe the Bug
When using webpack 5, enabling asyncWebAssembly: true
in the webpack config will cause errors when trying to load a module generated from wasm-bindgen:
ReferenceError: Cannot access '__wbindgen_string_new' before initialization
The Rust code in question:
#[wasm_bindgen]
pub fn foo(n: &web_sys::HtmlElement) {
web_sys::console::log_1(&n.tag_name().into());
}
Commenting out this code, or replacing it with a fn foo(n: i32) { // ... }
allows the code to function. Using syncWebAssembly: true
also works as it did in webpack 4.
#2110 seems to indicate that asyncWebAssembly
should work.
Steps to Reproduce
Minimal example repo based on rust-webpack-template:
-
git clone https://github.com/Herschel/webpack-test
-
cd webpack-test
-
npm install
-
npm run start
- Navigate to the page in the browser and view the JS console.
Expected Behavior
The code should run successfully.
Actual Behavior
The following error is thrown:
ReferenceError: Cannot access '__wbindgen_string_new' before initialization
Additional Context
rustc 1.49.0-nightly (ffa2e7ae8 2020-10-24)
wasm-bindgen = "0.2.68"
web-sys = "0.3.45"
[email protected]
wasm-pack 0.9.1
"webpack": "^5.4.0"
Thanks for the report! I'm not entirely sure what changed in Webpack 5, but it at least looks like we need to account for something! I'd need to dig into how things changed and also any recent updates on the ESM proposal, if any. I don't have a ton of time for this but I'll try to make some time in the near future.
FYI, this also happens when trying to return wasm-bindgen's built-in JsValue.
Here is a minimal repo demonstrating it (forked from Herschel's repo above): https://github.com/JohnForster/webpack-test
I am having a very similar issue, I believe the same thing.
When the single my_rust.js
, webpack wraps it in something like the following boilerplate:
((module, __webpack_exports__, __webpack_require__) => {
module.exports = async () => {
[...]
__webpack_require__.d(__webpack_exports__, {
[...],
"__wbindgen_throw": () => __wbindgen_throw,
});
[...]
var _my_rust_bg_wasm__WEBPACK_IMPORTED_MODULE = __webpack_require__(119);
_my_rust_bg_wasm__WEBPACK_IMPORTED_MODULE = await Promise.resolve(_my_rust_bg_wasm__WEBPACK_IMPORTED_MODULE);
[...]
const __wbindgen_throw = function([..]) { [..] };
[...]
return __webpack_exports__;
}();
})
__webpack_require__.d
assigns these getter functions as getters on the exports object.
When the _my_rust_bg_wasm__WEBPACK_IMPORTED_MODULE
promise awaited on, the wasm is loaded and initialized. This tries to require functions from the above module again, which will fail because that initial module is still awaiting on the promise, thus those functions are not yet initialized.
This is run with
experiments: {
asyncWebAssembly: true,
},
in the webpack config.
Hi guys,
any update on this bug? It's blocking my development with Webpack 5 completly.
This problem reproduces when generated webassembly has imports, it seems that mutual dependency between index_bg.js
and index_bg.wasm
causes this error.
No Imports
index.ts --(imports bindings)--> index_bg.js --(imports wasm's exports)--> index_bg.wasm
With Imports
index.ts --(imports bindings)--> index_bg.js --(imports wasm's exports)--> index_bg.wasm ______________________________________<--(imports runtime functions in JS)--
Short code samples in my hands show that no awaiting for index_bg.js
instantiation causes reference error in index.ts
, but awaiting for index_bg.js
instantiation causes reference error in index_bg.wasm
. Separating index_bg.js into index.ts
dependent parts and index_bg.wasm
dependent parts maybe resolves this problem.
Anyway I'm not familiar to webpack's await/async dependency resolution mechanism, I want to do further investigation.
Let me report what I have investigated.
ReferenceError: Cannot access '__wbindgen_string_new' before initialization
It seems that webpack still depends on hoisting variables (which is javascript spec), therefore using const
or let
in javascript modules will be problematic and this webpack's unexpected behavior will causes this reference error.
Rewriting functions emitted with wasm-bindgen in index_bg.js into export functions xxx
or export var xxx = ...
forms leads me to not callable import
error (is mentioned in wasm-pack and webpack 5 #835).
Further more, eliminating export var xxx = ...
resolves this not callable import
error.
I want to find the way to get along with webpack5 using export const
or export let
, but no good idea...
I'm getting a similar error while using a rust wasm and webpack 5:
ReferenceError: Cannot access 'heap' before initialization
at Module.greet_async (webline_core_bg.js:77)
at eval (index.js:9)
inspecting webline_core_bg.js:
function greet_async(f) {
try {
_webline_core_bg_wasm__WEBPACK_IMPORTED_MODULE_0__.greet_async(addBorrowedObject(f));
} finally {
heap[stack_pointer++] = undefined;
// ^^^^ <- webline_core_bg.js:77 error here
// heap is defined on line 34 of the same file as:
// const heap = new Array(32).fill(undefined);
}
}
webpack.config.js:
var path = require('path');
module.exports = {
mode: 'development',
watch: true,
output: {
path: path.join(__dirname, 'dist'),
filename: 'index.js'
},
experiments: {
asyncWebAssembly: true
}
};
Is this related?
I confirmed that release 0.2.74 (which included the fix in #2512) solved this issue for me.
Can this be closed then?
I'm running into a similar issue using webpack to bundle some bindgen generated code with other typescript/js code
function="__wbindgen_string_new" error: function import requires a callable
I'm using webpack 5 and on bindgen v0.2.76
any suggestions?
EDIT: more context/webpack setup (I'm using typescript but bundling/compiling it down to commonjs with tsc+webpack):
EDIT again: I was able to solve this. I was running the generated wasm-pack js code through the tsc compiler. To fix it, I used babel-loader for the generated code which then gets imported via require in the compiled js code (via tsc). The only issue is, I need to use a copy-webpack-plugin to copy over the .wasm
file from the final node_module into my build/dist output but thats a minor thing
import { myFunction } from './dist/src/index'
(async() => {
// this does an import('../rust-lib/pkg/') to init the wasm module
const a = await myFunction();
}()
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin')
const baseConfig = {
stats: { assets: false, modules: false },
entry: './main.js',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
},
]
},
mode: 'production',
resolve: {
extensions: ['.tsx', '.ts', '.js', '.wasm']
},
experiments: {
asyncWebAssembly: true
}
}
const browserConfig = {
output: {
filename: 'out.js',
path: path.resolve(__dirname, ''),
}
}
module.exports = (env) => {
return [{
...baseConfig,
...browserConfig
}];
};