New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Retain TDZ violations in common scenarios and fix similar var
bugs
#4124
Conversation
Thank you for your contribution! ❤️You can try out this pull request locally by installing Rollup via npm install kzc/rollup#retain-common-tdz or load it into the REPL: |
Codecov Report
@@ Coverage Diff @@
## master #4124 +/- ##
=======================================
Coverage 98.12% 98.12%
=======================================
Files 201 201
Lines 7077 7092 +15
Branches 2071 2081 +10
=======================================
+ Hits 6944 6959 +15
Misses 64 64
Partials 69 69
Continue to review full report at Codecov.
|
Odd... codecov only ran on the first push. |
console.log(function() { | ||
if (x) return "HELLO"; // TDZ | ||
const x = 1; // keep | ||
return "WORLD"; // not reached |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although dropping thereturn "WORLD";
line for this test doesn't affect the outcome of the preserved TDZ violation above, I am not sure why it was dropped due to a side effect being reported on the Identifier x
in if (x) return "HELLO";
. If this example were changed to use var
instead of const
, the rollup result in this PR would be incorrect due to the dropped return "WORLD";
:
Expected:
$ echo 'console.log(function(){if(x)return"HELLO";var x=1;return"WORLD"}());' | node
WORLD
Actual using this PR:
$ echo 'console.log(function(){if(x)return"HELLO";var x=1;return"WORLD"}());' | rollup --silent
console.log(function(){if(x)return "HELLO";var x=1;}());
$ echo 'console.log(function(){if(x)return"HELLO";var x=1;return"WORLD"}());' | rollup --silent | node
undefined
Note that rollup v2.50.6 is also wrong, but in a different way:
$ echo 'console.log(function(){if(x)return"HELLO";var x=1;return"WORLD"}());' | node_modules/.bin/rollup --silent
console.log(function(){return "HELLO";}());
$ echo 'console.log(function(){if(x)return"HELLO";var x=1;return"WORLD"}());' | node_modules/.bin/rollup --silent | node
HELLO
* Fix analogous bugs with `var` variable use before declaration.
@lukastaegert This PR is good to merge. It fixes a number of bugs with no test regressions, and JS inputs that used to fail prior to this PR and continue to fail with this PR have been documented with "TODO" comments. The vast majority of rollup optimized output will not be pessimized by this PR. Feel free to front-load and/or cache some of the checks performed in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. I am still not 100% sure about this one. Correctness is good, though correct TDZ violations are usually not as crucial as other things (though of course if people only ever execute code through Rollup, it becomes a meta language that deviates from how vanilla JavaScript runs, which is not desirable).
The good about your solution is that it is rather focused and does not deoptimize a lot. On the other hand, I find it really hard to read and understand, with extremely complicated conditionals. Also, using any
casts in several places carries the additional danger that refactoring elsewhere in the code base breaks this code without warning (well, the tests could still catch it, but TypeScript definitely does not help).
I was actually considering a switch to deoptimize all possible TDZ violations (read: All const
/let
variables accesses unless we are 100% certain) that is off by default. Then one could start working the other way, try to identify situations where there is definitely no violation. Similar for var accesses.
In any case, we could keep this still until we can do better. But I would move the code into a separate helper.
The TDZ/var issue is essentially a variation of Turing's Halting problem. One cannot know if there is a problem unless the code is actually executed. But there are fairly simple heuristics that can be employed to predict where such errors are likely to occur. Both TDZ masking and the incorrect use of In order to solve the remaining multiscope bugs involving function calls one would have to maintain a record of all captured variables within each function originating from a different scope. Then when Despite the use of I'm not a big fan of having another switch to deoptimize TDZ and vars, as there is already such a switch - |
Closing out this PR as it won't be merged. Consider it a working proof of concept for what has to be done to fix these tree shaking bugs. |
Well, technically Rollup is already doing a trial execution of the code, at least on a per function basis. That is how we handle removing code after errors or return statements. There is a context object to track state while doing that. If there were no function calls, the problem wouldn't actually be too difficult to solve. The question, though, is how to handle functions while avoiding a huge performance issue, as naively just following all function calls will make performance exponentially worse. |
Yes, the TDZ and I tend to err on the side of practicality. This PR handles the same-scope non-call cases automatically without a new treeshake option while still allowing optimizations for use-after-definition variables. The current implementation is small and self-contained. The code can easily be altered to avoid the use of |
As I said
even though I do not like the solution, I am not against merging it. However as you already seem to have deleted your branch for reasons unknown to me, this will likely not happen, and there is not reason to discuss further unless you recreate the PR. In any case, I will still implement the additional switch, as it is still much better than turning off tree-shaking. |
I had interpreted that comment in its original context as keeping the PR for reference but not merging it. No matter - it was only a partial solution. The proof of concept served its purpose to convey some ideas. It was a good discussion. Thanks for your continuing great work on rollup. |
Retain TDZ violations in some common scenarios and fix similar
var
bugs.This PR contains:
Are tests included?
Breaking Changes?
List any relevant issue numbers:
Fixes most scenarios in #4101 except for
bug1
, which is a more complex multi-scope problem involving function calls. It will be addressed in a future PR.Description
Rollup used to silently mask code related to TDZ violations for
let
andconst
variables, which would produce different runtime behavior without warning. This PR retains the TDZ violating code in many common scenarios to attempt to be bug compatible with the source input. Not coincidentally, the same TDZ logic also fixes a number of bugs related to usingvar
variables before their declarations.