fix: RegExp source/flags 原型访问器与 GC 根追踪

This commit is contained in:
2026-09-21 14:56:19 +00:00
parent fea24b6d26
commit 8e22c48b70
6 changed files with 190 additions and 24 deletions
+75 -3
View File
@@ -1004,14 +1004,15 @@ pub fn object_get_own_property_descriptor<H: VmHost>(vm: &mut H, args: &[u8]) ->
///
/// # 步骤
/// 1. ToObject 装箱接收者(null/undefined 抛 TypeError)
/// 2. 按规范三段序枚举自身键:整数索引数值升序 + 非整数串键插入序
/// (walk_own_keys),Symbol 键插入序(walk_own_symbol_keys)随后
/// 3. 逐键取描述符(缺失键跳过),以数据属性定义到结果对象
/// 2. String 盒:按规范枚举虚拟键(整数下标 0..len-1 + "length"),构造描述符
/// 3. 普通对象:按规范三段序枚举自身键(walk_own_keys + walk_own_symbol_keys)
/// 4. 逐键取描述符(缺失键跳过),以数据属性定义到结果对象
///
/// # 边界与前提
/// - 结果对象 proto = %Object.prototype%,各键属性恒 writable/enumerable/
/// configurable 全真(fresh 普通对象的 CreateDataPropertyOrThrow 形态)
/// - 描述符取值与 gOPD 同核;模块命名空间未初始化导出抛 ReferenceError 原值
/// - String 盒虚拟属性:字符下标 enumerable=true,length enumerable=false
///
/// # 副作用
/// - 分配结果对象与每个描述符对象
@@ -1020,6 +1021,12 @@ pub fn object_get_own_property_descriptors<H: VmHost>(vm: &mut H, args: &[u8]) -
Ok(ptr) => ptr,
Err(err) => return NativeResult::Err(err),
};
// String 盒:自身键为虚拟属性(从被包字符串值派生),不走 shape 链枚举。
if unsafe { &*obj_ptr }.is_string_obj() {
return string_exotic_get_own_property_descriptors(vm, obj_ptr);
}
let obj = unsafe { &*obj_ptr };
// 三段自身键序:字符串/整数键(整键升序在前)后接 Symbol 键插入序。
@@ -1046,6 +1053,71 @@ pub fn object_get_own_property_descriptors<H: VmHost>(vm: &mut H, args: &[u8]) -
NativeResult::Ok(result_val)
}
/// String 盒的 `getOwnPropertyDescriptors`:枚举虚拟键(字符下标 + length)。
///
/// # 步骤
/// 1. 取被包字符串值(boxed_value 或 hash_props[0] fallback)
/// 2. 按 UTF-16 单元枚举:整数下标 0..len-1(enumerable=true)+ "length"(enumerable=false)
/// 3. 逐键构造描述符对象并写入结果
///
/// # 边界与前提
/// - 空串:仅返回 length 描述符
/// - 非字符串被包值:退化为零键
fn string_exotic_get_own_property_descriptors<H: VmHost>(
vm: &mut H, obj_ptr: *mut JsObject,
) -> NativeResult {
let obj = unsafe { &*obj_ptr };
// 取被包字符串值:优先 boxed_value,回退 hash_props[0]。
let raw = obj.boxed_value();
let raw = if raw.is_undefined() { obj.get_prop_at(0) } else { raw };
let units = if raw.is_string() { vm.string_units(raw).to_vec() } else { Vec::new() };
let len = units.len();
// 结果对象:proto = %Object.prototype%。
let desc_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
let result = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(desc_proto)));
let result_val = JsValue::from_js_object(result);
// 预计算 interned 键。
let intern = vm.kernel_core().perm_interner();
let value_si = intern.intern("value").0;
let writable_si = intern.intern("writable").0;
let enumerable_si = intern.intern("enumerable").0;
let configurable_si = intern.intern("configurable").0;
let length_si = intern.intern("length").0;
// 整数下标键 0..len-1:enumerable=true, writable=false, configurable=false。
for (i, unit) in units.iter().enumerate() {
let si = make_int_key(i as u32);
let char_val = vm.new_string_units(std::slice::from_ref(unit));
let desc = alloc_desc_object(vm);
let desc_obj = unsafe { &mut *desc };
let desc_obj_ptr = JsValue::from_js_object(desc);
// 构造描述符对象:value + writable/enumerable/configurable。
vm.define_data_property(desc_obj, value_si, char_val, PropAttributes::DEFAULT_DATA).ok();
vm.define_data_property(desc_obj, writable_si, JsValue::bool(false), PropAttributes::DEFAULT_DATA).ok();
vm.define_data_property(desc_obj, enumerable_si, JsValue::bool(true), PropAttributes::DEFAULT_DATA).ok();
vm.define_data_property(desc_obj, configurable_si, JsValue::bool(false), PropAttributes::DEFAULT_DATA).ok();
if let Err(err) = vm.define_data_property(unsafe { &mut *result }, si, desc_obj_ptr, PropAttributes::DEFAULT_DATA) {
return NativeResult::Err(crate::error::create_type_error(vm, &err));
}
}
// "length" 键:enumerable=false, writable=false, configurable=false。
let desc = alloc_desc_object(vm);
let desc_obj = unsafe { &mut *desc };
let desc_obj_ptr = JsValue::from_js_object(desc);
vm.define_data_property(desc_obj, value_si, JsValue::int(len as i32), PropAttributes::DEFAULT_DATA).ok();
vm.define_data_property(desc_obj, writable_si, JsValue::bool(false), PropAttributes::DEFAULT_DATA).ok();
vm.define_data_property(desc_obj, enumerable_si, JsValue::bool(false), PropAttributes::DEFAULT_DATA).ok();
vm.define_data_property(desc_obj, configurable_si, JsValue::bool(false), PropAttributes::DEFAULT_DATA).ok();
if let Err(err) = vm.define_data_property(unsafe { &mut *result }, length_si, desc_obj_ptr, PropAttributes::DEFAULT_DATA) {
return NativeResult::Err(crate::error::create_type_error(vm, &err));
}
NativeResult::Ok(result_val)
}
/// 按 ToPropertyDescriptor 语义取描述符字段:沿原型链判存在性,存在时经
/// ordinary_get 取值(触发 accessor getter,receiver 为描述符对象本身)。
fn own_field<H: VmHost>(vm: &mut H, desc: JsValue, prop_si: u32) -> Option<JsValue> {
+47 -20
View File
@@ -357,11 +357,10 @@ pub fn regexp_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
}
set_prop(&mut obj, "lastIndex", JsValue::int(0), vm);
// 单元口径物化 source/flags(孤立 surrogate 单元保持往返)。7 个 flag 布尔
// 不再作实例数据属性:读侧走原型只读访问器,以 flags 串为唯一数据源。
set_prop(&mut obj, "source", vm.new_string_units_owned(pattern_units), vm);
set_prop(&mut obj, "flags", vm.new_string_units_owned(flags_units), vm);
obj.type_tag = JsObject::OBJ_TYPE_REGEXP;
// source/flags 存实例字段:原型访问器从本字段读取,不作为自身属性枚举。
obj.set_regexp_source(vm.new_string_units_owned(pattern_units));
obj.set_regexp_flags(vm.new_string_units_owned(flags_units));
let obj_ptr = vm.alloc_object(obj);
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
@@ -605,14 +604,8 @@ pub fn regexp_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
Err(err) => return NativeResult::Err(err),
};
let re = unsafe { &*re_ptr };
let source = {
let val = get_prop(re, 1);
vm.lookup_str(val).unwrap_or_default()
};
let flags = {
let val = get_prop(re, 2);
vm.lookup_str(val).unwrap_or_default()
};
let source = vm.lookup_str(re.get_regexp_source()).unwrap_or_default();
let flags = vm.lookup_str(re.get_regexp_flags()).unwrap_or_default();
let result = format!("/{}/{}", source, flags);
NativeResult::Ok(vm.new_string_owned(result))
}
@@ -732,14 +725,10 @@ const HEX_DIGITS: [u16; 16] = [
/// # 边界与前提
/// - `re` 须为持有编译正则的对象(RegExp 实例或 matchAll 载体);flags 缺失
/// 时各 flag 一律 false。
/// - 经自身+原型链解析 flags 键:flags 是实例数据属性,原型链回退仅覆盖
/// 用户删改自身属性后的读法。
pub(crate) fn regexp_has_flag<H: VmHost>(vm: &H, re: &JsObject, unit: char) -> bool {
let flags_si = vm.kernel_core().perm_interner().intern("flags").0;
match vm.resolve_property(re, flags_si) {
Some(val) => vm.lookup_str(val).unwrap_or_default().contains(unit),
None => false,
}
/// - 直接读实例 regexp_flags 字段(避免通过 ordinary_get 触发原型访问器,
/// 防止在 replace 等 builtin 调用期间触发 GC 导致悬垂)。
pub(crate) fn regexp_has_flag<H: VmHost>(vm: &mut H, re: &JsObject, unit: char) -> bool {
vm.lookup_str(re.get_regexp_flags()).unwrap_or_default().contains(unit)
}
/// 8 个单 flag 只读访问器的公共核:this 为 RegExp.prototype 本身返回
@@ -803,6 +792,44 @@ pub fn regexp_get_unicode_sets<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
regexp_flag_of(vm, args, 'v')
}
/// `RegExp.prototype.flags` getter:返回实例 flags 串。
pub fn regexp_get_flags<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(args[0]);
// proto 恒等判定:规范对 prototype 本身返回 undefined。
if this_val.is_object() {
let this_ptr = this_val.as_js_object_ptr();
let proto_ptr = vm.session().builtin_world().regexp_proto.as_ptr() as *mut JsObject;
if !this_ptr.is_null() && this_ptr == proto_ptr {
return NativeResult::Ok(JsValue::undefined());
}
}
let re_ptr = match get_regexp_ptr(vm, args) {
Ok(ptr) => ptr,
Err(err) => return NativeResult::Err(err),
};
let re = unsafe { &*re_ptr };
NativeResult::Ok(re.get_regexp_flags())
}
/// `RegExp.prototype.source` getter:返回实例 source 串。
pub fn regexp_get_source<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(args[0]);
// proto 恒等判定:规范对 prototype 本身返回 undefined。
if this_val.is_object() {
let this_ptr = this_val.as_js_object_ptr();
let proto_ptr = vm.session().builtin_world().regexp_proto.as_ptr() as *mut JsObject;
if !this_ptr.is_null() && this_ptr == proto_ptr {
return NativeResult::Ok(JsValue::undefined());
}
}
let re_ptr = match get_regexp_ptr(vm, args) {
Ok(ptr) => ptr,
Err(err) => return NativeResult::Err(err),
};
let re = unsafe { &*re_ptr };
NativeResult::Ok(re.get_regexp_source())
}
/// 取匹配文本参数,按 ToString 语义完整转换(对象经 toString/valueOf);
/// 结果按载荷形态 owned:良形文本走 Str 臂,含孤立 surrogate 等走 Units 臂。
/// 对象 ToString 抛出的原生异常原样传播。