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 function hoisting behaviour (non-strict mode) #1547

Open
wants to merge 7 commits into
base: static_h
Choose a base branch
from

Conversation

trossimel-sc
Copy link

Summary

Addresses #1455

Hermes currently has divergent function hoisting behavior compared to QuickJS, V8, and JSC in non-strict mode. The following code prints FAIL instead of SUCCESS

{
    function promotedFunction(endRecursion) {
        if(endRecursion) {
            promotedFunction(true);
            return;
        }
        print ('SUCCESS');
    }

    callback = promotedFunction;
}

{
    function promotedFunction(endRecursion) {
        if(endRecursion) {
            promotedFunction(true);
            return;
        }
        print ('FAIL');
    }
}

callback(false);

This happens as scoped function promoted to global scope are treated as global functions, like for var.
To fix this behaviour:

  • Two declarations are created for a promoted identifier instead of one: a global and a scoped declaration, stored in sideIdentifierDeclarationDecl_ and promotedFunctionDecls_
  • If the identifier is promoted, we call emitStore on both pointers associated to the declarations

Test Plan

  • Added a unit test with the failing code

Hermes currently has divergent function hoisting behavior compared to QuickJS, V8, and JSC in non-strict mode. The following code prints Error1; Error1 instead of Error; Error1

```
{
    function test1(a) {
        if(a == "a") {
            test1("b");
            return;
        }
        print ('Error');
    }

    callback1 = test1;
}

{
    function test1(a) {
        if(a == "a") {
            test1("b");
            return;
        }
        print ('Error1');
    }

    callback2 = test1;
}

callback1("a");
callback2("a");
```

This happens as scoped function promoted to global scope are treated as global function, like for `var`.
New approach:
- Creating two declaration associated with each promoted function identifier and storing the function code into two pointers, one global and one scoped. Each declaration has one pointer associated.
- Each identifier which is not a declaration is resolved into either the global or scoped declaration of the promoted function

Test 262 results:
- 100% on test/statements, 98.53% overall. **Nothing changed in the results** before and after the change.

---
@facebook-github-bot
Copy link
Contributor

Hi @trossimel-sc!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

@trossimel-sc
Copy link
Author

Seems like the failing errors are due to auto generated checks

@avp
Copy link
Contributor

avp commented Oct 24, 2024

@trossimel-sc The update-lit build target will update the tests that are autogenerated, then check-hermes to run them. LIT_FILTER will let you narrow down what exactly gets updated or run as well. Thanks for submitting this fix!

@tmikov
Copy link
Contributor

tmikov commented Nov 1, 2024

Hi, we really need the CLA before we can review it internally. Thanks!

@facebook-github-bot facebook-github-bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label Jan 17, 2025
@trossimel-sc
Copy link
Author

The CLA is signed and we can proceed with the review process 😄 .
We can start with this PR and for await of PR.
I also noticed the build is failing on static_h. Is this happening to me only?

Copy link
Contributor

@avp avp left a comment

Choose a reason for hiding this comment

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

Thanks!

Overall this seems like a reasonable solution to me, just one question/clarification on maintaining the binding table.

if (semCtx_.getDeclarationDecl(ident) &&
functionContext()->promotedFuncDecls.count(ident->_name)) {
decl = semCtx_.newDeclInScope(ident->_name, kind, curScope_);
bindingTable_.put(ident->_name, Binding{decl, ident});
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs a comment explaining what the special case is doing.

e.g. Why is it using put to override instead of try_emplace as below?

/// Resolve the identifier node associated with the promoted function
/// to the corresponding global variable in expression context.
Value *resolveIdentifierPromoted(ESTree::IdentifierNode *id) {
auto declPromoted = getIDDeclPromoted(id);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
auto declPromoted = getIDDeclPromoted(id);
auto *declPromoted = getIDDeclPromoted(id);

@avp
Copy link
Contributor

avp commented Jan 23, 2025

I also noticed the build is failing on static_h.

That should be fixed with ae18690

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Do not delete this pull request or issue due to inactivity.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants