Repository navigation
runtime/object-model: implement strict and sloppy function this binding #3576
Copy link
Copy link
Closed
Description
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
- Windows: 5 styling FFI stubs awaiting custom paint pass (#185 follow-up) #210 language/function-code/10.4.3-1-1-s.js (runtime-fail: Uncaught exception: bar.call(1) Expected SameValue(«"number"», «"object"») to be true)
- Class method body cannot capture enclosing-function locals #212 language/function-code/10.4.3-1-100gs.js (runtime-fail: Uncaught exception: 'this' had incorrect value!)
- String-array element access: arr[i] SIGSEGVs and indexOf returns -1 #214 language/function-code/10.4.3-1-102gs.js (runtime-fail: Uncaught exception: 'this' had incorrect value!)
- doc-tests: wasm_snippets.ts banner should constrain to
// targets: wasm— currently fails to link on every native target #219 language/function-code/10.4.3-1-13-s.js (runtime-fail: Uncaught exception: f() Expected SameValue(«"undefined"», «"undefined"») to be false) - Windows:
whoamicrate'sGetUserNameExWlink fails on every doc-test (missingsecur32import lib) #220 language/function-code/10.4.3-1-13gs.js (runtime-fail: Uncaught exception: 'this' had incorrect value!) - Codegen: arr[i]= writes silently dropped on module-level const arrays declared empty #221 language/function-code/10.4.3-1-15gs.js (runtime-fail: Uncaught exception: 'this' had incorrect value!)
- Codegen: FFI returns:"i64" treats string-pointer returns as Number; auto-detect to string is name-based #222 language/function-code/10.4.3-1-15-s.js (runtime-fail: Uncaught exception: f() Expected SameValue(«"undefined"», «"undefined"») to be false)
- fs.appendFileSync writes 0 bytes on native target — HIR variant has no LLVM codegen arm #226 language/function-code/10.4.3-1-2-s.js (runtime-fail: Uncaught exception: bar.call("1") Expected SameValue(«"string"», «"object"») to be true)
- perry/updater: bind version into signed payload to prevent old-binary replay #229 language/function-code/10.4.3-1-3-s.js (runtime-fail: Uncaught exception: bar.call() Expected SameValue(«"undefined"», «"object"») to be true)
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"), andbar.call()rows return the expectedtypeof this.- Generator/strict variants no longer report incorrect
this. - Direct function calls match strict/sloppy
thisrules.
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.jsonThis is Perry language/runtime semantics triage, not a Node core API compatibility issue.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
andrewtdiz commentedon Jun 3, 2026
Current main appears to have this strict/sloppy
thisbinding slice covered by the existing focused regression.Validation:
555e75715(currentorigin/mainin my checkout)target/release/perry --version->perry 0.5.1112target/release/perry test-files/test_issue_3586_function_this_call.tscompiled successfully./test_issue_3586_function_this_call->issue-3586-okThat regression covers the issue's main acceptance cases: strict
.call(1)preserves primitivethis, sloppy.call(1)boxes, sloppy direct call gets globalthis, first-classFunction.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/test262is 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.fix(runtime,hir): object-model semantics — Object boxing, this-bindin…
fix(runtime,hir): object-model semantics — Object boxing, this-bindin…
fix(runtime,hir): object-model semantics — Object boxing, this-bindin…
proggeramlug commentedon Jun 13, 2026
Verified on current
main(macOS arm64): everythis-binding behavior this issue lists now matchesnode --experimental-strip-typesbyte-for-byte:bar.call(1)objectobjectbar.call("1")objectobjectbar.call(true)objectobjectbar.call()/.call(null)/.call(undefined)objectobjectsbar.call(1)numbernumbersbar.call()undefinedundefinedf()object(globalThis)objectfs()undefinedundefinedg.call(5)object/numberbare()objectobjectSo sloppy primitive/nullish
thisboxing and strict exact-thispreservation are both implemented across direct,.call, generator, and method-extraction forms. I couldn't run the exactscripts/test262_subset.pydifferential here (it needs an external--rootTC39 corpus checkout, not vendored in-repo), so I'm not closing this — but the documented rows appear resolved. Suggest a differential run to confirm the10.4.3-1-*rows leave the gap report, then close.proggeramlug commentedon Jun 13, 2026
Verified resolved via the actual TC39 test262 differential (corpus pinned @ 4249661388, the repo's own pinned SHA):
All 9 rows this issue lists pass —
10.4.3-1-1-s,-2-s,-3-s,-13-s,-15-s,-100gs,-102gs,-13gs,-15gsare present in the corpus and none appear in the result's failure set. The only 4 remainingfunction-coderuntime-fails are a different category (10.4.3-1-64gs/-65gs/-83gs/-84gs— negative tests where Node rejects but Perry runs clean, unrelated tothis-binding).Independently confirmed byte-for-byte against
node --experimental-strip-types: sloppy primitive/nullishthisboxing (bar.call(1)→object,.call("1"),.call(true),.call()/null/undefined→object) and strict exact-thispreservation (.call(1)→number,.call()→undefined), across direct,.call, generator, and extracted-method forms.fix(json): stop dereferencing positive subnormal doubles as heap poin…