Both handlers called fn.ClearWeak() and fn.SetWeak() without checking
whether the persistent handle was still populated. After the callback
function is garbage collected following an earlier unrefCallback()
call, the handle is empty and both V8 methods dereference a null slot.
InvokeCallback() already guarded against this. Add the same check to
both handlers and throw ERR_INVALID_ARG_VALUE, matching the existing
behavior for a pointer that is not in the callback map.
Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com>
Assisted-by: claude:opus-5
PR-URL: https://github.com/nodejs/node/pull/64881
Fixes: https://github.com/nodejs/node/issues/64880
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
The text said functions with more than 7 arguments fall back to the
generic call path, contradicting the preceding sentence that Fast FFI
calls support up to 8 arguments.
The real limit is architecture- and type-dependent: the hard cap is 8
public arguments (src/ffi/fast.cc), but register pressure lowers the
effective count to 7 integer/pointer arguments on AArch64 and 6 on
x86-64, while floating-point arguments can use up to 8 on both. Describe
this instead of the previous, inaccurate single threshold.
Signed-off-by: Daijiro Wachi <daijiro.wachi@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/63960
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Add fast API trampolines for AArch64 and x86_64.
IsJitMemorySupported() maps an RW page, writes a ret instruction
(0xD65F03C0 AArch64 / 0xC3 x86_64), and mprotects it to RX. A
successful RX transition is treated as the support signal. The page is
deliberately not executed: this check may run during normal operation
(when an FFI function is first created), and executing freshly written
code from a capability probe could SIGSEGV/SIGKILL the process on
systems that block executable memory. The real trampoline emitter
performs the same mprotect at creation time and falls back to libffi
when it is rejected.
The result is computed once via std::call_once and cached for the
lifetime of the process, so concurrent callers never observe a
provisional value.
Wired into CreateFastFFIMetadata() for early nullptr bail-out when
JIT memory is unavailable.
Windows stub returns false (no trampolines yet on this branch).
IsFastCallEligible() validates at parse time whether a signature can
use the fast-call path, covering:
- Return and argument type eligibility (numeric, pointer; no structs)
- Argument count cap (8, matching V8 fast-call limit)
- Per-ABI register pressure limits that mirror the trampoline emitters
(AArch64 and x86_64 SysV). Platforms without an emitter, including
Win64, are reported ineligible.
- Buffer and float args cannot coexist, and buffer args additionally
consume an extra GP register slot on both supported ABIs.
The arg/arg-name lengths are checked before the per-arg loop so a
malformed signature cannot index out of bounds.
Returns nullptr from CreateFastFFIMetadata() for ineligible signatures,
falling back to libffi.
Co-authored-by: Bryan English <bryan@bryanenglish.com>
Signed-off-by: Paolo Insogna <paolo@cowtech.it>
Signed-off-by: Bryan English <bryan@bryanenglish.com>
Assisted-By: OpenAI:GPT-5.5 <openai/gpt-5.5>
PR-URL: https://github.com/nodejs/node/pull/63068
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>