Repository navigation
Conversation
The visited set of the console formatter records the values that are being rendered, so that a value that reaches itself prints [Circular]. The code that takes the pooled map on first use and records a value had two copies: print_as_prelude and the loop that prints the cause and the error-valued own properties of an error. Move it into Formatter::visited_insert, with visited_remove for the removal, and call them from both places and from the three sites that drop the record before print_as changes the tag of a value. No behaviour change.
18a3f20 to
7bfdf30
Compare
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: oven-sh/bun/.coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review. WalkthroughFormatter now provides methods to insert and remove visited values. Console object printing and error-chain cycle tracking use these methods instead of manipulating the visited map directly. ChangesVisited-map handling
Suggested reviewers: Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to No merge-blocking behavior change is identified; this refactor appears ready to merge after normal checks. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
@robobun wake up!! |
|
@robobun wake up!! |
|
I am here. The status:
If you want a different shape, for example one PR and not two, tell me and I change it. |
Behaviour change: none
Problem
print_as_prelude(src/jsc/ConsoleObject.rs) and the loop that prints the cause of an error (print_error_instance_body,src/jsc/VirtualMachine.rs).Fix
Formatter::visited_insertandFormatter::visited_remove.visited_inserttakes the pooled map on first use. It returnstruewhen the value was not in the set, likeHashSet::insert.print_as_prelude, the cause loop, and the three sites that drop the record beforeprint_aschanges the tag of a value call them.Background
[Circular].print_asrecurses once per nesting level, so its frame must stay small. The locals of the record live in an outlined leaf function for that reason.Downsides
print_as_preludenow callsvisited_insert: one more call per object, array, Map, Set, class, function, error and event printed. Pool takes and hash operations per print equal main in 15 of 15 measured cases. Release.text: 1280 bytes smaller. Instructions: not measured,perfandvalgrindare not installed.Notes
Status. #44268 covers this PR and lists it under Fixes. This PR stays open as the small fallback until that one lands. I do not push to it any more.
All numbers: linux x64, main a4f1429 and this branch.
Pool takes / hash inserts / hash lookups per print (release build, gdb hit counts of
LocalKey::withfor the pool,HashMap<JSValue, ()>::get_or_put_slotand::get_index, difference of 80 and 40 prints):Bun.inspect(new Error("x"))Bun.inspectof an Error with an object propertyBun.inspectof an Error with a causeBun.inspect({ a: 1, b: [1, { c: 2 }] })reportError(new Error("x"))reportErrorof an Error with an object propertyreportErrorof an Error with a causeThe other 8 cases (primitive properties, assigned cause, AggregateError of 2,
console.log) are equal too.Stack reserved per function (
sub ...,%rsp):Formatter::print_asFormatter::print_as_preludeFormatter::visited_insert(new leaf)VirtualMachine::print_error_instance_bodyprint_asis the frame that recurses.print_as_preludeandvisited_insertreturn before the formatter descends.Binary.
sizeof the release binary:.text80660492 -> 80659212 bytes. Code of the printer functions (nm -S): 18958 -> 18491 bytes.Output corpus. 38 values (plain errors, errors with properties, cause chains of 4 and 12, AggregateErrors, errors that reach themselves, objects and arrays that hold errors, primitives) through 10 entries (
console.log,console.error,console.log(v, v),Bun.inspectat depth 2, 0 and Infinity,util.inspect,throw,Promise.reject,reportError). One process per output, debug+ASAN build. 367 of 380 outputs are byte-identical.e.cause = e; e.errors = [e]anda.x = [b]; b.y = bon the six entries where main recurses until the stack ends. The exit status is the same (134 or 139). The recursion prints 7 more renders before that, because the frame ofprint_error_instance_bodyis 256 B smaller.Bun.inspectat depth Infinity of an AggregateError that lists itself: 1014 levels before the stack check stops it, main 1013.Suites run (debug+ASAN):
test/js/bun/util/inspect.test.js,test/js/bun/util/inspect-error.test.js,test/js/node/util/bun-inspect.test.ts,test/regression/issue/circular-error-stack.test.ts,test/js/web/console/console-log.test.ts,test/js/bun/util/reportError.test.ts,test/js/bun/test/stack.test.ts.no test proof · iteration 2 · the description declares no behaviour change, so there is no failing test to prove; the existing suite in CI is the check