Independent reproduction
No application bundle, credentials, network, subprocess, or extraction work is
required. Node 26.5.1 passes this program:
const hooks = process.getBuiltinModule('async_hooks');
const calls = [];
const callback = hooks.AsyncResource.bind(value => {
calls.push(value);
return value + 1;
});
console.log('created', typeof callback);
try {
const value = callback(41);
if (value !== 42 || calls.length !== 1 || calls[0] !== 41)
throw new Error('wrong callback dispatch');
console.log('PASS: builtin AsyncResource.bind');
} catch (error) {
console.error('FAIL:', String(error));
process.exitCode = 1;
}
Native Perry prints:
created function
FAIL: TypeError: Class constructor AsyncResource cannot be invoked without 'new'
Reproduced on macOS arm64 with Perry 0.5.1531: a frozen compiler and all nine
coherent runtime/provider archives from 7dc2a2700, based on current main
1a9c0de6c. The branch changes only the independent child-output fix #10042;
the dispatch code involved here is unchanged from main. Compilation used
--platform bun --enable-wasm-runtime --no-auto-optimize, LLVM Oz and the
explicit shadow-stack/full-outline profile. Compiler bound 120 s, execution
bound 10 s; this is an immediate failure, not a timeout.
Located dispatch error
object/native_module/callable_exports.rs::nm_attach_async_hooks already
attaches AsyncResource's own static bind implementation. However,
object/native_call_method.rs's early closure-property dispatch unconditionally
excludes method names apply | call | bind | toString to preserve dedicated
Function.prototype fast paths. The later common_methods::dispatch_common
bind arm calls js_function_bind on any callable receiver.
Thus this call resolves as Function.prototype.bind on the AsyncResource
constructor, rather than the constructor's own AsyncResource.bind method.
Calling the returned function then attempts an ordinary constructor invocation.
The runtime should honor an actual own method override without routing normal
inherited Function.prototype calls through generic thunks that lose
arguments-object semantics. Do not fix this by removing all bind functionality,
making constructors callable, or bypassing async-context propagation.
Required regression coverage
- The program above through
process.getBuiltinModule, plus import/require and
aliased constructor forms.
AsyncResource.bind actually calls the supplied callback, forwards arguments,
preserves its result and captured async context, and handles its optional
receiver/type arguments according to Node.
- An ordinary function with an own
bind override dispatches that override;
a non-callable own override throws rather than silently using the prototype.
- Existing Function.prototype.bind/call/apply behavior (including arguments
objects and bound functions) stays intact. Check O0/Os/Oz.
- Audit the analogous own
AsyncLocalStorage.bind and snapshot exposure,
without assuming they share every failure mode.
Application evidence / scope
The reduced bug was found in a headless CLI signal dispatcher:
getBuiltinModule('async_hooks').AsyncResource.bind(listener) creates a
listener which later throws during emission. LLDB stops at
nm_dispatch_async_hooks -> js_throw, through bound-function dispatch.
Its detached message producer subsequently rejects without closing its output
queue, leaving the consumer waiting. The application is not needed to fix or
validate this issue, and this report does not claim it is the only remaining
application blocker.
Related: #9980 concerns native constructor prototype metadata. This report
has a separate static-method call reproducer and a located dispatch bypass.
Independent reproduction
No application bundle, credentials, network, subprocess, or extraction work is
required. Node 26.5.1 passes this program:
Native Perry prints:
Reproduced on macOS arm64 with Perry 0.5.1531: a frozen compiler and all nine
coherent runtime/provider archives from
7dc2a2700, based on current main1a9c0de6c. The branch changes only the independent child-output fix #10042;the dispatch code involved here is unchanged from main. Compilation used
--platform bun --enable-wasm-runtime --no-auto-optimize, LLVM Oz and theexplicit shadow-stack/full-outline profile. Compiler bound 120 s, execution
bound 10 s; this is an immediate failure, not a timeout.
Located dispatch error
object/native_module/callable_exports.rs::nm_attach_async_hooksalreadyattaches
AsyncResource's own staticbindimplementation. However,object/native_call_method.rs's early closure-property dispatch unconditionallyexcludes method names
apply | call | bind | toStringto preserve dedicatedFunction.prototype fast paths. The later
common_methods::dispatch_commonbindarm callsjs_function_bindon any callable receiver.Thus this call resolves as Function.prototype.bind on the AsyncResource
constructor, rather than the constructor's own AsyncResource.bind method.
Calling the returned function then attempts an ordinary constructor invocation.
The runtime should honor an actual own method override without routing normal
inherited Function.prototype calls through generic thunks that lose
arguments-object semantics. Do not fix this by removing all bind functionality,
making constructors callable, or bypassing async-context propagation.
Required regression coverage
process.getBuiltinModule, plus import/require andaliased constructor forms.
AsyncResource.bindactually calls the supplied callback, forwards arguments,preserves its result and captured async context, and handles its optional
receiver/type arguments according to Node.
bindoverride dispatches that override;a non-callable own override throws rather than silently using the prototype.
objects and bound functions) stays intact. Check O0/Os/Oz.
AsyncLocalStorage.bindandsnapshotexposure,without assuming they share every failure mode.
Application evidence / scope
The reduced bug was found in a headless CLI signal dispatcher:
getBuiltinModule('async_hooks').AsyncResource.bind(listener)creates alistener which later throws during emission. LLDB stops at
nm_dispatch_async_hooks -> js_throw, through bound-function dispatch.Its detached message producer subsequently rejects without closing its output
queue, leaving the consumer waiting. The application is not needed to fix or
validate this issue, and this report does not claim it is the only remaining
application blocker.
Related: #9980 concerns native constructor prototype metadata. This report
has a separate static-method call reproducer and a located dispatch bypass.