Skip to content

console: print the named properties of an empty array - #45564

Open
robobun wants to merge 7 commits into
mainfrom
robobun/8068735c/print-named-keys-of-empty-array
Open

robobun wants to merge 7 commits into
mainfrom
robobun/8068735c/print-named-keys-of-empty-array

Conversation

@robobun

@robobun robobun commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #22569

Problem

  • console.log prints [] for an empty array with a named property: const a = []; a.total = 7001. With one element Bun prints [ 5, total: 7001 ]. An empty Bun.SQL result hides count and command.
  • print_array returns on len == 0 before its named-property pass (src/jsc/ConsoleObject.rs:4268).

Fix

Background

Downsides

  • Text changes for an empty array with an enumerable named property: console.log, Bun.inspect, matcher Received: lines, test.each titles with %p. Stored snapshots do not.
  • An object print costs 10 more instructions ({a:1,b:2}: 5252 to 5262), an arguments object 26 more. Release text: +1,536 bytes.
  • Still []: an empty array with only non-enumerable named properties (an empty postgres.js Result), as in Node.
Notes

Output

const a = []; a.total = 7001;
console.log(a);                          // [ total: 7001 ]             was [], Node prints [ total: 7001 ]
console.log({ rows: a });                // { rows: [ total: 7001 ], }  was { rows: [], }
console.log(await sql`INSERT ...`);      // [ count: 1, command: "INSERT", lastInsertRowid: 1, affectedRows: 1 ]   was []
Bun.inspect({ rows: a }, { depth: 0 });  // rows: [Array ...]          like an array with elements

The format is the one an array with elements has. util.inspect was correct before and does not use this code.

Bun.SQL and #22569

Which keys print

  • An empty array prints its named properties when one of them is enumerable. The property walk then prints what it prints for an array with elements. Since bun test: Vitest compatibility #44833 that walk skips own non-enumerable keys. So a non-enumerable key of an array does not print: alone or beside an enumerable key, with or without elements. Node prints the same keys. A new test entry holds an empty array that has both kinds of key. Before bun test: Vitest compatibility #44833 a non-enumerable key printed beside an enumerable one, and the first review asked about that difference. console: print the class name of Array subclass instances #47563 (for console.log has different output for pg #13946) is open. It adds the class name that Node prints before the bracket.
  • Still []: no named property, a deleted one, only non-enumerable ones (an empty postgres.js Result, and Node hides those keys too), only a private field, only constructor, Array.prototype, an empty arguments object.
  • The walk never prints a key whose text is constructor: the string key, and a symbol with that description (for_each_prelude compares the text). The helper skips the same keys by text. The self-review asked to delete that rule from the helper, because Print an object's own constructor key in console.log, Bun.inspect and bun:test #38937 proposes to change it in the walk. Without the rule an empty array with only such a key prints [Array ...] past the depth limit, where main prints []. The rule stays, and tests fail without it. A test also pins that an array with elements hides these keys, so a change to the walk fails beside the cases of the helper.
  • No known output differs from main outside the bug. Up to 7177d5ead8 the helper compared the constructor key by pointer, and an empty array whose only key is Symbol("constructor") printed [Array ...] past the depth limit. A review found it. 6172b53f81 compares the text.
  • A property value whose inspect.custom throws now throws from the print. Before, the value was never formatted.
  • { sorted: true } does not sort the named keys of an array, with or without elements. That is the behaviour on main for arrays with elements. The new tests use keys that are already in order.

The read

Bun__JSArray__lengthAndNamedKeys (UtilInspect.cpp) returns the length in the low 32 bits. Bit 32 is set for an array with no elements whose Structure has an enumerable key that is not a private name and whose text is not constructor. A Structure with no property slots is answered with no scan. The helper takes ArrayType cells. An instance of an Array subclass is ArrayType. Arguments objects and Array.prototype keep the old length read.

Measurements

Release builds of main 017b2439a7 and of this branch at 91bfe1c7cc. The branch was merged with main 675ee5b97b after these runs. The merge changes no added or removed line of the source diff, and the runs were not repeated. 6172b53f81 then changed one line of the helper: the constructor comparison, which runs only for an empty array that has named property slots. Tools: gdb 16.3 (breakpoint hit counts, stepi), size, nm, objdump. perf, valgrind, bloaty and strace are not installed in the container. Wall-clock is not reported: in an earlier run the same binary against a copy of itself differed by up to 34%.

Engine calls per 1000 prints: property walk / length read / PropertyNameArray built / new helper.

value main PR
[] 0 / 1000 / 0 / - 0 / 0 / 0 / 1000
[0] 1000 / 1000 / 1000 / - 1000 / 0 / 1000 / 1000
[[], [], []] 1000 / 4000 / 1000 / - 1000 / 0 / 1000 / 4000
Object.assign([5], { total: 1 }) 1000 / 1000 / 1000 / - 1000 / 0 / 1000 / 1000
Object.assign([], { total: 1 }) 0 / 1000 / 0 / - 1000 / 0 / 1000 / 1000
empty, one non-enumerable key 0 / 1000 / 0 / - 0 / 0 / 0 / 1000
{} 0 / 0 / 1000 / - 0 / 0 / 1000 / 0
arguments object 0 / 1000 / 0 / - 0 / 1000 / 0 / 1000

Instructions from the entry of print_array to its return (gdb stepi, colors off, two runs agree).

value main PR
[] inside { rows: [] } 144 126
[0] 1445 1420
[1, 2, 3] 3273 3259
[[], [], []] 6828 6781
Object.assign([5], { total: 1 }) 3686 3663
empty, one non-enumerable key 251 266
arguments object, 1 element 1277 1303
print_object of { a: 1, b: 2 } 5252 5262
  • Binary: release text 106,738,870 to 106,740,406 bytes (+1,536, size). print_array 4109/4189 to 4389/4501 bytes (colors off/on). for_each_prelude 1265/1295 to 1252/1346. New symbols 1,187 bytes.
  • Stack frame of print_array: release 216/216 to 216/216 bytes. Debug with ASAN 4416/4448 to 4544/4576. PropertyIteratorCtx stays 48 bytes (const assert).
  • Output check, not part of the PR: 686 values printed in 5 modes (default, sorted, compact, depth 0, colors) with both release builds, 3,430 outputs. 59 differ for an empty array with a named property that prints. 1 differed for the symbol case above. The other 3,370 are equal. The symbol case is equal too since 6172b53f81, and a test holds it. The corpus was not printed again.
  • Nested plain arrays, Bun.inspect(nest(N), { depth: Infinity }): the smallest N that throws RangeError is 13,471 on both release builds.
  • Dev server console formatter: Object.keys calls per empty array 0 to 1. One Object.getOwnPropertyDescriptor call per named key of an array, 0 before.

The walk skip that is not here

A draft skipped the property walk for an array with elements and no named property. That saved one walk and one allocation per print of [0]. The walk also holds JSC's stack check. Input: nested arrays where an inspect.custom object prints after a deeper array. On debug builds of the same tree, the draft aborts (exit 134) from 1,000 to 1,550 levels. Without the skip the abort is at 1,000 only and RangeError comes from 1,040. On release builds, main and this branch are equal for that input: abort at 13,300 and 13,450 levels, RangeError from 13,480. An inspect.custom at the bottom of the nest aborted on main 017b2439a7 and on this branch from 13,300 to 21,400 levels. #39382 had the fix for that abort, and main has it since #46021. All numbers in this paragraph are from before the merge with main.

Each clause is held by a test

Ten clauses of the diff were removed in three mutant builds, before the merge with main. Each removal failed at least one of the new tests.

One of the ten is now a guard: the [] that print_array writes when the helper reports a key and the walk prints none. The symbol case reached it. Since 6172b53f81 no known input does, and a build that writes [?] there passes the four suites. Without the guard, a disagreement between the helper and the walk would print ] with no opening bracket.

The text comparison of 6172b53f81 is held by the constructorSymbol case past the depth limit. That case fails with the pointer comparison.

Merge with main

The branch is merged with main 89452d2ac1 (2026-11-10), and before that with main 675ee5b97b (2026-11-03). Neither merge had a conflict, and neither changes an added or removed line of the source diff. Main has #44833, which changed the property walk and print_array. It also has #47893 and #47942, which changed the signature of the property callback and the walk. After the merge the suites below pass. On the source of main 89452d2ac1 the new tests fail as before: 9 in inspect.test.js, and 1 each in console-log.test.ts, sqlite-sql.test.ts and esm.test.ts.

One test in console-table.test.ts runs 64 full GCs. On the debug build in my container it takes 4.4 to 6.4 s with and without this change, so it meets the 5 s default timeout by chance on both.

Other open pull requests

Dev server console formatter

  • formatArray returned [] for every array with no elements. It now returns [] when neither the element loop nor the named-key loop prints.
  • The named-key loop read value[key]. With the first revisions of this PR an empty array reached that loop, so console.log in a page called its enumerable getters, and a getter that throws made console.log throw. A review found it.
  • Since a77bbf2e98 the loop reads the descriptor, as formatPlainObject does. An accessor prints [Getter], [Setter] or [Getter/Setter]. A property that throws prints undefined.
  • That also changes an array with elements: a named accessor printed its value before. The dev test counts the getter calls for both arrays, and it fails on 6172b53f81 with a count of 2.

Not in this PR

  • The dev server console formatter still calls an index accessor of an array, and a throw inside it (for example from an inspect.custom) goes out of the page's console.log. Both are on main.
  • Map, Set and typed arrays print no named property at any size. Arguments objects skip them.
  • The Jest-style formatter (pretty_format.rs) prints no named property of an array. A new test pins that a snapshot of such an array is [].

Suites

bun bd test on test/js/bun/util/inspect.test.js, test/js/web/console/console-log.test.ts, test/js/sql/sqlite-sql.test.ts, test/bake/dev/esm.test.ts, test/js/bun/console/bun-inspect-table.test.ts, console-table.test.ts, console-iterator.test.ts, test/js/node/util/bun-inspect.test.ts, custom-inspect.test.js, test/js/bun/util/inspect-error.test.js, test/js/bun/test/jest-each.test.ts. All ran again after the merge with main.

The console-log.js cases are from #23324 by @Pranav2612000.


no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/sql/sqlite-sql.test.ts, test/js/bun/util/inspect.test.js, test/bake/dev/esm.test.ts

@robobun

robobun commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 5:36 PM PT - Nov 10th, 2026

✅ @robobun, your commit 851defdcd981be2bf0c4346bce2918a9b3154744 passed in Build #139872! 🎉


🧪   To try this PR locally:

bunx bun-pr 45564

That installs a local version of the PR into your bun-45564 executable, so you can run:

bun-45564 --bun

@robobun

robobun commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: CI is green on 851defdcd9: build 139872 passed, 188 of 188 jobs. The branch is merged with main 89452d2ac1. The PR waits for a maintainer review.

Reproduced on 1.4.3 and on main 89452d2ac1: const a = []; a.total = 7001; console.log(a) prints []. With one element the same array prints [ 5, total: 7001 ]. An empty Bun.SQL INSERT result prints [] on main, and a result with rows prints count, command, lastInsertRowid and affectedRows.

This branch prints [ total: 7001 ], and [ count: 1, command: "INSERT", lastInsertRowid: 1, affectedRows: 1 ] for the INSERT result.

The 9 new tests in test/js/bun/util/inspect.test.js fail on a debug build of main 89452d2ac1 and pass on this branch. The same holds for the new test in test/js/sql/sqlite-sql.test.ts, the new cases in console-log.js and the new dev console test in test/bake/dev/esm.test.ts.

The design point from the first review is settled on main. Since #44833 the property walk skips own non-enumerable keys. An array prints no non-enumerable key, alone or beside an enumerable one, as in Node. A new test entry holds that for an empty array.

No known output differs from main outside the bug. A review found one that did: an empty array whose only key is Symbol("constructor") printed [Array ...] past the depth limit. 6172b53f81 compares that key by text, as the property walk does, and the array prints [] again.

A review also found that the dev server console formatter called the enumerable getters of an empty array. Since a77bbf2e98 it reads the descriptor of each named key of an array: an accessor prints [Getter] and is not called. The dev test counts the getter calls.

