Skip to content
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] fix safari syntax error - declare twice #1851

Merged
merged 3 commits into from
May 11, 2017

Conversation

Perlmint
Copy link

fix issue #1753
by enclosing variables of parent scope, avoid safari error.

lib/scope.js Outdated
@@ -122,6 +122,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
scope.uses_eval = save_scope.uses_eval;
scope.directives = save_scope.directives;
}
// issue 1753 - name mangling in for loop scope should consider parent scope
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mention:

// Safari/Webkit bug workaround - loop init let variable shadowing argument.

For bonus points if you have the webkit bug URL, please add it here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of "issue 1753" please include complete URL of issue:

// https://github.com/mishoo/UglifyJS2/issues/1753

@kzc
Copy link
Contributor

kzc commented Apr 28, 2017

With this PR:

$ echo 'function f(h){for(let i of a) for(let j of b) console.log(i,j); console.log(h)}' | bin/uglifyjs -b bracketize -m
function f(o) {
    for (let n of a) {
        for (let o of b) {
            console.log(n, o);
        }
    }
    console.log(o);
}
$ echo 'class C{f(h,a,b){for(let i of a)for(let j of b)console.log(i,j);console.log(h)}}' | bin/uglifyjs -b bracketize -m
class C {
     f(o, n, l) {
        for (let c of n) {
            for (let o of l) {
                console.log(c, o);
            }
        }
        console.log(o);
    }
}

let o shadows the argument o in cases above - will they run without error in buggy Safari 10?

@alexlamsl
Copy link
Collaborator

alexlamsl commented Apr 28, 2017

If I understand correctly, this PR:

  • alters the behaviour of mangle indiscriminately
  • aims to fix an issue that only applies to Safari

