mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
670 lines
32 KiB
Rust
670 lines
32 KiB
Rust
use crate::vm::Vm;
|
||
use crate::{vm_error, vm_trace};
|
||
use oxide_bytecode::opcode;
|
||
use oxide_runtime_api as coercion;
|
||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||
use oxide_types::arguments_map::ArgumentsMapState;
|
||
use oxide_types::mem::P;
|
||
use oxide_types::object::{JsObject, PropAttributes};
|
||
use oxide_types::private_key::{encode_symbol_key, make_int_key};
|
||
use oxide_types::value::{JsType, JsValue};
|
||
|
||
impl Vm {
|
||
#[inline(always)]
|
||
pub(crate) fn dispatch_load_const(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||
let idx = (instr >> 16) as usize;
|
||
vm_trace!("LOAD_CONST rd={} idx={}", rd, idx);
|
||
let imm = self.immutables();
|
||
if idx < imm.len() {
|
||
self.regs[rd] = imm[idx];
|
||
Ok(())
|
||
} else {
|
||
vm_error!("LOAD_CONST index {} out of bounds (len={})", idx, imm.len());
|
||
Err(format!("constant index {idx} out of bounds"))
|
||
}
|
||
}
|
||
|
||
/// 全局对象属性读(含访问器 getter 调用):命中属性时若为访问器则以
|
||
/// 全局对象为 receiver 调用 getter(getter 副作用先于后续写生效),返回
|
||
/// getter 结果;命中数据属性返回槽值;未命中返回 None。getter 抛错经
|
||
/// `raise_call_error` 就地展开(pc 已改写),调用方须按 pc 变化停行。
|
||
fn global_prop_read_with_getter(&mut self, global: &P<JsObject>, si: u32) -> Result<Option<JsValue>, String> {
|
||
let Some(val) = self.resolve_property(global, si) else {
|
||
return Ok(None);
|
||
};
|
||
let Some(getter) = self.resolve_property_getter(global, si) else {
|
||
return Ok(Some(val));
|
||
};
|
||
// SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。
|
||
let global_val = JsValue::from_js_object(global.as_ptr() as *mut JsObject);
|
||
match self.call_function_sync(getter, global_val, &[]) {
|
||
Ok(v) => Ok(Some(v)),
|
||
Err(err) => {
|
||
self.raise_call_error(&err)?;
|
||
Ok(None)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 未声明标识符读:查 global object 属性存在性,命中取属性值,未命中抛 ReferenceError。
|
||
///
|
||
/// # 步骤
|
||
/// 1. imm16 取常量池 key(标识符名字符串),解析为属性键 si。
|
||
/// 2. `resolve_property` 沿 shape 链 + 原型链解析 global 属性(覆盖全部属性存储)。
|
||
/// 3. 命中写 rd;未命中 raise ReferenceError(`{name} is not defined`)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - key 常量必须是字符串(emit 侧保证);非字符串按错误返回。
|
||
/// - 属性存在但值为 undefined(显式赋 undefined)时返回 undefined,不抛。
|
||
pub(crate) fn dispatch_load_global(&mut self, rd: usize, instr: u32) -> Result<bool, String> {
|
||
let idx = (instr >> 16) as usize;
|
||
vm_trace!("LOAD_GLOBAL rd={} idx={}", rd, idx);
|
||
let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined());
|
||
if !key_val.is_string() {
|
||
return Err(format!("LOAD_GLOBAL constant index {idx} is not a string key"));
|
||
}
|
||
let si = self.property_key_si(key_val)?;
|
||
let global = {
|
||
let session = self.realm.session.borrow();
|
||
session.global_object().clone()
|
||
};
|
||
let pc_before = self.pc;
|
||
let hit = self.global_prop_read_with_getter(&global, si)?;
|
||
if self.pc != pc_before {
|
||
// getter 在途抛错已展开到 catch/finally 入口:pc 由处理器接管,
|
||
// 不得续行(结果寄存器不得再写、不得抛未解析引用)。
|
||
return Ok(true);
|
||
}
|
||
if let Some(val) = hit {
|
||
self.regs[rd] = val;
|
||
Ok(false)
|
||
} else {
|
||
// SAFETY: key_val 已校验为字符串值;错误消息走 lossy 文本(不可解析名
|
||
// 含孤立 surrogate 时映射为 FFFD,仅为展示用途)。
|
||
let name = unsafe { (*key_val.as_string_ptr()).as_lossy_str() };
|
||
self.raise_error_kind("ReferenceError", &format!("{name} is not defined"))?;
|
||
Ok(true)
|
||
}
|
||
}
|
||
|
||
/// typeof 未声明标识符读:同 [`dispatch_load_global`] 查 global object 属性,
|
||
/// 但未命中求值为 undefined(IsUnresolvableReference)而非抛 ReferenceError。
|
||
///
|
||
/// # 步骤
|
||
/// 1. imm16 取常量池 key(标识符名字符串),解析为属性键 si。
|
||
/// 2. `resolve_property` 沿 shape 链 + 原型链解析 global 属性。
|
||
/// 3. 命中写 rd;未命中写 undefined。
|
||
pub(crate) fn dispatch_load_global_typeof(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||
let idx = (instr >> 16) as usize;
|
||
vm_trace!("LOAD_GLOBAL_TYPEOF rd={} idx={}", rd, idx);
|
||
let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined());
|
||
if !key_val.is_string() {
|
||
return Err(format!("LOAD_GLOBAL_TYPEOF constant index {idx} is not a string key"));
|
||
}
|
||
let si = self.property_key_si(key_val)?;
|
||
let global = {
|
||
let session = self.realm.session.borrow();
|
||
session.global_object().clone()
|
||
};
|
||
// typeof 访问器属性同样触发 getter(getter 抛错时 typeof 不豁免,异常照抛)。
|
||
let pc_before = self.pc;
|
||
let hit = self.global_prop_read_with_getter(&global, si)?;
|
||
if self.pc != pc_before {
|
||
// getter 在途抛错已展开:pc 由处理器接管,不得续行。
|
||
return Ok(());
|
||
}
|
||
self.regs[rd] = hit.unwrap_or(JsValue::undefined());
|
||
Ok(())
|
||
}
|
||
|
||
/// 全局词法环境读:先查 session 全局词法环境镜像(顶层 let/const 真源),
|
||
/// 命中取值;未命中回退全局对象属性读,缺失抛 ReferenceError。
|
||
///
|
||
/// # 步骤
|
||
/// 1. imm16 取常量池 key(标识符名字符串),解析为属性键 si。
|
||
/// 2. 查镜像对象属性,命中写 rd 返回。
|
||
/// 3. 未命中回退全局对象属性读,命中写 rd 返回;缺失 raise ReferenceError。
|
||
///
|
||
/// # 边界与前提
|
||
/// - key 常量必须是字符串(emit 侧保证);非字符串按错误返回。
|
||
/// - 镜像命中即返回(顶层词法绑定优先于全局对象属性,规范全局环境词法面)。
|
||
pub(crate) fn dispatch_load_global_lex(&mut self, rd: usize, instr: u32) -> Result<bool, String> {
|
||
let idx = (instr >> 16) as usize;
|
||
vm_trace!("LOAD_GLOBAL_LEX rd={} idx={}", rd, idx);
|
||
let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined());
|
||
if !key_val.is_string() {
|
||
return Err(format!("LOAD_GLOBAL_LEX constant index {idx} is not a string key"));
|
||
}
|
||
let si = self.property_key_si(key_val)?;
|
||
// 镜像优先:顶层 let/const 真源,命中即返回。
|
||
let lexical_hit = {
|
||
let session = self.realm.session.borrow();
|
||
self.resolve_property(session.global_lexical(), si)
|
||
};
|
||
if let Some(val) = lexical_hit {
|
||
self.regs[rd] = val;
|
||
return Ok(false);
|
||
}
|
||
// 回退全局对象属性读(未声明名与 known builtin 名同形 LOAD_GLOBAL);
|
||
// 访问器属性调用 getter(receiver = 全局对象),裸读必须触发 getter。
|
||
let global = {
|
||
let session = self.realm.session.borrow();
|
||
session.global_object().clone()
|
||
};
|
||
let pc_before = self.pc;
|
||
let global_hit = self.global_prop_read_with_getter(&global, si)?;
|
||
if self.pc != pc_before {
|
||
// getter 在途抛错已展开到 catch/finally 入口:pc 由处理器接管,
|
||
// 不得续行(结果寄存器不得再写、不得抛未解析引用)。
|
||
return Ok(true);
|
||
}
|
||
if let Some(val) = global_hit {
|
||
self.regs[rd] = val;
|
||
Ok(false)
|
||
} else {
|
||
// SAFETY: key_val 已校验为字符串值;错误消息走 lossy 文本。
|
||
let name = unsafe { (*key_val.as_string_ptr()).as_lossy_str() };
|
||
self.raise_error_kind("ReferenceError", &format!("{name} is not defined"))?;
|
||
Ok(true)
|
||
}
|
||
}
|
||
|
||
/// 全局词法环境写:把 a 槽值写入镜像对象(顶层 let/const 声明与赋值双写)。
|
||
///
|
||
/// # 步骤
|
||
/// 1. ext[0] 取键常量池下标,解析为属性键 si。
|
||
/// 2. 镜像对象由 session 解析,`set_or_create_prop_value` 写值(缺失建属性)。
|
||
///
|
||
/// # 副作用
|
||
/// - 修改镜像对象 shape/属性表/generation。
|
||
pub(crate) fn dispatch_store_global_lex(&mut self, a: usize, key_idx: u16) -> Result<(), String> {
|
||
let idx = key_idx as usize;
|
||
vm_trace!("STORE_GLOBAL_LEX value={} idx={}", a, idx);
|
||
let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined());
|
||
if !key_val.is_string() {
|
||
return Err(format!("STORE_GLOBAL_LEX constant index {idx} is not a string key"));
|
||
}
|
||
let si = self.property_key_si(key_val)?;
|
||
let value = self.regs[a];
|
||
let lexical_ptr = self.realm.session.borrow().global_lexical().as_ptr() as *mut JsObject;
|
||
// SAFETY: 镜像对象钉在 session 永久区,指针在 VM 生命周期内有效。
|
||
let obj = unsafe { &mut *lexical_ptr };
|
||
self.set_or_create_prop_value(obj, si, value);
|
||
Ok(())
|
||
}
|
||
|
||
#[inline(always)]
|
||
pub(crate) fn dispatch_typeof(&mut self, rd: usize, a: usize) {
|
||
vm_trace!("TYPEOF rd={} r{}={:?}", rd, a, self.regs[a]);
|
||
let val = self.regs[a];
|
||
let kind = match val.js_type() {
|
||
JsType::Undefined => 0,
|
||
JsType::Null => 1,
|
||
JsType::Bool => 2,
|
||
JsType::Int | JsType::Double => 3,
|
||
JsType::String => 4,
|
||
JsType::Symbol => 5,
|
||
JsType::BigInt => 6,
|
||
JsType::Object => {
|
||
// [[IsHTMLDDA]] 宿主对象按 "undefined" 报告(B.3.4,Type 仍是 object);
|
||
// 宿主对象可调用,判定须先于函数分支,typeof 仍报 "undefined"。
|
||
// 其余函数对象归为 "function"(语言类型是 object,typeof 有专属分支)。
|
||
let obj = unsafe { &*val.as_js_object_ptr() };
|
||
if obj.is_html_dda_obj() {
|
||
0
|
||
} else if obj.is_function() {
|
||
7
|
||
} else {
|
||
1
|
||
}
|
||
}
|
||
};
|
||
// 结果字符串复用进程级静态表(零分配、无锁、跨 VM 共享),
|
||
// 避免每次 typeof 新建 session 字符串。
|
||
self.regs[rd] = JsValue::string(oxide_kernel::string_forge::typeof_string_ptr(kind));
|
||
}
|
||
|
||
/// ToObject:rd 原地转换。对象直接返回;null/undefined 抛 TypeError;
|
||
/// 其余原始值包装为对应包装对象(with 对象环境需要)。
|
||
#[inline(always)]
|
||
pub(crate) fn dispatch_to_object(&mut self, rd: usize) -> Result<(), String> {
|
||
vm_trace!("TO_OBJECT rd={} r{}={:?}", rd, rd, self.regs[rd]);
|
||
let val = self.regs[rd];
|
||
if val.is_object() {
|
||
return Ok(());
|
||
}
|
||
if val.is_null() || val.is_undefined() {
|
||
// 抛 JS 异常而非返回 Err:外围 try/catch 须能捕获(对象解构空 pattern)。
|
||
self.raise_error_kind("TypeError", "Cannot convert null or undefined to object")?;
|
||
return Ok(());
|
||
}
|
||
let obj_val = oxide_runtime_api::to_object(val, self)?;
|
||
self.regs[rd] = obj_val;
|
||
Ok(())
|
||
}
|
||
|
||
/// ToPropertyKey:推导属性键值。Symbol 原样返回;对象经 ToPrimitive(string
|
||
/// hint) 后 ToString;其余原始值直接 ToString。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 对象键的 toString/valueOf 调用有用户副作用,可能抛异常;抛错经
|
||
/// `raise_call_error` 就地展开(pc 已改写),调用方须按 pc 变化停行。
|
||
///
|
||
/// # 副作用
|
||
/// - 成功路径返回推导后的字符串/Symbol 值;抛错路径经 `unwind` 改写
|
||
/// pc / 异常通道并返回 undefined(调用方不得再使用)。
|
||
pub(crate) fn derive_property_key_value(&mut self, val: JsValue) -> Result<JsValue, String> {
|
||
vm_trace!("derive_property_key_value: {:?}", val);
|
||
if val.is_symbol() {
|
||
return Ok(val);
|
||
}
|
||
let pc_before = self.pc;
|
||
let prim = if val.is_object() {
|
||
coercion::to_primitive(val, coercion::ToPrimitiveHint::String, self)?
|
||
} else {
|
||
val
|
||
};
|
||
if self.pc != pc_before {
|
||
// ToPrimitive 在途抛错已展开:pc 由处理器接管,结果值不得使用。
|
||
return Ok(JsValue::undefined());
|
||
}
|
||
if prim.is_symbol() {
|
||
return Ok(prim);
|
||
}
|
||
let units = oxide_runtime_api::to_units_full(prim, self)?;
|
||
Ok(self.new_string_units_owned(units))
|
||
}
|
||
|
||
pub(crate) fn dispatch_load_var(&mut self, rd: usize, a: usize) -> Result<bool, String> {
|
||
vm_trace!("LOAD_VAR rd={} r{}={:?}", rd, a, self.regs[a]);
|
||
// a==254 即读 this:derived 构造帧在 super() 成功前读 this 抛 ReferenceError。
|
||
if a == 254
|
||
&& self
|
||
.frames
|
||
.last()
|
||
.map(|frame| frame.is_derived_constructor && !frame.super_called)
|
||
.unwrap_or(false)
|
||
{
|
||
self.raise_error_kind("ReferenceError", "must call super constructor before using 'this'")?;
|
||
return Ok(true);
|
||
}
|
||
self.regs[rd] = self.regs[a];
|
||
Ok(false)
|
||
}
|
||
|
||
pub(crate) fn dispatch_store_var(&mut self, rd: usize, a: usize, b: usize) -> Result<bool, String> {
|
||
vm_trace!("STORE_VAR r{}={:?} const={}", rd, self.regs[a], b);
|
||
if b != 0 {
|
||
// const guard:检查目标槽是否已初始化。
|
||
if !self.regs[rd].is_undefined() {
|
||
self.raise_error_kind("TypeError", "Assignment to constant variable")?;
|
||
return Ok(true);
|
||
}
|
||
}
|
||
self.regs[rd] = self.regs[a];
|
||
// mapped arguments 参数直写同步:目标寄存器是创建帧的存活映射参数时,
|
||
// 把新值回写 arguments 存储值(读路径权威源),嵌套调用覆盖寄存器后
|
||
// 存储值仍正确。
|
||
self.sync_mapped_arguments_param(rd);
|
||
Ok(false)
|
||
}
|
||
|
||
/// mapped arguments 参数寄存器直写同步:目标寄存器是顶帧 mapped arguments 的
|
||
/// 存活映射参数时,把新值回写 arguments 对象存储值(读路径权威源)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 仅顶帧带 mapped arguments 对象(`frame.arguments_obj` 非 undefined)时
|
||
/// 生效;其余帧首检即返回(热路径零开销)。
|
||
/// - 映射失效(创建帧已弹出)或索引已 unmapped(收窄不可写 / 删除)时不同步。
|
||
///
|
||
/// # 副作用
|
||
/// - 写 arguments 对象存储值槽(shape 槽已存在,无 shape 变更)。
|
||
pub(crate) fn sync_mapped_arguments_param(&mut self, rd: usize) {
|
||
let Some(frame) = self.frames.last() else {
|
||
return;
|
||
};
|
||
if frame.arguments_obj.is_undefined() {
|
||
return;
|
||
}
|
||
let obj_ptr = frame.arguments_obj.as_js_object_ptr();
|
||
if obj_ptr.is_null() {
|
||
return;
|
||
}
|
||
// SAFETY: arguments_obj 是 GC 根(帧遍历期标记),session 内指针有效。
|
||
if !unsafe { (*obj_ptr).is_arguments_obj() } {
|
||
return;
|
||
}
|
||
let state_ptr = unsafe { (*obj_ptr).native_data() } as *const ArgumentsMapState;
|
||
if state_ptr.is_null() {
|
||
return;
|
||
}
|
||
// SAFETY: native_data 由创建期 Box::into_raw 分配,生命周期与对象一致。
|
||
let state = unsafe { &*state_ptr };
|
||
if !self.arguments_mapping_alive(state) {
|
||
return;
|
||
}
|
||
let param_base = state.param_base as usize;
|
||
let param_count = state.param_count as usize;
|
||
if rd < param_base || rd >= param_base + param_count {
|
||
return;
|
||
}
|
||
let index = (rd - param_base) as u16;
|
||
if !state.is_mapped(index) {
|
||
return;
|
||
}
|
||
let si = make_int_key(index as u32);
|
||
let val = self.regs[rd];
|
||
// SAFETY: obj_ptr 是有效对象指针(GC 根)。
|
||
let obj_mut = unsafe { &mut *obj_ptr };
|
||
self.set_or_create_prop_value(obj_mut, si, val);
|
||
}
|
||
|
||
/// SPILL:`regs[rd]` → `spill_stack[帧基址 + slot]`。
|
||
///
|
||
/// ext 字为 slot(u16);帧基址 = 当前 CallFrame.spill_offset(顶层模块无帧为 0)。
|
||
/// # 边界与前提
|
||
/// - slot 超 u16 范围返回错误(RegAlloc 保证发射在界内)。
|
||
/// # 副作用
|
||
/// - 写 spill 栈,可能触发 resize 扩容。
|
||
#[inline(always)]
|
||
pub(crate) fn dispatch_spill(&mut self, rd: usize) -> Result<(), String> {
|
||
let slot = self.bytecode[self.pc] as usize;
|
||
self.pc += 1;
|
||
if slot > u16::MAX as usize {
|
||
return Err(format!("SPILL slot {slot} out of range (max 65535)"));
|
||
}
|
||
let base = self.frames.last().map(|f| f.spill_offset as usize).unwrap_or(0);
|
||
let idx = base + slot;
|
||
if idx >= self.spill_stack.len() {
|
||
self.spill_stack.resize(idx + 1, JsValue::undefined());
|
||
}
|
||
vm_trace!("SPILL r{} -> slot[{}] base={}", rd, slot, base);
|
||
self.spill_stack[idx] = self.regs[rd];
|
||
Ok(())
|
||
}
|
||
|
||
/// UNSPILL:`spill_stack[帧基址 + slot]` → `regs[rd]`。
|
||
///
|
||
/// # 边界与前提
|
||
/// - slot 超 u16 范围返回错误。
|
||
/// - 越界读返回 undefined(防御:slot 由 RegAlloc 保证在界内)。
|
||
#[inline(always)]
|
||
pub(crate) fn dispatch_unspill(&mut self, rd: usize) -> Result<(), String> {
|
||
let slot = self.bytecode[self.pc] as usize;
|
||
self.pc += 1;
|
||
if slot > u16::MAX as usize {
|
||
return Err(format!("UNSPILL slot {slot} out of range (max 65535)"));
|
||
}
|
||
let base = self.frames.last().map(|f| f.spill_offset as usize).unwrap_or(0);
|
||
let idx = base + slot;
|
||
vm_trace!("UNSPILL r{} <- slot[{}] base={}", rd, slot, base);
|
||
self.regs[rd] = if idx < self.spill_stack.len() {
|
||
self.spill_stack[idx]
|
||
} else {
|
||
JsValue::undefined()
|
||
};
|
||
Ok(())
|
||
}
|
||
|
||
/// 创建空对象;带键常量表时(a 槽 = 属性数 ≤255,ext = 每键常量池下标)按纯静态
|
||
/// 数据键前缀批量构造:单次链式 `make_shape` 预建整条 shape、预分配全部数据槽,
|
||
/// 后续属性值经 SET_PROP_BATCH 纯槽写。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 逐键直读模块 si 侧表(模块装载期 `activate_immutables` 已预 intern,
|
||
/// 与逐属性 SET_PROP 路径逐字节同键),链式 `make_shape` 累加 shape。
|
||
/// 2. 分配对象、预填充 `nprops` 个数据槽、`generation += nprops`(与逐属性路径终值一致)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `nprops = 0` 时退化为普通空对象(无 ext 键表)。
|
||
/// - 侧表条目缺失(防御面,正常路径不出现)回退旧路径按键值重推 si。
|
||
#[inline(always)]
|
||
pub(crate) fn dispatch_new_object(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||
let nprops = opcode::a(instr) as usize;
|
||
vm_trace!("NEW_OBJECT rd={} nprops={}", rd, nprops);
|
||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||
let mut shape_id = EMPTY_SHAPE_ID;
|
||
if nprops > 0 {
|
||
let key_idxs: Vec<u32> = self.bytecode[self.pc..self.pc + nprops].to_vec();
|
||
self.pc += nprops;
|
||
// si 侧表与 immutables 同点激活:活动模块的侧表此刻必已填充。
|
||
let si_table = self.active_table().si_tables[self.active_flat_id as usize]
|
||
.get()
|
||
.expect("活动模块的 si 侧表已随不可变常量激活同步填充");
|
||
let key_si: Vec<Option<u32>> =
|
||
key_idxs.iter().map(|k| si_table.get(*k as usize).copied().flatten()).collect();
|
||
for (key_idx, si_opt) in key_idxs.iter().zip(key_si.iter()) {
|
||
let si = match si_opt {
|
||
Some(si) => *si,
|
||
None => {
|
||
let imm = self.immutables();
|
||
let key_val = imm.get(*key_idx as usize).copied().unwrap_or(JsValue::undefined());
|
||
self.property_key_si(key_val)?
|
||
}
|
||
};
|
||
shape_id = self.kernel_core.shape_forge().make_shape(shape_id, si);
|
||
}
|
||
}
|
||
let obj = self.alloc_object(JsObject::new_empty(shape_id, JsValue::from_js_object(proto_ptr)));
|
||
// 预分配数据槽并同步 generation:批量一次性 +nprops,与逐属性路径的终值一致。
|
||
let obj_ref = unsafe { &mut *obj };
|
||
for _ in 0..nprops {
|
||
obj_ref.push_prop(JsValue::undefined());
|
||
obj_ref.bump_generation();
|
||
}
|
||
self.regs[rd] = JsValue::object(obj as *mut u8);
|
||
Ok(())
|
||
}
|
||
|
||
/// 创建 session 直分的空普通对象([[Prototype]] = Object.prototype):与
|
||
/// `create_function_object` 的 prototype 子对象同策略——若按 epoch 分配,写入
|
||
/// 全局等逃逸根时晋升屏障会深克隆进 session,持有者(构造器 prototype 属性)
|
||
/// 与克隆体指针分裂、严格相等恒 false。
|
||
///
|
||
/// # 副作用
|
||
/// - 登记 session 对象表并计入堆账目;回收由 session GC mark/sweep 承担。
|
||
pub(crate) fn dispatch_new_session_object(&mut self, rd: usize) -> Result<(), String> {
|
||
vm_trace!("NEW_SESSION_OBJECT rd={}", rd);
|
||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr));
|
||
let obj_ptr = self.alloc_session_object(obj);
|
||
self.regs[rd] = JsValue::object(obj_ptr as *mut u8);
|
||
Ok(())
|
||
}
|
||
|
||
/// 创建 arguments 对象:索引属性取当前帧(或 inline 同步调用)的完整实参,
|
||
/// 附 length / callee / @@iterator 属性。索引与形参不同步(mapped 同步语义
|
||
/// 未实现);callee 按 strict ‖ !simple 分流为受限访问器或数据属性。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 实参区由统一压帧入口 `push_bytecode_frame` 在推帧时写入 spill 栈;
|
||
/// frames 为空(inline 同步调用)时读 `inline_args_*`。
|
||
/// - 索引属性用 shape 槽存储(普通对象),length 为不可枚举数据属性。
|
||
pub(crate) fn dispatch_create_arguments(&mut self, rd: usize) -> Result<(), String> {
|
||
let (base, count) = match self.frames.last() {
|
||
Some(frame) => (frame.arguments_base, frame.arguments_count),
|
||
None => (self.inline_args_base, self.inline_args_count),
|
||
};
|
||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||
let obj_ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
|
||
let obj = unsafe { &mut *obj_ptr };
|
||
obj.type_tag = JsObject::OBJ_TYPE_ARGUMENTS;
|
||
|
||
// mapped 判定:sloppy 且 simple(strict ‖ !simple → unmapped)。mapped 时
|
||
// 建同步状态盒(存 native_data):参数寄存器与 arguments 存储值双向同步,
|
||
// 帧身份经 (帧下标, 帧身份) 判定创建帧是否仍在栈上。unmapped 时 native_data
|
||
// 保持 null,exotic 四臂短路,行为与现树一致。
|
||
let strict = self.current_strict();
|
||
let simple = self.active_module().is_some_and(|m| m.has_simple_params);
|
||
if !strict && simple {
|
||
if let (Some(frame_id), Some(module)) = (self.frames.last().map(|f| f.frame_id), self.active_module()) {
|
||
let param_base = module.param_base;
|
||
let param_count = module.n_args as u16;
|
||
let words = (param_count as usize).div_ceil(64).max(1);
|
||
let mut mapped_mask = vec![u64::MAX; words];
|
||
let valid_bits = param_count as usize % 64;
|
||
if valid_bits != 0 {
|
||
mapped_mask[words - 1] &= (1u64 << valid_bits) - 1;
|
||
} else if param_count == 0 {
|
||
mapped_mask[0] = 0;
|
||
}
|
||
let state = ArgumentsMapState {
|
||
param_base,
|
||
param_count,
|
||
mapped_mask,
|
||
frame_depth: (self.frames.len() - 1) as u32,
|
||
frame_id,
|
||
};
|
||
obj.set_native_data(Box::into_raw(Box::new(state)) as *mut u8);
|
||
// 帧锚点:参数寄存器直写(STORE_VAR)据此同步 arguments 存储值,
|
||
// 使嵌套调用期参数寄存器被覆盖时存储值仍为权威读源。
|
||
if let Some(frame) = self.frames.last_mut() {
|
||
frame.arguments_obj = JsValue::from_js_object(obj_ptr);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 索引属性:按实参下标写入 shape 槽,属性描述符为默认(可写/可枚举/可配置)。
|
||
// 下标走整数键,保证 `arguments[0]`(property_key_si(int 0))键等价命中。
|
||
for i in 0..count as usize {
|
||
let si = make_int_key(i as u32);
|
||
let val = self.spill_stack.get(base as usize + i).copied().unwrap_or(JsValue::undefined());
|
||
self.set_or_create_prop_value(obj, si, val);
|
||
}
|
||
|
||
// length:实参个数,可写、不可枚举、可配置。
|
||
let length_si = self.length_si;
|
||
if let Err(msg) = self.define_data_property(
|
||
obj,
|
||
length_si,
|
||
JsValue::int(count as i32),
|
||
PropAttributes::new(true, false, true),
|
||
) {
|
||
return self.raise_error_kind("TypeError", &msg);
|
||
}
|
||
|
||
// callee:按 strict 与 simple 参数列表四分流(规范 CreateMappedArgumentsObject /
|
||
// CreateUnmappedArgumentsObject):strict ‖ !simple → 受限访问器(get/set
|
||
// 共享 %ThrowTypeError%,访问即抛);sloppy simple → 数据属性(值 = 当前
|
||
// 函数本身,可写、不可枚举、可配置)。
|
||
let callee_si = self.kernel_core.perm_interner().intern("callee").0;
|
||
let callee = self.current_callee().unwrap_or(JsValue::undefined());
|
||
if strict || !simple {
|
||
// SAFETY: 指针由绑定层在 session 构造期写入,session 存活期内有效。
|
||
let thrower_ptr = self.realm.session.borrow().builtin_world().throw_type_error.get();
|
||
if thrower_ptr.is_null() {
|
||
// 绑定层尚未写入共享对象(正常路径不出现):退化为数据属性保调用可用。
|
||
if let Err(msg) =
|
||
self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true))
|
||
{
|
||
return self.raise_error_kind("TypeError", &msg);
|
||
}
|
||
} else {
|
||
// SAFETY: thrower_ptr 由绑定层写入的存活对象,转 *mut 供 from_js_object 消费。
|
||
let thrower_val = JsValue::from_js_object(unsafe { &*thrower_ptr } as *const JsObject as *mut JsObject);
|
||
if let Err(msg) = self.define_accessor_property(
|
||
obj,
|
||
callee_si,
|
||
thrower_val,
|
||
thrower_val,
|
||
PropAttributes::new(false, false, false),
|
||
) {
|
||
return self.raise_error_kind("TypeError", &msg);
|
||
}
|
||
}
|
||
} else if let Err(msg) =
|
||
self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true))
|
||
{
|
||
return self.raise_error_kind("TypeError", &msg);
|
||
}
|
||
|
||
// sloppy 函数:own caller/arguments 数据属性(ES5 遗留,node 行为)——
|
||
// caller = 调用方函数(帧栈次顶帧 callee),arguments = 本对象。strict
|
||
// 函数无 own caller/arguments,经原型链解析到 FP 受限访问器(访问即抛)。
|
||
if !strict && callee.is_object() {
|
||
let caller = self
|
||
.frames
|
||
.get(self.frames.len().saturating_sub(2))
|
||
.map(|f| f.callee)
|
||
.unwrap_or(JsValue::undefined());
|
||
let callee_obj = unsafe { &mut *callee.as_js_object_ptr() };
|
||
let caller_si = self.kernel_core.perm_interner().intern("caller").0;
|
||
let arguments_si = self.kernel_core.perm_interner().intern("arguments").0;
|
||
let data_attrs = PropAttributes::new(true, false, true);
|
||
if let Err(msg) = self.define_data_property(callee_obj, caller_si, caller, data_attrs) {
|
||
return self.raise_error_kind("TypeError", &msg);
|
||
}
|
||
if let Err(msg) =
|
||
self.define_data_property(callee_obj, arguments_si, JsValue::from_js_object(obj_ptr), data_attrs)
|
||
{
|
||
return self.raise_error_kind("TypeError", &msg);
|
||
}
|
||
}
|
||
|
||
// @@iterator:与 Array.prototype[Symbol.iterator] 共享同一函数对象,
|
||
// 使 arguments 对象可迭代(可写、不可枚举、可配置)。
|
||
let sym_iter_si = encode_symbol_key(self.realm_id(), 0);
|
||
// SAFETY: array_proto 是 perm 层内置对象(BuiltinWorld 持有),地址稳定且
|
||
// 跨 epoch/session 存活,本调用期间无 GC 搬移。
|
||
let array_proto = self.realm.session.borrow().builtin_world().array_proto.as_ptr();
|
||
let iter_val = match self.ordinary_get(unsafe { &*array_proto }, sym_iter_si, JsValue::undefined()) {
|
||
Ok(v) => v,
|
||
Err(msg) => return self.raise_error_kind("TypeError", &msg),
|
||
};
|
||
if let Err(msg) = self.define_data_property(obj, sym_iter_si, iter_val, PropAttributes::new(true, false, true))
|
||
{
|
||
return self.raise_error_kind("TypeError", &msg);
|
||
}
|
||
|
||
self.regs[rd] = JsValue::from_js_object(obj_ptr);
|
||
Ok(())
|
||
}
|
||
|
||
/// 创建 rest 参数数组:实参区下标 ≥ `fixed_count` 的实参收集为数组存入 `rd`。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 读当前帧(或 inline 同步调用)的实参区基址与个数。
|
||
/// 2. 实参个数超出固定形参数的部分作为数组元素写入。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 实参来源与 CREATE_ARGUMENTS 相同(统一压帧入口 `push_bytecode_frame` 写入 spill 栈)。
|
||
/// - 实参不足 `fixed_count` 时数组为空。
|
||
pub(crate) fn dispatch_create_rest_array(&mut self, rd: usize, fixed_count: usize) -> Result<(), String> {
|
||
let (base, count) = match self.frames.last() {
|
||
Some(frame) => (frame.arguments_base, frame.arguments_count),
|
||
None => (self.inline_args_base, self.inline_args_count),
|
||
};
|
||
let n = count as usize;
|
||
let rest_len = n.saturating_sub(fixed_count);
|
||
let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||
let obj_ptr =
|
||
self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), rest_len));
|
||
let obj = unsafe { &mut *obj_ptr };
|
||
for i in 0..rest_len {
|
||
let val = self
|
||
.spill_stack
|
||
.get(base as usize + fixed_count + i)
|
||
.copied()
|
||
.unwrap_or(JsValue::undefined());
|
||
obj.set_prop_at(i as u32, val);
|
||
}
|
||
self.regs[rd] = JsValue::object(obj_ptr as *mut u8);
|
||
Ok(())
|
||
}
|
||
|
||
#[inline(always)]
|
||
pub(crate) fn dispatch_new_array(&mut self, rd: usize, instr: u32) {
|
||
let n = opcode::imm16(instr) as usize;
|
||
vm_trace!("NEW_ARRAY rd={} n={}", rd, n);
|
||
let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||
let obj = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), n));
|
||
self.regs[rd] = JsValue::object(obj as *mut u8);
|
||
}
|
||
|
||
#[inline(always)]
|
||
pub(crate) fn dispatch_void(&mut self, rd: usize) {
|
||
vm_trace!("VOID rd={}", rd);
|
||
self.regs[rd] = JsValue::undefined();
|
||
}
|
||
}
|