Skip to content

fix: clearTimeout illegal invocation with bundler (#187)#283

Merged
futursolo merged 4 commits into
ranile:masterfrom
flisky:master
Jan 20, 2023
Merged

fix: clearTimeout illegal invocation with bundler (#187)#283
futursolo merged 4 commits into
ranile:masterfrom
flisky:master

Conversation

@flisky
Copy link
Copy Markdown
Contributor

@flisky flisky commented Jan 10, 2023

It's a regression from #96 introduced by #185, and I have no idea why calling clearTimeout is failed in browser, but my patch definitely resolves the error.

It's a side effect introduced by bundler, and we workaround it. more details in #283 (comment)

Copy link
Copy Markdown
Contributor

@futursolo futursolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, the current timer implementation (in master) does not seem to work with wasm-pack test --node either when I tried it. Maybe this will also fix it?

CI should turn green when if you rebase from the master branch.

Comment thread crates/timers/src/callback.rs Outdated
@DogLooksGood
Copy link
Copy Markdown

FYI, ran into this with https://github.com/paritytech/jsonrpsee. What weird is, current version (gloo-timers 0.2.5) only works in tests, not in real cases. In wasm-pack test --chrome it works (when I open the browser to run tests). But if the it is compiled to wasm (with wasm-pack) and required by JavaScript, there's an illegal invocation error.

@flisky
Copy link
Copy Markdown
Contributor Author

flisky commented Jan 19, 2023

@futursolo good catch. It turns out the return type is different between browser (number) / nodejs (Timeout), and I push another commit for nodejs support.

@flisky
Copy link
Copy Markdown
Contributor Author

flisky commented Jan 19, 2023

@DogLooksGood @futursolo I take a deep look at ctm/gloo-timers-regression(thanks @ctm), and reproduce as the following code:

const foo = { bar: clearTimeout }
foo.bar()

It's a clearly true invocation error because clearTimeout is binding this to foo, instead of Window.


And setTimeout is not affected because the wasm-pack generates the following code:

function() { return handleError(function (arg0, arg1) {
    const ret = setTimeout(getObject(arg0), arg1);
    _assertNum(ret);
    return ret;
}, arguments) };

while clearTimeout goes to typeof clearTimeout == 'function' ? clearTimeout : notDefined('clearTimeout')

So, it's a bad side-effect introduced by bundler.


The currently clearTimeout goes to

function() { return logError(function (arg0, arg1) {
   getObject(arg0).clearTimeout(takeObject(arg1));
}, arguments) };

I think we can hack the binding further into

#[wasm_bindgen(js_name = "clearTimeout")]
fn clear_timeout(handle: JsValue) -> JsValue;

which generates

 function() { return logError(function (arg0) {
    const ret = clearTimeout(takeObject(arg0));
    return addHeapObject(ret);
}, arguments) }

@flisky flisky changed the title fix: clearTimeout illegal invocation in browser (#187) fix: clearTimeout illegal invocation with bundler (#187) Jan 19, 2023
@DogLooksGood
Copy link
Copy Markdown

@flisky I just tried with your master version, the issue hasn't solved in my case. The generated code is still

export const __wbg_clearTimeout_23ee6db72c0ad922 = typeof clearTimeout == 'function' ? clearTimeout : notDefined('clearTimeout');

@flisky
Copy link
Copy Markdown
Contributor Author

flisky commented Jan 20, 2023

@DogLooksGood, could you please double check? Make sure that:

  • something like git+https://github.com/flisky/gloo.git#74a5769b5e6def8fdcc11179378a1afcd1bacb7a in Cargo.lock
  • disable cache in devtools when reproducing

@flisky
Copy link
Copy Markdown
Contributor Author

flisky commented Jan 20, 2023

@futursolo, could you please take another review?

There're two binding changes:

  • the return type of setTimeout/setInterval from i32 to JsValue to support nodejs, which returns Timeout object.
  • add return type of clearTimeout/clearInterval to let wasm-bindgen wrapping the generated code, to avoid a bare function call in a different namespace.

I don't know how to run the same testcases in nodejs & browser with wasm-bindgen test, so I duplicates web.rs into node.rs. Please let me known if there's a better way. Thanks!

@futursolo
Copy link
Copy Markdown
Contributor

The implementation looks good to me. I will approve this once I hear back from @DogLooksGood.

I don't know how to run the same testcases in nodejs & browser with wasm-bindgen test, so I duplicates web.rs into node.rs. Please let me known if there's a better way. Thanks!

You can apply a feature flag to run_in_browser.

// It might be better to use not(node_tests) than browser_tests because most existing tests are browser tests.

#[cfg(not(feature = "node_tests"))]
wasm_bindgen_test_configure!(run_in_browser);
// Runs browser tests
wasm-pack test --firefox

// Runs node tests
wasm-pack test --features=node_tests --node

In addition, would you mind to also update GitHub Actions to run node tests?

@DogLooksGood
Copy link
Copy Markdown

@futursolo @flisky Sorry, it's my fault. It works.

@flisky
Copy link
Copy Markdown
Contributor Author

flisky commented Jan 20, 2023

@futursolo the current CI is heavily based on --all-features, and I don't think web_tests/node_tests works perfectly with other feature flags.

Tests / Node Tests job is setup, and works as expected

@futursolo
Copy link
Copy Markdown
Contributor

Whilst I think it's still possible to combine tests with a compiler flag, I am fine with merging this pull request as-is.

We can merge the tests together in the future.

@futursolo futursolo merged commit 01c127c into ranile:master Jan 20, 2023
@ranile
Copy link
Copy Markdown
Owner

ranile commented Jan 21, 2023

Released in gloo-timers 0.2.6 🎉

jsdanielh added a commit to jsdanielh/rust-libp2p that referenced this pull request Nov 19, 2023
Add support different WASM environments (such as workers, NodeJS)
that don't have a `window` global by binding to the global
`setInterval` and `clearInterval` functions directly.
This uses the same approach as used by gloo-timers implemented in
[ranile/gloo#185](ranile/gloo#185) and
[ranile/gloo#283](ranile/gloo#283) given
the `web-sys` lack of interes to support this (see discussion in
[this issue](wasm-bindgen/wasm-bindgen#1046)).

Co-authored-by: sisou <hello@soerenschwert.de>
jsdanielh added a commit to jsdanielh/rust-libp2p that referenced this pull request Nov 19, 2023
Add support different WASM environments (such as workers, NodeJS)
that don't have a `window` global. This is done by binding to the
global `setInterval` and `clearInterval` functions directly.
This uses the same approach used by gloo-timers implemented in
[ranile/gloo#185](ranile/gloo#185) and
[ranile/gloo#283](ranile/gloo#283) given
the `web-sys` lack of interes to support this (see discussion in
[this issue](wasm-bindgen/wasm-bindgen#1046)).

Co-authored-by: sisou <hello@soerenschwert.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants