mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: mapped arguments defineProperty 后参数与 arguments 再同步
This commit is contained in:
Generated
+19
-19
@@ -1160,7 +1160,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_builtins"
|
name = "oxide_builtins"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_bytecode"
|
name = "oxide_bytecode"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cfg"
|
name = "oxide_cfg"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_emit",
|
"oxide_emit",
|
||||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cli"
|
name = "oxide_cli"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@@ -1226,7 +1226,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_code_cache"
|
name = "oxide_code_cache"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru",
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
@@ -1235,7 +1235,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_compiler"
|
name = "oxide_compiler"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1255,7 +1255,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_dce"
|
name = "oxide_dce"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1269,7 +1269,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_emit"
|
name = "oxide_emit"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
@@ -1284,7 +1284,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_engine"
|
name = "oxide_engine"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1296,7 +1296,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_ir"
|
name = "oxide_ir"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1305,7 +1305,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_kernel"
|
name = "oxide_kernel"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1319,7 +1319,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_liveness"
|
name = "oxide_liveness"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1331,7 +1331,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_log"
|
name = "oxide_log"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
@@ -1340,7 +1340,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_parser"
|
name = "oxide_parser"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
@@ -1355,7 +1355,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_regalloc"
|
name = "oxide_regalloc"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1371,7 +1371,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_runtime_api"
|
name = "oxide_runtime_api"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -1384,7 +1384,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_test262"
|
name = "oxide_test262"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1402,7 +1402,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_types"
|
name = "oxide_types"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1415,7 +1415,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_vm"
|
name = "oxide_vm"
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.13.21"
|
version = "0.13.22"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
|
use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
|
||||||
use oxide_kernel::string_forge::PermInterner;
|
use oxide_kernel::string_forge::PermInterner;
|
||||||
|
use oxide_types::arguments_map::ArgumentsMapState;
|
||||||
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
||||||
use oxide_types::private_key::{
|
use oxide_types::private_key::{
|
||||||
decode_symbol_key, encode_symbol_key, int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key,
|
decode_symbol_key, encode_symbol_key, int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key,
|
||||||
@@ -365,6 +366,33 @@ pub fn delete_own_property_outcome<H: VmHost>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mapped arguments exotic [[Delete]]:整数索引且映射存活时,属性不可配置返回
|
||||||
|
// NonConfigurable(严格抛 TypeError、sloppy 返回 false);可配置时移除映射并
|
||||||
|
// 落穿普通删除路径。accessor 属性不移除映射(无参数寄存器同步)。
|
||||||
|
if obj.is_arguments_obj() {
|
||||||
|
let state_ptr = obj.native_data() as *mut ArgumentsMapState;
|
||||||
|
if !state_ptr.is_null() {
|
||||||
|
// SAFETY: native_data 由创建期 Box::into_raw 分配,生命周期与对象一致。
|
||||||
|
let state = unsafe { &mut *state_ptr };
|
||||||
|
if vm.arguments_mapping_alive(state) {
|
||||||
|
if let Some(index) = array_index_of(vm, key_si) {
|
||||||
|
if (index as u16) < state.param_count && state.is_mapped(index as u16) {
|
||||||
|
let pos = vm.get_own_property_slot(obj, key_si);
|
||||||
|
let meta = pos.and_then(|p| obj.prop_meta_at(p));
|
||||||
|
let is_accessor = meta.is_some_and(|m| m.is_accessor);
|
||||||
|
let configurable = meta.map(|m| m.attributes.configurable()).unwrap_or(true);
|
||||||
|
if !configurable {
|
||||||
|
return Ok(DeleteOutcome::NonConfigurable);
|
||||||
|
}
|
||||||
|
if !is_accessor {
|
||||||
|
state.unmap(index as u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 数组下标元素在元素区,不参与 shape 链,单独删除(保持 length 不变)。
|
// 数组下标元素在元素区,不参与 shape 链,单独删除(保持 length 不变)。
|
||||||
if obj.is_array() {
|
if obj.is_array() {
|
||||||
if let Some(index) = array_index_of(vm, key_si) {
|
if let Some(index) = array_index_of(vm, key_si) {
|
||||||
@@ -955,6 +983,38 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mapped arguments exotic [[DefineOwnProperty]]:整数索引且映射存活时,旧描述符
|
||||||
|
// 可写且新描述符不可写则移除映射,新描述符含 value 则写参数寄存器;随后落穿
|
||||||
|
// 普通 define 路径(描述符收窄校验由 define_data_property 承担)。
|
||||||
|
if unsafe { &*obj_ptr }.is_arguments_obj() {
|
||||||
|
let state_ptr = unsafe { &*obj_ptr }.native_data() as *mut ArgumentsMapState;
|
||||||
|
if !state_ptr.is_null() {
|
||||||
|
// SAFETY: native_data 由创建期 Box::into_raw 分配,生命周期与对象一致。
|
||||||
|
let state = unsafe { &mut *state_ptr };
|
||||||
|
if vm.arguments_mapping_alive(state) {
|
||||||
|
if let Some(index) = array_index_of(vm, key_si) {
|
||||||
|
if (index as u16) < state.param_count && state.is_mapped(index as u16) {
|
||||||
|
let old_writable = existing_meta.map(|m| m.attributes.writable()).unwrap_or(true);
|
||||||
|
let new_writable = if has_existing {
|
||||||
|
writable_field
|
||||||
|
.map(oxide_runtime_api::to_boolean)
|
||||||
|
.unwrap_or_else(|| existing_meta.map(|m| m.attributes.writable()).unwrap_or(false))
|
||||||
|
} else {
|
||||||
|
writable_field.map(oxide_runtime_api::to_boolean).unwrap_or(false)
|
||||||
|
};
|
||||||
|
if old_writable && !new_writable {
|
||||||
|
state.unmap(index as u16);
|
||||||
|
}
|
||||||
|
if let Some(v) = value_field {
|
||||||
|
let reg = (state.param_base as u16 + index as u16) as u8;
|
||||||
|
vm.set_reg(reg, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let obj = unsafe { &mut *obj_ptr };
|
let obj = unsafe { &mut *obj_ptr };
|
||||||
if has_accessor {
|
if has_accessor {
|
||||||
vm.define_accessor_property(obj, key_si, get, set, PropAttributes::new(false, enumerable, configurable))
|
vm.define_accessor_property(obj, key_si, get, set, PropAttributes::new(false, enumerable, configurable))
|
||||||
|
|||||||
@@ -253,6 +253,12 @@ pub trait VmHost {
|
|||||||
/// 守卫抛 TypeError / 失败缓存原值重抛),触发失败以 `Err` 返回。
|
/// 守卫抛 TypeError / 失败缓存原值重抛),触发失败以 `Err` 返回。
|
||||||
fn has_property(&mut self, obj: &JsObject, prop_name_si: u32) -> Result<bool, String>;
|
fn has_property(&mut self, obj: &JsObject, prop_name_si: u32) -> Result<bool, String>;
|
||||||
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32>;
|
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32>;
|
||||||
|
/// 判定 mapped arguments 映射是否存活:创建帧仍在帧栈上且帧身份匹配。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - `state` 为 arguments 对象 `native_data` 中的同步状态盒;`frame_depth`
|
||||||
|
/// 为创建帧下标,帧弹出后映射失效。
|
||||||
|
fn arguments_mapping_alive(&self, state: &oxide_types::arguments_map::ArgumentsMapState) -> bool;
|
||||||
|
|
||||||
// 属性访问
|
// 属性访问
|
||||||
fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String>;
|
fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String>;
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
//! mapped arguments 对象同步状态盒:参数寄存器与 arguments 对象存储值双向同步的
|
||||||
|
//! 创建期帧身份与逐索引映射存活位图。
|
||||||
|
|
||||||
|
/// mapped arguments 对象同步状态盒(存于 `native_data`,无 GC 边)。
|
||||||
|
///
|
||||||
|
/// 承载参数寄存器基址、形参数、逐索引映射存活位图与创建期帧身份,供参数寄存器
|
||||||
|
/// 与 arguments 对象存储值的双向同步(exotic `[[Get]]`/`[[Set]]`/`[[DefineOwnProperty]]`/
|
||||||
|
/// `[[Delete]]` 四臂共用)。
|
||||||
|
pub struct ArgumentsMapState {
|
||||||
|
/// 首形参寄存器下标(`CompiledModule.param_base`)。
|
||||||
|
pub param_base: u8,
|
||||||
|
/// 形参数(`[[ParameterMap]]`,`CompiledModule.n_args`)。
|
||||||
|
pub param_count: u16,
|
||||||
|
/// 逐索引映射存活位图:bit i = 1 表示索引 i 仍映射。长度 = ceil(param_count / 64),
|
||||||
|
/// 构造期全 1(尾部多余位清零)。
|
||||||
|
pub mapped_mask: Vec<u64>,
|
||||||
|
/// 创建帧在帧栈中的下标(创建期 `Vm.frames.len() - 1`)。
|
||||||
|
pub frame_depth: u32,
|
||||||
|
/// 创建期帧身份(单调递增 `frame_id`)。
|
||||||
|
pub frame_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArgumentsMapState {
|
||||||
|
/// 索引 `index` 的映射是否存活(位图对应位为 1)。
|
||||||
|
pub fn is_mapped(&self, index: u16) -> bool {
|
||||||
|
let word = (index as usize) / 64;
|
||||||
|
let bit = (index as usize) % 64;
|
||||||
|
word < self.mapped_mask.len() && (self.mapped_mask[word] >> bit) & 1 == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 移除索引 `index` 的映射(清位)。
|
||||||
|
pub fn unmap(&mut self, index: u16) {
|
||||||
|
let word = (index as usize) / 64;
|
||||||
|
let bit = (index as usize) % 64;
|
||||||
|
if word < self.mapped_mask.len() {
|
||||||
|
self.mapped_mask[word] &= !(1u64 << bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#![doc = "OxideJS 共享核心类型:ECMAScript 值、对象模型、形状存储、内存抽象与运行时错误类型。"]
|
#![doc = "OxideJS 共享核心类型:ECMAScript 值、对象模型、形状存储、内存抽象与运行时错误类型。"]
|
||||||
|
|
||||||
|
/// mapped arguments 对象同步状态盒(参数寄存器与 arguments 存储值双向同步)。
|
||||||
|
pub mod arguments_map;
|
||||||
/// 运行时错误类型([`JsError`] / [`JsErrorKind`])。
|
/// 运行时错误类型([`JsError`] / [`JsErrorKind`])。
|
||||||
pub mod error;
|
pub mod error;
|
||||||
/// 内存抽象:持久指针、arena 分配(epoch)与持久堆。
|
/// 内存抽象:持久指针、arena 分配(epoch)与持久堆。
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
//! mapped arguments 同步状态盒的 GC 支撑:size 核算与 drop 释放。
|
||||||
|
//!
|
||||||
|
//! 状态盒存于对象 `native_data`(`Box::into_raw`),无引用边(位图与帧身份均为
|
||||||
|
//! 原始值),仅 size/drop 链消费。
|
||||||
|
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
use oxide_types::arguments_map::ArgumentsMapState;
|
||||||
|
use oxide_types::object::JsObject;
|
||||||
|
|
||||||
|
/// 只读核算 mapped arguments 同步状态盒字节(不释放),供 GC 账目核算。
|
||||||
|
pub(crate) fn arguments_native_size(obj: &JsObject) -> u64 {
|
||||||
|
if !obj.is_arguments_obj() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let ptr = obj.native_data() as *mut ArgumentsMapState;
|
||||||
|
if ptr.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// SAFETY: 通过裸指针读取 capacity 字段,不解引用整个 Box(不移动/释放)。
|
||||||
|
unsafe { size_of::<ArgumentsMapState>() as u64 + (*ptr).mapped_mask.capacity() as u64 * size_of::<u64>() as u64 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 释放 mapped arguments 同步状态盒(对象被 GC 回收时),返回释放字节数。
|
||||||
|
pub(crate) fn drop_arguments_native(obj: &JsObject) -> u64 {
|
||||||
|
let bytes = arguments_native_size(obj);
|
||||||
|
if bytes == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let ptr = obj.native_data() as *mut ArgumentsMapState;
|
||||||
|
// SAFETY: ptr 非空(arguments_native_size 已验证),Box::from_raw 恰好释放一次。
|
||||||
|
let state = unsafe { Box::from_raw(ptr) };
|
||||||
|
drop(state);
|
||||||
|
bytes
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ use crate::vm::Vm;
|
|||||||
use crate::{vm_error, vm_trace};
|
use crate::{vm_error, vm_trace};
|
||||||
use oxide_bytecode::opcode;
|
use oxide_bytecode::opcode;
|
||||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||||
|
use oxide_types::arguments_map::ArgumentsMapState;
|
||||||
use oxide_types::object::{JsObject, PropAttributes};
|
use oxide_types::object::{JsObject, PropAttributes};
|
||||||
use oxide_types::private_key::{encode_symbol_key, make_int_key};
|
use oxide_types::private_key::{encode_symbol_key, make_int_key};
|
||||||
use oxide_types::value::{JsType, JsValue};
|
use oxide_types::value::{JsType, JsValue};
|
||||||
@@ -153,9 +154,63 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.regs[rd] = self.regs[a];
|
self.regs[rd] = self.regs[a];
|
||||||
|
// mapped arguments 参数直写同步:目标寄存器是创建帧的存活映射参数时,
|
||||||
|
// 把新值回写 arguments 存储值(读路径权威源),嵌套调用覆盖寄存器后
|
||||||
|
// 存储值仍正确。
|
||||||
|
self.sync_mapped_arguments_param(rd);
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// mapped arguments 参数寄存器直写同步:目标寄存器是顶帧 mapped arguments 的
|
||||||
|
/// 存活映射参数时,把新值回写 arguments 对象存储值(读路径权威源)。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - 仅顶帧带 mapped arguments 对象(`frame.arguments_obj` 非 undefined)时
|
||||||
|
/// 生效;其余帧首检即返回(热路径零开销)。
|
||||||
|
/// - 映射失效(创建帧已弹出)或索引已 unmapped(收窄不可写 / 删除)时不同步。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// - 写 arguments 对象存储值槽(shape 槽已存在,无 shape 变更)。
|
||||||
|
pub(crate) fn sync_mapped_arguments_param(&mut self, rd: usize) {
|
||||||
|
let Some(frame) = self.frames.last() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if frame.arguments_obj.is_undefined() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let obj_ptr = frame.arguments_obj.as_js_object_ptr();
|
||||||
|
if obj_ptr.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// SAFETY: arguments_obj 是 GC 根(帧遍历期标记),session 内指针有效。
|
||||||
|
if !unsafe { (*obj_ptr).is_arguments_obj() } {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let state_ptr = unsafe { (*obj_ptr).native_data() } as *const ArgumentsMapState;
|
||||||
|
if state_ptr.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// SAFETY: native_data 由创建期 Box::into_raw 分配,生命周期与对象一致。
|
||||||
|
let state = unsafe { &*state_ptr };
|
||||||
|
if !self.arguments_mapping_alive(state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let param_base = state.param_base as usize;
|
||||||
|
let param_count = state.param_count as usize;
|
||||||
|
if rd < param_base || rd >= param_base + param_count {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let index = (rd - param_base) as u16;
|
||||||
|
if !state.is_mapped(index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let si = make_int_key(index as u32);
|
||||||
|
let val = self.regs[rd];
|
||||||
|
// SAFETY: obj_ptr 是有效对象指针(GC 根)。
|
||||||
|
let obj_mut = unsafe { &mut *obj_ptr };
|
||||||
|
self.set_or_create_prop_value(obj_mut, si, val);
|
||||||
|
}
|
||||||
|
|
||||||
/// SPILL:`regs[rd]` → `spill_stack[帧基址 + slot]`。
|
/// SPILL:`regs[rd]` → `spill_stack[帧基址 + slot]`。
|
||||||
///
|
///
|
||||||
/// ext 字为 slot(u16);帧基址 = 当前 CallFrame.spill_offset(顶层模块无帧为 0)。
|
/// ext 字为 slot(u16);帧基址 = 当前 CallFrame.spill_offset(顶层模块无帧为 0)。
|
||||||
@@ -287,6 +342,40 @@ impl Vm {
|
|||||||
let obj = unsafe { &mut *obj_ptr };
|
let obj = unsafe { &mut *obj_ptr };
|
||||||
obj.type_tag = JsObject::OBJ_TYPE_ARGUMENTS;
|
obj.type_tag = JsObject::OBJ_TYPE_ARGUMENTS;
|
||||||
|
|
||||||
|
// mapped 判定:sloppy 且 simple(strict ‖ !simple → unmapped)。mapped 时
|
||||||
|
// 建同步状态盒(存 native_data):参数寄存器与 arguments 存储值双向同步,
|
||||||
|
// 帧身份经 (帧下标, 帧身份) 判定创建帧是否仍在栈上。unmapped 时 native_data
|
||||||
|
// 保持 null,exotic 四臂短路,行为与现树一致。
|
||||||
|
let strict = self.current_strict();
|
||||||
|
let simple = self.active_module().is_some_and(|m| m.has_simple_params);
|
||||||
|
if !strict && simple {
|
||||||
|
if let (Some(frame_id), Some(module)) = (self.frames.last().map(|f| f.frame_id), self.active_module()) {
|
||||||
|
let param_base = module.param_base;
|
||||||
|
let param_count = module.n_args as u16;
|
||||||
|
let words = (param_count as usize).div_ceil(64).max(1);
|
||||||
|
let mut mapped_mask = vec![u64::MAX; words];
|
||||||
|
let valid_bits = param_count as usize % 64;
|
||||||
|
if valid_bits != 0 {
|
||||||
|
mapped_mask[words - 1] &= (1u64 << valid_bits) - 1;
|
||||||
|
} else if param_count == 0 {
|
||||||
|
mapped_mask[0] = 0;
|
||||||
|
}
|
||||||
|
let state = ArgumentsMapState {
|
||||||
|
param_base,
|
||||||
|
param_count,
|
||||||
|
mapped_mask,
|
||||||
|
frame_depth: (self.frames.len() - 1) as u32,
|
||||||
|
frame_id,
|
||||||
|
};
|
||||||
|
obj.set_native_data(Box::into_raw(Box::new(state)) as *mut u8);
|
||||||
|
// 帧锚点:参数寄存器直写(STORE_VAR)据此同步 arguments 存储值,
|
||||||
|
// 使嵌套调用期参数寄存器被覆盖时存储值仍为权威读源。
|
||||||
|
if let Some(frame) = self.frames.last_mut() {
|
||||||
|
frame.arguments_obj = JsValue::from_js_object(obj_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 索引属性:按实参下标写入 shape 槽,属性描述符为默认(可写/可枚举/可配置)。
|
// 索引属性:按实参下标写入 shape 槽,属性描述符为默认(可写/可枚举/可配置)。
|
||||||
// 下标走整数键,保证 `arguments[0]`(property_key_si(int 0))键等价命中。
|
// 下标走整数键,保证 `arguments[0]`(property_key_si(int 0))键等价命中。
|
||||||
for i in 0..count as usize {
|
for i in 0..count as usize {
|
||||||
@@ -312,8 +401,6 @@ impl Vm {
|
|||||||
// 函数本身,可写、不可枚举、可配置)。
|
// 函数本身,可写、不可枚举、可配置)。
|
||||||
let callee_si = self.kernel_core.perm_interner().intern("callee").0;
|
let callee_si = self.kernel_core.perm_interner().intern("callee").0;
|
||||||
let callee = self.current_callee().unwrap_or(JsValue::undefined());
|
let callee = self.current_callee().unwrap_or(JsValue::undefined());
|
||||||
let strict = self.current_strict();
|
|
||||||
let simple = self.active_module().is_some_and(|m| m.has_simple_params);
|
|
||||||
if strict || !simple {
|
if strict || !simple {
|
||||||
// SAFETY: 指针由绑定层在 session 构造期写入,session 存活期内有效。
|
// SAFETY: 指针由绑定层在 session 构造期写入,session 存活期内有效。
|
||||||
let thrower_ptr = self.realm.session.borrow().builtin_world().throw_type_error.get();
|
let thrower_ptr = self.realm.session.borrow().builtin_world().throw_type_error.get();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
//! 核心入口为 [`vm::Vm`];`VmPool` 负责 VM 实例的复用;`session_gc` 提供
|
//! 核心入口为 [`vm::Vm`];`VmPool` 负责 VM 实例的复用;`session_gc` 提供
|
||||||
//! session 内存回收;`bindings` 把内置对象绑定到每个 session 的 global 上。
|
//! session 内存回收;`bindings` 把内置对象绑定到每个 session 的 global 上。
|
||||||
|
|
||||||
|
mod arguments_gc;
|
||||||
mod async_disposable;
|
mod async_disposable;
|
||||||
mod async_from_sync;
|
mod async_from_sync;
|
||||||
mod async_func;
|
mod async_func;
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ pub(crate) enum NativeBoxFamily {
|
|||||||
MessagePort,
|
MessagePort,
|
||||||
/// BroadcastChannel 通道对象(tag 34,mpsc 发送/接收对与通道名,无对象边)。
|
/// BroadcastChannel 通道对象(tag 34,mpsc 发送/接收对与通道名,无对象边)。
|
||||||
BroadcastChannel,
|
BroadcastChannel,
|
||||||
|
/// mapped arguments 对象(tag 20,同步状态盒存 `native_data`,无引用边)。
|
||||||
|
Arguments,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 一个家族的边操作集(每家族函数引用,单一注册面)。
|
/// 一个家族的边操作集(每家族函数引用,单一注册面)。
|
||||||
@@ -114,7 +116,7 @@ pub(crate) fn classify_by_tag(tag: u8) -> NativeBoxFamily {
|
|||||||
JsObject::OBJ_TYPE_DURATION => NativeBoxFamily::None,
|
JsObject::OBJ_TYPE_DURATION => NativeBoxFamily::None,
|
||||||
JsObject::OBJ_TYPE_ZONED_DATE_TIME => NativeBoxFamily::None,
|
JsObject::OBJ_TYPE_ZONED_DATE_TIME => NativeBoxFamily::None,
|
||||||
JsObject::OBJ_TYPE_PLAIN_DATE_TIME => NativeBoxFamily::None,
|
JsObject::OBJ_TYPE_PLAIN_DATE_TIME => NativeBoxFamily::None,
|
||||||
JsObject::OBJ_TYPE_ARGUMENTS => NativeBoxFamily::None,
|
JsObject::OBJ_TYPE_ARGUMENTS => NativeBoxFamily::Arguments,
|
||||||
JsObject::OBJ_TYPE_SYMBOL_OBJ => NativeBoxFamily::None,
|
JsObject::OBJ_TYPE_SYMBOL_OBJ => NativeBoxFamily::None,
|
||||||
JsObject::OBJ_TYPE_ERROR => NativeBoxFamily::None,
|
JsObject::OBJ_TYPE_ERROR => NativeBoxFamily::None,
|
||||||
JsObject::OBJ_TYPE_BOUND => NativeBoxFamily::None,
|
JsObject::OBJ_TYPE_BOUND => NativeBoxFamily::None,
|
||||||
@@ -221,6 +223,13 @@ pub(crate) fn ops_for(family: NativeBoxFamily) -> NativeBoxOps {
|
|||||||
string_edges: None,
|
string_edges: None,
|
||||||
cell_edges: None,
|
cell_edges: None,
|
||||||
},
|
},
|
||||||
|
// mapped arguments 同步状态盒:无引用边(位图与帧身份均为原始值),
|
||||||
|
// 三边函数全空,仅 size/drop 链消费。
|
||||||
|
NativeBoxFamily::Arguments => NativeBoxOps {
|
||||||
|
object_edges: None,
|
||||||
|
string_edges: None,
|
||||||
|
cell_edges: None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +261,7 @@ mod tests {
|
|||||||
JsObject::OBJ_TYPE_REGEXP | JsObject::OBJ_TYPE_REGEX_STUB => NativeBoxFamily::RegExp,
|
JsObject::OBJ_TYPE_REGEXP | JsObject::OBJ_TYPE_REGEX_STUB => NativeBoxFamily::RegExp,
|
||||||
JsObject::OBJ_TYPE_MESSAGE_PORT => NativeBoxFamily::MessagePort,
|
JsObject::OBJ_TYPE_MESSAGE_PORT => NativeBoxFamily::MessagePort,
|
||||||
JsObject::OBJ_TYPE_BROADCAST_CHANNEL => NativeBoxFamily::BroadcastChannel,
|
JsObject::OBJ_TYPE_BROADCAST_CHANNEL => NativeBoxFamily::BroadcastChannel,
|
||||||
|
JsObject::OBJ_TYPE_ARGUMENTS => NativeBoxFamily::Arguments,
|
||||||
_ => NativeBoxFamily::None,
|
_ => NativeBoxFamily::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ impl SessionGc {
|
|||||||
bytes += weak_map::weak_map_native_size(obj);
|
bytes += weak_map::weak_map_native_size(obj);
|
||||||
bytes += message_channel::message_port_native_size(obj);
|
bytes += message_channel::message_port_native_size(obj);
|
||||||
bytes += broadcast_channel::broadcast_channel_native_size(obj);
|
bytes += broadcast_channel::broadcast_channel_native_size(obj);
|
||||||
|
bytes += crate::arguments_gc::arguments_native_size(obj);
|
||||||
|
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -573,6 +574,7 @@ impl SessionGc {
|
|||||||
freed_bytes += weak_map::drop_weak_map_native(obj);
|
freed_bytes += weak_map::drop_weak_map_native(obj);
|
||||||
freed_bytes += message_channel::drop_message_port_native(obj);
|
freed_bytes += message_channel::drop_message_port_native(obj);
|
||||||
freed_bytes += broadcast_channel::drop_broadcast_channel_native(obj);
|
freed_bytes += broadcast_channel::drop_broadcast_channel_native(obj);
|
||||||
|
freed_bytes += crate::arguments_gc::drop_arguments_native(obj);
|
||||||
|
|
||||||
freed_bytes
|
freed_bytes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ fn gc_roots_contains_registers_frames_and_root_roots() {
|
|||||||
vm.frames.push(CallFrame {
|
vm.frames.push(CallFrame {
|
||||||
return_addr: 0,
|
return_addr: 0,
|
||||||
function_name: 0,
|
function_name: 0,
|
||||||
|
frame_id: 0,
|
||||||
caller_reg_limit: 1,
|
caller_reg_limit: 1,
|
||||||
caller_active_reg_limit: 1,
|
caller_active_reg_limit: 1,
|
||||||
saved_reg_offset: 0,
|
saved_reg_offset: 0,
|
||||||
@@ -98,6 +99,7 @@ fn gc_roots_contains_registers_frames_and_root_roots() {
|
|||||||
saved_this: JsValue::from_js_object(this_session),
|
saved_this: JsValue::from_js_object(this_session),
|
||||||
saved_new_target: JsValue::from_js_object(child_session),
|
saved_new_target: JsValue::from_js_object(child_session),
|
||||||
callee: JsValue::from_js_object(child_session),
|
callee: JsValue::from_js_object(child_session),
|
||||||
|
arguments_obj: JsValue::undefined(),
|
||||||
construct_result_reg: None,
|
construct_result_reg: None,
|
||||||
strict: false,
|
strict: false,
|
||||||
constructed_this: Some(JsValue::from_js_object(child_session)),
|
constructed_this: Some(JsValue::from_js_object(child_session)),
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ impl SuspendedFrame {
|
|||||||
f(frame.saved_this);
|
f(frame.saved_this);
|
||||||
f(frame.saved_new_target);
|
f(frame.saved_new_target);
|
||||||
f(frame.callee);
|
f(frame.callee);
|
||||||
|
f(frame.arguments_obj);
|
||||||
if let Some(ct) = frame.constructed_this {
|
if let Some(ct) = frame.constructed_this {
|
||||||
f(ct);
|
f(ct);
|
||||||
}
|
}
|
||||||
@@ -294,6 +295,7 @@ mod tests {
|
|||||||
CallFrame {
|
CallFrame {
|
||||||
return_addr: 7,
|
return_addr: 7,
|
||||||
function_name: 1,
|
function_name: 1,
|
||||||
|
frame_id: 0,
|
||||||
caller_reg_limit: 2,
|
caller_reg_limit: 2,
|
||||||
caller_active_reg_limit: 2,
|
caller_active_reg_limit: 2,
|
||||||
saved_reg_offset: 3,
|
saved_reg_offset: 3,
|
||||||
@@ -303,6 +305,7 @@ mod tests {
|
|||||||
saved_this: tag,
|
saved_this: tag,
|
||||||
saved_new_target: tag,
|
saved_new_target: tag,
|
||||||
callee: tag,
|
callee: tag,
|
||||||
|
arguments_obj: JsValue::undefined(),
|
||||||
construct_result_reg: None,
|
construct_result_reg: None,
|
||||||
constructed_this: Some(tag),
|
constructed_this: Some(tag),
|
||||||
is_derived_constructor: false,
|
is_derived_constructor: false,
|
||||||
|
|||||||
@@ -332,9 +332,14 @@ impl Vm {
|
|||||||
// 压帧时固化 callee 的 upvalue 表(非闭包为 null 空切片):热路径免
|
// 压帧时固化 callee 的 upvalue 表(非闭包为 null 空切片):热路径免
|
||||||
// 每指令重查 callee 对象,指针跨 GC 恒有效(callee 是 GC 根)。
|
// 每指令重查 callee 对象,指针跨 GC 恒有效(callee 是 GC 根)。
|
||||||
let up = obj.upvalues_slice() as *const [*mut Cell];
|
let up = obj.upvalues_slice() as *const [*mut Cell];
|
||||||
|
// 帧身份单调递增:mapped arguments 状态盒按 (帧下标, 帧身份) 判定创建帧
|
||||||
|
// 是否仍在栈上,帧弹出后身份不复用,映射随之失效。
|
||||||
|
self.next_frame_id += 1;
|
||||||
|
let frame_id = self.next_frame_id;
|
||||||
self.frames.push(CallFrame {
|
self.frames.push(CallFrame {
|
||||||
return_addr: self.pc,
|
return_addr: self.pc,
|
||||||
function_name,
|
function_name,
|
||||||
|
frame_id,
|
||||||
caller_reg_limit,
|
caller_reg_limit,
|
||||||
caller_active_reg_limit,
|
caller_active_reg_limit,
|
||||||
saved_reg_offset,
|
saved_reg_offset,
|
||||||
@@ -344,6 +349,7 @@ impl Vm {
|
|||||||
saved_this,
|
saved_this,
|
||||||
saved_new_target,
|
saved_new_target,
|
||||||
callee,
|
callee,
|
||||||
|
arguments_obj: JsValue::undefined(),
|
||||||
construct_result_reg,
|
construct_result_reg,
|
||||||
constructed_this,
|
constructed_this,
|
||||||
is_derived_constructor: obj.is_derived_constructor(),
|
is_derived_constructor: obj.is_derived_constructor(),
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ impl FrameArgs<'_> {
|
|||||||
pub struct CallFrame {
|
pub struct CallFrame {
|
||||||
pub return_addr: usize,
|
pub return_addr: usize,
|
||||||
pub function_name: u32,
|
pub function_name: u32,
|
||||||
|
/// 帧身份(VM 级单调计数器,压帧时分配):mapped arguments 状态盒按此判定
|
||||||
|
/// 创建帧是否仍在栈上(帧弹出后映射失效)。
|
||||||
|
pub frame_id: u64,
|
||||||
/// 压帧保存的调用方寄存器窗口长度(= save_stack 中本帧段大小)。
|
/// 压帧保存的调用方寄存器窗口长度(= save_stack 中本帧段大小)。
|
||||||
/// 普通字节码调用按调用点存活上界截断;运行时发起(accessor/内联)为
|
/// 普通字节码调用按调用点存活上界截断;运行时发起(accessor/内联)为
|
||||||
/// 调用方 `active_reg_limit` 全量。
|
/// 调用方 `active_reg_limit` 全量。
|
||||||
@@ -61,6 +64,9 @@ pub struct CallFrame {
|
|||||||
pub saved_this: JsValue,
|
pub saved_this: JsValue,
|
||||||
pub saved_new_target: JsValue,
|
pub saved_new_target: JsValue,
|
||||||
pub callee: JsValue,
|
pub callee: JsValue,
|
||||||
|
/// 本帧的 mapped arguments 对象(参数寄存器与存储值双向同步的锚点);
|
||||||
|
/// 无 mapped arguments 时为 undefined。GC 根(帧遍历期标记)。
|
||||||
|
pub arguments_obj: JsValue,
|
||||||
pub construct_result_reg: Option<u8>,
|
pub construct_result_reg: Option<u8>,
|
||||||
pub constructed_this: Option<JsValue>,
|
pub constructed_this: Option<JsValue>,
|
||||||
pub is_derived_constructor: bool,
|
pub is_derived_constructor: bool,
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ impl Vm {
|
|||||||
f(RootGroup::Frames, frame.saved_this);
|
f(RootGroup::Frames, frame.saved_this);
|
||||||
f(RootGroup::Frames, frame.saved_new_target);
|
f(RootGroup::Frames, frame.saved_new_target);
|
||||||
f(RootGroup::Frames, frame.callee);
|
f(RootGroup::Frames, frame.callee);
|
||||||
|
f(RootGroup::Frames, frame.arguments_obj);
|
||||||
f(RootGroup::Frames, frame.constructed_this.unwrap_or(JsValue::undefined()));
|
f(RootGroup::Frames, frame.constructed_this.unwrap_or(JsValue::undefined()));
|
||||||
}
|
}
|
||||||
for &v in &self.save_stack {
|
for &v in &self.save_stack {
|
||||||
|
|||||||
@@ -201,6 +201,9 @@ pub struct Vm {
|
|||||||
/// 用胖 `*const`:常量 Vec 归表代际所有且 OnceLock 只填一次(堆分配地址稳定)。
|
/// 用胖 `*const`:常量 Vec 归表代际所有且 OnceLock 只填一次(堆分配地址稳定)。
|
||||||
pub(crate) active_immutables: *const [JsValue],
|
pub(crate) active_immutables: *const [JsValue],
|
||||||
pub(crate) frames: SmallVec<[CallFrame; 16]>,
|
pub(crate) frames: SmallVec<[CallFrame; 16]>,
|
||||||
|
/// 帧身份单调计数器:压帧时分配 `CallFrame.frame_id`(mapped arguments 状态盒
|
||||||
|
/// 按此判定创建帧是否仍在栈上)。
|
||||||
|
pub(crate) next_frame_id: u64,
|
||||||
pub(crate) kernel_core: Arc<KernelCore>,
|
pub(crate) kernel_core: Arc<KernelCore>,
|
||||||
/// 动态编译服务句柄:`Function` 构造器 / eval / `$262.evalScript` 经它编译
|
/// 动态编译服务句柄:`Function` 构造器 / eval / `$262.evalScript` 经它编译
|
||||||
/// 源码(`dynamic.rs` 三方法)。缺省为 no-op stub(返 `Err`),生产 entry
|
/// 源码(`dynamic.rs` 三方法)。缺省为 no-op stub(返 `Err`),生产 entry
|
||||||
|
|||||||
@@ -215,6 +215,9 @@ impl oxide_runtime_api::VmHost for Vm {
|
|||||||
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32> {
|
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32> {
|
||||||
self.get_own_property_slot(obj, prop_name_si)
|
self.get_own_property_slot(obj, prop_name_si)
|
||||||
}
|
}
|
||||||
|
fn arguments_mapping_alive(&self, state: &oxide_types::arguments_map::ArgumentsMapState) -> bool {
|
||||||
|
self.arguments_mapping_alive(state)
|
||||||
|
}
|
||||||
fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String> {
|
fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String> {
|
||||||
self.ordinary_get(obj, prop_name_si, receiver)
|
self.ordinary_get(obj, prop_name_si, receiver)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,22 @@ use oxide_kernel::prop_forge::PropTemplate;
|
|||||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||||
use oxide_runtime_api as coercion;
|
use oxide_runtime_api as coercion;
|
||||||
use oxide_runtime_api::VmHost;
|
use oxide_runtime_api::VmHost;
|
||||||
|
use oxide_types::arguments_map::ArgumentsMapState;
|
||||||
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
|
/// 判定 mapped arguments 映射是否存活:创建帧仍在帧栈上且帧身份匹配。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - `frame_depth` 为创建帧在帧栈中的下标;帧弹出后 `frames.len() <= frame_depth`,
|
||||||
|
/// 映射失效(参数寄存器已被外层帧复用)。
|
||||||
|
/// - 帧身份(`frame_id`)单调递增不复用,防帧下标复用误判。
|
||||||
|
pub(crate) fn arguments_mapping_alive(&self, state: &ArgumentsMapState) -> bool {
|
||||||
|
let depth = state.frame_depth as usize;
|
||||||
|
self.frames.len() > depth && self.frames[depth].frame_id == state.frame_id
|
||||||
|
}
|
||||||
|
|
||||||
/// 属性读入口:解析 `obj[prop_name_si]`,依次尝试数组 length 虚拟属性、
|
/// 属性读入口:解析 `obj[prop_name_si]`,依次尝试数组 length 虚拟属性、
|
||||||
/// 数组元素区、TypedArray 整数索引、命名属性槽,最后沿原型链查找。
|
/// 数组元素区、TypedArray 整数索引、命名属性槽,最后沿原型链查找。
|
||||||
///
|
///
|
||||||
@@ -378,6 +390,39 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
// mapped arguments exotic [[Set]]:整数索引且映射存活时同步存储值(读路径
|
||||||
|
// 权威源)并写参数寄存器;accessor 属性落穿普通 setter 路径。参数寄存器
|
||||||
|
// 仅在创建帧为顶帧时写——嵌套调用期寄存器被被调方占用,写之串值,参数值
|
||||||
|
// 在 save_stack。
|
||||||
|
if obj.is_arguments_obj() {
|
||||||
|
let state_ptr = obj.native_data() as *const ArgumentsMapState;
|
||||||
|
if !state_ptr.is_null() {
|
||||||
|
// SAFETY: native_data 由创建期 Box::into_raw 分配,生命周期与对象一致。
|
||||||
|
let state = unsafe { &*state_ptr };
|
||||||
|
if self.arguments_mapping_alive(state) {
|
||||||
|
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
|
||||||
|
if (index as u16) < state.param_count && state.is_mapped(index as u16) {
|
||||||
|
let pos = self.get_own_property_slot(obj, prop_name_si);
|
||||||
|
let meta = pos.and_then(|p| obj.prop_meta_at(p));
|
||||||
|
let is_accessor = meta.is_some_and(|m| m.is_accessor);
|
||||||
|
if !is_accessor {
|
||||||
|
let writable = meta.map(|m| m.attributes.writable()).unwrap_or(true);
|
||||||
|
if !writable {
|
||||||
|
return self
|
||||||
|
.write_protection_failure(builtin, "cannot assign to read-only property");
|
||||||
|
}
|
||||||
|
self.set_or_create_prop_value(obj, prop_name_si, val);
|
||||||
|
if self.frames.len() == state.frame_depth as usize + 1 {
|
||||||
|
let reg = state.param_base as usize + index as usize;
|
||||||
|
self.regs[reg] = val;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// 统一数值键门(exotic [[Set]]):界内规范键写底层 buffer(自臂界内
|
// 统一数值键门(exotic [[Set]]):界内规范键写底层 buffer(自臂界内
|
||||||
// 写 / 越界静默;他臂先按自身判界再按 receiver 分类);数字无效键强转
|
// 写 / 越界静默;他臂先按自身判界再按 receiver 分类);数字无效键强转
|
||||||
// (可抛、kind 保真)后丢弃、receiver 不 consult(detach 同臂:live 长
|
// (可抛、kind 保真)后丢弃、receiver 不 consult(detach 同臂:live 长
|
||||||
@@ -2029,7 +2074,10 @@ mod tests {
|
|||||||
let ns = unsafe { &*ns_ptr };
|
let ns = unsafe { &*ns_ptr };
|
||||||
assert!(vm.ordinary_get(ns, x_si, ns_val).is_err(), "首次读应抛错");
|
assert!(vm.ordinary_get(ns, x_si, ns_val).is_err(), "首次读应抛错");
|
||||||
let ns = unsafe { &*ns_ptr };
|
let ns = unsafe { &*ns_ptr };
|
||||||
let cached_err = oxide_builtins::module::deferred_state(ns).unwrap().error.expect("失败应缓存错误");
|
let cached_err = oxide_builtins::module::deferred_state(ns)
|
||||||
|
.unwrap()
|
||||||
|
.error
|
||||||
|
.expect("失败应缓存错误");
|
||||||
|
|
||||||
// 再读:重抛同一错误对象(sameValue 身份)。
|
// 再读:重抛同一错误对象(sameValue 身份)。
|
||||||
let ns = unsafe { &*ns_ptr };
|
let ns = unsafe { &*ns_ptr };
|
||||||
@@ -2055,7 +2103,10 @@ mod tests {
|
|||||||
|
|
||||||
// 求值未触发:[[Evaluated]] 仍为 false。
|
// 求值未触发:[[Evaluated]] 仍为 false。
|
||||||
let ns = unsafe { &*ns_ptr };
|
let ns = unsafe { &*ns_ptr };
|
||||||
assert!(!oxide_builtins::module::deferred_state(ns).unwrap().evaluated, "symbol-like 键不应触发求值");
|
assert!(
|
||||||
|
!oxide_builtins::module::deferred_state(ns).unwrap().evaluated,
|
||||||
|
"symbol-like 键不应触发求值"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ impl Vm {
|
|||||||
bytecode: Arc::default(),
|
bytecode: Arc::default(),
|
||||||
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
|
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
|
||||||
frames: smallvec::SmallVec::new(),
|
frames: smallvec::SmallVec::new(),
|
||||||
|
next_frame_id: 0,
|
||||||
kernel_core: core,
|
kernel_core: core,
|
||||||
// 缺省 no-op 编译服务:动态编译返 Err,生产 entry points 与
|
// 缺省 no-op 编译服务:动态编译返 Err,生产 entry points 与
|
||||||
// 动态编译测试经 set_compiler_service 注入真实实现。
|
// 动态编译测试经 set_compiler_service 注入真实实现。
|
||||||
@@ -232,6 +233,7 @@ impl Vm {
|
|||||||
bytecode: Arc::default(),
|
bytecode: Arc::default(),
|
||||||
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
|
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
|
||||||
frames: smallvec::SmallVec::new(),
|
frames: smallvec::SmallVec::new(),
|
||||||
|
next_frame_id: 0,
|
||||||
kernel_core: core,
|
kernel_core: core,
|
||||||
// 缺省 no-op 编译服务:动态编译返 Err,生产 entry points 与
|
// 缺省 no-op 编译服务:动态编译返 Err,生产 entry points 与
|
||||||
// 动态编译测试经 set_compiler_service 注入真实实现。
|
// 动态编译测试经 set_compiler_service 注入真实实现。
|
||||||
|
|||||||
@@ -797,6 +797,7 @@ fn reset_clears_runtime_state_like_rerun() {
|
|||||||
vm.frames.push(crate::vm::CallFrame {
|
vm.frames.push(crate::vm::CallFrame {
|
||||||
return_addr: 1,
|
return_addr: 1,
|
||||||
function_name: 0,
|
function_name: 0,
|
||||||
|
frame_id: 0,
|
||||||
caller_reg_limit: 2,
|
caller_reg_limit: 2,
|
||||||
caller_active_reg_limit: 2,
|
caller_active_reg_limit: 2,
|
||||||
saved_reg_offset: 0,
|
saved_reg_offset: 0,
|
||||||
@@ -806,6 +807,7 @@ fn reset_clears_runtime_state_like_rerun() {
|
|||||||
saved_this: JsValue::undefined(),
|
saved_this: JsValue::undefined(),
|
||||||
saved_new_target: JsValue::undefined(),
|
saved_new_target: JsValue::undefined(),
|
||||||
callee: JsValue::undefined(),
|
callee: JsValue::undefined(),
|
||||||
|
arguments_obj: JsValue::undefined(),
|
||||||
construct_result_reg: None,
|
construct_result_reg: None,
|
||||||
strict: false,
|
strict: false,
|
||||||
constructed_this: None,
|
constructed_this: None,
|
||||||
|
|||||||
Reference in New Issue
Block a user