I'd prefer this special workaround to be gated by a new flag similar ie8, plus limiting the code changes to mangle_names() if possible. I don't think the current changes to AST_Scope.variables are correct and/or safe, as you will end up with the same SymbolDef instance being interpreted as belonging to multiple AST_Scope (and since compress also relies on figure_out_scope(), this won't end well...)

Please refer to the existing ie8 code which works around catch(e) scope-related issues.

@kzc
Copy link
Contributor

kzc commented Apr 28, 2017

@alexlamsl I have the same concerns.

Also, I don't see any code specific to function arguments and let/const variables which is apparently the source of the problem.

I'd also like to see a reference to the Safari/WebKit bug to understand the true nature of the issue. The Safari/WebKit bug does not appear to be well understood.

@Perlmint Perlmint changed the title [ES6] fix safari syntax error - declare twice [WIP] [ES6] fix safari syntax error - declare twice May 1, 2017
@Perlmint Perlmint force-pushed the harmony branch 3 times, most recently from 7e16b9c to 0d3f94c Compare May 3, 2017 15:21
@Perlmint
Copy link
Author

Perlmint commented May 3, 2017

First of all, thanks to fast and specific reviews.

@kzc I edited comments like you wrote. Codes you wrote doesn't occur error on Safari10(OSX).

@alexlamsl I tried to deal with this issue by editing only mangle_names, but I can't find way to know whether AST_Scope is created from For like nodes - For, ForIn, ForOf.
Also, if I understand correctly, already SymbolRef is shared by multiple scopes, mark_enclosed function can push same SymbolRef instance into scopes. so, I think it could not be the problem.

I just added flag support_safari10, logic is not changed from previous my commits.

@Perlmint Perlmint changed the title [WIP] [ES6] fix safari syntax error - declare twice [ES6] fix safari syntax error - declare twice May 3, 2017
@alexlamsl
Copy link
Collaborator

Putting aside where the logic should reside, the latest changes still doesn't look right because AST_Scope.variables would not have been fully populated when you push to scope.enclosed. You are exactly within the TreeWalker where we in the process of populating that array.

Also note that harmony is currently still on 2.x branch, and will soon merge with master after 3.0.0 is released. In the latest version, screw_ie8 is renamed simply to ie8, so you can use safari10 as the flag name instead. bin/uglifyjs also underwent a major overhaul.

@kzc
Copy link
Contributor

kzc commented May 3, 2017

@Perlmint Thanks for including the Webkit bug link:

https://bugs.webkit.org/show_bug.cgi?id=171041

@fractalzombie
Copy link

Want this PR

@Perlmint
Copy link
Author

Perlmint commented May 10, 2017

@alexlamsl Thanks to precise comment. I just committed fixed one.

I'm sorry to late response. but My macbook was broken, I couldn't work fewdays.

@alexlamsl
Copy link
Collaborator

@Perlmint no worries and take your time 😉

I think you need to rebase this PR before pushing those changes, as it is currently still based on 2.x and I can't merge at all due to conflicts.

lib/compress.js Outdated
pure_getters : !false_by_default && "strict",
pure_funcs : null,
reduce_vars : !false_by_default,
support_safari10 : true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change support_safari10 to safari10.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and revert all the row formatting of options unrelated to this PR.

lib/scope.js Outdated
// pass 4: add symbol definitions to loop scopes
// Safari/Webkit bug workaround - loop init let variable shadowing argument.
// https://github.com/mishoo/UglifyJS2/issues/1753
// related webkit bug ticket: https://bugs.webkit.org/show_bug.cgi?id=171041
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change line above to simply:

    // https://bugs.webkit.org/show_bug.cgi?id=171041

@Perlmint
Copy link
Author

@alexlamsl rebased.
@kzc Thanks a lot. I missed those points.

bin/uglifyjs Outdated
@@ -35,6 +35,7 @@ program.option("--comments [filter]", "Preserve copyright comments in the output
program.option("--config-file <file>", "Read minify() options from JSON file.");
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
program.option("--ie8", "Support non-standard Internet Explorer 8.");
program.option("--no-safari10", "Do not support Safari 10 by not applying patch about For loop related scope issue.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--safari10?

lib/minify.js Outdated
@@ -17,7 +17,7 @@ function read_source_map(code) {
}

function set_shorthand(name, options, keys) {
if (options[name]) {
if (options[name] != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for changing this?

lib/minify.js Outdated
@@ -36,6 +36,7 @@ function minify(files, options) {
options = defaults(options, {
compress: {},
ie8: false,
safari10: true,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default should be false, just like ie8. These are special case options, so don't force it upon everyone.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just thought that safari10 option should be enabled default because it is latest version. so I made changes -you commented previous- in same reason.
if default should be false, other changes are also not needed.
I'll change it to false

lib/minify.js Outdated
@@ -46,6 +47,7 @@ function minify(files, options) {
wrap: false,
}, true);
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("safari10", options, [ "mangle" ]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, why should it even be a toplevel option, if only mangle is affected?

Just keep it under options.mangle.safari10 for now. If it turns out Safari becomes the next IE6 in terms of quirks, we can add a toplevel option then. Right now it just add noise to the global namespace.

lib/scope.js Outdated
// Safari/Webkit bug workaround - loop init let variable shadowing argument.
// https://github.com/mishoo/UglifyJS2/issues/1753
// related webkit bug ticket: https://bugs.webkit.org/show_bug.cgi?id=171041
if (node instanceof AST_For ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap this if with:

    if (options.safari10) {
...
    }

lib/scope.js Outdated
@@ -122,6 +124,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
scope.uses_eval = save_scope.uses_eval;
scope.directives = save_scope.directives;
}
// Safari/Webkit bug workaround - loop init let variable shadowing argument.
// https://github.com/mishoo/UglifyJS2/issues/1753
// related webkit bug ticket: https://bugs.webkit.org/show_bug.cgi?id=171041
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this 3 line comment here as it's repeated below.

@Perlmint Perlmint force-pushed the harmony branch 2 times, most recently from cbd14d6 to 9621b0c Compare May 11, 2017 05:45
@alexlamsl
Copy link
Collaborator

alexlamsl commented May 11, 2017

@Perlmint so given the scope of this workaround, I think we don't need any new toplevel options at all.

Which means no changes required for bin/uglifyjs or lib/minify.js - it is just a new option within mangle, so you can already access this via uglify-js -m safari10 and minify(code, {mangle{safari10: true}})

Edit: sorry I missed your latest commits - it looks fine now 👍

@alexlamsl
Copy link
Collaborator

@Perlmint LGTM - waiting on greenlights from @kzc and Travis CI.

Thanks!

@kzc
Copy link
Contributor

kzc commented May 11, 2017

Even without understanding the fine points, all the code is behind options.safari10 with the flag defaulted to false, so there's no risk.

LGTM

Copy link
Collaborator

@alexlamsl alexlamsl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two more minor nits, and we are good to go!

lib/minify.js Outdated
@@ -54,6 +54,7 @@ function minify(files, options) {
cache: null,
eval: false,
ie8: false,
safari10: false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this down above toplevel so as to keep this section in alphabetical order?

lib/scope.js Outdated
@@ -102,6 +102,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, {
cache: null,
ie8: false,
safari10: false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comma here so as to minimise diff in future expansions.

@Perlmint
Copy link
Author

@alexlamsl fixed! Thanks.

lib/minify.js Outdated
@@ -58,6 +58,7 @@ function minify(files, options) {
properties: false,
reserved: [],
toplevel: false,
safari10: false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above, not below... 😅

Between reserved and toplevel.

Copy link
Author

@Perlmint Perlmint May 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... sorry. I'll fix it now.

lib/scope.js Outdated
if (options.safari10) {
if (node instanceof AST_For ||
node instanceof AST_ForIn ||
node instanceof AST_ForOf) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since:

var AST_ForOf = DEFNODE("ForOf", null, {
    $documentation: "A `for ... of` statement",
}, AST_ForIn);

you can remove the AST_ForOf condition.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and the if can be a single line.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean, suggest merging two if statement?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer keeping the if (options.safari10) separate to better show that code is definitely not run if the option is not in effect without any thinking.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a big fan of not thinking.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Perlmint so I think @kzc meant:

if (options.safari10) {
    if (node instanceof AST_For || node instanceof AST_ForIn) {
...

@kzc
Copy link
Contributor

kzc commented May 11, 2017

@Perlmint Thank you for contributing this patch and your patience in this review exercise.

fix mishoo#1753.
to mangle names properly - to avoid safari error, scope of for loop should enclose parent scope variables
@Perlmint
Copy link
Author

@kzc looks better. Thanks.

@alexlamsl alexlamsl merged commit fcd90db into mishoo:harmony May 11, 2017
@snuggs
Copy link

snuggs commented Aug 22, 2017

@alexlamsl @kzc @Perlmint quick question guys..

  1. How do I enable from CLI?
  2. Do you know why minify seems to be running twice per iteration? (I have 2 uglifyjs calls 4 outputs when console.logging from lib/minify.js
  3. Should I still be getting same let e issue on Safari after using safari10? Please see image
    capture d ecran 2017-08-22 a 12 55 43
    below.

@kzc
Copy link
Contributor

kzc commented Aug 22, 2017

$ cat t1753.js

        class SomeClass {
            constructor(props) {
                let pickedSets = [];
                for (let i = 0; i < 6; i++) {
                    pickedSets.push({
                        mainDrawNumbers: [],
                        extraDrawNumbers: []
                    });
                }
            }
        }
$ bin/uglifyjs -V
uglify-es 3.0.28

The Safari 10 loop bug workaround is disabled by default:

$ bin/uglifyjs t1753.js -m safari10=false -b
class SomeClass {
    constructor(r) {
        let s = [];
        for (let r = 0; r < 6; r++) {
            s.push({
                mainDrawNumbers: [],
                extraDrawNumbers: []
            });
        }
    }
}

Safari 10 loop bug workaround enabled:

$ bin/uglifyjs t1753.js -m safari10=true -b
class SomeClass {
    constructor(r) {
        let s = [];
        for (let a = 0; a < 6; a++) {
            s.push({
                mainDrawNumbers: [],
                extraDrawNumbers: []
            });
        }
    }
}

Do you know why minify seems to be running twice per iteration?

minify also parses command line options in bin/uglifyjs among other things.

blowery added a commit to Automattic/wp-calypso that referenced this pull request Jul 14, 2018
blowery added a commit to Automattic/wp-calypso that referenced this pull request Jul 15, 2018
* Build: Add work-around for safari 10 let bug

See mishoo/UglifyJS#1851

* Add the mangle option to the right minifier. Remove the redundant one.

* change the opt extension to min to cache break the uglify change
@nylen
Copy link

nylen commented Jul 15, 2018

Hi everyone, can I ask why this option was not enabled by default? ie8 is one thing, but safari10 is still a "modern" browser, and this is a pretty terrible bug to run into and debug, since for most of us it is only going to appear in production and only with certain builds.

@nvcken
Copy link

nvcken commented Sep 12, 2018

is this fix apply on master branch ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants