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_props.rs
T
pjh123 2239eeac24 perf: 字节码调用压帧窗口按调用点存活上界拷贝减少整窗搬移
RegAlloc 改写后补第三次 liveness,为每条 CALL/NEW/SUPER_CALL 计算调用点
存活上界(最大存活物理槽号 + 1,254/255 由帧单独保存不计入),编码进
ext 高 8 位(nargs 低 8 位不变)。三处手写压帧窗口 = min(调用方活动寄存
器, 存活上界),restore_frame 按窗口回拷;CallFrame 新增
caller_active_reg_limit 保存调用方真实活动上限,恢复时不随截断窗口缩水。
生成器/异步函数体调用点保持全量窗口。spread 调用与运行时发起压帧
(accessor/内联)走全量路径不受影响。
2026-08-15 09:56:22 +08:00

665 lines
31 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use crate::vm::{FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH};
use crate::{ic_trace, vm_trace};
use oxide_kernel::prop_forge::PropTemplate;
use oxide_runtime_api as coercion;
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> {
vm_trace!("ordinary_get_inner: shape={} prop_si={}", obj.shape_id(), prop_name_si);
let length_si = self.length_si;
let mut current = Some(obj);
let mut depth = 0usize;
while let Some(obj) = current {
if obj.is_array() && prop_name_si == length_si {
return Ok(obj.logical_len_value());
}
if obj.is_array() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
// 数组元素区:hole(删除标记)视为不存在,落到原型链。
if index < obj.array_prop_count && !obj.prop_meta_at(index).is_some_and(|m| m.is_hole()) {
// 数组元素为访问器属性(defineProperty getter)时须触发
// getter,而非直接读数据槽。
if let Some(meta) = obj.prop_meta_at(index) {
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 {
match self.call_function_sync(meta.get, receiver, &[]) {
Ok(v) => Ok(v),
Err(err) => Ok(self.raise_call_error(&err)?),
}
};
}
}
return Ok(obj.get_prop_at(index));
}
}
}
if obj.is_typed_array_obj() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
return oxide_builtins::typed_array::typed_array_element_get(self, obj, index);
}
}
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 {
match self.call_function_sync(meta.get, receiver, &[]) {
Ok(v) => Ok(v),
Err(err) => Ok(self.raise_call_error(&err)?),
}
};
}
}
return Ok(obj.get_prop_at(pos));
}
if depth >= MAX_PROTO_CHAIN_DEPTH {
break;
}
depth += 1;
let proto = obj.proto();
current = proto.is_object().then(|| unsafe { &*proto.as_js_object_ptr() });
if let Some(proto_obj) = current {
vm_trace!("ordinary_get proto step: depth={} shape={}", depth, proto_obj.shape_id());
}
}
Ok(JsValue::undefined())
}
pub(crate) fn push_bytecode_getter_frame(
&mut self, getter: JsValue, receiver: JsValue, target_reg: u8,
) -> Result<bool, String> {
vm_trace!(
"push_bytecode_getter_frame: target_reg={} native={}",
target_reg,
getter.is_object() && unsafe { &*getter.as_js_object_ptr() }.native_fn().is_some()
);
if !getter.is_object() {
return Err(self.error_message_text("TypeError", "getter is not callable"));
}
let getter_obj = unsafe { &*getter.as_js_object_ptr() };
if !getter_obj.is_function() {
return Err(self.error_message_text("TypeError", "getter is not callable"));
}
if getter_obj.native_fn().is_some() {
let result = match self.call_function_sync(getter, receiver, &[]) {
Ok(v) => v,
Err(err) => self.raise_call_error(&err)?,
};
self.regs[target_reg as usize] = result;
return Ok(false);
}
self.push_bytecode_frame(
getter,
receiver,
&[],
None,
None,
JsValue::undefined(),
FrameContinuation::AccessorGet { target_reg },
0,
)?;
self.accessor_frame_target_reg = Some(target_reg);
Ok(true)
}
fn inherited_property_meta(&self, obj: &JsObject, prop_name_si: u32) -> Option<PropMetaEntry> {
vm_trace!("inherited_property_meta: shape={} prop_si={}", obj.shape_id(), prop_name_si);
let mut proto = obj.proto();
let mut depth = 0usize;
while proto.is_object() && depth < MAX_PROTO_CHAIN_DEPTH {
depth += 1;
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> {
let val = self.promote_if_needed_for_write_ptr(obj as *mut JsObject, val);
self.ordinary_set_inner(obj, prop_name_si, val, receiver, false)
}
/// 分发期入口:调用方(dispatch_set_prop 等)已对值做过 promote。
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> {
vm_trace!(
"ordinary_set_inner: shape={} prop_si={} frame_push={}",
obj.shape_id(),
prop_name_si,
use_frame_push
);
// TypedArray 整数索引:receiver 为 TA 本体时写底层 buffer(越界静默忽略);
// receiver 非 TA 时按规范把写入落到 receiver 对象,不碰 TA buffer。
if obj.is_typed_array_obj() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
if !std::ptr::eq(receiver.as_js_object_ptr(), obj as *mut JsObject) {
return self.set_to_receiver(obj, prop_name_si, val, receiver, index as usize, use_frame_push);
}
return oxide_builtins::typed_array::typed_array_element_set(self, obj, index, val);
}
}
// 数组 length 赋值:ArraySetLength 语义(ToUint32 + 调整元素区)。
// 旧行为会把 length 存成影子命名属性,导致 `arr.length = N` 后
// prop_count/迭代/内置方法看到的长度不一致。
let length_si = self.length_si;
if obj.is_array() && prop_name_si == length_si {
let pc_before = self.pc;
let number_len = self.coerce_number_bounded(val)?;
// ToPrimitive 抛错(valueOf/toString throw)已被 unwind 定向到外围 catch 时
// pc 指向 catch 入口:主 dispatch 约定异常后 opcode 不得继续 raise,直接
// 返回由 dispatch 继续执行 catch,避免二次抛错覆盖原异常。
if self.pc != pc_before {
return Ok(());
}
let raw_new_len = if number_len == 0.0 || !number_len.is_finite() {
0
} else {
number_len.trunc().rem_euclid(4_294_967_296.0) as u32 as usize
};
// ToUint32 != ToNumber (e.g. 1.5 / NaN / Infinity / negative / 2**32) -> RangeError.
if raw_new_len as f64 != number_len {
return self.raise_error_kind("RangeError", "Invalid array length");
}
let old_logical = obj.logical_len() as usize;
let old_count = obj.array_prop_count as usize;
// ArraySetLength:收缩时若 [newLen, oldLen) 内存在不可配置元素,整个收缩
// 失败且不做任何修改(sloppy 赋值静默失败;VM 未实现 strict 标志,统一按 no-op)。
if raw_new_len < old_logical {
for idx in raw_new_len..old_count {
if let Some(meta) = obj.prop_meta_at(idx) {
if !meta.attributes.configurable() {
return Ok(());
}
}
}
}
// Dense storage caps at MAX_DENSE_PROPS; larger lengths stay at the cap.
let new_len_u = raw_new_len.min(oxide_types::object::MAX_DENSE_PROPS);
obj.set_prop_count(new_len_u);
// Grown slots are sparse holes: HasProperty / prototype reads must treat
// them as absent.
for idx in old_count..new_len_u {
obj.mark_hole_at(idx);
}
// Logical length override: lengths beyond the dense cap are recorded
// separately so `a.length` reads return the real value.
if raw_new_len > oxide_types::object::MAX_DENSE_PROPS {
obj.set_array_len_override(raw_new_len as u32);
} else {
obj.clear_array_len_override();
}
// 同步旧版可能残留的影子 length 属性(shape 槽),保证 IC 快路径读到新值。
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), length_si) {
let store_idx = obj.array_prop_count as usize + pos as usize;
obj.set_prop_storage(store_idx, obj.logical_len_value());
}
return Ok(());
}
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");
}
}
// pos 是存储索引(get_own_property_slot 对数组已加元素区偏移)。
obj.set_prop_storage(pos as usize, 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(())
}
/// TypedArray 整数索引在 `receiver` ≠ TA 时的 [[Set]] 语义:越界或非对象
/// receiver 直接返回 true(不写、不 ToNumber);界内对象 receiver 走普通 set
/// 把属性落到 receiver 自身。
fn set_to_receiver(
&mut self, ta_obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, index: usize,
use_frame_push: bool,
) -> Result<(), String> {
let Some((_, length)) = oxide_builtins::typed_array::typed_array_integer_index(self, ta_obj, prop_name_si)
else {
return Ok(());
};
if index >= length {
return Ok(());
}
let receiver_ptr = receiver.as_js_object_ptr();
if receiver_ptr.is_null() {
return Ok(());
}
let promoted = self.promote_if_needed_for_write_ptr(receiver_ptr, val);
let receiver_obj = unsafe { &mut *receiver_ptr };
self.ordinary_set_inner(receiver_obj, prop_name_si, promoted, receiver, use_frame_push)
}
pub(crate) fn call_or_push_setter(
&mut self, setter: JsValue, receiver: JsValue, val: JsValue, use_frame_push: bool,
) -> Result<(), String> {
vm_trace!("call_or_push_setter: frame_push={}", use_frame_push);
// native setter 抛错经 call_function_sync 以 String 返回(未 unwind)——
// 在此经 raise_call_error 恢复为可捕获的 JS 异常(与 getter 路径对称)。
let call_native = |vm: &mut Self| -> Result<(), String> {
if let Err(err) = vm.call_function_sync(setter, receiver, &[val]) {
vm.raise_call_error(&err)?;
}
Ok(())
};
if !use_frame_push {
return call_native(self);
}
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() {
return call_native(self);
}
self.push_bytecode_frame(
setter,
receiver,
&[val],
None,
None,
JsValue::undefined(),
FrameContinuation::AccessorSet,
0,
)?;
Ok(())
}
/// 读取 member 复合操作数(obj.x / obj["x"]),返回 (值, IC 扩展字起始位置)。
///
/// `ext_pc` 供同一指令的写侧 [`Self::set_member_prop`] 使用:读侧负责推进 pc
/// 越过扩展字,写侧显式接收该位置、不再接触 pc,消除隐式 pc 契约。
pub(crate) fn read_member_prop(
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue,
) -> Result<(JsValue, usize), String> {
vm_trace!("read_member_prop: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
let (cached_shape_id, cached_slot, cached_depth) =
crate::ic_helper::read_ic_slot0(&self.bytecode, &mut self.pc);
let ic_pc = self.pc;
if obj.has_prop_meta() {
return self.ordinary_get(obj, prop_name_si, receiver).map(|v| (v, ic_pc));
}
let val = if let Some(v) = crate::ic_helper::ic_get_hit(obj, cached_shape_id, cached_slot, cached_depth) {
v
} else if let Some(v) =
crate::ic_helper::ic_get_hit_poly(obj, &self.bytecode, ic_pc - oxide_bytecode::opcode::IC_EXT_WORDS)
{
v
} else if let Some(template) = self.kernel_core.prop_forge().get_template(obj.shape_id()) {
if template.prop_name != prop_name_si {
self.proto_chain_ic_get(obj, prop_name_si, receiver)?
} else if template.position < obj.prop_vec_len() as u32 {
crate::ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), template.position, 0);
obj.get_prop_shape(template.position)
} else {
self.proto_chain_ic_get(obj, prop_name_si, receiver)?
}
} else {
self.proto_chain_ic_get(obj, prop_name_si, receiver)?
};
Ok((val, ic_pc))
}
/// 执行 ordinary_get 并把 IC 按原型链深度写回(own 属性 depth=0,原型链按层计数)。
pub(crate) fn proto_chain_ic_get(
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue,
) -> Result<JsValue, String> {
let ic_pc = self.pc;
let resolved = self.ordinary_get(obj, prop_name_si, receiver)?;
// 快路径:自身属性(depth=0)。
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
if !obj.is_accessor_meta(pos) {
crate::ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), pos, 0);
}
return Ok(resolved);
}
// 沿原型链查找继承属性。
let mut cursor = obj.proto().as_js_object_ptr();
let mut depth = 1u8;
while !cursor.is_null() {
let co = unsafe { &*cursor };
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(co.shape_id(), prop_name_si) {
if !co.is_accessor_meta(pos) {
crate::ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, co.shape_id(), pos, depth);
}
break;
}
if !co.proto().is_object() {
break;
}
cursor = co.proto().as_js_object_ptr();
depth += 1;
}
Ok(resolved)
}
/// member 复合写(obj.x += 1 / obj.x++ 等)的写侧:优先 IC 写命中(depth==0 槽)
/// 直写返回,miss 走查表/写新属性快路径。
///
/// `ext_pc` 由调用方从 [`Self::read_member_prop`] 返回值带出(读侧已把 pc 推进
/// 越过本 IC 指令扩展字后的位置);写侧不再接触 pc,避免二次推进导致指令定位错位。
pub(crate) fn set_member_prop(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, ext_pc: usize,
) -> Result<(), String> {
ic_trace!("set_member_prop: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
let val = self.promote_if_needed_for_write_ptr(obj as *mut JsObject, val);
if obj.has_prop_meta() {
self.ordinary_set(obj, prop_name_si, val, receiver)?;
return Ok(());
}
if crate::ic_helper::ic_set_hit_own(obj, &self.bytecode, ext_pc, val) {
self.profiling.record_ic_hit();
return Ok(());
}
self.profiling.record_ic_miss();
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
obj.set_prop_shape(pos, val);
crate::ic_helper::write_ic_back(self.bytecode_mut(), ext_pc, obj.shape_id(), pos, 0);
} else if self.named_prop_create_needs_ordinary_set(obj, prop_name_si) {
// 数组 length / 整数索引键写新属性:分流回 ordinary_set(ArraySetLength /
// 元素区写),与 dispatch_ic_set_prop 对称,防快路径建影子槽破坏数组语义。
self.ordinary_set(obj, prop_name_si, val, receiver)?;
} else {
self.create_named_prop_fast(obj, prop_name_si, val, receiver, ext_pc, false)?;
}
Ok(())
}
/// 写新属性的 CreateDataProperty 快路径(shape 转换,IC 命中在数学上无法覆盖):
/// 原型链同名 accessor/只读检查后,一次 make_shape + 追加槽 + IC 写回 + 模板 upsert。
///
/// # 边界与前提
/// - 调用方已确认属性不存在于 shape 链(lookup_position miss)。
/// - 数组 length / 数组与 TA 整数索引键不进入本路径(调用方先分流到 ordinary_set)。
/// - `use_frame_push` 与 ordinary_set* 入口一致(member 写 false / IC_SET 分发 true)。
pub(crate) fn create_named_prop_fast(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, ext_pc: usize,
use_frame_push: bool,
) -> Result<(), String> {
// 原型链同名 accessor 须触发 setter、只读 data 须报错(与 ordinary_set 一致),
// 命中时不得创建 own 属性(规范 [[Set]] shadow 语义)。
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");
}
}
// 新 shape 槽位 = 追加前命名属性数(与 push_prop 的追加位置一致)。
let slot = obj.prop_vec_len() as u32;
let new_shape_id = self.kernel_core.shape_forge().make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
obj.push_prop(val);
obj.bump_generation();
crate::ic_helper::write_ic_back(self.bytecode_mut(), ext_pc, new_shape_id, slot, 0);
self.kernel_core.prop_forge().upsert(
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: slot,
generation: obj.generation(),
},
);
Ok(())
}
/// 写新属性须分流到完整 ordinary_set 语义的场景:数组 length(ArraySetLength)与
/// 数组/TA 整数索引键(元素区 / buffer 写)——这些键不在 shape 链上,CreateDataProperty
/// 快路径会错误地为其建命名属性。
pub(crate) fn named_prop_create_needs_ordinary_set(&self, obj: &JsObject, prop_name_si: u32) -> bool {
(obj.is_array() && prop_name_si == self.length_si)
|| (obj.is_typed_array_obj() || obj.is_array())
&& self.array_index_from_property_key(prop_name_si).is_some()
}
pub(crate) fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
vm_trace!("set_or_create_prop_value: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
// TypedArray 整数索引写 buffer(越界忽略),不进入 shape/prop 槽。
if obj.is_typed_array_obj() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
let _ = oxide_builtins::typed_array::typed_array_element_set(self, obj, index, val);
return;
}
}
// 数组下标键写入元素区(维护 array_prop_count),不进入 shape 链。
if obj.is_array() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
obj.set_prop_at(index, val);
return;
}
}
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
obj.set_prop_shape(pos, val);
} else {
let new_shape_id = self.kernel_core.shape_forge().make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
// 数组对象:属性追加到 hash_props 属性区(元素之后),array_prop_count 不变。
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> {
vm_trace!("define_data_property: shape={} prop_si={}", obj.shape_id(), prop_name_si);
let val = self.promote_if_needed_for_write_ptr(obj as *mut JsObject, val);
// TypedArray 整数索引:走元素定义(界内写 buffer,越界/非法描述符拒绝)。
if obj.is_typed_array_obj() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
return oxide_builtins::typed_array::typed_array_element_define(self, obj, index, val);
}
}
if obj.is_array() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
// 数组索引属性存元素区并维护 array_prop_count(元素数随索引增长)。
return self.define_array_index_element(
obj,
index,
val,
attributes,
false,
JsValue::undefined(),
JsValue::undefined(),
);
}
}
let pos = if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
// shape 槽位 → 存储索引(数组属性在元素区之后)。
if obj.is_array() {
obj.array_prop_count as usize + pos as usize
} else {
pos as usize
}
} else {
let new_shape_id = self.kernel_core.shape_forge().make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
obj.push_prop(JsValue::undefined()) as usize
};
if let Some(current) = obj.prop_meta_at(pos) {
if !current.attributes.configurable() {
if current.is_accessor {
return Err("cannot redefine non-configurable property".to_string());
}
if current.attributes.enumerable() != attributes.enumerable() {
return Err("cannot redefine non-configurable property".to_string());
}
if !current.attributes.writable()
&& (attributes.writable() || !coercion::same_value(obj.get_prop_at(pos), val))
{
return Err("cannot redefine non-configurable property".to_string());
}
if current.attributes.configurable() != attributes.configurable() {
return Err("cannot redefine non-configurable property".to_string());
}
}
}
obj.set_prop_storage(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> {
vm_trace!("define_accessor_property: shape={} prop_si={}", obj.shape_id(), prop_name_si);
let target_ptr = obj as *mut JsObject;
let get = self.promote_if_needed_for_write_ptr(target_ptr, get);
let set = self.promote_if_needed_for_write_ptr(target_ptr, set);
if obj.is_array() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
return self.define_array_index_element(obj, index, JsValue::undefined(), attributes, true, get, set);
}
}
let pos = if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
// shape 槽位 → 存储索引(数组属性在元素区之后)。
if obj.is_array() {
obj.array_prop_count as usize + pos as usize
} else {
pos as usize
}
} else {
let new_shape_id = self.kernel_core.shape_forge().make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
obj.push_prop(JsValue::undefined()) as usize
};
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 Err("cannot redefine non-configurable property".to_string());
}
}
obj.set_prop_storage(pos, JsValue::undefined());
obj.set_accessor_meta(pos, get, set, attributes);
obj.bump_generation();
Ok(())
}
/// 数组索引属性(`"0"`~`"4294967294"`)的 define 路径:存入元素区并维护
/// `array_prop_count`(length 随最高索引增长),meta 与元素槽对齐。
#[allow(clippy::too_many_arguments)]
fn define_array_index_element(
&mut self, obj: &mut JsObject, index: u32, val: JsValue, attributes: PropAttributes, is_accessor: bool,
get: JsValue, set: JsValue,
) -> Result<(), String> {
let pos = index as usize;
if pos > oxide_types::object::MAX_DENSE_PROPS {
return Err("array index out of dense range".to_string());
}
let pos = pos as u32;
if let Some(current) = obj.prop_meta_at(pos) {
if !current.attributes.configurable() {
if current.is_accessor != is_accessor {
return Err("cannot redefine non-configurable property".to_string());
}
if current.attributes.enumerable() != attributes.enumerable() {
return Err("cannot redefine non-configurable property".to_string());
}
if current.attributes.configurable() != attributes.configurable() {
return Err("cannot redefine non-configurable property".to_string());
}
if is_accessor {
if current.get != get || current.set != set {
return Err("cannot redefine non-configurable property".to_string());
}
} else if !current.attributes.writable()
&& (attributes.writable() || !coercion::same_value(obj.get_prop_at(pos), val))
{
return Err("cannot redefine non-configurable property".to_string());
}
}
}
obj.set_prop_at(pos, val);
if is_accessor {
obj.set_accessor_meta(pos, get, set, attributes);
} else {
obj.set_data_meta(pos, attributes);
}
obj.bump_generation();
Ok(())
}
}