mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
refactor: dispatch 循环与 VmHost 动态编译拆入 vm 子模块
This commit is contained in:
@@ -0,0 +1,800 @@
|
||||
//! dispatch 主循环:逐指令解码与 156 个 OpCode 臂分派、执行期 GC 安全点、
|
||||
//! 步数/分配上限采样与二元运算宏 `binary_arith!`。
|
||||
|
||||
use num_traits::Zero;
|
||||
use oxide_bytecode::opcode::{self, OpCode};
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use super::Vm;
|
||||
use crate::{vm_error, vm_trace, vm_warn};
|
||||
|
||||
macro_rules! binary_arith {
|
||||
($self:ident, $a:expr, $b:expr, $rd:expr, $op:tt, $check_zero:expr) => {{
|
||||
let lv = $self.regs[$a];
|
||||
let rv = $self.regs[$b];
|
||||
if lv.is_int() && rv.is_int() {
|
||||
$self.regs[$rd] = JsValue::float(lv.as_int() as f64 $op rv.as_int() as f64);
|
||||
} else if lv.is_bigint() && rv.is_bigint() {
|
||||
let l = $self.bigint_value(lv).clone();
|
||||
let r = $self.bigint_value(rv).clone();
|
||||
if r.is_zero() && $check_zero {
|
||||
// 仅除/模对 BigInt 零除数抛 RangeError;SUB/MUL 等其余二元运算
|
||||
// 遇到 0n 必须正常计算(Number 路径走 f64 inf/NaN)。
|
||||
$self.raise_error_kind("RangeError", "Division by zero")?;
|
||||
$self.regs[$rd] = JsValue::undefined();
|
||||
} else {
|
||||
$self.regs[$rd] = $self.new_bigint(l $op r);
|
||||
}
|
||||
} else {
|
||||
// 注意:混合 BigInt/Number 检查不能在此前置(对象操作数如
|
||||
// `{valueOf: () => 2n}` 需先 ToPrimitive 再判定),统一放 coerce 之后。
|
||||
let l = $self.coerce_primitive_bounded(lv, false)?;
|
||||
let r = $self.coerce_primitive_bounded(rv, false)?;
|
||||
if l.is_bigint() && r.is_bigint() {
|
||||
// 包装对象 coerce 后暴露双 BigInt(如 Object(2n) / 2n)。
|
||||
let lv = $self.bigint_value(l).clone();
|
||||
let rv = $self.bigint_value(r).clone();
|
||||
if rv.is_zero() && $check_zero {
|
||||
$self.raise_error_kind("RangeError", "Division by zero")?;
|
||||
$self.regs[$rd] = JsValue::undefined();
|
||||
} else {
|
||||
$self.regs[$rd] = $self.new_bigint(lv $op rv);
|
||||
}
|
||||
} else if l.is_bigint() != r.is_bigint() {
|
||||
// 包装对象 coerce 后暴露 BigInt 混合(如 Object(1n) - 1)。
|
||||
$self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions")?;
|
||||
$self.regs[$rd] = JsValue::undefined();
|
||||
} else {
|
||||
let ln = coercion::to_number(l);
|
||||
let rn = coercion::to_number(r);
|
||||
$self.regs[$rd] = JsValue::float(ln $op rn);
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn dispatch(&mut self) -> Result<JsValue, String> {
|
||||
// config.max_steps / max_alloc_bytes 逐指令只读且循环内不变:提到循环外,
|
||||
// 免每次经 kernel_core Arc 指针追寻读取(热点内仅有的 config 访问)。
|
||||
let max_steps = self.kernel_core.config.max_steps;
|
||||
let max_alloc_bytes = self.kernel_core.config.max_alloc_bytes;
|
||||
let mut steps: u64 = 0;
|
||||
// 小重入泵送盲区:native 终端循环泵送短 JS 重入时,顶层 steps 不推进、
|
||||
// 每次重入 dispatch 太短到不了循环内 64 指令采样点,分配上限结构性永不
|
||||
// 采样。重入边界按 hop 计数,每 64 跳强制一次顶层轻采样兜底。
|
||||
if self.native_call_depth > 0 {
|
||||
self.reentry_hops += 1;
|
||||
if self.reentry_hops & 0x3F == 0 {
|
||||
if let Some(cap) = max_alloc_bytes {
|
||||
let used = self.run_alloc_bytes();
|
||||
if used > cap {
|
||||
vm_warn!(
|
||||
"dispatch: memory limit {cap} exceeded at reentry hop {} (used {used}) at pc={}",
|
||||
self.reentry_hops,
|
||||
self.pc
|
||||
);
|
||||
return Err(format!("VM memory limit {cap} exceeded (used {used}) at pc={}", self.pc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loop {
|
||||
steps += 1;
|
||||
// 执行期 GC 安全点:仅在顶层 dispatch(native_call_depth == 0)
|
||||
// 的指令边界触发——嵌套 dispatch(builtin 经 call_function_sync 重入
|
||||
// 执行 JS 回调、generator/async 恢复)期间,调用方寄存器窗口副本存于
|
||||
// inline 状态(非 GC 根),此时回收会把调用方 regs 中的活值当死值释放。
|
||||
// 返回顶层后检查恢复,存活值此时已回拷为执行根。账目未超水位时仅
|
||||
// 少量字段比较。
|
||||
if self.native_call_depth == 0 {
|
||||
// 峰值高水位:同一边界采样 session 堆账目上界
|
||||
let bytes = self.gc_state.session_bytes_allocated;
|
||||
if bytes > self.gc_state.session_bytes_peak {
|
||||
self.gc_state.session_bytes_peak = bytes;
|
||||
}
|
||||
// 单 run 分配包络高水位:O(1) 三计数器读(双 arena + session 账目)。
|
||||
let alloc = self.run_alloc_bytes();
|
||||
if alloc > self.gc_state.run_alloc_peak {
|
||||
self.gc_state.run_alloc_peak = alloc;
|
||||
}
|
||||
if bytes >= self.gc_state.string_gc_watermark {
|
||||
self.maybe_collect_session_strings();
|
||||
}
|
||||
// 执行期两档收集:O(1) 包络超触发水位 → 门控(活跃/挂起 for-in)
|
||||
// + epoch 晋升收集 + session 原地 sweep(边界契约见 maybe_collect_in_run)。
|
||||
if alloc >= self.gc_state.gc_watermark {
|
||||
self.maybe_collect_in_run();
|
||||
}
|
||||
}
|
||||
if let Some(max_steps) = max_steps {
|
||||
if steps > max_steps {
|
||||
vm_warn!("dispatch: step limit {} exceeded at pc={}", max_steps, self.pc);
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Err(format!("VM step limit exceeded at pc={}", self.pc));
|
||||
}
|
||||
}
|
||||
// 单 run 分配上限:账目盲区(属性区扩容)靠两层采样兜住——轻层每
|
||||
// 64 指令读三个计数器,深层每 2^18 指令全量重算(含逐对象属性区
|
||||
// 重算);超限 run 按步数超限同款处理(默认 skip / --no-skip 下
|
||||
// fail),防单测试 arena 高水位拖垮宿主。
|
||||
if let Some(cap) = max_alloc_bytes {
|
||||
let deep = (steps & 0x3FFFF) == 0;
|
||||
if deep || (steps & 0x3F) == 0 {
|
||||
let used = if deep { self.run_alloc_bytes_full() as usize } else { self.run_alloc_bytes() };
|
||||
if used > cap {
|
||||
vm_warn!("dispatch: memory limit {cap} exceeded (used {used}) at pc={}", self.pc);
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Err(format!("VM memory limit {cap} exceeded (used {used}) at pc={}", self.pc));
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.pc >= self.bytecode.len() {
|
||||
let tail: Vec<String> = self
|
||||
.bytecode
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.take(5)
|
||||
.map(|(i, &instr)| format!("{i}:{:?}", opcode::opcode(instr)))
|
||||
.collect();
|
||||
let fn_names: Vec<String> = self
|
||||
.frames
|
||||
.iter()
|
||||
.map(|f| {
|
||||
self.kernel_core
|
||||
.perm_interner()
|
||||
.lookup(f.function_name)
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| "?".into())
|
||||
})
|
||||
.collect();
|
||||
vm_error!(
|
||||
"dispatch: program counter out of bounds pc={} len={} frames={} cell_stack={} tail={:?} fns={:?}",
|
||||
self.pc,
|
||||
self.bytecode.len(),
|
||||
self.frames.len(),
|
||||
self.cell_stack.len(),
|
||||
tail,
|
||||
fn_names
|
||||
);
|
||||
vm_error!("dispatch: program counter out of bounds pc={} len={}", self.pc, self.bytecode.len());
|
||||
self.profiling.instruction_count = steps;
|
||||
return Err("program counter out of bounds".into());
|
||||
}
|
||||
|
||||
let instr = self.bytecode[self.pc];
|
||||
let op = opcode::opcode(instr);
|
||||
let rd = opcode::rd(instr) as usize;
|
||||
let a = opcode::a(instr) as usize;
|
||||
let b = opcode::b(instr) as usize;
|
||||
self.pc += 1;
|
||||
|
||||
match op {
|
||||
OpCode::NOP => {}
|
||||
|
||||
OpCode::HALT => {
|
||||
vm_trace!("HALT: regs[0]={:?}", self.regs[0]);
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Ok(self.regs[0]);
|
||||
}
|
||||
|
||||
OpCode::LOAD_CONST => {
|
||||
self.dispatch_load_const(rd, instr)?;
|
||||
}
|
||||
|
||||
OpCode::LOAD_GLOBAL => match self.dispatch_load_global(rd, instr) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::LOAD_GLOBAL_TYPEOF => {
|
||||
self.dispatch_load_global_typeof(rd, instr)?;
|
||||
}
|
||||
|
||||
OpCode::CREATE_CLOSURE => {
|
||||
self.dispatch_create_closure(rd, instr)?;
|
||||
}
|
||||
OpCode::MAKE_CELL => {
|
||||
self.dispatch_make_cell(rd, instr)?;
|
||||
}
|
||||
OpCode::MAKE_CELL_FRESH => {
|
||||
self.dispatch_make_cell_fresh(rd, instr)?;
|
||||
}
|
||||
OpCode::CELL_GET => {
|
||||
self.dispatch_cell_get(rd, a, b)?;
|
||||
}
|
||||
OpCode::CELL_SET => {
|
||||
self.dispatch_cell_set(a, b)?;
|
||||
}
|
||||
OpCode::LOAD_UPVALUE => {
|
||||
self.dispatch_load_upvalue(rd, instr)?;
|
||||
}
|
||||
OpCode::STORE_UPVALUE => {
|
||||
self.dispatch_store_upvalue(rd, a, b)?;
|
||||
}
|
||||
OpCode::CREATE_REGEXP => match self.dispatch_create_regexp(rd, a, b) {
|
||||
Ok(Some(result)) => return Ok(result),
|
||||
Ok(None) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::ADD => {
|
||||
self.dispatch_add(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::CONCAT_N => {
|
||||
self.dispatch_concat_n(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::SUB => {
|
||||
binary_arith!(self, a, b, rd, -, false);
|
||||
}
|
||||
|
||||
OpCode::MUL => {
|
||||
binary_arith!(self, a, b, rd, *, false);
|
||||
}
|
||||
|
||||
OpCode::DIV => {
|
||||
binary_arith!(self, a, b, rd, /, true);
|
||||
}
|
||||
|
||||
OpCode::MOD => {
|
||||
binary_arith!(self, a, b, rd, %, true);
|
||||
}
|
||||
|
||||
OpCode::EXP => {
|
||||
self.dispatch_exp(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::NEG => {
|
||||
self.dispatch_neg(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::BIT_AND => {
|
||||
self.dispatch_bit_and(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::BIT_OR => {
|
||||
self.dispatch_bit_or(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::BIT_XOR => {
|
||||
self.dispatch_bit_xor(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::SHL => {
|
||||
self.dispatch_shl(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::SHR => {
|
||||
self.dispatch_shr(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::USHR => {
|
||||
self.dispatch_ushr(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::BIT_NOT => {
|
||||
self.dispatch_bit_not(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::EQ => {
|
||||
self.dispatch_eq(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::NEQ => {
|
||||
self.dispatch_neq(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::LT => {
|
||||
self.dispatch_lt(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::GT => {
|
||||
self.dispatch_gt(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::LTE => {
|
||||
self.dispatch_lte(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::GTE => {
|
||||
self.dispatch_gte(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::STRICT_EQ => {
|
||||
self.dispatch_strict_eq(rd, a, b);
|
||||
}
|
||||
|
||||
OpCode::STRICT_NEQ => {
|
||||
self.dispatch_strict_neq(rd, a, b);
|
||||
}
|
||||
|
||||
OpCode::UNARY_PLUS => {
|
||||
self.dispatch_unary_plus(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::JMP => {
|
||||
self.dispatch_jmp(instr);
|
||||
}
|
||||
|
||||
OpCode::BREAK => {
|
||||
self.dispatch_break(instr)?;
|
||||
}
|
||||
|
||||
OpCode::CONTINUE => {
|
||||
self.dispatch_continue(instr)?;
|
||||
}
|
||||
|
||||
OpCode::JMP_IF_FALSE => {
|
||||
self.dispatch_jmp_if_false(rd, instr);
|
||||
}
|
||||
|
||||
OpCode::JMP_IF_TRUE => {
|
||||
self.dispatch_jmp_if_true(rd, instr);
|
||||
}
|
||||
|
||||
OpCode::JMP_IF_NULLISH => {
|
||||
self.dispatch_jmp_if_nullish(rd, instr);
|
||||
}
|
||||
|
||||
OpCode::LOAD_VAR => match self.dispatch_load_var(rd, a) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::STORE_VAR => match self.dispatch_store_var(rd, a, b) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::MOV => {
|
||||
vm_trace!("MOV r{} = r{}", rd, a);
|
||||
self.regs[rd] = self.regs[a];
|
||||
}
|
||||
|
||||
OpCode::SPILL => {
|
||||
self.dispatch_spill(rd)?;
|
||||
}
|
||||
|
||||
OpCode::UNSPILL => {
|
||||
self.dispatch_unspill(rd)?;
|
||||
}
|
||||
|
||||
OpCode::CALL => match self.dispatch_call(rd, a, b) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::CALL_NATIVE => {
|
||||
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,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
OpCode::SET_HOME_OBJECT => match self.dispatch_set_home_object(rd, a) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::DEFINE_ACCESSOR => {
|
||||
self.dispatch_define_accessor(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_ACCESSOR_DYNAMIC => {
|
||||
self.dispatch_define_accessor_dynamic(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_PROP => {
|
||||
self.dispatch_define_prop(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_GLOBAL_PROP => {
|
||||
self.dispatch_define_global_prop(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_GLOBAL_PROP_C => {
|
||||
let key_idx = self.bytecode[self.pc] as u16;
|
||||
self.pc += 1;
|
||||
self.dispatch_define_global_prop_c(a, key_idx)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_GLOBAL_PROP_IF_ABSENT => {
|
||||
self.dispatch_define_global_prop_if_absent(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_GLOBAL_PROP_C_IF_ABSENT => {
|
||||
let key_idx = self.bytecode[self.pc] as u16;
|
||||
self.pc += 1;
|
||||
self.dispatch_define_global_prop_c_if_absent(a, key_idx)?;
|
||||
}
|
||||
|
||||
OpCode::DELETE_GLOBAL_PROP_C => {
|
||||
let key_idx = self.bytecode[self.pc] as u16;
|
||||
self.pc += 1;
|
||||
self.dispatch_delete_global_prop_c(rd, a, key_idx)?;
|
||||
}
|
||||
|
||||
OpCode::CAN_DECLARE_GLOBAL_FUNC => {
|
||||
self.dispatch_can_declare_global_func(rd, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_GLOBAL_FUNC_BIND => {
|
||||
self.dispatch_define_global_func_bind(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_PROP_ATTRS => {
|
||||
let attrs = self.bytecode[self.pc] as u8;
|
||||
self.pc += 1;
|
||||
self.dispatch_define_prop_attrs(rd, a, b, attrs)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_ACCESSOR_ATTRS => {
|
||||
let key_word = self.bytecode[self.pc];
|
||||
let attrs = self.bytecode[self.pc + 1] as u8;
|
||||
self.pc += 2;
|
||||
self.dispatch_define_accessor_attrs(rd, a, b, key_word, attrs)?;
|
||||
}
|
||||
|
||||
OpCode::RETURN => match self.dispatch_return(instr) {
|
||||
Ok(Some(result)) => return Ok(result),
|
||||
Ok(None) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::IC_GET_PROP
|
||||
| OpCode::IC_SET_PROP
|
||||
| OpCode::GET_PROP
|
||||
| OpCode::SET_PROP
|
||||
| OpCode::SET_PROP_BATCH
|
||||
| OpCode::GET_PROP_DYNAMIC
|
||||
| OpCode::SET_PROP_DYNAMIC
|
||||
| OpCode::SET_ELEM
|
||||
| OpCode::GET_PRIVATE
|
||||
| OpCode::SET_PRIVATE
|
||||
| OpCode::INIT_PRIVATE
|
||||
| OpCode::PRIVATE_BRAND_IN => {
|
||||
self.dispatch_property_op(op, rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::NEW_OBJECT => {
|
||||
self.dispatch_new_object(rd, instr)?;
|
||||
}
|
||||
|
||||
OpCode::NEW_SESSION_OBJECT => {
|
||||
self.dispatch_new_session_object(rd)?;
|
||||
}
|
||||
|
||||
OpCode::CREATE_ARGUMENTS => {
|
||||
self.dispatch_create_arguments(rd)?;
|
||||
}
|
||||
|
||||
OpCode::CREATE_REST_ARRAY => {
|
||||
let fixed_count = opcode::b(instr) as usize;
|
||||
self.dispatch_create_rest_array(rd, fixed_count)?;
|
||||
}
|
||||
|
||||
OpCode::NEW_ARRAY => {
|
||||
self.dispatch_new_array(rd, instr);
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_ADD => {
|
||||
self.dispatch_compound_add(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_SUB => {
|
||||
self.dispatch_compound_sub(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_MUL => {
|
||||
self.dispatch_compound_mul(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_DIV => {
|
||||
self.dispatch_compound_div(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_MOD => {
|
||||
self.dispatch_compound_mod(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_EXP => {
|
||||
self.dispatch_compound_exp(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_AND => {
|
||||
self.dispatch_compound_bit_and(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_OR => {
|
||||
self.dispatch_compound_bit_or(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_XOR => {
|
||||
self.dispatch_compound_bit_xor(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_SHL => {
|
||||
self.dispatch_compound_shl(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_SHR => {
|
||||
self.dispatch_compound_shr(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::COMPOUND_USHR => {
|
||||
self.dispatch_compound_ushr(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::TYPEOF => {
|
||||
self.dispatch_typeof(rd, a);
|
||||
}
|
||||
|
||||
OpCode::TO_OBJECT => {
|
||||
self.dispatch_to_object(rd)?;
|
||||
}
|
||||
|
||||
OpCode::VOID => {
|
||||
self.dispatch_void(rd);
|
||||
}
|
||||
|
||||
OpCode::TEMPLATE_STR => {
|
||||
self.dispatch_template_str(rd)?;
|
||||
}
|
||||
|
||||
OpCode::GET_TEMPLATE_OBJECT => {
|
||||
self.dispatch_get_template_object(rd)?;
|
||||
}
|
||||
|
||||
OpCode::DELETE_PROP_STATIC => match self.dispatch_delete_prop_static(rd) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::DELETE_PROP_DYNAMIC => match self.dispatch_delete_prop_dynamic(rd, b) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::INSTANCEOF => {
|
||||
self.dispatch_instanceof(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::IN => {
|
||||
self.dispatch_in(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::NOT => {
|
||||
self.dispatch_not(rd, a);
|
||||
}
|
||||
|
||||
OpCode::AND => {
|
||||
self.dispatch_and(rd, a, b);
|
||||
}
|
||||
|
||||
OpCode::OR => {
|
||||
self.dispatch_or(rd, a, b);
|
||||
}
|
||||
|
||||
OpCode::NULLISH => {
|
||||
self.dispatch_nullish(rd, a, b);
|
||||
}
|
||||
|
||||
OpCode::INC_PRE => {
|
||||
self.dispatch_inc_pre(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::INC_POST => {
|
||||
self.dispatch_inc_post(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::DEC_PRE => {
|
||||
self.dispatch_dec_pre(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::DEC_POST => {
|
||||
self.dispatch_dec_post(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::MEMBER_INC
|
||||
| OpCode::MEMBER_DEC
|
||||
| OpCode::DYN_MEMBER_INC
|
||||
| OpCode::DYN_MEMBER_DEC
|
||||
| OpCode::COMPOUND_MEMBER_ADD
|
||||
| OpCode::COMPOUND_MEMBER_SUB
|
||||
| OpCode::COMPOUND_MEMBER_MUL
|
||||
| OpCode::COMPOUND_MEMBER_DIV
|
||||
| OpCode::COMPOUND_MEMBER_MOD
|
||||
| OpCode::COMPOUND_MEMBER_EXP
|
||||
| OpCode::COMPOUND_MEMBER_BIT_AND
|
||||
| OpCode::COMPOUND_MEMBER_BIT_OR
|
||||
| OpCode::COMPOUND_MEMBER_BIT_XOR
|
||||
| OpCode::COMPOUND_MEMBER_SHL
|
||||
| OpCode::COMPOUND_MEMBER_SHR
|
||||
| OpCode::COMPOUND_MEMBER_USHR => {
|
||||
self.dispatch_member_op(op, rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_IN_INIT => {
|
||||
self.dispatch_for_in_init(a)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_IN_NEXT => {
|
||||
self.dispatch_for_in_next(rd)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_IN_DONE => {
|
||||
self.dispatch_for_in_done(rd);
|
||||
}
|
||||
|
||||
OpCode::FOR_IN_CLEANUP => {
|
||||
self.dispatch_for_in_cleanup();
|
||||
}
|
||||
|
||||
OpCode::FOR_OF_INIT => {
|
||||
self.dispatch_for_of_init(a)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_OF_NEXT => {
|
||||
self.dispatch_for_of_next(rd)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_OF_DONE => {
|
||||
self.dispatch_for_of_done(rd)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_OF_CLOSE => {
|
||||
self.dispatch_for_of_close()?;
|
||||
}
|
||||
|
||||
OpCode::FOR_AWAIT_OF_INIT => {
|
||||
self.dispatch_for_await_of_init(a)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_AWAIT_OF_NEXT => {
|
||||
self.dispatch_for_await_of_next(rd)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_AWAIT_OF_DONE => {
|
||||
self.dispatch_for_await_of_done(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::FOR_AWAIT_OF_CLOSE => {
|
||||
self.dispatch_for_await_of_close()?;
|
||||
// 异步 IteratorClose 可能经 await 挂起(return() 的 promise),
|
||||
// 挂起时须像 AWAIT 一样让内嵌 dispatch 返回,由恢复方快照状态。
|
||||
if self.async_suspended || self.async_gen_suspended {
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
}
|
||||
|
||||
OpCode::REST_OBJECT => {
|
||||
self.dispatch_rest_object(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::SPREAD_OBJECT => {
|
||||
self.dispatch_spread_object(rd, a)?;
|
||||
}
|
||||
|
||||
OpCode::THROW => match self.dispatch_throw(rd) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::TRY_BEGIN => {
|
||||
self.dispatch_try_begin(instr);
|
||||
}
|
||||
|
||||
OpCode::TRY_END => {
|
||||
self.dispatch_try_end();
|
||||
}
|
||||
|
||||
OpCode::TRY_FINALLY_BEGIN => {
|
||||
self.dispatch_try_finally_begin(instr);
|
||||
}
|
||||
|
||||
OpCode::TRY_FINALLY_ENTER => {
|
||||
self.dispatch_try_finally_enter();
|
||||
}
|
||||
|
||||
OpCode::TRY_FINALLY_END => match self.dispatch_try_finally_end() {
|
||||
Ok(Some(result)) => return Ok(result),
|
||||
Ok(None) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::YIELD => {
|
||||
// 生成器让出:把让出值存入信号,内嵌 dispatch 返回,恢复方快照挂起状态。
|
||||
let value = self.regs[rd];
|
||||
self.generator_suspended = Some(value);
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
|
||||
OpCode::YIELD_STAR => {
|
||||
// `yield*` 委托:取内层迭代器并推进一步。
|
||||
// 未 done → 挂起让出(存委托迭代器);done → 委托完成值写 reg 0 继续外层;
|
||||
// unwind 捕获到异常 → 继续 dispatch(已展开到 catch/finally)。
|
||||
match self.dispatch_yield_star(rd)? {
|
||||
crate::generator::YieldStarOutcome::Suspend(value) => {
|
||||
self.generator_suspended = Some(value);
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
crate::generator::YieldStarOutcome::Continue(value) => {
|
||||
self.regs[0] = value;
|
||||
}
|
||||
crate::generator::YieldStarOutcome::Unwind => {}
|
||||
}
|
||||
}
|
||||
|
||||
OpCode::SUSPEND_BODY => {
|
||||
// 生成器调用时参数初始化结束:挂起在 body 起点;正常 next() 恢复时直接穿过。
|
||||
if self.generator_init_step {
|
||||
self.generator_body_started = true;
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
}
|
||||
|
||||
OpCode::AWAIT => {
|
||||
// 异步帧挂起:登记恢复反应后内嵌 dispatch 返回,恢复方快照挂起状态。
|
||||
self.dispatch_await(rd)?;
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(format!("opcode {op} not yet implemented"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
//! 动态编译:`Function` 构造器与脚本动态编译入口,及子树 flat_id 重编号。
|
||||
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
use oxide_bytecode::opcode::{self, OpCode};
|
||||
use oxide_types::object::PropAttributes;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use super::Vm;
|
||||
|
||||
impl Vm {
|
||||
/// 动态编译函数(`Function` 构造器路径):把参数列表与函数体 wrap 成匿名函数
|
||||
/// 源码,走完整编译链后取匿名函数模块追加进 VM 平表,返回对应函数对象。
|
||||
///
|
||||
/// # 源契约
|
||||
/// `params` 与 `body` 须为 `string_forge::source_escape` 产物(源码域转义
|
||||
/// 形态):孤立 surrogate / FFFD 单元以 `\uXXXX` 转义文本承载,反斜杠
|
||||
/// 原样透传。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. wrap 源码 `function anonymous(p...) { body }`,parse + compile。
|
||||
/// 2. 取 `sub_modules[0]`(匿名函数模块),把其子树 flat_id 重编号到平表末尾
|
||||
/// 并重写子树内每条 `CREATE_CLOSURE` 的 imm16。
|
||||
/// 3. 同步扩容当前代际的常量缓存,建函数对象并设置 name/length。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 编译或解析失败返回 `Err`(由 builtin 层转 SyntaxError)。
|
||||
/// - 追加的子树原 flat_id 自 1 连续(flatten 后 1=匿名体,2…=其嵌套函数);
|
||||
/// 新 id = 平表长度 + (old - 1)。
|
||||
/// - 动态函数在函数对象存活期间跨 run 有效:扩展落当前代际平表,存活函数
|
||||
/// 对象按代际引用保活该表;full_reset 重建 session 后失效。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
|
||||
pub fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String> {
|
||||
// wrap 源码:末尾换行防止 body 以行注释结尾吞掉右花括号。
|
||||
let params_str = params.join(", ");
|
||||
let source = format!("function anonymous({params_str}) {{\n{body}\n}}");
|
||||
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, &source)
|
||||
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
|
||||
// 动态路径源契约:调用方以源码域转义(`string_forge::source_escape`)
|
||||
// 形态传源;正则字面量源切片经 `source_escape_to_key` 还原注入
|
||||
// marker 后入池(编码源口径,区别于静态源的 `pool_key_plain`)。
|
||||
let mut module = oxide_compiler::compiler::Compiler::new()
|
||||
.with_source_encoded(true)
|
||||
.compile(&program)?;
|
||||
let anonymous = module.sub_modules.remove(0);
|
||||
// 形参数以编译结果为准:单个实参 "a,b,c" 拼接后解析为 3 个形参
|
||||
// (ES 动态函数把非末位实参以逗号连接成参数串再解析)。
|
||||
let formal_count = anonymous.n_args as i32;
|
||||
|
||||
// 子树重编号 + 追加:base = 当前代际平表长度,DFS 前序压入,push 序即新
|
||||
// flat_id。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
|
||||
let table = self.current_table_mut();
|
||||
let base = table.modules.len() as u32;
|
||||
let mut added = Vec::new();
|
||||
rehome_subtree(&anonymous, base, &mut added);
|
||||
Arc::make_mut(&mut table.modules).extend(added);
|
||||
// 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。
|
||||
table.immutables.resize(table.modules.len(), OnceLock::new());
|
||||
|
||||
let func_val = self.create_function_object(base, self.current_gen, false, false, false, false);
|
||||
let func_obj = unsafe { &mut *func_val.as_js_object_ptr() };
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let name_si = self.kernel_core.perm_interner().intern("name").0;
|
||||
// length/name 为不可写不可枚举可配置,且 length 先于 name(规范属性顺序)。
|
||||
let attrs = PropAttributes::new(false, false, true);
|
||||
let length_val = JsValue::int(formal_count);
|
||||
let name_val = self.new_string("anonymous");
|
||||
self.define_data_property(func_obj, length_si, length_val, attrs)?;
|
||||
self.define_data_property(func_obj, name_si, name_val, attrs)?;
|
||||
Ok(func_val)
|
||||
}
|
||||
|
||||
/// 动态编译脚本(eval 脚本模式):把源码按脚本模式编译,var/函数声明落全局对象
|
||||
/// (属性 configurable:true,区别于普通脚本顶层的 false)。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true,
|
||||
/// Compiler 置 is_eval_script=true)。
|
||||
/// 2. 整棵模块树(根 flat_id=0 + 嵌套函数)追加进平表:`rehome_subtree(&module, base+1)`,
|
||||
/// 使根落 base、子函数 old→base+old,CREATE_CLOSURE imm16 同步重写。
|
||||
/// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 顶层 return 不报 SyntaxError(emit 无此检查,与 CLI 脚本路径一致)——已知偏差。
|
||||
/// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。
|
||||
/// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
|
||||
pub fn create_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, code)
|
||||
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
|
||||
// eval 脚本:顶层 var/function 声明落全局属性 configurable:true。
|
||||
// 动态路径源契约同 `create_dynamic_function`(源码域转义形态传源)。
|
||||
let module = oxide_compiler::compiler::Compiler::new()
|
||||
.with_eval_script(true)
|
||||
.with_source_encoded(true)
|
||||
.compile(&program)?;
|
||||
// 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0
|
||||
// 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
|
||||
let table = self.current_table_mut();
|
||||
let base = table.modules.len() as u32;
|
||||
let mut added = Vec::new();
|
||||
rehome_subtree(&module, base + 1, &mut added);
|
||||
Arc::make_mut(&mut table.modules).extend(added);
|
||||
// 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。
|
||||
table.immutables.resize(table.modules.len(), OnceLock::new());
|
||||
Ok(self.create_function_object(base, self.current_gen, false, false, false, false))
|
||||
}
|
||||
|
||||
/// 动态编译脚本(普通脚本模式):顶层 var/function 声明落全局对象
|
||||
/// configurable:false,与静态脚本顶层一致(区别于 [`create_dynamic_script`]
|
||||
/// 的 eval 脚本 configurable:true 面)。
|
||||
///
|
||||
/// 供 test262 宿主 `$262.evalScript` 使用:按 test262 harness 规范它是
|
||||
/// 当前 realm 的一段普通 script,不是 eval。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true,
|
||||
/// 不设 is_eval_script,与普通脚本同一编译臂)。
|
||||
/// 2. 整棵模块树追加进平表:同 [`create_dynamic_script`]
|
||||
/// (`rehome_subtree(&module, base+1)`,根落 base、子函数 old→base+old)。
|
||||
/// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 编译或解析失败返回 `Err`(由宿主层转 SyntaxError)。
|
||||
/// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。
|
||||
/// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
|
||||
pub fn create_plain_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, code)
|
||||
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
|
||||
// 普通脚本:顶层 var/function 声明落全局属性 configurable:false,
|
||||
// let/const 落全局词法环境。动态路径源契约同 `create_dynamic_function`
|
||||
// (源码域转义形态传源)。
|
||||
let module = oxide_compiler::compiler::Compiler::new()
|
||||
.with_source_encoded(true)
|
||||
.compile(&program)?;
|
||||
// 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0
|
||||
// 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
|
||||
let table = self.current_table_mut();
|
||||
let base = table.modules.len() as u32;
|
||||
let mut added = Vec::new();
|
||||
rehome_subtree(&module, base + 1, &mut added);
|
||||
Arc::make_mut(&mut table.modules).extend(added);
|
||||
// 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。
|
||||
table.immutables.resize(table.modules.len(), OnceLock::new());
|
||||
Ok(self.create_function_object(base, self.current_gen, false, false, false, false))
|
||||
}
|
||||
}
|
||||
|
||||
/// 把 flatten 后的子模块子树重编号到平表偏移 `base`:DFS 前序拷贝进 `out`,
|
||||
/// 新 flat_id = base + (old - 1),子树内每条 `CREATE_CLOSURE` 的 imm16 同步重写。
|
||||
/// 原子树 flat_id 自 1 连续,因此拷贝顺序即新 id 顺序,`out` 下标对齐平表槽位。
|
||||
fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec<Arc<CompiledModule>>) {
|
||||
let new_id = base + module.flat_id - 1;
|
||||
let mut bytecode = module.bytecode.to_vec();
|
||||
for instr in &mut bytecode {
|
||||
if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE {
|
||||
let old = opcode::imm16(*instr) as u32;
|
||||
let new_flat = base + (old - 1);
|
||||
*instr = opcode::encode(
|
||||
OpCode::CREATE_CLOSURE,
|
||||
opcode::rd(*instr),
|
||||
(new_flat & 0xFF) as u8,
|
||||
((new_flat >> 8) & 0xFF) as u8,
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut rehomed = module.clone();
|
||||
rehomed.bytecode = Arc::from(bytecode);
|
||||
rehomed.flat_id = new_id;
|
||||
out.push(Arc::new(rehomed));
|
||||
for sub in &module.sub_modules {
|
||||
rehome_subtree(sub, base, out);
|
||||
}
|
||||
}
|
||||
+10
-1155
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,185 @@
|
||||
//! `oxide_runtime_api::VmHost` 委托实现:宿主接口各项委托到 `Vm` 同名固有方法。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::mem::Epoch;
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use super::Vm;
|
||||
|
||||
impl oxide_runtime_api::VmHost for Vm {
|
||||
fn reg(&self, idx: u8) -> JsValue {
|
||||
self.reg(idx)
|
||||
}
|
||||
fn set_reg(&mut self, idx: u8, val: JsValue) {
|
||||
self.set_reg(idx, val);
|
||||
}
|
||||
fn native_overflow_count(&self) -> usize {
|
||||
self.native_overflow_count
|
||||
}
|
||||
fn native_overflow_at(&self, i: usize) -> JsValue {
|
||||
self.spill_stack[self.native_overflow_base + i]
|
||||
}
|
||||
fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject {
|
||||
self.alloc_object(obj)
|
||||
}
|
||||
fn new_string(&mut self, s: &str) -> JsValue {
|
||||
self.new_string(s)
|
||||
}
|
||||
fn new_string_owned(&mut self, s: String) -> JsValue {
|
||||
Vm::new_string_owned(self, s)
|
||||
}
|
||||
fn new_bigint(&mut self, v: num_bigint::BigInt) -> JsValue {
|
||||
Vm::new_bigint(self, v)
|
||||
}
|
||||
fn bigint_value(&mut self, val: JsValue) -> &num_bigint::BigInt {
|
||||
Vm::bigint_value(self, val)
|
||||
}
|
||||
fn kernel_core(&self) -> &Arc<KernelCore> {
|
||||
self.kernel_core()
|
||||
}
|
||||
fn session(&self) -> &KernelSession {
|
||||
self.session()
|
||||
}
|
||||
fn epoch(&self) -> &Epoch {
|
||||
self.epoch()
|
||||
}
|
||||
fn take_uncaught_value(&mut self) -> Option<JsValue> {
|
||||
self.last_uncaught_value.take()
|
||||
}
|
||||
fn restore_uncaught_value(&mut self, value: Option<JsValue>) {
|
||||
self.last_uncaught_value = value;
|
||||
}
|
||||
fn property_key_si(&mut self, val: JsValue) -> u32 {
|
||||
// Object-key conversion (to_string_full) failures degrade to the empty key here:
|
||||
// this trait path is used by Reflect/Object builtins; computed property access
|
||||
// uses the inherent Result-returning version to preserve the full exception.
|
||||
self.property_key_si(val)
|
||||
.unwrap_or_else(|_| self.kernel_core.perm_interner().intern("").0)
|
||||
}
|
||||
fn to_property_key_si(&mut self, val: JsValue) -> Result<u32, String> {
|
||||
// ToPropertyKey 完整路径:转换异常(对象 ToPrimitive 抛错 / Symbol 处理)
|
||||
// 原样返回,供需传播异常的 builtins(groupBy / __defineGetter__ 等)使用。
|
||||
self.property_key_si(val)
|
||||
}
|
||||
fn string_key_si(&mut self, s: &str) -> u32 {
|
||||
// 与运行时字符串键同口径:含 FFFD 的键文本按 encode_key 形态入键空间
|
||||
// (FFFD 键的 JSON.parse 键与字面量/拼接构造的运行时键身份一致)。
|
||||
self.string_key_text(s)
|
||||
}
|
||||
fn string_units(&self, val: JsValue) -> std::borrow::Cow<'_, [u16]> {
|
||||
// SAFETY: 调用方保证 val 为字符串值。perm 串由内核持有永不释放;session
|
||||
// 串仅经 &mut self 路径(new_string/GC)释放,&self 借用期间编译器强制
|
||||
// 不存在 &mut 存续,字符串不会在借用期内回收。
|
||||
unsafe { (*val.as_string_ptr()).units() }
|
||||
}
|
||||
fn new_string_units(&mut self, units: &[u16]) -> JsValue {
|
||||
Vm::new_string_units(self, units)
|
||||
}
|
||||
fn new_string_units_owned(&mut self, units: Vec<u16>) -> JsValue {
|
||||
Vm::new_string_units_owned(self, units)
|
||||
}
|
||||
fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
|
||||
self.resolve_property(obj, prop_name_si)
|
||||
}
|
||||
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32> {
|
||||
self.get_own_property_slot(obj, prop_name_si)
|
||||
}
|
||||
fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String> {
|
||||
self.ordinary_get(obj, prop_name_si, receiver)
|
||||
}
|
||||
fn ordinary_set(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, strict: bool,
|
||||
) -> Result<(), String> {
|
||||
self.ordinary_set(obj, prop_name_si, val, receiver, strict)
|
||||
}
|
||||
fn define_data_property(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes,
|
||||
) -> Result<(), String> {
|
||||
self.define_data_property(obj, prop_name_si, val, attributes)
|
||||
}
|
||||
fn define_accessor_property(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, get: JsValue, set: JsValue, attributes: PropAttributes,
|
||||
) -> Result<(), String> {
|
||||
self.define_accessor_property(obj, prop_name_si, get, set, attributes)
|
||||
}
|
||||
fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
|
||||
self.set_or_create_prop_value(obj, prop_name_si, val)
|
||||
}
|
||||
fn sync_global_builtin_mirror(&mut self, obj: &JsObject, key_si: u32, val: JsValue) {
|
||||
self.sync_global_builtin_mirror(obj, key_si, val)
|
||||
}
|
||||
fn lookup_str(&self, val: JsValue) -> Option<String> {
|
||||
self.lookup_str(val)
|
||||
}
|
||||
fn coerce_primitive_bounded(&mut self, value: JsValue, prefer_string: bool) -> Result<JsValue, String> {
|
||||
self.coerce_primitive_bounded(value, prefer_string)
|
||||
}
|
||||
fn coerce_number_bounded(&mut self, value: JsValue) -> Result<f64, String> {
|
||||
self.coerce_number_bounded(value)
|
||||
}
|
||||
fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result<JsValue, String> {
|
||||
self.call_function_sync(callee, receiver, args)
|
||||
}
|
||||
fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result<Option<*mut JsObject>, String> {
|
||||
self.checked_object_ptr(val, error_msg)
|
||||
}
|
||||
fn raise_type_error(&mut self, msg: &str) -> Result<(), String> {
|
||||
self.raise_type_error(msg)
|
||||
}
|
||||
fn error_message_text(&self, kind: &str, msg: &str) -> String {
|
||||
self.error_message_text(kind, msg)
|
||||
}
|
||||
fn call_stack_function_names(&self) -> Vec<String> {
|
||||
self.frames
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|f| {
|
||||
self.kernel_core
|
||||
.perm_interner()
|
||||
.lookup(f.function_name)
|
||||
.unwrap_or("<anonymous>")
|
||||
.to_string()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue {
|
||||
self.promote_if_needed_for_write_ptr(target_ptr, value)
|
||||
}
|
||||
fn step_rng(&mut self) {
|
||||
self.step_rng()
|
||||
}
|
||||
fn math_rng_value(&self) -> f64 {
|
||||
self.math_rng_value()
|
||||
}
|
||||
fn sub_module_function_name(&self, gen: u32, sub_idx: u16) -> String {
|
||||
self.tables
|
||||
.get(&gen)
|
||||
.and_then(|t| t.modules.get(sub_idx as usize))
|
||||
.and_then(|m| m.function_name.clone())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String> {
|
||||
self.create_dynamic_function(params, body)
|
||||
}
|
||||
fn create_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> {
|
||||
self.create_dynamic_script(code)
|
||||
}
|
||||
fn symbol_intern(&mut self, desc: Option<String>) -> u32 {
|
||||
self.symbols.intern(desc)
|
||||
}
|
||||
fn symbol_description(&self, idx: u32) -> Option<&str> {
|
||||
self.symbols.description(idx)
|
||||
}
|
||||
fn symbol_lookup_global(&self, key: &str) -> Option<u32> {
|
||||
self.symbols.lookup_global(key)
|
||||
}
|
||||
fn symbol_register_global(&mut self, key: String, idx: u32) {
|
||||
self.symbols.register_global(key, idx)
|
||||
}
|
||||
fn symbol_key_for_id(&self, idx: u32) -> Option<String> {
|
||||
self.symbols.key_for_id(idx)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user