fix crash in unsafe replacement of undefined#1443
Conversation
| function n(n) { | ||
| return n * n; | ||
| } | ||
| return r === n; |
There was a problem hiding this comment.
How does it arrive at this result?
typeof n is "function"
"function" == "undefined" is false
There was a problem hiding this comment.
What optimization does the triple equals come from?
There was a problem hiding this comment.
This is utilising the rule typeof a == "undefined" ➡️ a === undefined, specifically this line turns == into ===.
evaluate is not enabled in this test case, so typeof function(){} wouldn't get optimised any further.
There was a problem hiding this comment.
In light of the fact that undefined is a pseudo variable that can be overridden in the parameter of f(undefined) is this expected result just accidentally correct because a local function will not triple equal anything passed into the outer function f? Had n in the original input code been an AST_Constant it could produce incorrect results.
I think this why that specific transform you mention above is in unsafe. It assumes that undefined is not overridden.
There was a problem hiding this comment.
(I'm little confused by your comment, so please bear with me...)
I added this test to ensure my removal of ref.refernce(); call within compress.js would not cause #1431 to resurface in the form of this unsafe optimisation on undefined.
The reason why I use the roundabout way of typeof n == "undefined" instead of plain undefined is to make sure I end up with AST_Undefined instead of the local variable by that same name (which can be verified by running the code snippet against master and see the program crash).
Not sure if I've answered all/any of your questions? 😅
There was a problem hiding this comment.
The problem I'm describing is related to the test itself and the unsafe typeof transform you cited above. undefined is an overridden variable that is no longer equivalent to void 0. The test in this PR accidentally produces correct output because the local function n will not triple equal anything passed as an argument to f().
Here's a concrete example of why the unsafe typeof transform is incorrect when undefined is overridden:
$ cat u.js
function f(undefined) {
return function() {
var n = 1;
return typeof n == "undefined";
};
}
console.log( f(1)() );
expected result:
$ node u.js
false
incorrect code generated:
$ uglifyjs u.js -b -c unsafe
function f(undefined) {
return function() {
var n = 1;
return undefined === n;
};
}
console.log(f(1)());
incorrect actual result:
$ uglifyjs u.js -b -c unsafe | node
true
|
@kzc would the following tests more preferable? The difference here is they don't rely on unsafe_undefined: {
options = {
if_return: true,
unsafe: true
}
mangle = {}
input: {
function f(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
}
}
}
expect: {
function f(n) {
return function() {
if (a)
return b;
if (c)
return d;
else
return n;
}
}
}
}
keep_fnames: {
options = {
if_return: true,
unsafe: true
}
mangle = {
keep_fnames: true
}
input: {
function f(undefined) {
return function() {
function n(a) {
return a*a;
}
if (a)
return b;
if (c)
return d;
}
}
}
expect: {
function f(r) {
return function() {
function n(n) {
return n*n;
}
if (a)
return b;
if (c)
return d;
else
return r;
}
}
}
} |
|
That would be preferable since the previous expected code is not valid as explained here: #1443 (comment) The test you had just happened to illustrate why the typeof transform was deemed to be |
9518452 to
fe0d0de
Compare
|
@kzc tests updated and pushed. These new tests should still be vulnerable to the same problem, i.e. I use this particular transformation in |
Redefining If the generated code is known to be incorrect - even with the An issue for another PR: Is there a way to fix this |
|
Will add comment after 🎥 🍿 |
fe0d0de to
6380b13
Compare
|
@kzc comment added As for your suggestion for that new PR, is the goal being able to apply If so, one suggestion would be to "graduate" the |
|
If I'm reading the history correctly, So I propose swapping |
| @@ -0,0 +1,68 @@ | |||
| // these tests assume local variable "undefined" has undefined as value | |||
There was a problem hiding this comment.
Could this be rephrased as:
// tests assume that variable `undefined` not redefined and has `void 0` as value
Also, please put an empty line after this comment.
|
I was not aware that #105 was the motivation behind the "unsafe" flag use in the In the wild |
remove extraneous call to AST_SymbolRef.reference()
6380b13 to
60899d5
Compare
There is a call to
AST_SymbolRef.reference()withincompress.js, which is the optimisation rule of mappingundefinedto variable of the same name, guarded underunsafeflag. As it does not pass in any mangleoptionsobject, this results inTypeError: Unable to get property 'keep_fnames' of undefined or null reference.AST_SymbolRef.reference()is used to populateAST_Scope.enclosedarray (alsoAST_SymbolRef.frameinteger, but seems unused), which is only used by mangler.Now in both command-line and API use cases, call to
mangle_names()is preceded by a call tofigure_out_scope(<mangle options>), so there is no need for code withincompress.jsto callAST_SymbolRef.reference()at all.