#47563 is open on the same statement in print_array, and the two PRs conflict in four files. Either order works. The PR that lands second needs a rebase, and it folds its flag into the call of the first, so an array print stays one call. If #47563 lands first, I rebase this PR. The PR body has the details under Notes, in "Other open pull requests".

An earlier version of this comment said that this PR does not close #22569. That was based on a draft of #45304. The merged #45304 keeps the four result properties in console.log(rows), so this PR closes #22569.

@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • Resume reviews
  • Trigger review

Walkthrough

Array inspection now displays enumerable named properties on empty arrays. The change updates native and bake-client formatting and adds regression tests for inspection and console output, including rowless SQLite results.

Changes

Array inspection

Layer / File(s) Summary
Array metadata query
src/jsc/bindings/UtilInspect.cpp, src/jsc/JSValue.rs
A native query reports array length and whether an empty array has enumerable named properties. The Rust wrapper decodes the result.
Native array formatting
src/jsc/ConsoleObject.rs, test/js/bun/util/inspect.test.js, test/js/web/console/*, test/js/sql/sqlite-sql.test.ts
The native inspector opens brackets when it prints the first named property on an empty array. Tests cover depth limits, layout, property handling, console output, and rowless SQLite result inspection.
Bake-client formatting
src/runtime/bake/client/inspect.ts, test/bake/dev/esm.test.ts
The formatter handles empty arrays with enumerable own properties. A development-console test checks forwarded output.

Possibly related PRs

  • oven-sh/bun#47563: Both changes update array inspection and the native array metadata path. PR #47563 adds subclass flags and enumerable-property walking; this PR adds empty-array named-property detection and deferred bracket output.

Suggested reviewers: alii

Severity of issue fixed: Low

Merge Risk: Low · up to a77bb

Development-console output can display ambiguous property names on empty arrays. The change is mergeable with awareness of this bounded formatting issue; quoting keys and adding a regression probe would address it.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: printing named properties for empty arrays in console output.
Linked Issues check Passed Issue #22569 requires console output to show the properties of empty Bun.SQL result arrays. Bun__JSArray__lengthAndNamedKeys detects qualifying enumerable named properties without running JavaScript…
Out of Scope Changes check Passed The native query, formatter changes, dev-server formatter change, and regression tests all support the empty-array named-property behavior. The descriptor-based dev-server change prevents accessor exe…
Description check Passed The description explains the problem, the fix, and how the author verified it. It does not use the template’s exact headings, but it provides the required information and includes relevant behavior, t…
Full details: Linked Issues check

Explanation

Issue #22569 requires console output to show the properties of empty Bun.SQL result arrays. Bun__JSArray__lengthAndNamedKeys detects qualifying enumerable named properties without running JavaScript. print_array then runs the named-property pass. The SQLite regression test checks empty INSERT, UPDATE, and SELECT results and their metadata. The implementation meets the linked coding requirement.

Full details: Out of Scope Changes check

Explanation

The native query, formatter changes, dev-server formatter change, and regression tests all support the empty-array named-property behavior. The descriptor-based dev-server change prevents accessor execution while formatting the newly reached property pass. The reported effects on console output, inspection, and test diagnostics follow the shared formatter behavior. No unrelated change is demonstrated.

Full details: Description check

Explanation

The description explains the problem, the fix, and how the author verified it. It does not use the template’s exact headings, but it provides the required information and includes relevant behavior, tests, and limitations.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 6172b53 and a77bbf2.

Files selected for processing: 2

  • src/runtime/bake/client/inspect.ts
  • test/bake/dev/esm.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟣 src/jsc/ConsoleObject.rs — nit, pre-existing: callers of Bun.inspect(arr, { sorted: true }) get the named keys of an array in insertion order, and this PR's new test certifies that. ConsoleObject.rs:4542 always uses for_each_property_non_indexed, never the ordered walk that print_object picks at ConsoleObject.rs:5468 when ordered_properties is set. The change routes empty keyed arrays through the same walk, and inspect.test.js:1160 records "[\n total: 7001, b: 2\n]" under the sorted key. Fix: when ordered_properties is set, sort the named keys of arrays (elements first, then sorted keys, as Node does) for empty and non-empty arrays alike, and update the sorted and sortedWithOneElement expectations instead of pinning the unsorted output.

    Why this was flagged

    A user calls Bun.inspect(Object.assign([], { total: 7001, b: 2 }), { sorted: true }); sorted is documented in packages/bun-types/bun.d.ts:4972-4974 as sorting the properties and sets ordered_properties at ConsoleObject.rs:1278. print_array reaches the named-property walk at ConsoleObject.rs:4542 and calls for_each_property_non_indexed unconditionally, while print_object at ConsoleObject.rs:5467-5472 calls for_each_property_ordered for the same option. The output is [\n total: 7001, b: 2\n] with total before b. The base branch printed [] for this input and had the same unsorted order for Object.assign([1], {...}), so this is pre-existing for non-empty arrays and newly visible for empty ones. The new expectation at test/js/bun/util/inspect.test.js:1160 (and sortedWithOneElement at the next line) records the unsorted order, so the test now locks in the behaviour rather than checking it.

    Verification: pre-existing. Trigger: any caller passing { sorted: true } to Bun.inspect on an array with named (non-index) keys. print_array at ConsoleObject.rs:4542 calls value.for_each_property_non_indexed(...) unconditionally, with no sort step. The base commit has the same unconditional call. The new test at test/js/bun/util/inspect.test.js pins sorted: "[\n total: 7001, b: 2\n]".

Comment thread src/jsc/bindings/UtilInspect.cpp Resolved
Comment thread src/jsc/ConsoleObject.rs Resolved
console.log and Bun.inspect printed `[]` for an array that has no
elements and has an own named property. print_array returned on
`len == 0` before its named-property pass.

print_array now reads the length in one call that runs no JS and that
also says whether an array with no elements has an enumerable named
property (Bun__JSArray__lengthAndNamedKeys). Such an array continues
into the named-property pass, and the first property that prints
writes the opening bracket. Past the depth limit it prints the
`[Array ...]` marker, like an array with elements.

The dev server console formatter (bake/client/inspect.ts) had the same
early return. It prints the keys now.

The console-log.js cases are from #23324.

Co-authored-by: Pranav Joglekar <pranav2000joglekar@gmail.com>
@robobun
robobun force-pushed the robobun/8068735c/print-named-keys-of-empty-array branch from d213a1d to a4cc20b Compare October 17, 2026 19:32
Comment thread src/jsc/ConsoleObject.rs Resolved
Comment thread src/jsc/ConsoleObject.rs Resolved
Comment thread src/jsc/ConsoleObject.rs Resolved
Comment thread src/jsc/ConsoleObject.rs Resolved
Comment thread src/jsc/ConsoleObject.rs Resolved
Comment thread src/jsc/JSValue.rs Resolved
Comment thread src/jsc/bindings/UtilInspect.cpp Resolved
@robobun

robobun commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator Author

Two review points that are not on a diff line.

{ sorted: true } and the named keys of an array. The finding is correct and it is on main: Bun.inspect(Object.assign([1], { total: 1, b: 2 }), { sorted: true }) prints total before b, and Node prints b first. The fix is a change to arrays with elements, so it is not in this PR. The new tests no longer pin that order: they use keys that are already sorted (a4cc20b).

Linked issue #22569. The first version of this PR did not close it, because a draft of #45304 made the Bun.SQL result metadata non-enumerable. The merged #45304 keeps count, command, lastInsertRowid and affectedRows as enumerable fields. On main 017b2439a7 an empty INSERT result prints [] and a result with rows prints the four fields. This branch prints [ count: 1, command: "INSERT", lastInsertRowid: 1, affectedRows: 1 ]. The new test in test/js/sql/sqlite-sql.test.ts holds that and fails on main. The PR body and the status comment now say Fixes #22569.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I re-reviewed the rewritten revision (a4cc20b, 91bfe1c) and found no new bugs; because this changes user-visible console.log/Bun.inspect output and adds a deferred-bracket state to the array printer, a human look is still worthwhile.

What was reviewed:

  • The new ArrayOpen flow in print_array/for_each_prelude: the Pending → [] early return, Multiline forcing the closing bracket onto its own line, i starting at 0 and always_newline forced off for the zero-length case — line-length accounting matches the array-with-elements path.
  • Bun__JSArray__lengthAndNamedKeys: type is validated before uncheckedDowncast, no call enters JS, and the constructor exclusion now keeps { x: Object.assign([], { constructor: 5 }) } at x: [] past the depth limit (covered by the new test).
  • One thing I could not settle from the checkout: the helper accepts only ArrayType, while inherits<JSArray> elsewhere also covers DerivedArrayType. The subclass and sqlite-sql.test.ts cases assert subclass instances print their keys, so CI settles whether class X extends Array instances are ArrayType here.
Extended reasoning...

The change touches the Rust console formatter (src/jsc/ConsoleObject.rs), a new FFI helper in src/jsc/JSValue.rs and src/jsc/bindings/UtilInspect.cpp, the bake dev-server client inspector, and adds tests in four suites. It touches no security-sensitive surface: no auth, injection or data-exposure paths, and the native helper runs no user JS. The prior run's inline finding about constructor-only arrays past the depth limit is addressed by the helper's constructor exclusion with a test; the non-enumerable-key behavior is unchanged and is a design call the description defends as matching arrays with elements. Deferring rather than approving because the output change is user-visible (Bun.SQL results, matcher messages) and the ArrayType-vs-DerivedArrayType question for subclass instances can only be confirmed by running the new tests, which I could not do here. No CODEOWNERS entry covers the changed files and no outstanding third-party objection is visible in the timeline.

Still open from earlier reviews (1):

  • Unresolved: 1 minor or pre-existing.

@robobun

robobun commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

An answer to the open question in the last review: an instance of class X extends Array is an ArrayType cell.

  • JSC builds the structure of a subclass instance from the base structure of Array and keeps its type info (StructureCache::emptyStructureForPrototypeFromBaseStructure, StructureCache.cpp:104 at WebKit 87ee10da08).
  • DerivedArrayType is the type of a C++ class that derives from JSArray, for example Array.prototype (ArrayPrototype::createStructure). For such a value the helper returns "not an array", and print_array reads the length as on main. The arrayPrototype case in inspect.test.js holds the output [].
  • Buildkite build 127701 ran the subclass case in test/js/bun/util/inspect.test.js and the new Bun.SQL test in test/js/sql/sqlite-sql.test.ts on every platform. Both pass.

@robobun

robobun commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator Author

The { sorted: true } finding from the review (the order of the named properties of an array) is now #46156.

  • Bun.inspect: sort the named properties of an array when sorted is set #46156 sorts the named properties of an array that has elements. It does not change the tests here, because their keys are already in order.
  • This PR sends an array with no elements through the same walk, so that array sorts too when both PRs land.
  • The PR that lands second adds the test row for Bun.inspect(Object.assign([], { total: 1, b: 2 }), { sorted: true }).

@coderabbitai coderabbitai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • Fix comments: Fix CodeRabbit comments on this PR

Comment thread src/jsc/bindings/UtilInspect.cpp Resolved

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I re-reviewed the current head (merge of main plus the besideNonEnumerable test) and found no bugs; since this changes user-visible console.log/Bun.inspect/matcher output on a hot formatter path and adds an FFI probe, a human look at the format decision is still worthwhile.

What was reviewed:

  • After the merge, for_each_property_non_indexed skips own DontEnum keys (bindings.cpp:6154), so the helper's predicate (enumerable, non-private, not constructor) now matches what the walk prints; the new test pins that. The PR text "a non-enumerable key prints beside an enumerable one" is stale against the code.
  • Ruled out class extends Array instances falling to the non-ArrayType fallback: JS subclasses keep ArrayType (only Array.prototype/C++ subclasses are DerivedArrayType), and the Rows subclass and SQLite zero-row tests cover it.
  • Traced ArrayOpen: Pending -> [] returns inside the scoped depth/indent/quote restores; i >= 1 for every non-empty array so handle_first_property is never reached from print_array; always_newline/was_good_time only differ for len == 0.
  • The u64 packing cannot collide with the u64::MAX sentinel (bit 32 is only set when length is 0); the probe runs no JS, so no exception check is needed.
Extended reasoning...

The change touches the native console formatter (src/jsc/ConsoleObject.rs print_array and the property-iterator callback), adds a no-JS FFI probe in src/jsc/bindings/UtilInspect.cpp with its Rust wrapper in src/jsc/JSValue.rs, and mirrors the fix in the bake client inspector, with tests across inspect, console-log expected output, sqlite results and the dev server. It touches no security-sensitive surface. Both of my earlier inline concerns are now covered: the helper excludes constructor, and main's merged walk skips own non-enumerable keys so the helper and the walk agree. I chose defer rather than approve because the PR changes user-visible output (including expect() Received: lines) on a hot path, the bracket-deferral state machine is non-trivial, the PR was bot-authored, and the description is partly stale relative to the code.

The property walk prints no key whose text is `constructor`, a string or
a symbol. The check compared the string key by pointer, so an array with
no elements whose only key is Symbol("constructor") printed `[Array ...]`
past the depth limit, where it prints `[]` at any other depth.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline finding on the dev-server formatter, I checked the native side after the last two commits and found nothing further: the helper's key filter in Bun__JSArray__lengthAndNamedKeys (own DontEnum, private names, constructor compared by text for string and symbol keys) now matches what the property walk actually prints (bindings.cpp skips own DontEnum keys for the iterated object; for_each_prelude drops any key whose text is constructor), so the depth-limit marker and the Pending -> [] fallback agree with top-level output for those cases. I also confirmed open_array is only reachable for a zero-length array, since a non-empty array (even one that is all holes) hands the property iterator an index of at least 1, so the object-style { prefix in handle_first_property cannot fire for arrays.

Extended reasoning...

The change touches the console/Bun.inspect array printer in Rust, a new C++ structure-scan helper, the bake client formatter, and tests; it touches no auth, crypto or input-parsing surface. One inline finding remains on the browser-side formatter, and the native helper/walk consistency and the lazy-bracket control flow were examined and ruled out this run.

Comment thread src/runtime/bake/client/inspect.ts Resolved
The console formatter of the dev server read each named property of an
array with `value[key]`. Since an array with no elements reaches that
loop, `console.log` in a page called its enumerable getters, and a
getter that throws made `console.log` throw.

The loop reads the descriptor first, as the object formatter does. An
accessor prints `[Getter]`, `[Setter]` or `[Getter/Setter]`. A property
that throws prints `undefined`.

@coderabbitai coderabbitai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • Fix comments: Fix CodeRabbit comments on this PR

Comment thread test/bake/dev/esm.test.ts Resolved

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

The latest commit addresses the getter concern in the bake inspector (accessors print as [Getter]/[Setter] without being invoked, and the new esm.test.ts case pins it). Beyond the inline nit, I also checked that the C++ helper's key filter (DontEnum, private names, constructor text) matches what the walk in bindings.cpp actually prints for own keys of an array, so "does anything print" and "what prints" agree, and that when every key is skipped (hidden value, failed tag) the Pending state still falls back to [] and exceptions propagate through ?. The remaining user-visible change (empty SQL results now print their fields) is a product call worth a maintainer's glance.

Extended reasoning...

The change replaces the len == 0 early return in print_array (src/jsc/ConsoleObject.rs) with a C++ structure probe (Bun__JSArray__lengthAndNamedKeys in src/jsc/bindings/UtilInspect.cpp) and a deferred-bracket state machine (ArrayOpen) in the property iterator, plus the parallel fix in the bake client inspector; it touches no security-sensitive surface. All three concerns raised in earlier runs (DontEnum consistency, depth marker for constructor-only keys, bake getters) are now addressed in code with tests. I did not approve because the formatter is a hot, widely-observed path with a new two-place rule for which keys count, and the output change for empty Bun.SQL results is a product decision a maintainer should confirm.

Comment thread src/runtime/bake/client/inspect.ts Resolved

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bun.SQL: inserts and updates dont console.log properties if the result is an empty array, will only print []

2 participants