Repository navigation
Conversation
Collaborator
Author
|
Updated 1:57 AM PT - Nov 10th, 2026
✅ @robobun, your commit 88c790d88905404c68944d52ed80c7ab473d6998 passed in 🧪 To try this PR locally: bunx bun-pr 48408That installs a local version of the PR into your bun-48408 --bun |
Collaborator
Author
|
Status
|
…has when bind() runs The tests need two JavaScriptCore changes. Function.prototype.bind() reads the name of a target that was named by a key in brackets or made for a native accessor. A static accessor called name or length that is defined by itself goes through the defineOwnProperty of the class. The next commit moves WEBKIT_VERSION to a build that has both.
The engine that main pins (ee59053d32) plus that change: bind() reads the name of a target that was named at run time, and a static accessor that is defined by itself goes through the defineOwnProperty of the class.
robobun
force-pushed
the
robobun/cb47260c/bind-reads-run-time-name
branch
from
November 10, 2026 04:09
ac470a1 to
88c790d
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
a.one.bind(a).nameis"bound "since 1.4.3 (1.4.2:"bound one").nameproperty, andbind()took the name from the code. Any{ [key]() {} }has it too.class A { static get name() { return "custom" } @dec m() {} }binds asbound A.Fix
bind()read such a name, and defines a lone static accessor throughJSFunction::defineOwnProperty. This PR pins its build.nameinsidebind(), and Node answers every row the same.test/js/bun/jsc/bound-function-name.test.ts(22 of 23 fail onmain),es-decorators.test.ts, two jsc-stress fixtures, fourtest/aot/parallelprograms.Background
WEBKIT_VERSION(scripts/build/deps/webkit.ts) names the oven-sh/WebKit release the build links.bind()defers the name and length of its result while the target's are original.Downsides
bind()of such a function costs 344 → 529 instructions in JIT code. Five decorated methods bound per instance: 2320 → 3240 (1.4.2: 765).console.log(a.one)still prints[Function](inspect: display a class or function name set with Object.defineProperty #38922).src/ofmain.Notes
Where this comes from. No user reported it. A comparison with Node found it, and there is no issue to link.
The pin. The preview build is the engine that
mainpins (ee59053d32) plus the two commits of oven-sh/WebKit#1023, so nothing else of the engine moves. All 7Testjobs of that pull request pass on those commits.The base moved once. #48120 moved
mainfrom the engine 5a61071f79 to ee59053d32. Both branches were rebased onto the new base. The two engine commits did not change (git range-diff), and the tests below ran again on the new base.Rows. Standard decorators, run from a folder with no tsconfig. The last column is a debug build with this pin, and Node agrees with it where Node can run the row.
class A { @keep one() {} },a.one.bind(a).namebound onebound oneboundbound onebound <name>bound <name>boundbound <name>bound get gbound get gbound getbound get gclass A { @keep #one() {} call() {} },a.call.bind(a).namebound callbound callboundbound calladdInitializer(function () { this[name] = this[name].bind(this) }),a.onTick.namebound onTickbound onTickboundbound onTickaccessorbound getbound getbound getbound get accclass A { static get name() { return "custom" } @keep m() {} },A.bind().namebound custombound custombound Abound customclass B { constructor(a, b) {} static get length() { return 9 } @keep m() {} },B.bind(null, 1).length({ [k]() {} })[k].bind(null).nameboundboundboundbound oneObject.getOwnPropertyDescriptor(Request.prototype, "url").get.bind().namebound get urlbound urlbound urlbound get url"bound " + name: 632 of 4466 on a debug build ofmain, 0 of 4466 with this pin. Node v26.3.0: 0 of 3406.Tests.
test/js/bun/jsc/bound-function-name.test.ts: 17 kinds of target throughnew Function(so that the transpiler leaves the keys as written), four native accessor functions, the name at the time ofbind(), and the length.test/bundler/transpiler/es-decorators.test.ts: every kind of decorated member, the member that holds the list of a#privateneighbour,addInitializerwithbind, abindinside the decorator, and a class with a static accessor callednameorlengthin front of a decorated member.test/js/bun/jsc-stress/fixtures/: the two files of [JSC] bind() reads the name of a target that was named at run time WebKit#1023 as they are there, with// @bunas the first line. They run in every tier.test/aot/parallel/inferred-names/a-bound-class-takes-the-name-and-the-length-that-its-static-accessors-answer.mjsis new: 24 rows of bound names and lengths, the executable against plain Bun.a-bound-function-takes-the-name-that-a-computed-key-gave-its-target.mjs(plain Bun now prints it), acontinueinclosed-bugs/1600-bound-function-under-a-computed-key, and areturninanswers-of-the-engine/function-objects-of-every-kind.mjs. All four pass in every way of the runner with this pin (21 builds). A run of 64 programs undertest/aot/parallelthat bind a function and read a name passes too (258 builds, three of the four among them).test/aot/fuzz/generators/object-mutations.tsno longer leaves out the bind of a method under a computed key. 300 programs of that product (--holders=computed-method-in-literal --looks=bind-it --sample=300 --seed=1) print the same as Node v26.3.0 with this pin. 241 of them differ onmain.mainand of this pin, 1584 tests in 46 files (test/js/bun/util/inspect*,test/js/node/util/node-inspect-tests,test/js/bun/test/mock-fn.test.js,test/js/bun/jsc*,bundler_bytecode_portable.test.ts): no test fails with this pin and passes onmain. 30 tests fail on both builds in the container that ran them: 28 pass a time limit of the debug build, and 2 fail behind such a timeout. With a longer limit one of the 30 still fails, on the stored hash of a bundle of libraries fromtest/node_modules. The text that the bundler writes for it differs in that container, before the engine sees it. Every other hash ofbundler_bytecode_portable.test.tsis the stored one with this pin.Text that changes.
console.log(a.one.bind(a))prints[Function: one]again, as 1.4.2 did.String(bound)givesfunction one() { [native code] }for these targets, as it does for a target with a plain key.Cost. The numbers are from release builds of the engine at 5a61071f79 and at this change on that base. The files of the
bind()path are the same at ee59053d32. oven-sh/WebKit#1023 has the full table. A function with a name in the code binds at the same cost (34.9 instructions in JIT code on both). Node v26.3.0 pays 21,901 instructions per instance for the class with 5 bound methods as Bun lowers it, and 2,339 with plain keys.Left for other changes.
console.log(fn)andBun.inspect(fn)of an unbound function under a key in brackets: inspect: display a class or function name set with Object.defineProperty #38922.spyOn(fn, "length")orspyOn(fn, "name")is not seen by a laterfn.bind(). That is so on every release and has its own change.bound.bind()in JIT code converts alengththat is not a number: upstream's [JSC] Read the bind target's prototype before length and name WebKit/WebKit#75885, which has its own change.[policy-decision:webkit] gate passed · iteration 0 · 11 files touched
passes on PR (with fix)
diff hotspot
gate history · 1 passed · 0 rejected · iteration 0
evidence per changed file