refactor: 拆分属性访问与 accessor 辅助逻辑
This commit is contained in:
@@ -7,6 +7,7 @@ mod dispatch;
|
||||
pub mod native;
|
||||
pub mod vm;
|
||||
pub mod vm_pool;
|
||||
mod vm_props;
|
||||
mod vm_runtime;
|
||||
mod vm_support;
|
||||
pub use oxide_types::value::JsValue;
|
||||
|
||||
@@ -459,327 +459,6 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn ordinary_get(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue,
|
||||
) -> Result<JsValue, String> {
|
||||
self.ordinary_get_inner(obj, prop_name_si, receiver, None)
|
||||
}
|
||||
|
||||
/// `ordinary_get` with an optional dispatch-loop target register.
|
||||
///
|
||||
/// When `target_reg` is `Some(r)` and the property is a bytecode accessor getter,
|
||||
/// this method pushes a bytecode frame with `FrameContinuation::AccessorGet { target_reg: r }`
|
||||
/// and sets `self.accessor_frame_target_reg = Some(r)`. The caller must then return
|
||||
/// `Ok(JsValue::undefined())` — the real value will be stored by the `RETURN` handler.
|
||||
///
|
||||
/// When `target_reg` is `None`, bytecode getters run inline via `call_function_sync`
|
||||
/// (same epoch, no sub-VM). Safe for callers outside the dispatch loop.
|
||||
pub(crate) fn ordinary_get_with_target(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue, target_reg: u8,
|
||||
) -> Result<JsValue, String> {
|
||||
self.ordinary_get_inner(obj, prop_name_si, receiver, Some(target_reg))
|
||||
}
|
||||
|
||||
fn ordinary_get_inner(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue, target_reg: Option<u8>,
|
||||
) -> Result<JsValue, String> {
|
||||
let length_si = self.kernel.string_forge().intern("length").0;
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
return Ok(JsValue::int(obj.prop_count() as i32));
|
||||
}
|
||||
if let Some(pos) = self.get_own_property_slot(obj, prop_name_si) {
|
||||
if let Some(meta) = obj.prop_meta_at(pos) {
|
||||
if meta.is_accessor {
|
||||
return if meta.get.is_undefined() {
|
||||
Ok(JsValue::undefined())
|
||||
} else if let Some(tr) = target_reg {
|
||||
// Dispatch loop path: push bytecode frame, return sentinel.
|
||||
let getter = meta.get;
|
||||
let pushed = self.push_bytecode_getter_frame(getter, receiver, tr)?;
|
||||
if pushed {
|
||||
return Ok(JsValue::undefined()); // RETURN handler delivers value
|
||||
}
|
||||
// Native getter — value already written to regs[tr] by push_bytecode_getter_frame
|
||||
Ok(self.regs[tr as usize])
|
||||
} else {
|
||||
self.call_function_sync(meta.get, receiver, &[])
|
||||
};
|
||||
}
|
||||
}
|
||||
return Ok(obj.get_prop_at(pos));
|
||||
}
|
||||
let proto = obj.proto();
|
||||
if proto.is_object() {
|
||||
let proto_obj = unsafe { &*proto.as_js_object_ptr() };
|
||||
return self.ordinary_get_inner(proto_obj, prop_name_si, receiver, target_reg);
|
||||
}
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// Push a bytecode frame for a getter accessor, or call native getters inline.
|
||||
///
|
||||
/// Returns `true` if a bytecode frame was pushed (dispatch loop must `continue`).
|
||||
/// Returns `false` if the getter was native (result already in `regs[target_reg]`).
|
||||
fn push_bytecode_getter_frame(
|
||||
&mut self, getter: JsValue, receiver: JsValue, target_reg: u8,
|
||||
) -> Result<bool, String> {
|
||||
if !getter.is_object() {
|
||||
return Err("TypeError: getter is not callable".into());
|
||||
}
|
||||
let getter_obj = unsafe { &*getter.as_js_object_ptr() };
|
||||
if !getter_obj.is_function() {
|
||||
return Err("TypeError: getter is not callable".into());
|
||||
}
|
||||
|
||||
if getter_obj.native_fn().is_some() {
|
||||
// Native getter — call inline, store result
|
||||
let result = self.call_function_sync(getter, receiver, &[])?;
|
||||
self.regs[target_reg as usize] = result;
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Bytecode getter — push frame
|
||||
self.push_bytecode_frame(
|
||||
getter,
|
||||
receiver,
|
||||
&[],
|
||||
None,
|
||||
None,
|
||||
JsValue::undefined(),
|
||||
FrameContinuation::AccessorGet { target_reg },
|
||||
)?;
|
||||
self.accessor_frame_target_reg = Some(target_reg);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn inherited_property_meta(&self, obj: &JsObject, prop_name_si: u32) -> Option<oxide_types::object::PropMetaEntry> {
|
||||
let mut proto = obj.proto();
|
||||
while proto.is_object() {
|
||||
let proto_obj = unsafe { &*proto.as_js_object_ptr() };
|
||||
if let Some(pos) = self.get_own_property_slot(proto_obj, prop_name_si) {
|
||||
return proto_obj.prop_meta_at(pos);
|
||||
}
|
||||
proto = proto_obj.proto();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn ordinary_set(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
|
||||
) -> Result<(), String> {
|
||||
self.ordinary_set_inner(obj, prop_name_si, val, receiver, false)
|
||||
}
|
||||
|
||||
/// `ordinary_set` variant for the dispatch loop: bytecode setters push a frame instead
|
||||
/// of spawning a sub-VM.
|
||||
pub(crate) fn ordinary_set_dispatch(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
|
||||
) -> Result<(), String> {
|
||||
self.ordinary_set_inner(obj, prop_name_si, val, receiver, true)
|
||||
}
|
||||
|
||||
fn ordinary_set_inner(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, use_frame_push: bool,
|
||||
) -> Result<(), String> {
|
||||
if let Some(pos) = self.get_own_property_slot(obj, prop_name_si) {
|
||||
if let Some(meta) = obj.prop_meta_at(pos) {
|
||||
if meta.is_accessor {
|
||||
if meta.set.is_undefined() {
|
||||
return self.raise_type_error("property has no setter");
|
||||
}
|
||||
return self.call_or_push_setter(meta.set, receiver, val, use_frame_push);
|
||||
}
|
||||
if !meta.attributes.writable() {
|
||||
return self.raise_type_error("cannot assign to read-only property");
|
||||
}
|
||||
}
|
||||
obj.set_prop_at(pos, val);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(meta) = self.inherited_property_meta(obj, prop_name_si) {
|
||||
if meta.is_accessor {
|
||||
if meta.set.is_undefined() {
|
||||
return self.raise_type_error("property has no setter");
|
||||
}
|
||||
return self.call_or_push_setter(meta.set, receiver, val, use_frame_push);
|
||||
}
|
||||
if !meta.attributes.writable() {
|
||||
return self.raise_type_error("cannot assign to read-only property");
|
||||
}
|
||||
}
|
||||
|
||||
self.set_or_create_prop_value(obj, prop_name_si, val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Call a setter — either inline (for native setters or sub-VM path) or via frame push
|
||||
/// (for bytecode setters inside the dispatch loop).
|
||||
fn call_or_push_setter(
|
||||
&mut self, setter: JsValue, receiver: JsValue, val: JsValue, use_frame_push: bool,
|
||||
) -> Result<(), String> {
|
||||
if !use_frame_push {
|
||||
self.call_function_sync(setter, receiver, &[val])?;
|
||||
return Ok(());
|
||||
}
|
||||
if !setter.is_object() {
|
||||
return self.raise_type_error("setter is not callable");
|
||||
}
|
||||
let setter_obj = unsafe { &*setter.as_js_object_ptr() };
|
||||
if !setter_obj.is_function() {
|
||||
return self.raise_type_error("setter is not callable");
|
||||
}
|
||||
if setter_obj.native_fn().is_some() {
|
||||
// Native setter — call inline
|
||||
self.call_function_sync(setter, receiver, &[val])?;
|
||||
return Ok(());
|
||||
}
|
||||
// Bytecode setter — push frame; result is discarded via AccessorSet
|
||||
self.push_bytecode_frame(
|
||||
setter,
|
||||
receiver,
|
||||
&[val],
|
||||
None,
|
||||
None,
|
||||
JsValue::undefined(),
|
||||
FrameContinuation::AccessorSet,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn read_member_prop(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue,
|
||||
) -> Result<JsValue, String> {
|
||||
let ext0 = self.bytecode[self.pc];
|
||||
let ext1 = self.bytecode[self.pc + 1];
|
||||
let _ext2 = self.bytecode[self.pc + 2];
|
||||
self.pc += 3;
|
||||
if obj.has_prop_meta() {
|
||||
return self.ordinary_get(obj, prop_name_si, receiver);
|
||||
}
|
||||
let cached_shape_id = ext0 & 0x00FF_FFFF;
|
||||
let cached_slot = ext1;
|
||||
|
||||
let val =
|
||||
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_slot < obj.prop_vec_len() as u32 {
|
||||
obj.get_prop_at(cached_slot)
|
||||
} else if let Some(template) = self.kernel.prop_forge().get_template(obj.shape_id()) {
|
||||
if template.prop_name != prop_name_si {
|
||||
self.ordinary_get(obj, prop_name_si, receiver)?
|
||||
} else if template.position < obj.prop_vec_len() as u32 {
|
||||
self.write_ic_back(obj.shape_id(), template.position);
|
||||
obj.get_prop_at(template.position)
|
||||
} else {
|
||||
self.ordinary_get(obj, prop_name_si, receiver)?
|
||||
}
|
||||
} else {
|
||||
let resolved = self.ordinary_get(obj, prop_name_si, receiver)?;
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
if !obj.is_accessor_meta(pos) {
|
||||
self.write_ic_back(obj.shape_id(), pos);
|
||||
}
|
||||
}
|
||||
resolved
|
||||
};
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
pub(crate) fn set_member_prop(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
|
||||
) -> Result<(), String> {
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
if obj.has_prop_meta() {
|
||||
self.ordinary_set(obj, prop_name_si, val, receiver)?;
|
||||
return Ok(());
|
||||
}
|
||||
obj.set_prop_at(pos, val);
|
||||
// IC write-back: 3 extension words (shape_id + slot_index + reserved)
|
||||
self.write_ic_back(obj.shape_id(), pos);
|
||||
} else {
|
||||
self.ordinary_set(obj, prop_name_si, val, receiver)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_at(pos, val);
|
||||
} else {
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(val);
|
||||
obj.bump_generation();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn define_data_property(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes,
|
||||
) -> Result<(), String> {
|
||||
let pos = if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
pos
|
||||
} else {
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(JsValue::undefined())
|
||||
};
|
||||
if let Some(current) = obj.prop_meta_at(pos) {
|
||||
if !current.attributes.configurable() {
|
||||
if current.is_accessor {
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
if current.attributes.enumerable() != attributes.enumerable() {
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
if !current.attributes.writable()
|
||||
&& (attributes.writable() || !coercion::same_value(obj.get_prop_at(pos), val))
|
||||
{
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
if current.attributes.configurable() != attributes.configurable() {
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
}
|
||||
}
|
||||
obj.set_prop_at(pos, val);
|
||||
obj.set_data_meta(pos, attributes);
|
||||
obj.bump_generation();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn define_accessor_property(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, get: JsValue, set: JsValue, attributes: PropAttributes,
|
||||
) -> Result<(), String> {
|
||||
let pos = if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
pos
|
||||
} else {
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(JsValue::undefined())
|
||||
};
|
||||
if let Some(current) = obj.prop_meta_at(pos) {
|
||||
if !current.attributes.configurable()
|
||||
&& (!current.is_accessor
|
||||
|| current.attributes.enumerable() != attributes.enumerable()
|
||||
|| current.attributes.configurable() != attributes.configurable()
|
||||
|| current.get != get
|
||||
|| current.set != set)
|
||||
{
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
}
|
||||
obj.set_prop_at(pos, JsValue::undefined());
|
||||
obj.set_accessor_meta(pos, get, set, attributes);
|
||||
obj.bump_generation();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn write_ic_back(&mut self, shape_id: u32, slot_index: u32) {
|
||||
debug_assert!(self.pc >= 3, "IC write-back requires 3 extension words before pc");
|
||||
self.bytecode[self.pc - 3] = shape_id & 0x00FF_FFFF;
|
||||
self.bytecode[self.pc - 2] = slot_index;
|
||||
self.bytecode[self.pc - 1] = 0;
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch(&mut self) -> Result<JsValue, String> {
|
||||
let mut steps: u64 = 0;
|
||||
loop {
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
use crate::coercion;
|
||||
use crate::vm::{FrameContinuation, Vm};
|
||||
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn ordinary_get(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue,
|
||||
) -> Result<JsValue, String> {
|
||||
self.ordinary_get_inner(obj, prop_name_si, receiver, None)
|
||||
}
|
||||
|
||||
pub(crate) fn ordinary_get_with_target(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue, target_reg: u8,
|
||||
) -> Result<JsValue, String> {
|
||||
self.ordinary_get_inner(obj, prop_name_si, receiver, Some(target_reg))
|
||||
}
|
||||
|
||||
fn ordinary_get_inner(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue, target_reg: Option<u8>,
|
||||
) -> Result<JsValue, String> {
|
||||
let length_si = self.kernel.string_forge().intern("length").0;
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
return Ok(JsValue::int(obj.prop_count() as i32));
|
||||
}
|
||||
if let Some(pos) = self.get_own_property_slot(obj, prop_name_si) {
|
||||
if let Some(meta) = obj.prop_meta_at(pos) {
|
||||
if meta.is_accessor {
|
||||
return if meta.get.is_undefined() {
|
||||
Ok(JsValue::undefined())
|
||||
} else if let Some(tr) = target_reg {
|
||||
let getter = meta.get;
|
||||
let pushed = self.push_bytecode_getter_frame(getter, receiver, tr)?;
|
||||
if pushed {
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
Ok(self.regs[tr as usize])
|
||||
} else {
|
||||
self.call_function_sync(meta.get, receiver, &[])
|
||||
};
|
||||
}
|
||||
}
|
||||
return Ok(obj.get_prop_at(pos));
|
||||
}
|
||||
let proto = obj.proto();
|
||||
if proto.is_object() {
|
||||
let proto_obj = unsafe { &*proto.as_js_object_ptr() };
|
||||
return self.ordinary_get_inner(proto_obj, prop_name_si, receiver, target_reg);
|
||||
}
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
fn push_bytecode_getter_frame(
|
||||
&mut self, getter: JsValue, receiver: JsValue, target_reg: u8,
|
||||
) -> Result<bool, String> {
|
||||
if !getter.is_object() {
|
||||
return Err("TypeError: getter is not callable".into());
|
||||
}
|
||||
let getter_obj = unsafe { &*getter.as_js_object_ptr() };
|
||||
if !getter_obj.is_function() {
|
||||
return Err("TypeError: getter is not callable".into());
|
||||
}
|
||||
|
||||
if getter_obj.native_fn().is_some() {
|
||||
let result = self.call_function_sync(getter, receiver, &[])?;
|
||||
self.regs[target_reg as usize] = result;
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
self.push_bytecode_frame(
|
||||
getter,
|
||||
receiver,
|
||||
&[],
|
||||
None,
|
||||
None,
|
||||
JsValue::undefined(),
|
||||
FrameContinuation::AccessorGet { target_reg },
|
||||
)?;
|
||||
self.accessor_frame_target_reg = Some(target_reg);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn inherited_property_meta(&self, obj: &JsObject, prop_name_si: u32) -> Option<PropMetaEntry> {
|
||||
let mut proto = obj.proto();
|
||||
while proto.is_object() {
|
||||
let proto_obj = unsafe { &*proto.as_js_object_ptr() };
|
||||
if let Some(pos) = self.get_own_property_slot(proto_obj, prop_name_si) {
|
||||
return proto_obj.prop_meta_at(pos);
|
||||
}
|
||||
proto = proto_obj.proto();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn ordinary_set(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
|
||||
) -> Result<(), String> {
|
||||
self.ordinary_set_inner(obj, prop_name_si, val, receiver, false)
|
||||
}
|
||||
|
||||
pub(crate) fn ordinary_set_dispatch(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
|
||||
) -> Result<(), String> {
|
||||
self.ordinary_set_inner(obj, prop_name_si, val, receiver, true)
|
||||
}
|
||||
|
||||
fn ordinary_set_inner(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, use_frame_push: bool,
|
||||
) -> Result<(), String> {
|
||||
if let Some(pos) = self.get_own_property_slot(obj, prop_name_si) {
|
||||
if let Some(meta) = obj.prop_meta_at(pos) {
|
||||
if meta.is_accessor {
|
||||
if meta.set.is_undefined() {
|
||||
return self.raise_type_error("property has no setter");
|
||||
}
|
||||
return self.call_or_push_setter(meta.set, receiver, val, use_frame_push);
|
||||
}
|
||||
if !meta.attributes.writable() {
|
||||
return self.raise_type_error("cannot assign to read-only property");
|
||||
}
|
||||
}
|
||||
obj.set_prop_at(pos, val);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(meta) = self.inherited_property_meta(obj, prop_name_si) {
|
||||
if meta.is_accessor {
|
||||
if meta.set.is_undefined() {
|
||||
return self.raise_type_error("property has no setter");
|
||||
}
|
||||
return self.call_or_push_setter(meta.set, receiver, val, use_frame_push);
|
||||
}
|
||||
if !meta.attributes.writable() {
|
||||
return self.raise_type_error("cannot assign to read-only property");
|
||||
}
|
||||
}
|
||||
|
||||
self.set_or_create_prop_value(obj, prop_name_si, val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn call_or_push_setter(
|
||||
&mut self, setter: JsValue, receiver: JsValue, val: JsValue, use_frame_push: bool,
|
||||
) -> Result<(), String> {
|
||||
if !use_frame_push {
|
||||
self.call_function_sync(setter, receiver, &[val])?;
|
||||
return Ok(());
|
||||
}
|
||||
if !setter.is_object() {
|
||||
return self.raise_type_error("setter is not callable");
|
||||
}
|
||||
let setter_obj = unsafe { &*setter.as_js_object_ptr() };
|
||||
if !setter_obj.is_function() {
|
||||
return self.raise_type_error("setter is not callable");
|
||||
}
|
||||
if setter_obj.native_fn().is_some() {
|
||||
self.call_function_sync(setter, receiver, &[val])?;
|
||||
return Ok(());
|
||||
}
|
||||
self.push_bytecode_frame(
|
||||
setter,
|
||||
receiver,
|
||||
&[val],
|
||||
None,
|
||||
None,
|
||||
JsValue::undefined(),
|
||||
FrameContinuation::AccessorSet,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn read_member_prop(
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue,
|
||||
) -> Result<JsValue, String> {
|
||||
let ext0 = self.bytecode[self.pc];
|
||||
let ext1 = self.bytecode[self.pc + 1];
|
||||
let _ext2 = self.bytecode[self.pc + 2];
|
||||
self.pc += 3;
|
||||
if obj.has_prop_meta() {
|
||||
return self.ordinary_get(obj, prop_name_si, receiver);
|
||||
}
|
||||
let cached_shape_id = ext0 & 0x00FF_FFFF;
|
||||
let cached_slot = ext1;
|
||||
|
||||
let val =
|
||||
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_slot < obj.prop_vec_len() as u32 {
|
||||
obj.get_prop_at(cached_slot)
|
||||
} else if let Some(template) = self.kernel.prop_forge().get_template(obj.shape_id()) {
|
||||
if template.prop_name != prop_name_si {
|
||||
self.ordinary_get(obj, prop_name_si, receiver)?
|
||||
} else if template.position < obj.prop_vec_len() as u32 {
|
||||
self.write_ic_back(obj.shape_id(), template.position);
|
||||
obj.get_prop_at(template.position)
|
||||
} else {
|
||||
self.ordinary_get(obj, prop_name_si, receiver)?
|
||||
}
|
||||
} else {
|
||||
let resolved = self.ordinary_get(obj, prop_name_si, receiver)?;
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
if !obj.is_accessor_meta(pos) {
|
||||
self.write_ic_back(obj.shape_id(), pos);
|
||||
}
|
||||
}
|
||||
resolved
|
||||
};
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
pub(crate) fn set_member_prop(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
|
||||
) -> Result<(), String> {
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
if obj.has_prop_meta() {
|
||||
self.ordinary_set(obj, prop_name_si, val, receiver)?;
|
||||
return Ok(());
|
||||
}
|
||||
obj.set_prop_at(pos, val);
|
||||
self.write_ic_back(obj.shape_id(), pos);
|
||||
} else {
|
||||
self.ordinary_set(obj, prop_name_si, val, receiver)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_at(pos, val);
|
||||
} else {
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(val);
|
||||
obj.bump_generation();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn define_data_property(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes,
|
||||
) -> Result<(), String> {
|
||||
let pos = if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
pos
|
||||
} else {
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(JsValue::undefined())
|
||||
};
|
||||
if let Some(current) = obj.prop_meta_at(pos) {
|
||||
if !current.attributes.configurable() {
|
||||
if current.is_accessor {
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
if current.attributes.enumerable() != attributes.enumerable() {
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
if !current.attributes.writable()
|
||||
&& (attributes.writable() || !coercion::same_value(obj.get_prop_at(pos), val))
|
||||
{
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
if current.attributes.configurable() != attributes.configurable() {
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
}
|
||||
}
|
||||
obj.set_prop_at(pos, val);
|
||||
obj.set_data_meta(pos, attributes);
|
||||
obj.bump_generation();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn define_accessor_property(
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, get: JsValue, set: JsValue, attributes: PropAttributes,
|
||||
) -> Result<(), String> {
|
||||
let pos = if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
pos
|
||||
} else {
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(JsValue::undefined())
|
||||
};
|
||||
if let Some(current) = obj.prop_meta_at(pos) {
|
||||
if !current.attributes.configurable()
|
||||
&& (!current.is_accessor
|
||||
|| current.attributes.enumerable() != attributes.enumerable()
|
||||
|| current.attributes.configurable() != attributes.configurable()
|
||||
|| current.get != get
|
||||
|| current.set != set)
|
||||
{
|
||||
return self.raise_type_error("cannot redefine non-configurable property");
|
||||
}
|
||||
}
|
||||
obj.set_prop_at(pos, JsValue::undefined());
|
||||
obj.set_accessor_meta(pos, get, set, attributes);
|
||||
obj.bump_generation();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn write_ic_back(&mut self, shape_id: u32, slot_index: u32) {
|
||||
debug_assert!(self.pc >= 3, "IC write-back requires 3 extension words before pc");
|
||||
self.bytecode[self.pc - 3] = shape_id & 0x00FF_FFFF;
|
||||
self.bytecode[self.pc - 2] = slot_index;
|
||||
self.bytecode[self.pc - 1] = 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user