fix(vm): 子模块全局扁平化根治逃逸闭包上下文错位
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::native::NativeFn;
|
||||
use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm};
|
||||
use crate::{vm_debug, vm_trace};
|
||||
@@ -89,7 +87,7 @@ impl Vm {
|
||||
pub(crate) fn dispatch_create_closure(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||||
let sub_idx = opcode::imm16(instr) as u32;
|
||||
vm_trace!("CREATE_CLOSURE rd={} sub_idx={}", rd, sub_idx);
|
||||
if sub_idx == 0 || (sub_idx as usize) > self.sub_modules.len() {
|
||||
if sub_idx == 0 || (sub_idx as usize) >= self.sub_modules.len() {
|
||||
// 逃逸闭包(函数对象在定义模块之外被创建)时 sub_idx 相对定义模块,
|
||||
// 超出当前 sub_modules 上下文——按运行时错误处理而非索引越界 panic。
|
||||
return Err(format!(
|
||||
@@ -98,7 +96,7 @@ impl Vm {
|
||||
self.sub_modules.len()
|
||||
));
|
||||
}
|
||||
let sub = &self.sub_modules[sub_idx as usize - 1];
|
||||
let sub = &self.sub_modules[sub_idx as usize];
|
||||
let is_arrow = sub.is_arrow;
|
||||
let is_class_constructor = sub.is_class_constructor;
|
||||
let is_derived_constructor = sub.is_derived_constructor;
|
||||
@@ -366,7 +364,7 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
} else if super_obj.sub_module_index() > 0 {
|
||||
let sub_idx = super_obj.sub_module_index() as usize - 1;
|
||||
let sub_idx = super_obj.sub_module_index() as usize;
|
||||
if sub_idx >= self.sub_modules.len() {
|
||||
return Err(format!(
|
||||
"SUPER_CALL: sub_module_index {} out of bounds (max {})",
|
||||
@@ -421,7 +419,7 @@ impl Vm {
|
||||
|
||||
self.bytecode = sub_bytecode;
|
||||
let subs = Arc::clone(&self.sub_modules);
|
||||
self.activate_immutables(sub_idx + 1, &subs[sub_idx].constants);
|
||||
self.activate_immutables(sub_idx, &subs[sub_idx].constants);
|
||||
self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize));
|
||||
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map.clone() {
|
||||
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
|
||||
@@ -431,15 +429,6 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
let callee_subs = &subs[sub_idx].sub_modules;
|
||||
if callee_subs.is_empty() {
|
||||
self.sub_module_stack.push((Arc::clone(&self.sub_modules), None));
|
||||
} else {
|
||||
self.sub_module_stack
|
||||
.push((Arc::clone(&self.sub_modules), Some(std::mem::take(&mut self.immutables_cache))));
|
||||
self.sub_modules = Arc::new(callee_subs.clone());
|
||||
self.immutables_cache = (0..=callee_subs.len()).map(|_| OnceLock::new()).collect();
|
||||
}
|
||||
self.active_reg_limit = sub_n_registers.max(1);
|
||||
self.pc = 0;
|
||||
return Ok(true);
|
||||
|
||||
+29
-16
@@ -188,6 +188,8 @@ pub struct Vm {
|
||||
pub epoch: Epoch,
|
||||
pub object_prototype: P<JsObject>,
|
||||
pub math_rng_state: u64,
|
||||
/// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。
|
||||
/// 闭包 `sub_module_index` 即 flat_id,逃逸闭包也能自足解析。
|
||||
pub(crate) sub_modules: Arc<Vec<CompiledModule>>,
|
||||
pub(crate) saved_bytecode_stack: Vec<Vec<opcode::Instr>>,
|
||||
pub(crate) saved_immutables_stack: Vec<*const [JsValue]>,
|
||||
@@ -225,9 +227,7 @@ pub struct Vm {
|
||||
pub(crate) iters: IterState,
|
||||
/// 分组保存 inline cache 与指令计数器。
|
||||
pub(crate) profiling: ProfilingState,
|
||||
pub(crate) sub_module_stack: Vec<(Arc<Vec<CompiledModule>>, Option<Vec<OnceLock<Vec<JsValue>>>>)>,
|
||||
pub(crate) cell_stack: Vec<Vec<*mut Cell>>,
|
||||
pub(crate) temp_immutables: Vec<Vec<JsValue>>,
|
||||
/// 可复用字符串缓冲区,避免每次 `+` 拼接都分配。
|
||||
pub(crate) string_buf: String,
|
||||
}
|
||||
@@ -608,6 +608,31 @@ impl Vm {
|
||||
let s = unsafe { &(*val.as_string_ptr()).data };
|
||||
return self.kernel_core.perm_interner().intern(s).0;
|
||||
}
|
||||
// well-known symbol 是空对象:按指针比对映射为各自专属键,避免全部塌缩。
|
||||
if val.is_object() {
|
||||
let world = self.session.builtin_world();
|
||||
let ptr = val.as_js_object_ptr();
|
||||
let key = if std::ptr::eq(ptr, world.sym_iterator.as_ptr()) {
|
||||
"@@iterator"
|
||||
} else if std::ptr::eq(ptr, world.sym_match.as_ptr()) {
|
||||
"@@match"
|
||||
} else if std::ptr::eq(ptr, world.sym_replace.as_ptr()) {
|
||||
"@@replace"
|
||||
} else if std::ptr::eq(ptr, world.sym_search.as_ptr()) {
|
||||
"@@search"
|
||||
} else if std::ptr::eq(ptr, world.sym_split.as_ptr()) {
|
||||
"@@split"
|
||||
} else if std::ptr::eq(ptr, world.sym_to_primitive.as_ptr()) {
|
||||
"@@toPrimitive"
|
||||
} else if std::ptr::eq(ptr, world.sym_has_instance.as_ptr()) {
|
||||
"@@hasInstance"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
if !key.is_empty() {
|
||||
return self.kernel_core.perm_interner().intern(key).0;
|
||||
}
|
||||
}
|
||||
let key = coercion::to_string(val);
|
||||
self.kernel_core.perm_interner().intern(&key).0
|
||||
}
|
||||
@@ -748,7 +773,7 @@ impl Vm {
|
||||
if !obj.is_function() || obj.sub_module_index() == 0 {
|
||||
return Err(self.error_message_text("TypeError", "CALL target is not callable"));
|
||||
}
|
||||
let sub_idx = obj.sub_module_index() as usize - 1;
|
||||
let sub_idx = obj.sub_module_index() as usize;
|
||||
if sub_idx >= self.sub_modules.len() {
|
||||
return Err(format!(
|
||||
"CALL: sub_module_index {} out of bounds (max {})",
|
||||
@@ -804,9 +829,7 @@ impl Vm {
|
||||
self.pc = 0;
|
||||
self.bytecode = sub_bytecode;
|
||||
let subs = Arc::clone(&self.sub_modules);
|
||||
let converted = self.convert_immutables(&subs[sub_idx].constants);
|
||||
self.temp_immutables.push(converted);
|
||||
self.active_immutables = self.temp_immutables.last().unwrap().as_slice() as *const [JsValue];
|
||||
self.activate_immutables(sub_idx, &subs[sub_idx].constants);
|
||||
self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize));
|
||||
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map.clone() {
|
||||
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
|
||||
@@ -816,16 +839,6 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
let callee_subs = &subs[sub_idx].sub_modules;
|
||||
if callee_subs.is_empty() {
|
||||
self.sub_module_stack.push((Arc::clone(&self.sub_modules), None));
|
||||
} else {
|
||||
self.sub_module_stack
|
||||
.push((Arc::clone(&self.sub_modules), Some(std::mem::take(&mut self.immutables_cache))));
|
||||
self.sub_modules = Arc::new(callee_subs.clone());
|
||||
self.immutables_cache = (0..=callee_subs.len()).map(|_| OnceLock::new()).collect();
|
||||
}
|
||||
|
||||
self.active_reg_limit = sub_n_registers.max(1);
|
||||
self.pc = 0;
|
||||
Ok(())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::native::NativeFn;
|
||||
use crate::vm::{native_fn_ptr_to_fn, CallFrame, ForInIter, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH};
|
||||
@@ -83,7 +83,7 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
} else if ctor_obj.sub_module_index() > 0 {
|
||||
let sub_idx = ctor_obj.sub_module_index() as usize - 1;
|
||||
let sub_idx = ctor_obj.sub_module_index() as usize;
|
||||
if sub_idx >= self.sub_modules.len() {
|
||||
return Err(format!(
|
||||
"NEW_EXPRESSION: sub_module_index {} out of bounds (max {})",
|
||||
@@ -142,7 +142,7 @@ impl Vm {
|
||||
|
||||
self.bytecode = sub_bytecode;
|
||||
let subs = Arc::clone(&self.sub_modules);
|
||||
self.activate_immutables(sub_idx + 1, &subs[sub_idx].constants);
|
||||
self.activate_immutables(sub_idx, &subs[sub_idx].constants);
|
||||
self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize));
|
||||
|
||||
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map {
|
||||
@@ -153,16 +153,6 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
let callee_subs = &subs[sub_idx].sub_modules;
|
||||
if callee_subs.is_empty() {
|
||||
self.sub_module_stack.push((Arc::clone(&self.sub_modules), None));
|
||||
} else {
|
||||
self.sub_module_stack
|
||||
.push((Arc::clone(&self.sub_modules), Some(std::mem::take(&mut self.immutables_cache))));
|
||||
self.sub_modules = Arc::new(callee_subs.clone());
|
||||
self.immutables_cache = (0..=callee_subs.len()).map(|_| OnceLock::new()).collect();
|
||||
}
|
||||
|
||||
self.active_reg_limit = sub_n_registers.max(1);
|
||||
self.pc = 0;
|
||||
Ok(true)
|
||||
|
||||
@@ -7,6 +7,24 @@ use crate::{vm_debug, vm_info, vm_trace, vm_warn};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
/// 按 `flat_id` 下标收集整棵子模块树为平表,供 `run()` 装载。
|
||||
fn collect_flat_modules(module: &CompiledModule) -> Vec<CompiledModule> {
|
||||
let mut out: Vec<Option<CompiledModule>> = Vec::new();
|
||||
place_flat(module, &mut out);
|
||||
out.into_iter().map(|m| m.expect("flat_id slot must be filled")).collect()
|
||||
}
|
||||
|
||||
fn place_flat(module: &CompiledModule, out: &mut Vec<Option<CompiledModule>>) {
|
||||
let idx = module.flat_id as usize;
|
||||
if idx >= out.len() {
|
||||
out.resize(idx + 1, None);
|
||||
}
|
||||
out[idx] = Some(module.clone());
|
||||
for sub in &module.sub_modules {
|
||||
place_flat(sub, out);
|
||||
}
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn call_bytecode_function_inline(
|
||||
&mut self, callee: JsValue, callee_obj: &JsObject, receiver: JsValue, args: &[JsValue],
|
||||
@@ -14,7 +32,7 @@ impl Vm {
|
||||
if callee_obj.sub_module_index() == 0 {
|
||||
return Err(self.error_message_text("TypeError", "accessor is not callable"));
|
||||
}
|
||||
let sub_idx = callee_obj.sub_module_index() as usize - 1;
|
||||
let sub_idx = callee_obj.sub_module_index() as usize;
|
||||
vm_debug!(
|
||||
"call_bytecode_function_inline: sub_idx={}, args={}, depth={}",
|
||||
sub_idx,
|
||||
@@ -68,7 +86,7 @@ impl Vm {
|
||||
self.regs = [JsValue::undefined(); 256];
|
||||
self.pc = 0;
|
||||
self.bytecode = sub.bytecode.clone();
|
||||
self.activate_immutables(sub_idx + 1, &sub.constants);
|
||||
self.activate_immutables(sub_idx, &sub.constants);
|
||||
self.active_reg_limit = sub.n_registers.max(1);
|
||||
self.root_reg_limit = self.active_reg_limit;
|
||||
self.cell_stack.push(Vec::with_capacity(sub.cells_needed as usize));
|
||||
@@ -134,13 +152,6 @@ impl Vm {
|
||||
if let Some(saved_imm) = self.saved_immutables_stack.pop() {
|
||||
self.active_immutables = saved_imm;
|
||||
}
|
||||
if let Some((saved_subs, saved_cache)) = self.sub_module_stack.pop() {
|
||||
self.sub_modules = saved_subs;
|
||||
if let Some(saved_cache) = saved_cache {
|
||||
self.immutables_cache = saved_cache;
|
||||
}
|
||||
}
|
||||
self.temp_immutables.pop();
|
||||
let offset = frame.saved_reg_offset as usize;
|
||||
let len = frame.caller_reg_limit as usize;
|
||||
self.regs[..len].copy_from_slice(&self.save_stack[offset..offset + len]);
|
||||
@@ -171,8 +182,8 @@ impl Vm {
|
||||
self.clear_execution_state();
|
||||
self.cell_stack.clear();
|
||||
self.cell_stack.push(Vec::new());
|
||||
self.sub_modules = Arc::new(module.sub_modules.clone());
|
||||
self.immutables_cache = (0..=self.sub_modules.len()).map(|_| OnceLock::new()).collect();
|
||||
self.sub_modules = Arc::new(collect_flat_modules(module));
|
||||
self.immutables_cache = (0..self.sub_modules.len()).map(|_| OnceLock::new()).collect();
|
||||
self.bytecode = module.bytecode.clone();
|
||||
self.activate_immutables(0, &module.constants);
|
||||
self.root_reg_limit = module.n_registers.max(1);
|
||||
|
||||
@@ -73,9 +73,7 @@ impl Vm {
|
||||
instruction_count: 0,
|
||||
},
|
||||
string_buf: String::new(),
|
||||
sub_module_stack: Vec::new(),
|
||||
cell_stack: Vec::new(),
|
||||
temp_immutables: Vec::new(),
|
||||
};
|
||||
vm_info!("Vm created");
|
||||
vm
|
||||
@@ -138,9 +136,7 @@ impl Vm {
|
||||
instruction_count: 0,
|
||||
},
|
||||
string_buf: String::new(),
|
||||
sub_module_stack: Vec::new(),
|
||||
cell_stack: Vec::new(),
|
||||
temp_immutables: Vec::new(),
|
||||
};
|
||||
vm_info!("Vm created (pool)");
|
||||
vm
|
||||
@@ -213,14 +209,12 @@ impl Vm {
|
||||
self.regs = [JsValue::undefined(); 256];
|
||||
self.pc = 0;
|
||||
self.frames.clear();
|
||||
self.sub_module_stack.clear();
|
||||
self.iters.reset();
|
||||
self.saved_bytecode_stack.clear();
|
||||
self.saved_immutables_stack.clear();
|
||||
self.save_stack.clear();
|
||||
self.spill_stack.clear();
|
||||
self.cell_stack.clear();
|
||||
self.temp_immutables.clear();
|
||||
self.try_stack.clear();
|
||||
self.exception_value = None;
|
||||
self.pending_exception = None;
|
||||
|
||||
Reference in New Issue
Block a user