napi: propagate Proxy trap exceptions from napi_get_all_property_names descriptor filter - #32263
napi: propagate Proxy trap exceptions from napi_get_all_property_names descriptor filter#32263robobun wants to merge 2 commits into
Conversation
|
Updated 1:27 AM PT - Jul 15th, 2026
❌ @robobun, your commit 9540af6 has 2 failures in
🧪 To try this PR locally: bunx bun-pr 32263That installs a local version of the PR into your bun-32263 --bun |
Walkthrough
ChangesProxy trap exception handling in
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/napi/napi-app/module.js`:
- Around line 962-985: The test_get_all_property_names_throwing_proxy function
currently only exercises the path where getOwnPropertyDescriptor throws, but the
PR fix adds exception checks after both getOwnPropertyDescriptor and
getPrototypeOf calls. Add a second Proxy test case named throwingPrototype that
has a working ownKeys and getOwnPropertyDescriptor but throws in the
getPrototypeOf trap to verify the prototype chain walking path is also fixed.
Add two test calls for this new throwingPrototype proxy (one with key filter
mode 1 for napi_key_own_only and one with mode 0 for
napi_key_include_prototypes) to ensure both property name collection modes
properly handle getPrototypeOf exceptions.
In `@test/napi/napi.test.ts`:
- Around line 931-938: In the test "returns napi_pending_exception when a Proxy
trap throws during descriptor filtering" within the describe block
"napi_get_all_property_names", verify whether the corresponding module.js test
fixture has been expanded to include a getPrototypeOf throwing case. If it has
been added, include an additional expect assertion to verify the exception
message for the getPrototypeOf trap throwing scenario. Additionally, replace the
individual expect calls checking for status lines with a more robust assertion
that filters the output for all status lines and verifies the exact count
matches the number of test cases being run, to catch unexpected output changes
and ensure the test remains maintainable as more cases are added.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6591cd5a-da10-47e3-a289-ee6fa9fb7cc8
📒 Files selected for processing (4)
src/jsc/bindings/napi.cpptest/napi/napi-app/module.jstest/napi/napi-app/standalone_tests.cpptest/napi/napi.test.ts
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
There was a problem hiding this comment.
LGTM — thanks for clarifying the fixture comment; the NAPI_RETURN_IF_EXCEPTION insertions and loop restructuring look correct.
Extended reasoning...
Overview
This PR fixes napi_get_all_property_names to propagate exceptions thrown by Proxy traps (getOwnPropertyDescriptor / getPrototypeOf) during the descriptor-filter loop, instead of continuing with a pending exception and hitting assertNoException() (segfault in release). The core change in src/jsc/bindings/napi.cpp (~15 lines) inserts NAPI_RETURN_IF_EXCEPTION(env) after each potentially-throwing JSC call (exportKeys->get, toPropertyKey, getOwnPropertyDescriptor, getPrototype, filteredKeys->push), hoists toPropertyKey out of the inner loop, and restructures the prototype walk from while(!getOwnPropertyDescriptor(...)) into an explicit while(true)/break body so the exception check runs regardless of the return value. The remaining three files add a native test helper, a JS fixture, and a checkSameOutput regression test comparing Bun against Node for both napi_key_own_only and napi_key_include_prototypes.
Security risks
None. This is a defensive correctness fix in the N-API bindings — it adds early-return-on-exception checks, which can only narrow behavior toward Node compatibility. No auth, crypto, permissions, filesystem, or network surface is touched.
Level of scrutiny
Low-to-medium. The NAPI_RETURN_IF_EXCEPTION macro is the established idiom throughout napi.cpp for exactly this purpose, and each insertion follows a call that is documented to invoke user-observable traps. The loop rewrite is a mechanical equivalence transform (while(!X){Y} → while(true){x=X; check; if(x)break; Y; check;}). The non-throwing filter paths remain covered by the existing test/napi/node-napi-tests/test/js-native-api/test_object/ suite per the PR description.
Other factors
- The bug-hunting system found no issues.
- All inline review threads are resolved: CodeRabbit withdrew both suggestions, and my earlier nit about
getPrototypeOfcoverage was addressed — the author explained convincingly that acheckSameOutputtest for that branch is infeasible because V8 and JSC walk the prototype chain a different number of times, and pushed d5a8dcb to scope the fixture comment to what it actually exercises. - The duplicate-PR bot noted overlap with #29642 / #24985; the PR description already explains this is the focused napi-only fix with a regression test and covers additional throw sites those PRs do not. That's a merge-ordering concern for maintainers, not a correctness concern for this diff.
There was a problem hiding this comment.
LGTM — the comment clarification addresses my earlier nit, and the exception guards follow the established pattern in this file.
Extended reasoning...
Overview
This PR adds NAPI_RETURN_IF_EXCEPTION(env) guards after each potentially-throwing JSC call inside the descriptor-filter loop of napi_get_all_property_names in src/jsc/bindings/napi.cpp (~20 lines net), restructures the prototype-walk while condition into an explicit body so the exception check runs unconditionally, and hoists toPropertyKey out of the inner loop. It adds a native test helper in standalone_tests.cpp, a JS fixture in module.js, and a checkSameOutput test in napi.test.ts that asserts byte-identical output against Node.js for both napi_key_own_only and napi_key_include_prototypes.
Security risks
None. This is defensive exception-propagation in a property-enumeration path; no auth, crypto, permissions, or untrusted-input parsing is involved. The change strictly narrows behavior (early-return on pending exception) rather than adding new code paths.
Level of scrutiny
Low-to-moderate. While napi.cpp is core runtime code, the change is mechanical: it inserts the same NAPI_RETURN_IF_EXCEPTION macro already used at line 1832 (immediately above this loop) after each call that can invoke a Proxy trap. The while(true)/break restructuring is a transparent semantic equivalent of the original while(!getOwnPropertyDescriptor(...)) condition. The fix replaces a segfault / assertNoException() violation with the Node-matching napi_pending_exception return.
Other factors
- The bug hunting system found no issues.
- My earlier nit (test coverage for the
getPrototypecheck) was addressed: the author explained why acheckSameOutputtest cannot deterministically reach that line across V8/JSC, and updated the fixture comment in commit d5a8dcb to accurately describe only what it exercises. Both CodeRabbit threads were similarly resolved and withdrawn. - The PR description explicitly addresses overlap with #29642 and #24985, noting this is the focused napi-only fix with additional throw-site coverage and a dedicated regression test.
- No CODEOWNERS entry covers these paths.
e715a66 to
2d35d72
Compare
There was a problem hiding this comment.
LGTM — comment clarification addresses my earlier note; the exception checks are mechanical and follow the existing NAPI_RETURN_IF_EXCEPTION pattern.
Extended reasoning...
Overview
This PR adds exception-propagation guards to the descriptor-filter loop in napi_get_all_property_names (src/jsc/bindings/napi.cpp). It inserts NAPI_RETURN_IF_EXCEPTION(env) after each JSC call that can throw (exportKeys->get, toPropertyKey, getOwnPropertyDescriptor, getPrototype, filteredKeys->push), restructures the prototype-walk while(!getOwnPropertyDescriptor(...)) into an explicit while(true)/break body so the exception check runs regardless of the boolean result, and hoists toPropertyKey to once per key. A native test helper, JS fixture, and checkSameOutput test are added covering both napi_key_own_only and napi_key_include_prototypes with a Proxy whose getOwnPropertyDescriptor trap throws.
Security risks
None. This is pure exception-safety plumbing in the NAPI binding layer — it changes a crash / napi_ok-with-pending-exception path into the correct napi_pending_exception return. No new data flow, no auth/crypto/permission surface.
Level of scrutiny
Low–medium. The production change is ~30 lines, entirely mechanical, and applies the same NAPI_RETURN_IF_EXCEPTION macro used throughout napi.cpp. The restructured loop is semantically identical on the non-throwing path (protoValue.getObject() returns nullptr for non-objects including null, preserving the original break). The existing test_object Node-API suite covers the non-throwing filter paths, and the new checkSameOutput test pins behavior to Node for the throwing path.
Other factors
- No bugs found by the bug-hunting system.
- All prior review threads (CodeRabbit ×2, my earlier nit on
getPrototypeOfcoverage) are resolved; the author explained why a cross-enginecheckSameOutputtest for the innergetPrototypecheck is not feasible and pushed 2d35d72 to align the fixture comment with what the test actually exercises. - No CODEOWNERS entry for
src/jsc/bindings/napi.cpp. - The PR description notes overlap with #29642 / #24985 and explains why this is the focused, more complete change with a regression test.
There was a problem hiding this comment.
LGTM — focused exception-propagation fix following the established NAPI_RETURN_IF_EXCEPTION pattern, with a Node-comparison regression test.
Extended reasoning...
Overview
This PR adds NAPI_RETURN_IF_EXCEPTION(env) checks after each JSC call that can throw inside the descriptor-filter loop of napi_get_all_property_names (src/jsc/bindings/napi.cpp:1838-1881): exportKeys->get, toPropertyKey, getOwnPropertyDescriptor, getPrototype, and filteredKeys->push. The prototype walk is restructured from while(!getOwnPropertyDescriptor(...)) into an explicit while(true) body so the exception check runs regardless of the boolean return. toPropertyKey is hoisted to compute once per key. A new native test helper plus JS/TS fixtures verify status napi_pending_exception (10) for both napi_key_own_only and napi_key_include_prototypes against Node via checkSameOutput. An incidental split(/\r?\n/) fix is included for the adjacent napi_is_arraybuffer test on Windows.
Security risks
None. This is purely additive exception-check plumbing in an existing N-API entry point; no new inputs, no auth/crypto/permissions surface, no data exposure paths. The only behavioral change is returning napi_pending_exception instead of napi_ok (and avoiding a segfault) when a Proxy trap throws — strictly a correctness/safety improvement.
Level of scrutiny
Low-to-moderate. The change is mechanical: every added line is the established NAPI_RETURN_IF_EXCEPTION macro applied immediately after a call that can run JS (Proxy traps). The restructured loop is semantically equivalent to the original modulo the new exception checks. No CODEOWNERS paths are touched. The regression test is byte-for-byte compared against Node, so divergent behavior would be caught in CI.
Other factors
I previously left a nit about getPrototypeOf coverage; the author explained convincingly that V8 and JSC walk the prototype chain a different number of times, making a checkSameOutput-style test for that branch inherently engine-divergent, and adjusted the fixture comment to accurately describe what it covers. CodeRabbit raised and withdrew the same point. All inline threads are resolved, the bug-hunting system found nothing, and the existing test_object N-API suite continues to exercise the non-throwing paths.
|
CI status for 13321a2 (build 64566): 277 jobs passed, 3 failed, all unrelated to this diff.
The new |
13321a2 to
d673000
Compare
|
Rebased onto main at Re-verified on the rebased build:
|
There was a problem hiding this comment.
LGTM — straightforward exception-propagation fix with a Node-comparison regression test.
Extended reasoning...
Overview
This PR adds NAPI_RETURN_IF_EXCEPTION(env) checks after each potentially-throwing JSC call in the descriptor-filter loop of napi_get_all_property_names (src/jsc/bindings/napi.cpp, ~20 net lines). The prototype walk is restructured from while(!getOwnPropertyDescriptor(...)) into an explicit while(true)/break body so the exception check runs regardless of the return value, and toPropertyKey is hoisted to run once per key. The remaining three files are test scaffolding: a native helper in standalone_tests.cpp, a JS fixture in module.js, and a checkSameOutput test in napi.test.ts that asserts Bun's output matches Node's byte-for-byte.
Security risks
None. The change only adds early-return paths on pending exceptions in an existing N-API function; no new inputs, no auth/crypto/permissions surface, no resource ownership changes.
Level of scrutiny
Low-to-moderate. The production change is mechanical — applying the established NAPI_RETURN_IF_EXCEPTION macro after get, toPropertyKey, getOwnPropertyDescriptor, getPrototype, and push, all of which can throw via Proxy traps. The loop restructuring is semantically equivalent to the original. This fixes a documented segfault (returning napi_ok with a pending exception, then crashing on the next call) and brings behavior in line with Node.
Other factors
- CI is green on all lanes including x64-asan and Windows; the three reported failures were unrelated (artifact-download timeout, TLS test, OOM).
- All inline review threads (CodeRabbit's and my prior nit about
getPrototypeOfcoverage) are resolved; the author gave a sound explanation for why agetPrototypeOf-throwing fixture can't be made engine-stable in acheckSameOutputtest, and tightened the fixture comment accordingly. - The existing
test_objectN-API suite (non-throwing filter paths) continues to pass per the rebase verification.
|
CI on No napi failures on any lane. The The diff is green; the only red is network flake in an install test. This should be ready to merge. |
d673000 to
217657e
Compare
|
Rebased onto main at Conflict resolved: Re-verified on the rebased build:
|
There was a problem hiding this comment.
LGTM — the fixture comment now matches what the test exercises, and the exception-check additions follow the established NAPI_RETURN_IF_EXCEPTION pattern exactly.
Extended reasoning...
Overview
This PR adds NAPI_RETURN_IF_EXCEPTION(env) after every JSC call that can throw inside the descriptor-filter loop of napi_get_all_property_names (src/jsc/bindings/napi.cpp): exportKeys->get, toPropertyKey, getOwnPropertyDescriptor (both branches), getPrototype, and filteredKeys->push. The prototype walk is restructured from while(!getOwnPropertyDescriptor(...)) into an explicit while(true) body so the exception check fires regardless of the boolean return, and toPropertyKey is hoisted so it is computed and checked once per key. Test coverage is added via a native fixture in standalone_tests.cpp, a JS driver in module.js, and a checkSameOutput assertion in napi.test.ts that compares Bun's status/exception output byte-for-byte against Node for both napi_key_own_only and napi_key_include_prototypes.
Security risks
None. No new allocations, ownership transfers, or user-controlled parsing are introduced; the change is purely additive exception propagation using the existing NAPI_RETURN_IF_EXCEPTION macro. The loop restructure is behavior-preserving apart from the new early returns on pending exceptions.
Level of scrutiny
Low-to-moderate. While napi.cpp is JSC-bindings C++, this specific change is the textbook fix CLAUDE.md prescribes ("Exception checks after every call that can enter JS") and mirrors dozens of identical macro applications elsewhere in the file. The refactor of the while-condition into an explicit body is trivially equivalent. No CODEOWNERS entry covers these files.
Other factors
My prior inline nit (the fixture comment naming getPrototypeOf when only getOwnPropertyDescriptor is exercised) was addressed in commit 217657e — the comment now accurately describes what the fixture does, and the author gave a sound engine-divergence justification for not adding a getPrototypeOf case to a checkSameOutput test. All CodeRabbit threads are resolved. CI has been green on three successive builds (64566, 65420, 68654 pending) across all lanes including Windows and x64-asan, with only unrelated network/OOM flakes. The existing test_object napi suite continues to pass. The duplicate-PR bot flagged #29642/#24985 as overlapping; the description explains this PR is the focused napi fix with a napi-level regression test and covers additional throw sites — which PR to land is a maintainer call, but this one is correct on its own.
|
CI on the rebased
Both are the same two flakes seen on build 64566 before the rebase, on different shards. The The diff is green. Ready to merge whenever a maintainer picks it up. |
217657e to
14c028c
Compare
|
Rebased onto main at Conflict resolved: Re-verified on the rebased build:
|
|
CI on the rebased
The The diff is green. This is ready to merge. |
14c028c to
962aae2
Compare
|
Rebased onto main at Conflicts resolved (~13 napi PRs landed since the last base):
Re-verified on the rebased build:
|
There was a problem hiding this comment.
LGTM — mechanical application of NAPI_RETURN_IF_EXCEPTION after each throwing JSC call in the descriptor-filter loop, matching the pattern used everywhere else in napi.cpp.
What was reviewed:
- Verified each new check sits immediately after a call that can enter JS (
get,toPropertyKey,getOwnPropertyDescriptor,getPrototype,push), and that thewhile(true)restructure preserves the original loop's exit conditions. - Confirmed
toPropertyKeyhoisting is behavior-preserving (keys fromownPropertyKeys/collectInheritedPropertyKeysare already property names) and only avoids redundant per-prototype-step conversion. - Test uses
checkSameOutputagainst Node for both key modes; CI green on all lanes including x64-asan across four rebases. All prior inline threads resolved.
Extended reasoning...
Overview
This PR adds NAPI_RETURN_IF_EXCEPTION(env) after every JSC call that can throw inside napi_get_all_property_names's descriptor-filter loop in src/jsc/bindings/napi.cpp: exportKeys->get, key.toPropertyKey, getOwnPropertyDescriptor (both branches), getPrototype, and filteredKeys->push. The prototype walk is restructured from while(!getOwnPropertyDescriptor(...)) into an explicit while(true)/break body so the exception check runs regardless of the return value, and toPropertyKey is hoisted so it's computed once per key instead of once per prototype step. Test scaffolding adds a native helper in standalone_tests.cpp, a JS fixture in module.js, and a checkSameOutput test in napi.test.ts.
Security risks
None. This is exception-propagation hygiene in a napi property-enumeration path; no auth, crypto, filesystem, or network involvement. The fix strictly reduces attack surface by turning a segfault-on-next-call (release) / assertNoException() failure (debug) into the correct napi_pending_exception return.
Level of scrutiny
Medium-low. The change is a textbook instance of the CLAUDE.md rule "Exception checks after every call that can enter JS" — it inserts the file's own NAPI_RETURN_IF_EXCEPTION macro (which uses the napi_preamble_throw_scope__ established by NAPI_PREAMBLE at function entry) after each potentially-throwing call, exactly as dozens of neighboring napi functions already do. The only non-trivial part is the loop restructure, which I traced against the original: same entry (current_object = object), same two exit conditions (found → break, !proto → break), same desc population semantics including the pre-existing case where the chain is exhausted without a match (default-constructed PropertyDescriptor, unchanged from before).
Other factors
- Test quality:
checkSameOutputasserts Bun's output is byte-identical to Node's for bothnapi_key_own_onlyandnapi_key_include_prototypes, plus targetedtoContaindiagnostics. The existingtest_objectnode-napi suite continues to pass. - Review history: CodeRabbit and my own prior inline nit both suggested a
getPrototypeOf-throwing case; robobun explained (correctly) that V8 and JSC walk the chain a different number of times, so no proxy shape produces identical output in acheckSameOutputtest. The fixture comment was tightened accordingly. All threads resolved. - CI: Green on four separate builds across rebases (64566, 65420, 68654, 72671, plus 73114 in flight), including Windows and x64-asan; the only failures were unrelated infra flakes (artifact-download timeout, v8-heap-snapshot OOM, tracked worker-terminate assertion).
- No CODEOWNERS for this path. Related PRs #29642 and #24985 are acknowledged in the description; this one is the focused napi fix with a napi regression test and covers throw sites those do not.
|
CI on the rebased
Two darwin shards are still scheduled waiting on agents. The The diff is green. Ready to merge. |
…s descriptor filter When napi_get_all_property_names is called with napi_key_enumerable, napi_key_writable, or napi_key_configurable, it iterates the collected keys and calls getOwnPropertyDescriptor/getPrototype on the target to filter by descriptor attributes. If the target is a Proxy whose getOwnPropertyDescriptor or getPrototypeOf trap throws, the loop kept iterating with a pending exception and then NAPI_RETURN_SUCCESS would assert (debug) or return napi_ok with the exception still pending (release). In release builds the next iteration would dereference an empty JSValue from exportKeys->get and segfault. Add NAPI_RETURN_IF_EXCEPTION after every call in the filter loop that can run user JS (get, toPropertyKey, getOwnPropertyDescriptor, getPrototype, push) and restructure the prototype walk so the exception check runs regardless of getOwnPropertyDescriptor's return value.
The fixture only exercises getOwnPropertyDescriptor throwing; V8 and JSC walk the prototype chain a different number of times so a getPrototypeOf case cannot be expressed as a checkSameOutput test.
962aae2 to
9540af6
Compare
|
Rebased onto main at Conflict resolved:
Re-verified on the rebased build:
|
There was a problem hiding this comment.
No issues found — the exception checks follow the file's established NAPI_RETURN_IF_EXCEPTION pattern and the loop restructure is behavior-preserving. Deferring because this is native JSC bindings and there are overlapping open PRs (#29642, #24985) a maintainer should reconcile.
What was reviewed:
- Each new
NAPI_RETURN_IF_EXCEPTIONsits directly after a call that can enter JS (get,toPropertyKey,getOwnPropertyDescriptor,getPrototype,push);protoValue.getObject()is only reached after the check. - The
while(true)/breakrewrite preserves the non-throwing path; hoistingtoPropertyKeyis safe sincekeyis loop-invariant in the prototype walk. - Test uses
checkSameOutput(byte-for-byte Node comparison) for both key modes; the earliergetPrototypeOf-coverage nit was addressed and the fixture comment scoped accordingly.
Extended reasoning...
Overview
Adds NAPI_RETURN_IF_EXCEPTION(env) after every throwing JSC call in napi_get_all_property_names's descriptor-filter loop (src/jsc/bindings/napi.cpp), restructures the prototype walk from while(!getOwnPropertyDescriptor(...)) into an explicit body so the exception check runs regardless of the return value, and hoists toPropertyKey out of the inner loop. Test coverage is a new native helper in standalone_tests.cpp, a JS fixture in module.js, and a checkSameOutput assertion in napi.test.ts.
Security risks
None. This is exception-propagation hardening in a native API; no auth, crypto, permissions, or untrusted-input parsing beyond what already existed.
Level of scrutiny
Native C++ JSC bindings are a critical code path (memory safety, exception-scope discipline). The change itself is mechanical — the same macro appears ~100+ times in this file and is applied here exactly as elsewhere — but per guidelines, native bindings warrant a human sign-off even for mechanical changes. Additionally, two open PRs (#29642, #24985) touch the same loop; a maintainer should decide merge order rather than an automated approval.
Other factors
- The bug-hunting system found no issues.
- All prior review feedback (CodeRabbit's two suggestions, my earlier nit on
getPrototypeOfcoverage) is resolved; the fixture comment was tightened to describe only what it exercises. - CI has been green on the napi test across multiple rebases (builds 64566, 65420, 68654, 72671, 73114) including Windows and x64-asan; the existing
test_objectsuite continues to pass. - I verified the restructured loop against the current tree: the non-throwing path is identical to the old
while(!found)shape, andgetObject()onprotoValueis only called after the exception check so it never sees the throw sentinel.
|
CI on the rebased
The |
…d String wrapper (#34479) ## What does this PR do? `napi_get_all_property_names` with `napi_key_own_only` and `napi_key_writable` / `napi_key_configurable` returned `napi_ok` with an **empty** key array for Proxy objects and for `new String(...)` character indices, where Node returns the keys. Follow-up to #34128, which fixed the abort on the writable filter but left this over-filtering behind. ### Repro ```js // addon: napi_get_all_property_names(env, obj, napi_key_own_only, filter, napi_key_keep_numbers, &keys) const proxy = new Proxy({ a: 1 }, { ownKeys: () => ["x", "y"], getOwnPropertyDescriptor: () => ({ value: 1, enumerable: true, configurable: true }), }); apn(proxy, own_only, writable); // bun: napi_ok, [] node: napi_ok, ["x","y"] apn(new String("ab"), own_only, writable); // bun: napi_ok, [] node: napi_ok, [0,1] apn(new String("ab"), own_only, configurable); // bun: napi_ok, [] node: napi_ok, [0,1] ``` ### Cause Bun's descriptor-filter loop calls `getOwnPropertyDescriptor` per key and checks `desc.writable()` / `desc.configurable()`. That is the spec-visible descriptor, but V8's `KeyAccumulator` does not consult those bits on two paths: - `FilterProxyKeys` only applies `ONLY_ENUMERABLE`; `ONLY_WRITABLE` / `ONLY_CONFIGURABLE` are never checked for Proxy keys (with or without traps). - `StringWrapperElementsAccessor::CollectElementIndicesImpl` adds every character index unconditionally, without consulting the `PropertyFilter`. So V8/Node pass those keys through where Bun filtered them out. ### Fix Track the owning object for each key and exempt it from the writable/configurable predicate when the owner is a `ProxyObject`, or when the owner is a `StringObject` / derived `StringObject` and the key is a character index (`index < internalValue()->length()`). `napi_key_enumerable` is still applied everywhere (V8 does filter proxies by enumerable), and ordinary properties (plain objects, arrays, frozen objects, a String wrapper's `length`, extra own properties on a String wrapper) are still filtered by the real descriptor. ### Verification New `test_get_all_property_names_proxy_and_string_wrapper` fixture in `test/napi/napi-app/module.js`, compared against Node via `checkSameOutput`. Fails on `main` (proxy/String cases return `[]`), passes with this change. The existing `napi_get_all_property_names` tests (own_only skip_strings/skip_symbols, accessor filtering, cache-poisoning) continue to pass. Note: #32263 touches the same loop to add exception propagation for throwing Proxy traps; the two changes are complementary and will need a small merge resolve whichever lands second. <!-- robobun:evidence:begin --> --- **no test proof** · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/napi/napi.test.ts <!-- robobun:evidence:end -->
Summary
napi_get_all_property_nameswith a descriptor-basedkey_filter(napi_key_enumerable/napi_key_writable/napi_key_configurable) iterates the collected keys and callsgetOwnPropertyDescriptor/getPrototypeon the target to filter by attribute. When the target is a Proxy whosegetOwnPropertyDescriptororgetPrototypeOftrap throws, the loop kept iterating with a pending exception and then hitNAPI_RETURN_SUCCESS, which callsassertNoException().Repro
Node.js returns
napi_pending_exception. Bun returnednapi_okwith the exception still pending (release) and then segfaulted at address0x5on the next call withnapi_key_include_prototypes:Fix
Add
NAPI_RETURN_IF_EXCEPTION(env)after every call in the filter loop that can throw:exportKeys->get,toPropertyKey,getOwnPropertyDescriptor,getPrototype, andfilteredKeys->push. The prototype walk is restructured from awhile(!getOwnPropertyDescriptor(...))condition into an explicit body so the exception check runs regardless of the return value, andtoPropertyKeyis hoisted out of the loop so it is computed (and checked) once per key.Related: #29642 also touches this loop as part of a broader
Bun.inspectfix; this PR is the focused napi change with a napi regression test and covers the additional throw sites that PR does not (get/toPropertyKey/push, and the case wheregetOwnPropertyDescriptorthrows after returning true from the while condition).Test
test/napi/napi.test.ts→napi_get_all_property_names > returns napi_pending_exception when a Proxy trap throws during descriptor filteringcompares Bun against Node for bothnapi_key_own_onlyandnapi_key_include_prototypes. The existingtest/napi/node-napi-tests/test/js-native-api/test_object/suite (which exercises the non-throwing filter paths) continues to pass.no test proof · iteration 20 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/napi/napi.test.ts