fix: Function.prototype.apply 解除实参上限,native 大实参集走 spill 溢出区

apply 曾把实参数组静默截断到 55 个(寄存器窗口限制),导致
String.fromCodePoint.apply(null, codePoints) 这类大参数集调用只取前 55 个。
改为直接物化全部实参经 TailCall 下发:字节码目标走帧参数区(spill 栈)任意
个数;native 目标新增 spill 溢出区通道,寄存器窗口(253)之外的实参转存
spill 栈(GC 根、嵌套安全),builtin 经 VmHost::native_arg_count/native_arg_at
读取,String.fromCodePoint 迁移为新接口。大实参集不再静默截断。
This commit is contained in:
2026-08-15 20:31:32 +08:00
parent 9e16d7687c
commit cec7d04b86
6 changed files with 143 additions and 28 deletions
+30
View File
@@ -72,6 +72,36 @@ pub trait VmHost {
fn reg(&self, idx: u8) -> JsValue;
fn set_reg(&mut self, idx: u8, val: JsValue);
/// 当前 native 调用 spill 溢出区的实参个数(寄存器窗口 253 之外的部分)。
///
/// # 边界与前提
/// - 仅在 native 实现内部、实参仍有效时读取;无溢出时为 0。
fn native_overflow_count(&self) -> usize;
/// 读 spill 溢出区第 `i` 个实参(0 基,相对溢出区起点)。
///
/// # 边界与前提
/// - `i` 必须小于 [`Self::native_overflow_count`]。
fn native_overflow_at(&self, i: usize) -> JsValue;
/// 当前 native 调用的完整实参个数(寄存器窗口 + spill 溢出区,不含 receiver)。
///
/// 供支持大实参集的 builtin(如 `String.fromCodePoint`)遍历全部实参;
/// 未迁移的 builtin 仍按 `args` 索引读寄存器,行为不变。
fn native_arg_count(&self, args: &[u8]) -> usize {
args.len().saturating_sub(1) + self.native_overflow_count()
}
/// 第 `idx` 个实参(0 基,不含 receiver):窗口内读寄存器,窗口外读 spill 溢出区。
///
/// # 边界与前提
/// - `idx` 必须小于 [`Self::native_arg_count`]。
fn native_arg_at(&self, args: &[u8], idx: usize) -> JsValue {
let window = args.len().saturating_sub(1);
if idx < window {
self.reg(args[idx + 1])
} else {
self.native_overflow_at(idx - window)
}
}
// 对象分配 / 字符串创建
fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject;
fn new_string(&mut self, s: &str) -> JsValue;