Summary
Since 06e1ab349 (merge landing #8652/#8651/#8647/#8646/#8650), constructing ArrayBuffer / SharedArrayBuffer / DataView via a captured value throws TypeError: Constructor requires 'new' — e.g. const D = DataView; new D(buf), Reflect.construct(DataView, [buf]), class X extends DataView {}, const AB = globalThis.ArrayBuffer; new AB(8). Direct new DataView(buf) / new globalThis.DataView(buf) still work.
Minified/bundled code captures globals into locals pervasively, so this breaks module init broadly. The natively-compiled Claude Code cli.js 2.1.112 bundle throws this at startup on essentially every command (--help, doctor, mcp list, -p, …; only --version escapes via its early exit). Node runs the same bundle fine.
Repro
const ab = () => new ArrayBuffer(8);
const D = DataView; new D(ab()); // THROWS (node: ok)
Reflect.construct(DataView,[ab()]); // THROWS (node: ok)
class MV extends DataView {} new MV(ab()); // THROWS (node: ok)
const AB = (globalThis as any).ArrayBuffer; new AB(8); // THROWS (node: ok)
new DataView(ab()); // ok
new (globalThis as any).DataView(ab()); // ok
Map/Set/Promise/Uint8Array captured-then-constructed all work — only AB/SAB/DataView regressed.
Root cause
06e1ab349 switched ArrayBuffer/SharedArrayBuffer/DataView's global [[Call]] behavior from global_this_builtin_noop_thunk to the shared construct_only_builtin_call_thunk (global_this/populate.rs:203). But identify_global_builtin_constructor (class_registry/class_meta.rs) — the func_ptr allow-list the dynamic-new path uses to recover a captured builtin's name and route it to construct.rs's real factory — was never updated to accept the new thunk. So a captured AB/SAB/DataView closure is no longer recognized (returns None), never reaches the "ArrayBuffer" | "SharedArrayBuffer" / "DataView" arms in construct.rs:891/900, and falls through to invoke the bare-call thunk → "Constructor requires 'new'". The previous noop thunk was in the allow-list, which is why this worked before 06e1ab349. Same bug class the WeakMap/RegExp comments in that function already document.
Fix
Add construct_only_builtin_call_thunk to the identify_global_builtin_constructor allow-list — the name-dispatch construct arms already exist. PR incoming.
Summary
Since
06e1ab349(merge landing #8652/#8651/#8647/#8646/#8650), constructingArrayBuffer/SharedArrayBuffer/DataViewvia a captured value throwsTypeError: Constructor requires 'new'— e.g.const D = DataView; new D(buf),Reflect.construct(DataView, [buf]),class X extends DataView {},const AB = globalThis.ArrayBuffer; new AB(8). Directnew DataView(buf)/new globalThis.DataView(buf)still work.Minified/bundled code captures globals into locals pervasively, so this breaks module init broadly. The natively-compiled Claude Code cli.js 2.1.112 bundle throws this at startup on essentially every command (
--help,doctor,mcp list,-p, …; only--versionescapes via its early exit). Node runs the same bundle fine.Repro
Map/Set/Promise/Uint8Arraycaptured-then-constructed all work — only AB/SAB/DataView regressed.Root cause
06e1ab349switched ArrayBuffer/SharedArrayBuffer/DataView's global [[Call]] behavior fromglobal_this_builtin_noop_thunkto the sharedconstruct_only_builtin_call_thunk(global_this/populate.rs:203). Butidentify_global_builtin_constructor(class_registry/class_meta.rs) — the func_ptr allow-list the dynamic-newpath uses to recover a captured builtin's name and route it toconstruct.rs's real factory — was never updated to accept the new thunk. So a captured AB/SAB/DataView closure is no longer recognized (returnsNone), never reaches the"ArrayBuffer" | "SharedArrayBuffer"/"DataView"arms inconstruct.rs:891/900, and falls through to invoke the bare-call thunk → "Constructor requires 'new'". The previous noop thunk was in the allow-list, which is why this worked before06e1ab349. Same bug class the WeakMap/RegExp comments in that function already document.Fix
Add
construct_only_builtin_call_thunkto theidentify_global_builtin_constructorallow-list — the name-dispatch construct arms already exist. PR incoming.