feat: 调用实参 spread(f(...args) / new F(...args) / super(...args))

This commit is contained in:
爱特每羊队
2026-08-08 10:33:16 +08:00
parent ea86a2c0ac
commit 2777b00c82
14 changed files with 811 additions and 140 deletions
+349 -8
View File
@@ -1,9 +1,10 @@
use crate::native::NativeFn;
use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm};
use crate::{vm_debug, vm_trace};
use oxide_builtins::iterator::make_iterator_for_value;
use oxide_builtins::{builtins_debug, builtins_trace};
use oxide_bytecode::opcode;
use oxide_runtime_api::NativeResult;
use oxide_runtime_api::{to_boolean, NativeResult};
use oxide_types::object::{Cell, JsObject};
use oxide_types::value::JsValue;
use std::sync::Arc;
@@ -130,14 +131,15 @@ impl Vm {
// 函数名推断:emit 端在变量声明/对象属性赋值点设置 function_name。
let func_obj = unsafe { &mut *result.as_js_object_ptr() };
let name_si = self.kernel_core.perm_interner().intern("name").0;
let name_val = function_name.as_deref().map(|n| self.new_string(n)).unwrap_or_else(|| self.new_string(""));
let name_val = function_name
.as_deref()
.map(|n| self.new_string(n))
.unwrap_or_else(|| self.new_string(""));
self.set_or_create_prop_value(func_obj, name_si, name_val);
if !upvalue_captures.is_empty() {
// 链式捕获(parent_uv_idx):从父闭包(创建者)的 upvalues 取 cell。
let parent_upvalues: Vec<*mut Cell> = match self.current_callee() {
Some(callee) if callee.is_object() => {
unsafe { &*callee.as_js_object_ptr() }.upvalues_slice().to_vec()
}
Some(callee) if callee.is_object() => unsafe { &*callee.as_js_object_ptr() }.upvalues_slice().to_vec(),
_ => Vec::new(),
};
if let Some(current_cells) = self.cell_stack.last_mut() {
@@ -156,8 +158,7 @@ impl Vm {
if current_cells[cell_idx].is_null() {
// 占位 Cell 保持未初始化(TDZ)直到 MAKE_CELL 置位;
// 若绑定为 var 则其初始化 MAKE_CELL 在函数序言先于任何读取执行。
let cell =
self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), false));
let cell = self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), false));
current_cells[cell_idx] = cell as *mut Cell;
}
current_cells[cell_idx]
@@ -254,7 +255,8 @@ impl Vm {
if !cell.is_null() {
let c = unsafe { &*cell };
if !c.is_initialized() {
return self.raise_error_kind("ReferenceError", "Cannot access variable before initialization");
return self
.raise_error_kind("ReferenceError", "Cannot access variable before initialization");
}
self.regs[rd] = c.value;
return Ok(());
@@ -590,3 +592,342 @@ impl Vm {
Ok(false)
}
}
impl Vm {
/// 解析 spread 调用系 ext:读走 `nstatic | (nspread<<8)` 首字与 `nstatic+nspread`
/// 个有序实参字(静态字 = 寄存器号,spread 字高位标记)。
fn read_spread_ext(&mut self) -> Vec<usize> {
let header = self.bytecode[self.pc];
self.pc += 1;
let nstatic = (header & 0xFF) as usize;
let nspread = (header >> 8) as usize;
let mut words = Vec::with_capacity(nstatic + nspread);
for _ in 0..nstatic + nspread {
words.push(self.bytecode[self.pc] as usize);
self.pc += 1;
}
words
}
/// 按源码求值序物化完整实参 Vec:静态字直接读寄存器,spread 字迭代展开。
///
/// # 步骤
/// 1. 静态实参逐个从寄存器读入;spread 源经 `make_iterator_for_value` 迭代追加。
/// 2. 迭代中途抛错 → 当前迭代器经 return() 关闭(IteratorClose),异常恢复为 JS
/// 异常后 unwind。
/// 3. 展开总数超过 u16 上限(arguments_count 字段)→ RangeError。
///
/// # 返回值
/// - `Ok(Some(vec))` 成功;
/// - `Ok(None)` 异常已展开(调用方按 continue 处理);
/// - `Err` 引擎错误直接传播。
fn materialize_spread_args(&mut self, words: &[usize]) -> Result<Option<Vec<JsValue>>, String> {
let mut args = Vec::with_capacity(words.len());
let next_si = self.kernel_core.perm_interner().intern("next").0;
let done_si = self.kernel_core.perm_interner().intern("done").0;
let value_si = self.kernel_core.perm_interner().intern("value").0;
for &w in words {
if w >> 31 == 1 {
let value = self.regs[w & 0x7FFF_FFFF];
let iterator = match make_iterator_for_value(self, value) {
Ok(it) => it,
Err(err) => {
self.exception_value = Some(err);
self.pending_error_kind = Some(self.thrown_error_kind(err));
return self.unwind().map(|_| None);
}
};
loop {
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
let next_fn = match self.ordinary_get(iter_obj, next_si, iterator) {
Ok(v) => v,
Err(e) => return self.spread_iter_error(iterator, e, "Error"),
};
let result = match self.call_function_sync(next_fn, iterator, &[]) {
Ok(v) => v,
Err(e) => return self.spread_iter_error(iterator, e, "Error"),
};
if !result.is_object() {
return self.spread_iter_error(
iterator,
self.error_message_text("TypeError", "iterator result is not an object"),
"TypeError",
);
}
let result_obj = unsafe { &*result.as_js_object_ptr() };
let done = match self.ordinary_get(result_obj, done_si, result) {
Ok(v) => to_boolean(v),
Err(e) => return self.spread_iter_error(iterator, e, "Error"),
};
if done {
break;
}
let val = match self.ordinary_get(result_obj, value_si, result) {
Ok(v) => v,
Err(e) => return self.spread_iter_error(iterator, e, "Error"),
};
args.push(val);
if args.len() > u16::MAX as usize {
self.raise_error_kind("RangeError", "Too many arguments in function call")?;
return Ok(None);
}
}
} else {
args.push(self.regs[w]);
if args.len() > u16::MAX as usize {
self.raise_error_kind("RangeError", "Too many arguments in function call")?;
return Ok(None);
}
}
}
Ok(Some(args))
}
/// spread 迭代中途异常:先关闭当前迭代器(suppress return 自身错误,保留在途异常),
/// 再把原异常(native 错误经 last_uncaught_value 恢复)重新抛出并展开。
fn spread_iter_error(
&mut self, iterator: JsValue, msg: String, fallback_kind: &'static str,
) -> Result<Option<Vec<JsValue>>, String> {
let exc = self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_kind_error(self, fallback_kind, &msg));
self.close_for_of_iterator(iterator, true)?;
self.exception_value = Some(exc);
self.pending_error_kind = Some(self.thrown_error_kind(exc));
self.unwind().map(|_| None)
}
/// CALL_SPREAD:运行期物化完整实参后按目标分派(native 走值传递,字节码走帧)。
pub(crate) fn dispatch_call_spread(&mut self, rd: usize, a: usize, _b: usize) -> Result<bool, String> {
let callee = self.regs[rd];
let this_value = self.regs[a];
let words = self.read_spread_ext();
let args = match self.materialize_spread_args(&words)? {
Some(args) => args,
None => return Ok(true),
};
if callee.is_object() {
let obj_ptr = callee.as_js_object_ptr();
if !obj_ptr.is_null() {
let obj = unsafe { &*obj_ptr };
if obj.is_function() {
if obj.is_class_constructor() {
return self
.raise_type_error("class constructor cannot be invoked without 'new'")
.map(|_| true);
}
if obj.native_fn().is_some() {
match self.call_function_sync(callee, this_value, &args) {
Ok(v) => {
self.regs[0] = v;
return Ok(false);
}
Err(_) => {
let exc = self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_error(self, "call failed"));
self.exception_value = Some(exc);
self.pending_error_kind = Some(self.thrown_error_kind(exc));
return self.unwind().map(|_| true);
}
}
} else if obj.sub_module_index() > 0 {
self.push_bytecode_frame(
callee,
this_value,
&args,
None,
None,
JsValue::undefined(),
FrameContinuation::None,
)?;
return Ok(true);
}
}
}
}
self.raise_type_error("CALL target is not callable").map(|_| true)
}
/// NEW_EXPRESSION_SPREAD:物化实参后按构造函数目标分派(native 走值传递,字节码走帧)。
pub(crate) fn dispatch_new_expression_spread(&mut self, rd: usize, a: usize, _b: usize) -> Result<bool, String> {
let constructor_reg = a;
let constructor = self.regs[constructor_reg];
if !constructor.is_object() {
return self
.raise_type_error("NEW_EXPRESSION: constructor is not an object")
.map(|_| true);
}
let ctor_ptr = constructor.as_js_object_ptr();
if ctor_ptr.is_null() {
return self.raise_type_error("NEW_EXPRESSION: constructor is null").map(|_| true);
}
let ctor_obj = unsafe { &*ctor_ptr };
if !ctor_obj.is_function() {
return self
.raise_type_error("NEW_EXPRESSION: constructor is not a function")
.map(|_| true);
}
if ctor_obj.is_arrow() {
return self
.raise_type_error("arrow functions cannot be used as constructors")
.map(|_| true);
}
// native 方法(非构造器)不可 new。
if ctor_obj.native_fn().is_some() && ctor_obj.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR {
return self.raise_type_error("object is not a constructor").map(|_| true);
}
let words = self.read_spread_ext();
let args = match self.materialize_spread_args(&words)? {
Some(args) => args,
None => return Ok(true),
};
let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject;
let new_obj = self.alloc_object(JsObject::new_empty(
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
JsValue::from_js_object(proto_ptr),
));
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
if let Some(proto_val) = self.resolve_property(ctor_obj, proto_si) {
if proto_val.is_object() {
let new_obj_mut = unsafe { &mut *new_obj };
let proto_obj_ptr = proto_val.as_js_object_ptr();
let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr));
}
}
let new_obj_val = JsValue::object(new_obj as *mut u8);
if ctor_obj.native_fn().is_some() {
// native 构造器:receiver 为新对象,值传递调用。
match self.call_function_sync(constructor, new_obj_val, &args) {
Ok(v) => {
self.regs[rd] = if v.is_object() { v } else { new_obj_val };
Ok(false)
}
Err(_) => {
let exc = self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_error(self, "constructor call failed"));
self.exception_value = Some(exc);
self.pending_error_kind = Some(self.thrown_error_kind(exc));
self.unwind().map(|_| true)
}
}
} else if ctor_obj.sub_module_index() > 0 {
if self.frames.len() >= self.kernel_core.config.max_call_depth {
return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded"));
}
let this_value = if ctor_obj.is_derived_constructor() {
JsValue::undefined()
} else {
new_obj_val
};
self.push_bytecode_frame(
constructor,
this_value,
&args,
Some(rd as u8),
Some(new_obj_val),
constructor,
FrameContinuation::None,
)?;
Ok(true)
} else {
let error =
oxide_builtins::error::create_error(self, "NEW_EXPRESSION: bytecode constructors not yet supported");
self.exception_value = Some(error);
self.pending_error_kind = Some(self.thrown_error_kind(error));
self.unwind().map(|_| true)
}
}
/// SUPER_CALL_SPREAD:物化实参后按父构造器目标分派(native 走值传递,字节码走帧)。
pub(crate) fn dispatch_super_call_spread(&mut self, rd: usize, _a: usize) -> Result<bool, String> {
let words = self.read_spread_ext();
let Some(frame) = self.frames.last() else {
self.raise_error_kind("ReferenceError", "super() used outside class constructor")?;
return Ok(true);
};
if !frame.is_derived_constructor {
self.raise_error_kind("ReferenceError", "super() used outside derived constructor")?;
return Ok(true);
}
if !self.regs[254].is_undefined() {
self.raise_error_kind("ReferenceError", "super() called more than once")?;
return Ok(true);
}
let Some(derived_this) = frame.constructed_this else {
self.raise_error_kind("ReferenceError", "super() without derived this")?;
return Ok(true);
};
let new_target = self.regs[255];
if !new_target.is_object() {
self.raise_error_kind("TypeError", "super() new.target is not an object")?;
return Ok(true);
}
let new_target_obj = unsafe { &*new_target.as_js_object_ptr() };
let super_ctor = new_target_obj.proto();
if !super_ctor.is_object() {
self.raise_error_kind("TypeError", "super constructor is not an object")?;
return Ok(true);
}
let super_obj = unsafe { &*super_ctor.as_js_object_ptr() };
if !super_obj.is_function() {
self.raise_error_kind("TypeError", "super constructor is not a function")?;
return Ok(true);
}
let args = match self.materialize_spread_args(&words)? {
Some(args) => args,
None => return Ok(true),
};
if super_obj.native_fn().is_some() {
match self.call_function_sync(super_ctor, derived_this, &args) {
Ok(val) => {
// super() 返回实例的 [[Prototype]] 须设为 new.target.prototype
let instance = if val.is_object() { val } else { derived_this };
if instance.is_object() {
self.set_constructed_proto(instance, new_target_obj)?;
}
self.regs[254] = instance;
self.regs[rd] = instance;
}
Err(_) => {
let exc = self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_error(self, "super() call failed"));
self.exception_value = Some(exc);
self.pending_error_kind = Some(self.thrown_error_kind(exc));
self.unwind().map(|_| true)?;
}
}
Ok(false)
} else if super_obj.sub_module_index() > 0 {
if self.frames.len() >= self.kernel_core.config.max_call_depth {
return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded"));
}
self.push_bytecode_frame(
super_ctor,
derived_this,
&args,
Some(254),
Some(derived_this),
new_target,
FrameContinuation::None,
)?;
Ok(true)
} else {
self.raise_error_kind("TypeError", "super constructor is not callable")?;
Ok(true)
}
}
}
+18
View File
@@ -1110,18 +1110,36 @@ impl Vm {
self.dispatch_call_native(rd, a, b)?;
}
OpCode::CALL_SPREAD => match self.dispatch_call_spread(rd, a, b) {
Ok(true) => continue,
Ok(false) => {}
Err(e) => return Err(e),
},
OpCode::NEW_EXPRESSION => match self.dispatch_new_expression(rd, a, b) {
Ok(true) => continue,
Ok(false) => {}
Err(e) => return Err(e),
},
OpCode::NEW_EXPRESSION_SPREAD => match self.dispatch_new_expression_spread(rd, a, b) {
Ok(true) => continue,
Ok(false) => {}
Err(e) => return Err(e),
},
OpCode::SUPER_CALL => match self.dispatch_super_call(rd, a) {
Ok(true) => continue,
Ok(false) => {}
Err(e) => return Err(e),
},
OpCode::SUPER_CALL_SPREAD => match self.dispatch_super_call_spread(rd, a) {
Ok(true) => continue,
Ok(false) => {}
Err(e) => return Err(e),
},
OpCode::SUPER_GET_PROP | OpCode::SUPER_STATIC_GET_PROP => {
match self.dispatch_super_get_prop(rd, a, b) {
Ok(true) => continue,
+131
View File
@@ -0,0 +1,131 @@
//! 调用实参 spread 执行语义测试:`f(...args)`、`new F(...args)`、`super(...args)`。
//! 覆盖静态/混合实参顺序、迭代器协议、IteratorClose、native 目标、求值顺序。
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module)
}
fn to_str(vm: &Vm, val: JsValue) -> String {
if val.is_string() {
vm.lookup_str(val).unwrap_or_default()
} else {
format!("{}", val)
}
}
fn to_num(val: JsValue) -> f64 {
if val.is_int() {
val.as_int() as f64
} else {
val.as_double()
}
}
#[test]
fn spread_single_literal_preserves_all_args() {
let mut vm = Vm::new();
let result = eval(&mut vm, "function f(){return arguments.length}; f(...[1,2,3])").unwrap();
assert_eq!(result.as_int(), 3);
}
#[test]
fn spread_interleaved_static_and_spread_keeps_source_order() {
let mut vm = Vm::new();
let result = eval(&mut vm, "function f(a,b,c){return [a,b,c].join('-')}; f('x', ...['y'], 'z')").unwrap();
assert_eq!(to_str(&vm, result), "x-y-z");
}
#[test]
fn spread_multiple_sources_append_in_order() {
let mut vm = Vm::new();
let result = eval(&mut vm, "function f(){return arguments.length}; f(...[1], 2, ...[3,4])").unwrap();
assert_eq!(result.as_int(), 4);
}
#[test]
fn spread_native_target() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Math.max(...[1,5,3])").unwrap();
assert_eq!(to_num(result), 5.0);
}
#[test]
fn spread_new_expression_bytecode_ctor() {
let mut vm = Vm::new();
let result = eval(&mut vm, "new (function(a,b){this.sum=a+b})(...[2,3]).sum").unwrap();
assert_eq!(to_num(result), 5.0);
}
#[test]
fn spread_new_expression_native_ctor() {
let mut vm = Vm::new();
let result = eval(&mut vm, "new Date(...[2020,1,1]).getFullYear()").unwrap();
assert_eq!(to_num(result), 2020.0);
}
#[test]
fn spread_super_call_passes_args() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class A { constructor(a,b,c){ this.v=[a,b,c].join('') } }; \
class B extends A { constructor(){ super(...[1,2,3]) } }; new B().v",
)
.unwrap();
assert_eq!(to_str(&vm, result), "123");
}
#[test]
fn spread_non_iterable_throws_type_error() {
let mut vm = Vm::new();
let result = eval(&mut vm, "try { (function(){})(...null); 'no-throw' } catch(e) { e.constructor.name }").unwrap();
assert_eq!(to_str(&vm, result), "TypeError");
}
#[test]
fn spread_invokes_iterator_close_on_abrupt_completion() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var closed=0; try { (function(){}) (...{[Symbol.iterator]:function(){ return { \
next:function(){ throw new Error('boom') }, \
return:function(){ closed=1; return {} } } }}) } catch(e) {}; closed",
)
.unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn spread_does_not_close_iterator_on_normal_completion() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var closed=0; var i=0; (function(){}) (...{[Symbol.iterator]:function(){ return { \
next:function(){ return ++i<=2 ? {done:false,value:i} : {done:true} }, \
return:function(){ closed=1; return {} } } }}); closed",
)
.unwrap();
assert_eq!(result.as_int(), 0);
}
#[test]
fn spread_closure_capture_survives() {
let mut vm = Vm::new();
let result =
eval(&mut vm, "function outer(){var x=42; function g(a){return a+x}; return g(...[x])} outer()").unwrap();
assert_eq!(to_num(result), 84.0);
}
#[test]
fn spread_string_source() {
let mut vm = Vm::new();
let result = eval(&mut vm, "function f(){return arguments.length}; f(...'ab')").unwrap();
assert_eq!(result.as_int(), 2);
}