wasm-bindgen icon indicating copy to clipboard operation
wasm-bindgen copied to clipboard

Incompatibility when using web_sys along with asyncWebAssembly in webpack 5

Open Herschel opened this issue 4 years ago • 10 comments

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:

  1. git clone https://github.com/Herschel/webpack-test
  2. cd webpack-test
  3. npm install
  4. npm run start
  5. 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"

Herschel avatar Nov 04 '20 22:11 Herschel

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.

alexcrichton avatar Nov 05 '20 14:11 alexcrichton

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

JohnForster avatar Jan 19 '21 21:01 JohnForster

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.

hansihe avatar Jan 24 '21 15:01 hansihe

Hi guys,

any update on this bug? It's blocking my development with Webpack 5 completly.

Roba1993 avatar Feb 04 '21 21:02 Roba1993

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.

nokotan avatar Mar 16 '21 05:03 nokotan

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...

nokotan avatar Mar 29 '21 10:03 nokotan

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?

ConnorJamesLow avatar Apr 08 '21 19:04 ConnorJamesLow

I confirmed that release 0.2.74 (which included the fix in #2512) solved this issue for me.

Contextualist avatar Jun 03 '21 05:06 Contextualist

Can this be closed then?

entropylost avatar Aug 10 '21 00:08 entropylost

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
	}];
};

ks2211 avatar Aug 25 '21 13:08 ks2211