This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_vm/src/vm_support.rs
T
2026-06-14 17:48:31 +08:00

278 lines
11 KiB
Rust

#![allow(clippy::arc_with_non_send_sync)]
use std::sync::Arc;
use oxide_compiler::compiler::Constant;
use crate::bindings;
use crate::vm::{native_fn_ptr_to_fn, Vm};
use oxide_kernel::kernel::{KernelConfig, KernelCore, KernelSession};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::error::JsError;
use oxide_types::mem::{Epoch, P};
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::value::JsValue;
impl Vm {
pub fn new() -> Self {
let core = KernelCore::new(KernelConfig::minimal());
let mut session = KernelSession::new(&core);
bindings::init_kernel_builtins(&core, &mut session);
let obj_proto = P::clone(&session.builtin_world().object_proto);
Self {
regs: [JsValue::undefined(); 256],
pc: 0,
bytecode: Vec::new(),
constants: Vec::new(),
frames: smallvec::SmallVec::new(),
for_in_iters: Vec::new(),
kernel_core: core,
session,
interned_strings: Vec::new(),
epoch: Epoch::new(),
object_prototype: obj_proto,
math_rng_state: 0,
sub_modules: Vec::new(),
sub_module_constants: Vec::new(),
saved_bytecode_stack: Vec::new(),
saved_constants_stack: Vec::new(),
try_stack: Vec::new(),
exception_value: None,
pending_exception: None,
pending_error_kind: None,
symbol_counter: 0,
symbol_descriptions: Vec::new(),
symbol_registry: std::collections::HashMap::new(),
for_of_iters: Vec::new(),
root_reg_limit: 0,
active_reg_limit: 0,
native_call_depth: 0,
accessor_frame_target_reg: None,
}
}
pub fn with_kernel_core(core: Arc<KernelCore>) -> Self {
let mut session = KernelSession::new(&core);
bindings::init_kernel_builtins(&core, &mut session);
let obj_proto = P::clone(&session.builtin_world().object_proto);
Self {
regs: [JsValue::undefined(); 256],
pc: 0,
bytecode: Vec::new(),
constants: Vec::new(),
frames: smallvec::SmallVec::new(),
for_in_iters: Vec::new(),
kernel_core: core,
session,
interned_strings: Vec::new(),
epoch: Epoch::new(),
object_prototype: obj_proto,
math_rng_state: 0,
sub_modules: Vec::new(),
sub_module_constants: Vec::new(),
saved_bytecode_stack: Vec::new(),
saved_constants_stack: Vec::new(),
try_stack: Vec::new(),
exception_value: None,
pending_exception: None,
pending_error_kind: None,
symbol_counter: 0,
symbol_descriptions: Vec::new(),
symbol_registry: std::collections::HashMap::new(),
for_of_iters: Vec::new(),
root_reg_limit: 0,
active_reg_limit: 0,
native_call_depth: 0,
accessor_frame_target_reg: None,
}
}
pub fn full_reset(&mut self) {
let mut new_session = KernelSession::new(&self.kernel_core);
bindings::init_kernel_builtins(&self.kernel_core, &mut new_session);
self.object_prototype = P::clone(&new_session.builtin_world().object_proto);
self.session = new_session;
self.clear_execution_state();
self.bytecode.clear();
self.constants.clear();
self.epoch.reset();
self.interned_strings.clear();
self.symbol_counter = 0;
self.symbol_descriptions.clear();
self.symbol_registry.clear();
self.root_reg_limit = 0;
self.active_reg_limit = 0;
}
pub(crate) fn clear_execution_state(&mut self) {
// Reset contract:
// - Clears register file, pc, frame/iterator stacks, saved execution stacks,
// try handlers, pending exceptions, and native call depth.
// - Leaves kernel-owned shared state intact.
// - `reset()` additionally clears bytecode/constants and resets epoch ownership.
self.regs = [JsValue::undefined(); 256];
self.pc = 0;
self.frames.clear();
self.for_in_iters.clear();
self.for_of_iters.clear();
self.saved_bytecode_stack.clear();
self.saved_constants_stack.clear();
self.try_stack.clear();
self.exception_value = None;
self.pending_exception = None;
self.pending_error_kind = None;
self.native_call_depth = 0;
}
pub fn reset(&mut self) {
self.clear_execution_state();
self.bytecode.clear();
self.constants.clear();
self.epoch.reset();
self.interned_strings.clear();
self.root_reg_limit = 0;
self.active_reg_limit = 0;
}
pub fn intern(&mut self, s: &str) -> JsValue {
let (idx, hash) = self.kernel_core.string_forge().intern(s);
self.interned_strings.push(idx);
JsValue::string(idx, hash)
}
/// Create a function JsObject for a BytecodeFunc constant.
/// When `is_arrow` is true, captures the current `this` (regs[254])
/// for lexical this binding at call time (D-01).
fn create_function_object(
&mut self, sub_idx: u32, is_arrow: bool, is_class_constructor: bool, is_derived_constructor: bool,
needs_home_object: bool,
) -> JsValue {
let func_proto_ptr = self.session.builtin_world().function_proto.as_ptr() as *mut JsObject;
let proto_val = JsValue::from_js_object(func_proto_ptr);
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val);
obj.set_function(true);
obj.set_sub_module_index(sub_idx);
obj.set_class_constructor(is_class_constructor);
obj.set_derived_constructor(is_derived_constructor);
let _ = needs_home_object;
if is_arrow {
obj.set_arrow(true);
obj.set_captured_this(self.regs[254]);
}
let obj_ptr = self.epoch.alloc(obj);
let func_val = JsValue::object(obj_ptr as *mut u8);
if !is_arrow {
let object_proto_ptr = self.session.builtin_world().object_proto.as_ptr() as *mut JsObject;
let prototype_obj = self
.epoch
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto_ptr)));
let prototype_val = JsValue::from_js_object(prototype_obj);
let constructor_si = self.kernel_core.string_forge().intern("constructor").0;
let constructor_shape = self.kernel_core.shape_forge().make_shape(EMPTY_SHAPE_ID, constructor_si);
let prototype = unsafe { &mut *prototype_obj };
prototype.set_shape_id(constructor_shape);
let constructor_pos = prototype.push_prop(func_val);
prototype.set_data_meta(constructor_pos, PropAttributes::new(true, false, true));
prototype.bump_generation();
let prototype_si = self.kernel_core.string_forge().intern("prototype").0;
let func = unsafe { &mut *obj_ptr };
let prototype_shape = self.kernel_core.shape_forge().make_shape(func.shape_id(), prototype_si);
func.set_shape_id(prototype_shape);
func.ensure_hash_props().push(prototype_val);
func.bump_generation();
}
func_val
}
pub(crate) fn error_text(&self, val: JsValue) -> String {
if let Some(s) = self.lookup_str(val) {
return s;
}
if val.is_object() {
let obj = unsafe { &*val.as_js_object_ptr() };
let name_si = self.kernel_core.string_forge().intern("name").0;
let message_si = self.kernel_core.string_forge().intern("message").0;
let name = self
.resolve_property(obj, name_si)
.and_then(|v| self.lookup_str(v))
.unwrap_or_else(|| "Error".to_string());
let message = self
.resolve_property(obj, message_si)
.and_then(|v| self.lookup_str(v))
.unwrap_or_default();
return if message.is_empty() { name } else { format!("{name}: {message}") };
}
format!("{val}")
}
fn convert_constant(&mut self, constant: &Constant) -> Result<JsValue, JsError> {
match constant {
Constant::Number(v) => Ok(JsValue::float(*v)),
Constant::Int(v) => Ok(JsValue::int(*v)),
Constant::String(s) => Ok(self.intern(s)),
Constant::Boolean(b) => Ok(JsValue::bool(*b)),
Constant::Null => Ok(JsValue::null()),
Constant::Undefined => Ok(JsValue::undefined()),
Constant::BytecodeFunc(idx) => {
let sub_idx = *idx as usize;
let (is_arrow, is_class_constructor, is_derived_constructor, needs_home_object) =
if sub_idx > 0 && sub_idx <= self.sub_modules.len() {
let sub_module = &self.sub_modules[sub_idx - 1];
(
sub_module.is_arrow,
sub_module.is_class_constructor,
sub_module.is_derived_constructor,
sub_module.needs_home_object,
)
} else {
(false, false, false, false)
};
Ok(self.create_function_object(
*idx,
is_arrow,
is_class_constructor,
is_derived_constructor,
needs_home_object,
))
}
Constant::RegExp(pattern, flags) => {
let pat_si = self.kernel_core.string_forge().intern(pattern).0;
let flags_si = self.kernel_core.string_forge().intern(flags).0;
let pat_val = JsValue::string(pat_si, 0);
let flags_val = JsValue::string(flags_si, 0);
let ctor_ptr = self.session.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &*ctor_ptr };
let Some(native_fn) = ctor.native_fn() else {
return Err(JsError::syntax_error("RegExp constructor unavailable"));
};
let saved_0 = self.regs[0];
let saved_1 = self.regs[1];
let saved_2 = self.regs[2];
self.regs[0] = JsValue::undefined();
self.regs[1] = pat_val;
self.regs[2] = flags_val;
let func = unsafe { native_fn_ptr_to_fn(native_fn) };
let result = func(self, &[0, 1, 2]);
self.regs[0] = saved_0;
self.regs[1] = saved_1;
self.regs[2] = saved_2;
result.map_err(|err| JsError::syntax_error(self.error_text(err)))
}
}
}
pub(crate) fn convert_constants(&mut self, constants: &[Constant]) -> Result<Vec<JsValue>, JsError> {
let mut values = Vec::with_capacity(constants.len());
for constant in constants {
values.push(self.convert_constant(constant)?);
}
Ok(values)
}
}