refactor: 拆分初始化与常量转换辅助逻辑
This commit is contained in:
@@ -7,4 +7,5 @@ mod dispatch;
|
|||||||
pub mod native;
|
pub mod native;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
pub mod vm_pool;
|
pub mod vm_pool;
|
||||||
|
mod vm_support;
|
||||||
pub use oxide_types::value::JsValue;
|
pub use oxide_types::value::JsValue;
|
||||||
|
|||||||
+1
-244
@@ -2,16 +2,14 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use oxide_compiler::compiler::Constant;
|
|
||||||
use oxide_compiler::module::CompiledModule;
|
use oxide_compiler::module::CompiledModule;
|
||||||
use oxide_compiler::opcode::{self, OpCode};
|
use oxide_compiler::opcode::{self, OpCode};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::bindings;
|
|
||||||
pub use crate::bindings::init_kernel_builtins;
|
pub use crate::bindings::init_kernel_builtins;
|
||||||
use crate::coercion;
|
use crate::coercion;
|
||||||
use crate::native::{NativeFn, NativeResult};
|
use crate::native::{NativeFn, NativeResult};
|
||||||
use oxide_kernel::kernel::{KernelConfig, OxideKernel};
|
use oxide_kernel::kernel::OxideKernel;
|
||||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||||
use oxide_kernel::{vm_debug, vm_trace};
|
use oxide_kernel::{vm_debug, vm_trace};
|
||||||
use oxide_types::error::{JsError, JsErrorKind};
|
use oxide_types::error::{JsError, JsErrorKind};
|
||||||
@@ -202,72 +200,6 @@ impl Vm {
|
|||||||
self.raise_error_kind("TypeError", msg)
|
self.raise_error_kind("TypeError", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let kernel = Arc::new(OxideKernel::new(KernelConfig::minimal()));
|
|
||||||
bindings::init_kernel_builtins(&kernel);
|
|
||||||
let obj_proto = P::clone(&kernel.builtin_world().object_proto);
|
|
||||||
Self {
|
|
||||||
regs: [JsValue::undefined(); 256],
|
|
||||||
pc: 0,
|
|
||||||
bytecode: Vec::new(),
|
|
||||||
constants: Vec::new(),
|
|
||||||
frames: SmallVec::new(),
|
|
||||||
for_in_iters: Vec::new(),
|
|
||||||
kernel,
|
|
||||||
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(),
|
|
||||||
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(kernel: Arc<OxideKernel>) -> Self {
|
|
||||||
let obj_proto = P::clone(&kernel.builtin_world().object_proto);
|
|
||||||
Self {
|
|
||||||
regs: [JsValue::undefined(); 256],
|
|
||||||
pc: 0,
|
|
||||||
bytecode: Vec::new(),
|
|
||||||
constants: Vec::new(),
|
|
||||||
frames: SmallVec::new(),
|
|
||||||
for_in_iters: Vec::new(),
|
|
||||||
kernel,
|
|
||||||
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(),
|
|
||||||
for_of_iters: Vec::new(),
|
|
||||||
root_reg_limit: 0,
|
|
||||||
active_reg_limit: 0,
|
|
||||||
native_call_depth: 0,
|
|
||||||
accessor_frame_target_reg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn step_rng(&mut self) {
|
pub fn step_rng(&mut self) {
|
||||||
if self.math_rng_state == 0 {
|
if self.math_rng_state == 0 {
|
||||||
self.math_rng_state = std::time::SystemTime::now()
|
self.math_rng_state = std::time::SystemTime::now()
|
||||||
@@ -314,181 +246,6 @@ impl Vm {
|
|||||||
&self.epoch
|
&self.epoch
|
||||||
}
|
}
|
||||||
|
|
||||||
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.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.kernel.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);
|
|
||||||
// Capture lexical `this` from the enclosing scope (regs[254]).
|
|
||||||
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.kernel.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.string_forge().intern("constructor").0;
|
|
||||||
let constructor_shape = self.kernel.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.string_forge().intern("prototype").0;
|
|
||||||
let func = unsafe { &mut *obj_ptr };
|
|
||||||
let prototype_shape = self.kernel.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
|
|
||||||
}
|
|
||||||
|
|
||||||
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.string_forge().intern("name").0;
|
|
||||||
let message_si = self.kernel.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.string_forge().intern(pattern).0;
|
|
||||||
let flags_si = self.kernel.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.kernel.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;
|
|
||||||
// SAFETY: regexp constructor was registered via set_native_fn with a valid NativeFn pointer;
|
|
||||||
// native_fn_ptr_to_fn is the single coercion point for NativeFnPtr → NativeFn.
|
|
||||||
let func: crate::native::NativeFn = 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)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a module constant pool into runtime values.
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_str(&self, val: JsValue) -> Option<String> {
|
pub fn lookup_str(&self, val: JsValue) -> Option<String> {
|
||||||
if !val.is_string() {
|
if !val.is_string() {
|
||||||
return None;
|
return None;
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
#![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, OxideKernel};
|
||||||
|
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 kernel = Arc::new(OxideKernel::new(KernelConfig::minimal()));
|
||||||
|
bindings::init_kernel_builtins(&kernel);
|
||||||
|
let obj_proto = P::clone(&kernel.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,
|
||||||
|
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(),
|
||||||
|
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(kernel: Arc<OxideKernel>) -> Self {
|
||||||
|
let obj_proto = P::clone(&kernel.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,
|
||||||
|
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(),
|
||||||
|
for_of_iters: Vec::new(),
|
||||||
|
root_reg_limit: 0,
|
||||||
|
active_reg_limit: 0,
|
||||||
|
native_call_depth: 0,
|
||||||
|
accessor_frame_target_reg: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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.kernel.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.kernel.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.string_forge().intern("constructor").0;
|
||||||
|
let constructor_shape = self.kernel.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.string_forge().intern("prototype").0;
|
||||||
|
let func = unsafe { &mut *obj_ptr };
|
||||||
|
let prototype_shape = self.kernel.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.string_forge().intern("name").0;
|
||||||
|
let message_si = self.kernel.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.string_forge().intern(pattern).0;
|
||||||
|
let flags_si = self.kernel.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.kernel.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user