Skip to content

runtime/object-model: implement strict and sloppy function this binding #3576

Closed

Description

@andrewtdiz

Summary

Sloppy calls box primitive/nullish this as required and strict calls preserve exact this values; direct and .call forms agree with ECMAScript.

Affected Test262 rows

Perry actual behavior from report

  • 4 x runtime-fail: Uncaught exception: 'this' had incorrect value!
  • 2 x runtime-fail: Uncaught exception: f() Expected SameValue(«"undefined"», «"undefined"») to be false
  • 1 x runtime-fail: Uncaught exception: bar.call("1") Expected SameValue(«"string"», «"object"») to be true
  • 1 x runtime-fail: Uncaught exception: bar.call() Expected SameValue(«"undefined"», «"object"») to be true
  • 1 x runtime-fail: Uncaught exception: bar.call(1) Expected SameValue(«"number"», «"object"») to be true

Node / ECMAScript expected behavior

Sloppy calls box primitive/nullish this as required and strict calls preserve exact this values; direct and .call forms agree with ECMAScript.

Likely subsystem

function call dispatch / this-binding conversion

Acceptance criteria

  • bar.call(1), bar.call("1"), and bar.call() rows return the expected typeof this.
  • Generator/strict variants no longer report incorrect this.
  • Direct function calls match strict/sloppy this rules.

Validation

Run the Test262 differential against the affected area and confirm these rows leave the gap report:

scripts/test262_subset.py --root vendor/test262 --dir language/function-code --sample-cap 99999 --report /tmp/FUNC_THIS_BINDING.json

This is Perry language/runtime semantics triage, not a Node core API compatibility issue.

Activity

  1. andrewtdiz commented on Jun 3, 2026

    @andrewtdiz
    ContributorAuthor

    Current main appears to have this strict/sloppy this binding slice covered by the existing focused regression.

    Validation:

    • base: 555e75715 (current origin/main in my checkout)
    • target/release/perry --version -> perry 0.5.1112
    • target/release/perry test-files/test_issue_3586_function_this_call.ts compiled successfully
    • ./test_issue_3586_function_this_call -> issue-3586-ok

    That regression covers the issue's main acceptance cases: strict .call(1) preserves primitive this, sloppy .call(1) boxes, sloppy direct call gets global this, first-class Function.prototype.call/apply, strict/sloppy replacement callback receivers, primitive getter boxing, and Function-constructor sloppy body behavior.

    I could not run the exact Test262 command because vendor/test262 is not present in this checkout. Based on the local regression and the closed #3586 epic, this looks stale-open and ready for maintainer closure rather than a new PR.

  2. proggeramlug commented on Jun 13, 2026

    @proggeramlug
    Contributor

    Verified on current main (macOS arm64): every this-binding behavior this issue lists now matches node --experimental-strip-types byte-for-byte:

    form Perry Node
    sloppy bar.call(1) object object
    sloppy bar.call("1") object object
    sloppy bar.call(true) object object
    sloppy bar.call() / .call(null) / .call(undefined) object object
    strict sbar.call(1) number number
    strict sbar.call() undefined undefined
    direct sloppy f() object (globalThis) object
    direct strict fs() undefined undefined
    generator sloppy/strict g.call(5) object / number same
    bare extracted method bare() object object

    So sloppy primitive/nullish this boxing and strict exact-this preservation are both implemented across direct, .call, generator, and method-extraction forms. I couldn't run the exact scripts/test262_subset.py differential here (it needs an external --root TC39 corpus checkout, not vendored in-repo), so I'm not closing this — but the documented rows appear resolved. Suggest a differential run to confirm the 10.4.3-1-* rows leave the gap report, then close.

  3. proggeramlug commented on Jun 13, 2026

    @proggeramlug
    Contributor

    Verified resolved via the actual TC39 test262 differential (corpus pinned @ 4249661388, the repo's own pinned SHA):

    python3 scripts/test262_subset.py --root <test262> --dir language/function-code --sample-cap 99999
    → language/function-code  pass=213  diff=0  rt-fail=4  compile-fail=0  parity=98.2%
    

    All 9 rows this issue lists pass — 10.4.3-1-1-s, -2-s, -3-s, -13-s, -15-s, -100gs, -102gs, -13gs, -15gs are present in the corpus and none appear in the result's failure set. The only 4 remaining function-code runtime-fails are a different category (10.4.3-1-64gs/-65gs/-83gs/-84gs — negative tests where Node rejects but Perry runs clean, unrelated to this-binding).

    Independently confirmed byte-for-byte against node --experimental-strip-types: sloppy primitive/nullish this boxing (bar.call(1)→object, .call("1"), .call(true), .call()/null/undefined→object) and strict exact-this preservation (.call(1)→number, .call()→undefined), across direct, .call, generator, and extracted-method forms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions