-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Fix PropertySlot assertion by stripping static hash table attribute bits #28335
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
Open
robobun
wants to merge
7
commits into
main
Choose a base branch
from
farm/2e6b20d2/fix-property-slot-attributes
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
8476e0e
Strip static hash table attribute bits before storing in PropertySlot…
robobun 6e085da
Add regression test for spyOn on static hash table properties
robobun 2834d49
[autofix.ci] apply automated fixes
autofix-ci[bot] 72de067
Add stderr assertions to test
robobun fb78cf3
Use test.concurrent for independent subprocess tests
robobun e56ad3b
Simplify test to use bun:test spyOn directly instead of subprocess
robobun b35c001
[autofix.ci] apply automated fixes
autofix-ci[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { expect, spyOn, test } from "bun:test"; | ||
|
|
||
| test("spyOn works on static hash table function properties", () => { | ||
| const spy = spyOn(Bun, "gc"); | ||
| try { | ||
| Bun.gc(true); | ||
| expect(spy).toHaveBeenCalledTimes(1); | ||
| } finally { | ||
| spy.mockRestore(); | ||
| } | ||
| }); | ||
|
|
||
| test("spyOn preserves correct attributes after mockRestore", () => { | ||
| const spy = spyOn(Bun, "peek"); | ||
| spy.mockRestore(); | ||
| const p = Promise.resolve(42); | ||
| expect(Bun.peek(p)).toBe(42); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 The inner
if (hasValue)on line 1536 ofJSMockFunction.cppis always true and is dead code - the outerif (hasValue && ...)condition already guaranteeshasValueis truthy before reaching it. This is a pre-existing issue; the PR correctly updated the assignment inside that guard fromslot.attributes()toattributesForStructure(slot.attributes()), but left the redundant guard in place.Extended reasoning...
In
JSMock__jsSpyOn(JSMockFunction.cpp), the spying logic for callable values contains a doubly-nested hasValue check. The outer condition is: if (hasValue && ((slot.attributes() & PropertyAttribute::Function) != 0 || (value.isCell() && value.isCallable()))). Inside that block there is another: if (hasValue) attributes = attributesForStructure(slot.attributes());Why the inner check is always true: C++ short-circuit evaluation means the && operator in the outer condition only evaluates the right-hand side when hasValue is truthy. Therefore, any code inside the outer if block already has hasValue == true as a guaranteed precondition. The inner if (hasValue) can never be false when reached.
Code path that triggers it: Any call to spyOn() where the target property exists (hasValue = true) and the property is either flagged as Function in the static hash table or holds a callable value (e.g., spyOn(Bun, "gc")) will enter the outer branch and then unconditionally execute the inner assignment.
Why existing code does not prevent it: The redundant guard predates this PR. The PR only changed the RHS of the assignment from slot.attributes() to attributesForStructure(slot.attributes()) - a correct and necessary fix - while preserving the existing (redundant) if (hasValue) wrapper structure.
Impact: At runtime this is completely harmless: attributes is always assigned attributesForStructure(slot.attributes()) when the outer condition holds. However, the dead inner guard is misleading - a future maintainer refactoring the outer condition (e.g., separating the hasValue check) might incorrectly rely on the inner if as a safety net, leading to a latent logic error where attributes silently stays at 0 when it should be set.
Step-by-step proof:
Fix: Simply remove the inner if (hasValue) guard, leaving the assignment as an unconditional statement within the outer if block.