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

fix multiple nested function substitutions #2458

Merged
merged 1 commit into from
Nov 9, 2017

Conversation

alexlamsl
Copy link
Collaborator

fixes #2449

@alexlamsl
Copy link
Collaborator Author

@kzc so one thing I thought was safe back in #2427 which turns out not to be the case:

function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
    // ...
    return directives.sort(byPriority);
}
function byPriority(a, b) {
    // ...
}

i.e. byPriority cannot be inlined.

@alexlamsl alexlamsl merged commit 1127a2c into mishoo:master Nov 9, 2017
@alexlamsl alexlamsl deleted the issue-2449 branch November 9, 2017 15:30
@kzc
Copy link
Contributor

kzc commented Nov 9, 2017

byPriority cannot be inlined.

That's surprising to me. Clearly my understanding of closures is wrong.

What heuristic do you now use to replace AST_Defun with AST_Function? (In words rather than code).

@alexlamsl
Copy link
Collaborator Author

This subtle difference from the example above could be substituted without issue:

function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
    // ...
    return directives.sort(byPriority);
    function byPriority(a, b) {
        // ...
    }
}

The reason should be clear when you consider multiple invocations of collectDirectives().

What heuristic do you now use to replace AST_Defun with AST_Function? (In words rather than code).

(function ↔️ AST_Defun, reference ↔️ AST_SymbolRef)

Common criteria:

  • reduce_vars & unused are enabled
  • no eval or with
  • single reference
  • function to reference does not cross any loop boundaries

In addition, if function to reference crosses any scope boundaries:

  • reference does not escape its local scope, i.e. no return f or grab(f) etc.
  • reference does not reside within function (recursive reference)
  • function does not contain any out-of-scope variable references that would conflict with reference's scope

@kzc
Copy link
Contributor

kzc commented Nov 9, 2017

Wow. I have to do some reading on closures.

How confident are you with the current state of this function symbol substitution logic?

@kzc
Copy link
Contributor

kzc commented Nov 9, 2017

function does not contain any out-of-scope variable references that would conflict with reference's scope

That's not immediately obvious and certainly worthy of a comment in the code. Could you point to the place in the code where that is now determined?

@alexlamsl
Copy link
Collaborator Author

How confident are you with the current state of this function symbol substitution logic?

Fairly so.

That's not immediately obvious and certainly worthy of a comment in the code. Could you point to the place in the code where that is now determined?

That would be AST_Lambda.is_constant_expression(scope):
https://github.com/mishoo/UglifyJS2/blob/1127a2caf305ad1df370c34faf7283e53423cc10/lib/compress.js#L2192-L2214

@kzc
Copy link
Contributor

kzc commented Nov 9, 2017

That would be AST_Lambda.is_constant_expression

That's an interesting place for it. So a constant lambda is one that does not have any out of scope references. That makes sense.

Could the fuzzer technically generate test cases for this in its present form?

Are there any implications for harmony block scopes within functions? I guess it'd just have AST_Lambda.is_constant_expression() report false in such cases which would mean no substitution would take place. So be it.

@alexlamsl
Copy link
Collaborator Author

Could the fuzzer technically generate test cases for this in its present form?

I'd think so, seeing that we are getting [Function XXXXXX] in the output, i.e. AST_Lambda.names are being supplied to other expressions.

Are there any implications for harmony block scopes within functions?

We utilise AST_Scope.enclosed array here to detect if a reference is going out of scope, so block-scoped variables should not hinder anything here if AST_Toplevel.figure_out_scope() is doing its job correctly.

@th317erd
Copy link

th317erd commented Nov 10, 2017

Any idea when this will be published? This is affecting me as well

@kzc
Copy link
Contributor

kzc commented Nov 10, 2017

workaround until next release: -c reduce_vars=false

@JumalDB
Copy link

JumalDB commented Nov 10, 2017

Can you release a new version with this fix? When it will be released?

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.

bug with nested cross-scope function substitutions
4 participants