RESOLVED FIXED 223533
[JSC] Redeclaring parameter of a generator / async function makes it `undefined`
https://bugs.webkit.org/show_bug.cgi?id=223533
Summary [JSC] Redeclaring parameter of a generator / async function makes it `undefined`
Martin
Reported 2021-03-19 13:29:36 PDT
The following code prints undefined in the console: async function test(a) { console.log(a); var a; } test("a"); It should print "a" to the console as it does in firefox and chrome. In fact, if the function is not async it will print "a" as expected. While I don't recommend writing code like this, this code was produced by our minifier and it did lead to the chat feature on our website being unavailable for all safari users. The code worked as expected on v8, therefore nothing was picked up by our tests.
Attachments
Patch (31.48 KB, patch)
2023-12-11 15:07 PST, Alexey Shvayka
no flags
Radar WebKit Bug Importer
Comment 1 2021-03-26 13:30:13 PDT
Ingvar Stepanyan
Comment 2 2022-12-13 16:36:04 PST
We ran into this breaking on code generated by Emscripten too. https://github.com/emscripten-core/emscripten/issues/18357 Emscripten will have to add a workaround, but, considering that this is a core JS bug, it would be great to fix this on the JavaScriptCore side too.
Ross Kirsling
Comment 3 2022-12-13 23:51:00 PST
This is actually even weirder than the above example suggests, as it's not actually the value of the var that's being printed -- the following *still* prints `undefined`: ``` async function test(foo) { print(foo); var foo = 42; } test('blah'); ```
Ross Kirsling
Comment 4 2022-12-13 23:52:18 PST
(In reply to Ross Kirsling from comment #3) > This is actually even weirder than the above example suggests, as it's not > actually the value of the var that's being printed -- the following *still* > prints `undefined`: > > ``` > async function test(foo) { print(foo); var foo = 42; } > test('blah'); > ``` Oh wait no, that's just me forgetting that it's only the declaration itself that gets hoisted. My bad.
Sam Clegg
Comment 5 2022-12-14 10:37:18 PST
This bug is effecting emscripten users: https://github.com/emscripten-core/emscripten/issues/18357. It seems that we output this pattern in some modes and the code works on chrome and firefox, but not safari.
Ross Kirsling
Comment 6 2023-01-10 19:05:52 PST
(Moving my December investigation comments over from Slack...) This is a surprisingly tricky matter -- it's basically rooted in bug 164087, the fact that our implementation separates async and generator functions into "wrapper" and "body" functions. This separation makes it incorrect for us to hoist var decls to the top of the body, since: > async function f(foo) { print(foo); var foo; } f('blah'); would have hoisting behavior analogous to... > function f(foo) { (function () { print(foo); var foo; })(); } f('blah'); when it would need to be like... > function f(foo) { (function () { print(foo); })(); var foo; } f('blah'); It's not entirely clear what the appropriate solution is here. 1. If we really view the wrapper/body separation as a mistake, then the current problem would just be a direct result of that mistake. 2. If not, then we need magic to happen, relative to normal nested function behavior...but the magic can't depend on actually *checking* whether the var shadows a param, because we literally don't have that information when reparsing the body (i.e. hasDeclaredParameter isn't valid to call at that timing because the wrapper scope, where the params actually live, is already gone). 2a. Like, we can pretend that the real var decl was up in the wrapper scope and "unconditionally use" it in the body instead, but this falls apart for cases where there wasn't actually a param to capture (i.e. we end up setting to a global). 2b. So maybe it's that the param should come down to the body scope...but I'm not quite sure how we'd mean for this to happen. It's clear that a lot of work was done in order to propagate the `arguments` object down into the body, but then we're also taking great care to not bring any unnecessary individual params down, so it seems like we just end up at the same place: the body needs to be able to say that a given param is "necessary".
Ross Kirsling
Comment 7 2023-01-10 19:15:30 PST
Test cases below. # Control cases: function test0(foo) { print(foo); var foo = 42; } test0('blah'); const test0a = (foo) => { print(foo); var foo = 42; }; test0a('blah'); # Bugged cases: async function test1(foo) { print(foo); var foo = 42; } test1('blah'); const test1a = async (foo) => { print(foo); var foo = 42; }; test1a('blah'); function* test2(foo) { print(foo); var foo = 42; } test2('blah').next(); async function* test3(foo) { print(foo); var foo = 42; } test3('blah').next();
Sam Clegg
Comment 8 2023-01-11 04:04:44 PST
FWIW emscripten now avoids this pattern: https://github.com/emscripten-core/emscripten/pull/18482
Theodore Brown
Comment 9 2023-03-22 09:38:54 PDT
I also ran into this issue since UglifyJS frequently declares a variable with the same name as a parameter when minifying code.
Alexey Shvayka
Comment 10 2023-10-15 11:58:56 PDT
*** Bug 182414 has been marked as a duplicate of this bug. ***
Alexey Shvayka
Comment 11 2023-10-15 11:59:27 PDT
*** Bug 226335 has been marked as a duplicate of this bug. ***
Jarred Sumner
Comment 12 2023-10-23 02:37:36 PDT
Tracking issue in Bun's github repository: https://github.com/oven-sh/bun/issues/6663
Alexey Shvayka
Comment 13 2023-12-11 15:07:52 PST Comment hidden (obsolete)
Alexey Shvayka
Comment 14 2023-12-11 15:10:25 PST
EWS
Comment 15 2024-01-04 18:40:57 PST
Committed 272666@main (8f6b2012d16c): <https://commits.webkit.org/272666@main> Reviewed commits have been landed. Closing PR #21657 and removing active labels.
wards.afar.0j
Comment 16 2024-03-05 07:41:34 PST
*** Bug 270517 has been marked as a duplicate of this bug. ***
Note You need to log in before you can comment on or make changes to this bug.