-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[ES6] Function hoisting + if-return optimisation breaks let scope #1317
Comments
Most uglify compress and mangle optimizations predated ES6 and as such would be incorrect in ES6 code. Harmony users should disable compress and mangle. As a stop-gap measure the harmony branch ought to scan the tree for The same scoping issues apply to |
Going to take a look into this issue. |
There are no functions to be moved and es5 isn't aware of block scoping, so why disabling |
For now I might be forced to disable the optimization, even though the code can be transformed into something that doesn't use block variables or doesn't affect code context. |
If statement and return statements can't have var/let/scope in their statements. Use cases are limited to code within statement bodies that have at least 1 non-return statements. |
Runnable test case: "use strict";
var foo = function(n, m) {
function getResult() {
return [n, m, bar];
}
if (n === m) {
return;
}
let bar = n % m;
return getResult();
}
foo(0, 0);
foo(1, 0);
foo(0, 1); |
Related to the Disabling these lines results in code that seems slightly shorter
|
Both cases deal with a situation where one branch returns a result and the other branch does not return anything (a.k.a. returns void). This is why the resultant code is longer when transformed into a return of a further optimized ternary operator with a void expression. I'm not the author of the code but I can only surmise that the hope was there would be optimization opportunities later in some cases when statement chains were transformed into expressions. That's not true in those particular cases. Perhaps there exists a case where it is true? |
I think I am going to remove this optimization case, as it adds |
@kzc yeah, I wanted to be careful with removing |
Also @mishoo is the only author on that specific code and he did added that comment (a bit later) as well that he had no clue where it is for. The code was the result of a big refactoring on the This is just a slight case that was probably overlooked I guess. |
Going to remove that optimization on master soon. |
No need for guesswork - run |
I qualified that statement with "unminified" because once transformed into expression sequence form uglify cannot revert them back to regular statements. |
Patch on #1437. Feel free to battle out for a better solution. |
Counter case working better with uglify-js@2.7.5: #1437 (comment) |
@alexlamsl This bug is presently the biggest showstopper in |
The previous fix for "use strict";
(function (a) {
if (!a) return;
let foo = "bar";
function doSomething() {
console.log(foo);
};
doSomething.call(null);
})(process); This is currently optimised into, with "use strict";
!function(a) {
if (a) {
let foo = "bar";
doSomething.call(null);
}
function doSomething() {
console.log(foo);
}
}(process); So one might think we can just stuff all the trailing |
May be extracting the block-scoped declarations and shove them back up? "use strict";
!function(a) {
let foo;
if (a) {
foo = "bar";
doSomething.call(null);
}
function doSomething() {
console.log(foo);
}
}(process); This will create larger code, especially if we get a bunch of these |
That's not correct. Consider:
By the way, it was difficult to create a test case because |
The culprit of your example is $ cat 1317.js | node
1
1
$ uglifyjs 1317.js -c if_return=0 | node
1
1
$ uglifyjs 1317.js -c if_return=1 | node
[stdin]:1
!function(){function foo(){console.log(x)}if(Math){let x=1;foo(),foo()}}();
^
ReferenceError: x is not defined
at foo ([stdin]:1:40) |
$ uglifyjs 1317.js -c if_return=0 -b bracketize
!function() {
function foo() {
console.log(x);
}
if (!Math) {
return;
}
let x = 1;
foo(), foo();
}(); |
Fair enough - I wasn't terribly concerned which option was the problem. Default |
So the problem is actually moving the |
That I agree - but in order to fix the issue I need to know where to change the code... 😅 Anyway, in the process of looking around I've discovered these two blocks of code have overlapping functionality: So my fix in #2010 only applies to the first block, and if I inject |
Sorry for my rumblings in #1317 (comment) where I was trying to point that out 😅 |
Not at all - if I'm wrong, I'm wrong. I just needed to write a program to better understand the issue. |
You know what, this statement still holds true:
|
That's the route I'm taking, as it won't produce larger code under any circumstances. But then I hit #1317 (comment) |
Input:
Compile with
--compress --beautify
:Using
var
works because it is scoped to the whole function, and disabling if_return also fixes it, but disabling hoist_funs does not appear to change the output code.The text was updated successfully, but these errors were encountered: