-
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
[WIP][ES6] Function declarations should be block scoped in ES2015 #1953
Conversation
@OndrejSpanel Could you please make a test case using |
@alexlamsl It would be nice to run ES6 tests with If the node version is differently numbered in ChakraNode than Node/V8 we'd need a different scheme. |
@OndrejSpanel thanks for the PR - please be patient as we may need to enhance our test framework a little to enable thorough testing on @kzc can we safely assumes that Node.js which aren't ES6-compliant would fail to parse the If so, we can avoid |
Not necessarily. Block scope issues for functions could parse fine in ES5, but produce the wrong result. We have a few harmony tickets along those lines. You raise a good point - we should also factor
There could be inadvertent syntax errors from incorrectly constructed tests that we should not skip. I'd prefer to keep the test logic explicit with a minimum node major version. Either it passes or fails with no doubt. |
@kzc thanks for the detailed analysis - your suggestion of simply specifying a minimum Node.js version makes sense.
We should be able to specify |
@OndrejSpanel @kzc Travis failures in Node.js 0.10 and 0.12 for this and #1954 are caused by a recent |
@alexlamsl If you look at the npm pages for https://www.npmjs.com/package/uglify-js I suspect that the following line in the README no longer works - can you please remove it?
|
Since ES6 was enabled mainly into nodejs@4.x and above I would recommend dumping the tests in prior versions of nodejs... unless you plan on using the |
@kzc I think the badge is working just fine - there is a test time-out on |
But what's the point of it? What is it showing exactly? It's not release status - it's the status the last commit? |
Good point - never thought about that. In fact, a |
Done in 0813c53 😉 |
I am not sure if I am able to do this. I can create a mocha test verifying the AST and symbol scopes (something similar to what I did in https://github.com/mishoo/UglifyJS2/blob/89e94b2ca431b31ffda41d9f51e11c541aa9b626/test/mocha/accessorTokens-1492.js before), but I am afraid I am not enough familiar with JS to be able to create an example where this changes results in a different compressed result, which I think is what you want. |
@OndrejSpanel would this help? $ cat test.js
function f() {
return 1;
}
if (console.log(f())) {
function f() {
return 2;
}
} else {
function f() {
return 3;
}
}
$ nvs use node/0.10
$ node test.js
3
$ nvs use node/0.12
$ node test.js
3
$ nvs use node/4
$ node test.js
3
$ nvs use node/6
$ node test.js
1
$ nvs use node/7
$ node test.js
1 |
I will try that once #1967 is merged. |
console.log("unreachable"); | ||
var foo; | ||
function bar() {} |
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.
This actually flags an issue with the code changes in this PR. Consider the following:
while (console.log(bar)) {
function bar() {}
}
Passing it to Node.js 7 actually prints undefined
instead of throwing ReferenceError: bar is not defined
.
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.
Does minify()
need a top level ecma
option to specify whether to parse/compress in ecma: 5
or ecma: 6
mode? output.js
already has ecma: 6
support to print ES6 properties.
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.
parse()
doesn't have ecma
mode switches AFAICT.
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.
parse() doesn't have ecma mode switches AFAICT.
I appreciate that, but is there a need for ecma
in parse and compress? Is there ambiguity that could only be resolved via such an explicit option?
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.
acorn
has different ecma parsing modes:
usage: acorn [--ecma3|--ecma5|--ecma6|--ecma7|...|--ecma2015|--ecma2016|...]
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.
Interesting idea. While adding complexity to lib/parse.js
on the harmony
branch sounds worrying, we may end up needing to do so if ECMAScript breaks forward-compatibility, i.e. code valid in the older version is no longer accepted in a latter version.
IMHO parse()
serves a slightly different purpose than acorn
, in that we don't need it to throw stringent syntax errors rather than obeying GIGO.
This is fun... console.log(f);
if (console.log(f)) {
function f() {}
}
while (console.log(f)) {
function f() {}
}
console.log(f); gives: undefined
undefined
undefined
undefined But flipping the console.log(f);
if (!console.log(f)) {
function f() {}
}
while (console.log(f)) {
function f() {}
}
console.log(f); gives: undefined
undefined
[Function: f]
[Function: f] It's almost as if |
FF53 produces the same results, not just V8. |
The tests you are doing, are they strict mode or not? I think the change should probably be applied in strict mode only. |
If |
I am not sure, but what you describe seems to me to behave like "web extensions" (B.3.3 Block-Level Function Declarations Web Legacy Compatibility Semantics) described in http://www.ecma-international.org/ecma-262/6.0/index.html#sec-additional-ecmascript-features-for-web-browsers |
By default the codebase operates in normal mode unless it explicitly calls |
So here's my dilemma: $ cat test.js
function f() {
g();
if (0) {
function g() {};
}
}
f();
$ nvs use node/4
$ node test.js
$ nvs use node/6
$ node test.js
test.js:2
g();
^
TypeError: g is not a function
at f Essentially, we have an issue whereby on function f() {
g();
function g() {};
} But on function f() {
g();
var g;
} So that means if I were to "fix" it for |
Next time when anybody finds IE8 $ cat test.js
console.log(typeof f);
if (console.log(typeof f)) {
// unreachable
f();
} else {
console.log(typeof f);
function f() {}
console.log(typeof f);
}
console.log(typeof f);
$ nvs use node/4
$ node test.js
1 'function'
2 'function'
3 'function'
4 'function'
5 'function'
$ nvs use node/6
$ node test.js
1 'undefined'
2 'undefined'
3 'function'
4 'function'
5 'function' The definition is "global" within the block scope, but sequential outside of that. 🙈 |
It's a bit more sane in strict mode for both ES5 and ES6 JS engines. $ cat strict_test.js
"use strict";
console.log(typeof f);
if (console.log(typeof f)) {
// unreachable
f();
} else {
console.log(typeof f);
function f() {}
console.log(typeof f);
}
console.log(typeof f); ES5 JS engine:
ES6 JS engine:
It appears that uglify master does not behave correctly in strict mode - it applies non-strict ES5 function scope behavior in all cases:
|
To be fair, declaring functions in block scope is not valid ES5. Using an older ES5 mozilla JS engine:
It's undefined behavior at best. |
Perhaps both master and harmony branches should adopt the rule "if a given function declaration is subject to I noticed that although |
So I think the fix is to add a check for |
The two subjects are intricately linked. DCE is subject to scope, which directly influences the conditions to hoist. |
PR for #1952
The change is straighforward, function declaration is now handled like DefClass, no function scope search is performed.
The only expected change was I had to simplify one test case for dead code elimination which assumed functions are function scoped.
It might be nice to add some test for the case fixed by this, the only test I can think of is parsing AST, peforming
figure_out_scope()
and checking if symbol scopes match expectation.