Repository navigation
Conversation
Collaborator
Author
Collaborator
Author
|
Status
|
This was referenced Oct 30, 2026
robobun
force-pushed
the
robobun/2cdb9aa5/default-export-display-name
branch
from
October 31, 2026 01:15
5870c1d to
f18aaf0
Compare
JavaScriptCore binds an anonymous default export to the private name `*default*`, whose text is `starDefault`. The tests hold each reader of a function name to `default`: stack frames and call sites, `Bun.inspect`, mock names, `describe(Class)` titles, the two heap snapshot formats, the debugger, `--cpu-prof` and `v8::Function::GetName`. The tests pass with the JavaScriptCore change that stores `default` in the executable. `JSC__JSValue__getNameProperty` no longer reads the raw name of the executable when `JSFunction::name()` is empty: the two are the same string now.
JavaScriptCore now has the name `default` as the declared name of an
anonymous default class. The ahead-of-time compiler kept that class as one
with an inferred name, so compiled code left the name out of the two class
errors, of mock names and of snapshot text. `finish_constructor_header`
keeps `HAS_EXPLICIT_NAME` for the class, as `header.rs` does for the
function of `export default function () {}`.
More tests: snapshot text, the scope that holds the built-in constructor
under its name in the debugger, a source that is already transpiled, and
`bun test --aot` against `bun test`. The `--cpu-prof` test runs again
when the profile has no sample in the function it looks for.
robobun
force-pushed
the
robobun/2cdb9aa5/default-export-display-name
branch
from
November 1, 2026 05:52
f18aaf0 to
9926993
Compare
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
Bun.inspect, snapshots and the debugger show an anonymous default export asstarDefault. Examples:at new starDefault,[class starDefault]. Thenameproperty and Node saydefault.jest.fn(fn).name.Fix
defaultfor an anonymous default export WebKit#950, which stores the namedefault. Bun changes no name itself.JSC__JSValue__getNamePropertyloses a dead fallback (src/jsc/bindings/bindings.cpp:5406). The ahead-of-time compiler keepsHAS_EXPLICIT_NAMEforexport default class {}, so compiled code names the class as plain Bun does.toStrictEqualresult, which Jest does not give.Background
JSFunction::name()).export default class {}has one.export default (class {})has an inferred name only.Downsides
export default class {}: snapshot values,describe(Class)titles andjest.fn(Class).name. Notes has the list.toStrictEqualcompares class names. An instance of an anonymous default class now equals a like instance of another class nameddefault.export default function () {}from a file is still<file>_default(js_parser: attach the generated default name to an anonymous default function only in a bundle #34932). An async arrow with anawaithas an unnamed frame.Notes
This is a draft for CI.
WEBKIT_VERSIONis the preview build of oven-sh/WebKit#950 (autobuild-preview-pr-950-c5dabb29: Bun's pin 4d45755fed plus that one commit). It must not merge like this. Before a merge the pin has to be a merged WebKit commit with builds for every platform. Fork main is ahead of Bun's pin (six commits on 1 November), so the commit reaches Bun main with the first pin bump after it merges, or with a bump to a merged commit of this one alone. The tests and the two source changes here land with that bump or right after it.Where the rule is. oven-sh/WebKit#950 has the cause, the design, the measurements and four questions for a maintainer: is the change of text wanted, is JavaScriptCore the place, patch or minor release, and one compare per new function. #38517 and #38530 are the other place for the rule, a filter in Bun's bindings. They stay open until that is answered. If WebKit#950 merges, this pull request takes their place. #34932 lands after this one.
Text that changes
bun test -u:[class starDefault]to[class default],starDefault {}todefault {},<starDefault />to<default />,Any<starDefault>toAny<default>. GitHub code search finds no publicBun Snapshot v1file withstarDefaultin it, of about 5,400.describe(Class),test(fn)and.each:starDefault > titletodefault > title. A snapshot key, a JUnit name and a-tpattern withstarDefaultin it change with the title. Such a-tpattern matches nothing any more.jest.fn(fn).nameandmock(fn).getMockName():"default"for every form in the table below. Bun 1.4.3 gives"". Main gives"starDefault"since bun test: Vitest compatibility #44833, which reads the raw name of the function (copyNameAndLengthinsrc/jsc/bindings/JSMockFunction.cpp).jest.fn(fn).getMockName()is"jest.fn()"on main, whateverfnis.at new default, wasat new starDefault. A tool that groups errors by frame text sees a new group once.Cannot call a class constructor default without |new|,Cannot return a non-object type in the constructor of a derived class default.export default class {}has a scope with a variabledefault. A class with a written name shows its name there in the same way.What gets the name, by form
export default class {}defaultin frames,Bun.inspect, snapshots, both class errors, mock names, heap snapshots, the debugger,--cpu-profexport default () => {},(function () {}),(class {})defaultin frames,Bun.inspectand mock names. The name is inferred, so a snapshot prints[Function]or[class], and the class errors have no nameexport default function () {}, alsofunction*andasync function<file>_defaultfirst, as before (#34932). They getdefaultwhere the text reaches JavaScriptCore as written:node:vmSourceTextModule, and a file that starts with// @bunexport default async () => { await x }Not changed
displayName, still change text once.nameis read (functionNameinsrc/jsc/bindings/ErrorStackTrace.cpp). That is not about default exports.starDefault, and so are three SyntaxErrors for a secondexport defaultdeclaration in one module. They show the binding, not the function.Error.captureStackTrace(err, fn)matches frames by name as before. A check of the source range emptied the stack forreturn super.method(), so it is not here (see Error.captureStackTrace: keep frames when the callee frame was elided by a tail call #32136).<file>_default, as before.Source changes
JSC__JSValue__getNameProperty: the deleted arm returned the raw name of the executable whenJSFunction::name()was empty. With the new engine the two are the same string. bun:test: print a function's own name instead of its AsyncFunction / GeneratorFunction tag #38959 changes the same function, and the second of the two to land rebases.finish_constructor_headerin the ahead-of-time compiler: a class declaration with no name isexport default class {}. It keepsHAS_EXPLICIT_NAMEwhen the name is its own, assrc/aot/lower/function/header.rsdoes forexport default function () {}. Compiled code then gives the same mock names, class errors and snapshot text as plain Bun. Before, plain Bun saidstarDefaultthere and compiled code said nothing.defaultfor an anonymous default export WebKit#950: +2 instructions for each function executable that is made, and name reads get cheaper..textofbun: 108,951,913 to 108,951,337 bytes (-576)..rodata,.dataand.bssdo not change.require("typescript"): +56,613 user instructions of 814,854,823 (+0.007%, minimum of 9 interleaved rounds, control +7,940). After that load, no count inheapStats().objectTypeCountsdiffers (70 object types).test/bundler/compile-aot-size.test.ts, red on main for its budget) reports the bytes that a trimmed--aothello world maps: 25,953,252 here and 25,961,444 on main at the same commit (builds 136363 and 136333), 8,192 fewer.Tests
test/js/bun/test/stack.test.ts:error.stack,CallSite#getFunctionName, the two class errors and Bun's error printer, for nine forms, before and after.nameis read.test/js/bun/util/inspect.test.js,test/js/bun/test/snapshot-tests/snapshots/moremore.test.ts:Bun.inspectand snapshot text.test/js/bun/test/mock-fn.test.js,test/js/bun/test/describe.test.ts: mock names anddescribe(Class)titles.test/js/node/v8/capture-stack-trace.test.js:Error.captureStackTracewith a resumed anonymous default function.test/js/bun/util/v8-heap-snapshot.test.ts,test/cli/inspect/inspect.test.ts,test/cli/run/cpu-prof.test.ts,test/v8/v8.test.ts: both heap snapshot formats, the debugger, an inlined frame of the profiler,v8::Function::GetName.test/cli/test/test-aot.test.ts,test/aot/ir/lower/classes/anonymous-class-names.ts,test/aot/parallel/errors/names-of-functions-behind-export-default: compiled code against plain Bun.bun testfiles 1,178 tests pass and 1 fails. Intest/v8/v8.test.tsI ran the new test alone, and it passes.a matcher after .resolves or .rejects waits for a pending promiseintest/cli/test/test-aot.test.ts, a test that main has. On a debug build it fails with both engines: plainbun testprints one more<anonymous>frame than the compiled run.cargo test -p bun_aot --test ir_tests: the changed file passes. The programs undertest/aot/parallelneed a canary build, so CI runs them.treetrum/amazon-kindle-bulk-downloaderissue 148 andextractus/article-extractorissue 412 haveat starDefaultframes. Neither asks for another name.no test proof · iteration 3 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/v8/v8.test.ts, test/js/bun/util/inspect.test.js, test/js/bun/test/describe.test.ts, test/cli/run/cpu-prof.test.ts