Repository navigation
Blob, Request, Response, structuredClone, alert, confirm, prompt: set the length of the Web IDL - #48470
Blob, Request, Response, structuredClone, alert, confirm, prompt: set the length of the Web IDL#48470robobun wants to merge 1 commit into
length of the Web IDL#48470Conversation
…b IDL Web IDL sets the `length` of an operation to the length of its shortest argument list, and the `length` of an interface object to that of its constructor. These functions had another value: Blob.prototype.slice 2 -> 0 Blob.prototype.stream 1 -> 0 Request 0 -> 1 Request.prototype.clone 1 -> 0 Response.prototype.clone 1 -> 0 Response.json 0 -> 1 Response.redirect 0 -> 1 alert, confirm, prompt 1 -> 0 structuredClone 2 -> 1 Three functions hold a copy of the same constants and get the same values: `clone` of the request that a `Bun.serve` route receives, and `slice` and `stream` of a `BuildArtifact`. No call changes. `clearTimeout` and `clearInterval` keep 1: they are the exports of `node:timers`, which have 1 in Node.js.
|
Status How I reproduced it: console.log(Blob.prototype.slice.length, Blob.prototype.stream.length, Request.length, Request.prototype.clone.length, Response.prototype.clone.length, Response.json.length, Response.redirect.length, alert.length, confirm.length, prompt.length, structuredClone.length);
CI on 81d4d8f (build 139757):
PR: #48470 |
|
Updated 8:52 AM PT - Nov 10th, 2026
✅ @robobun, your commit 81d4d8f4c4703b9816ed86b31b26d70250cff4ab passed in 🧪 To try this PR locally: bunx bun-pr 48470That installs a local version of the PR into your bun-48470 --bun |
|
No actionable comments were generated in the recent review. WalkthroughThis change updates declared JavaScript function lengths for selected Bun globals, request and response APIs, Blob methods, and BuildArtifact methods. Tests add length assertions for these APIs and update one response-size snapshot. ChangesFunction length metadata
Possibly related PRs
Suggested reviewers: Merge Risk:
|
There was a problem hiding this comment.
I reviewed this PR and didn't find any bugs; the constant edits and tests look correct. A maintainer should still weigh in because structuredClone.length 2 -> 1 is a deliberate move away from Node.js (the description offers to drop that row), which is a policy call rather than a correctness one.
What was reviewed:
- Each new value against the quoted Web IDL (shortest argument list), and the bindgen default for the
clonehelper once its explicitlength: 1is removed (0 required args,requiredCountinsrc/codegen/bindgen-lib-internal.ts);Response.errordoes not use thefactoryhelper, so the test'serror: 0is consistent. - The
8.81 KBinline snapshot: the post-edittest/js/web/fetch/response.test.tsis 8808 bytes, whichSizeFormatterinsrc/bun_core/fmt.rsrenders as8.81 KB(and the old 8026 bytes as8.0 KB). - Searched
test/for assertions or snapshots on the old lengths (includingvisible-surface.test.ts.snap): none reference these functions, so nothing stale remains. - Not run: the new tests themselves (
bun bd test/USE_SYSTEM_BUN=1); failure-on-main is inferred from the old constants, not observed.
Extended reasoning...
The change edits 13 integer length values across one C++ HashTableValue entry (src/jsc/bindings/JSBunRequest.cpp), four rows of src/jsc/bindings/ZigGlobalObject.lut.txt, and three bindgen schemas (Blob.bind.ts, response.bind.ts, JSBundler.bind.ts), plus new .length assertions in five existing test files and one snapshot size update. It touches no security-sensitive surface: no call behavior, argument handling, or memory management changes, only the JS-visible Function.prototype.length. The values match the Web IDL rules cited and the bindgen default was confirmed by reading requiredCount; the snapshot size was verified from the blob size at HEAD. The decision to defer rather than approve rests on the one user-visible compat divergence from Node.js (structuredClone.length) that the author explicitly leaves to maintainer preference, and on the fact that no tests were executed in this review.
Problem
lengththat differs from their Web IDL:Blob.prototype.sliceandstream,Request,Request.prototype.clone,Response.prototype.clone,Response.json,Response.redirect,alert,confirm,prompt,structuredClone.Request.lengthis 0, where the IDL gives 1.src/runtime/webcore/Blob.bind.ts:45and:48,src/runtime/webcore/response.bind.ts:25,:37and:65,src/jsc/bindings/ZigGlobalObject.lut.txt:6,:12,:16and:23.Fix
lengthchanges, no call result.clearTimeoutandclearIntervalkeep 1 (IDL: 0): asnode:timersexports they follow Node.js.TextEncoder.prototype.encodeis TextEncoder: set thelengthofencodeto 0, as its IDL requires #48395.Background
lengthto the length of the shortest argument list:optionaland variadic arguments do not count.lengthkey in a bindgen schema (*.bind.ts) sets the value.Function Nsets it inZigGlobalObject.lut.txt, the global object's property table.b.any, soBlob(parts, options)(0) andRequest(input, init)(1) look alike.Downsides
structuredClone.lengthbecomes 1, and Node.js v26.3.0 has 2.Response.json()andResponse.redirect()still accept no argument. WPTidlharnessgains one subtest only: it checksconfigurablefirst, and these members are not.bun-linux-x64, -3.0 KB onbun-windows-aarch64, +0.0 KB elsewhere (Notes).Notes
The 14 values
Blob.prototype.sliceBlob.prototype.streamRequestRequest.prototype.cloneResponse.prototype.cloneResponse.jsonResponse.redirectalert,confirm,promptstructuredClonecloneof the request that aBun.serveroute receives (src/jsc/bindings/JSBunRequest.cpp:52)Request.prototype.cloneBuildArtifact.prototype.slice(src/runtime/api/JSBundler.bind.ts:24)Blob.prototype.sliceBuildArtifact.prototype.stream(src/runtime/api/JSBundler.bind.ts:28)Blob.prototype.streamclone, which hidesRequest.prototype.clone.BuildArtifactisextends Blobinbun-typesand has its ownsliceandstream.File.prototype,Bun.file(), S3 files,node:bufferandundiciuse the same function objects as the globals, so they need no edit.Repro
2 1 0 1 1 0 0 1 1 1 2Worker,ShadowRealm,bun build --compile --aot0 0 1 0 0 1 1 0 0 0 10 0 1 0 0 1 1, no dialogs, then2The IDL lines (
interfaces/FileAPI.idl,fetch.idlandhtml.idlof web-platform-tests)Where the old values come from
clone1: 93b663e (2022-08-17).slice2 andstream1: 2c19269 (2022-09-22). The four table rows: 29b2217 (2023-09-15, feat(runtime): addprocess.bindinguv/natives/config+ make global object properties lazy #5355).Request,Response.jsonandResponse.redirecthad the default 0 of the old class generator.generate-classes.ts#46329 moved the classes to bindgen schemas and kept everylength. No commit and no review comment gives a reason for one of these values.new Request()throws, andstructuredClone()throws "structuredClone requires 1 argument" (src/jsc/bindings/webcore/StructuredClone.cpp:45). So for both, the oldlengthalso differs from what Bun requires.URL.canParse), FixqueueMicrotask.lengthto be 1 #32419 (queueMicrotask, the same table), MoveCrypto,TextDecoder,Bun.FileSystemRouterand three more classes to bindgen schemas #45888 (three values, "as Node and the IDL").Audit
@webref/idl3.86.0 withwebidl2. Compute the shortest argument list of each constructor, operation and static operation. Compare it withfn.lengthof each function that exists onglobalThis.WindowandWorkerGlobalScope). On this branch 5 rows differ:clearTimeoutandclearInterval(twice each) andTextEncoder.prototype.encode.globalThis. It does not see the three copies in the table above. Those came from a search for the same constants.Not changed here
clearTimeoutandclearInterval(1, IDL 0).globalThis.clearTimeoutis the same function object asrequire("node:timers").clearTimeout, and Node.js has 1 for both.test/js/node/__snapshots__/visible-surface.test.ts.snap:2696and:2710record the 1.structuredCloneis different: nonode:*module exports it, in Bun or in Node.js. The landing rules of this repo make the specification and WPT the reference for a Web-standard API, not Node.js, so it gets the IDL value. This is the one value that moves away from Node.js. If a maintainer prefers the value of Node.js, I remove that row.TextEncoder.prototype.encode(1, IDL 0) is TextEncoder: set thelengthofencodeto 0, as its IDL requires #48395, from the same audit. Its table is insrc/jsc/bindings/webcore/JSTextEncoder.cpp.Response.json()andResponse.redirect()with no argument. Node.js throwsTypeError: Response.json: 1 argument required, but 0 found.Bun returns a response for both. The comment onfactoryinresponse.bind.tsnow states this.Blob,RequestandResponseare not configurable (Object.getOwnPropertyDescriptor(Blob.prototype, "slice").configurableisfalse). The IDL and Node.js havetrue. It is the default of a bindgen class. Make Crypto.prototype members configurable #40617 proposes the change forCrypto.src/jsdefines:require("node-fetch").Request.lengthis 2 and.Response.lengthis 2,require("undici").WebSocket.lengthis 2. Theirlengthcomes from a JS parameter list, and the reference is the npm package.What WPT idlharness measures
test/js/third_party/wpt-streams/resources/idlharness.jsis the vendored harness. Its runner covers the streams classes only.A stand-alone run of that harness with
interfaces/FileAPI.idlandinterfaces/fetch.idl, with the global presented asWindow(as the WPT runner of Node.js does):The subtest that changes is
Request interface object length.For an operation, the harness asserts
configurablebeforelength(idlharness.js:2359, then:2370). Soslice,stream,clone,jsonandredirectfail on "property should be configurable" before and after. Theirlengthis the next assertion.alert,confirmandpromptareWindowonly. No run of the harness in Bun reaches them.This is why the new tests are hand-written tables, in the form of Move
Crypto,TextDecoder,Bun.FileSystemRouterand three more classes to bindgen schemas #45888 ("has the lengths of the IDL").What did not change
blob.slice()with 0 to 3 arguments,blob.stream(4),new Request()(throws),Response.json(),Response.json(undefined)(throws),Response.redirect(),request.clone(1, 2)andstructuredClone()(throws).Blob.length,File.length,Response.length,Response.error.length,Blob.prototype.write.lengthandBlob.prototype.writer.lengthstay0 2 0 0 2 1..bind()follows:Blob.prototype.slice.bind(blob).lengthis 0 (was 2).Who reads these values
.lengthfinds no reader in the 81,480 tracked files, outside the new tests.test/node_modulesandnode_modules.core-js3.37.1 replaces a native function whoselengthis wrong. It checksqueueMicrotaskandURL.canParseand citesqueueMicrotask.lengthshould be 1 #9249 andURL.canParse.lengthshould be 1 #9250 for them. It has no such check for these eleven.Measurements (debug builds of main 98cda0d and of this branch)
GeneratedBlob.cpp2,GeneratedResponse.cpp5,GeneratedJSBundler.cpp2,ZigGlobalObject.lut.h4). +0 host functions (2649) and +0 table rows (2375). The generated Rust,Bindgen.d.tsand every other generated file are byte-identical (diff -rofbuild/debug/codegen).text,dataandbssofbun-debugare 847392166, 79802913 and 12228904 bytes on both (llvm-size). In four of the five recompiled objects, 9 bytes differ in total (cmp -l). In the fifth,setupRequestClassStructureis 3 bytes shorter at-O0.setupRequestClassStructure, which runs once per realm (llvm-objdump -dof the five objects).bun-linux-x64and -3.0 KB onbun-windows-aarch64. 4.0 KB is one page.bun-windows-aarch64too, so that value does not come from this change.bun-linux-x64is not attributed to a section. My container cannot download the release artifacts of CI, andbloaty,perf,valgrindandstraceare not installed there.Tests
test/js/web/fetch/blob.test.ts), "Request has the lengths of the IDL" (test/js/web/request/request.test.ts), "Response has the lengths of the IDL" (test/js/web/fetch/response.test.ts), "alert, confirm, prompt and structuredClone have the lengths of the IDL" (test/js/web/web-globals.test.js), "request.clone, which is not Request.prototype.clone, has the length of the IDL" (test/js/bun/http/bun-serve-routes.test.ts).src/of main,bun bd testfails these six tests and passes the other 326 tests of the five files. With this branch all 332 pass.BuildArtifacttest is inblob.test.ts.test/bundler/bun-build-api.test.tshas a test of 400 builds that does not finish in 180 s on a debug build in my container.Response (8.81 KB)inresponse.test.tsis the size of that test file. Each change of the file changes it.test/internal/bindgen.test.ts(244 pass), and the five structured clone files undertest/js/web(451 pass, 1 skip).test/internal/builtins-table/(149 pass, 1 skip), which runs the generators of the table of built-ins on these schemas, andtest/integration/bun-types/bun-types.test.ts(30 pass).test/js/node/visible-surface.test.tshas no line for one of the 14 values.Open PRs on the same lines
b.anyand.variadic#46789 rewrites these schemas and keeps the old numbers. At its head b2011a2 they are atsrc/runtime/webcore/response.bind.ts:76(clone, 1),:88(Request, 0),:155(Response.json, 0) and:162(Response.redirect, 0),src/runtime/webcore/Blob.bind.ts:49(slice, 2) andsrc/runtime/api/JSBundler.bind.ts:25(slice, 2). The twostreamentries (Blob.bind.ts:52,JSBundler.bind.ts:29) have no key and derive 1. The one that lands second must carry the new numbers. The new tests fail if it does not.@nativesowns, not the globals at first load #48081 rewrites six other rows of the same table inZigGlobalObject.lut.txt. The conflict is textual.typethat is not a string innew Blob,new Fileandslice#47740 and Blob.slice(): convert start/end as WebIDL [Clamp] long long (round half to even) #41961 change other hunks ofweb-globals.test.js,Blob.bind.tsandblob.test.ts.Self-review
structuredCloneis in this PR. TheResponsetest does not assert thatResponse.redirect()with no argument returns a 302. The body gives the origin, the measured effect on idlharness, and each exclusion with its reason.lengthofencodeto 0, as its IDL requires #48395 into this PR: TextEncoder: set thelengthofencodeto 0, as its IDL requires #48395 is open and has passed its checks, so this PR links to it. To file an issue forclearTimeoutandclearInterval: their value is a decision with a reason, not work that is left.