mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: Error 子类型构造器描述符与原型链对齐规范(prototype/name meta + [[Prototype]] 指向 Error)
This commit is contained in:
@@ -12,9 +12,10 @@ fn bind_error_subtype_constructor(
|
||||
) {
|
||||
let sf = core.perm_interner().as_ref();
|
||||
let sh = core.shape_forge().as_ref();
|
||||
let function_proto_ptr = session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto_ptr)));
|
||||
// 子类型构造器 [[Prototype]] 指向 Error 构造器(规范:NativeError 构造器继承 Error
|
||||
// 构造器,而非直接继承 Function.prototype;instanceof 走 @@hasInstance 不受影响)。
|
||||
let error_ctor_ptr = session.builtin_world().error_constructor.as_ptr() as *mut JsObject;
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(error_ctor_ptr)));
|
||||
ctor.set_function(true);
|
||||
// Error 子类型须标记可构造:new TypeError(...) 走 NEW_EXPRESSION 校验。
|
||||
ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR;
|
||||
@@ -34,6 +35,10 @@ fn bind_error_subtype_constructor(
|
||||
ctor.ensure_hash_props().push(JsValue::from_js_object(proto_ptr));
|
||||
ctor.ensure_hash_props().push(JsValue::perm_string(sf.string_ptr(name_si)));
|
||||
ctor.ensure_hash_props().push(JsValue::int(arg_count as i32));
|
||||
// 构造器 prototype 槽按规范 { writable:false, enumerable:false, configurable:false }。
|
||||
ctor.set_data_meta(0u32, PropAttributes::new(false, false, false));
|
||||
// 构造器 name 槽按规范 { writable:false, enumerable:false, configurable:true }。
|
||||
ctor.set_data_meta(1u32, PropAttributes::new(false, false, true));
|
||||
// 构造器 length 按规范为不可写不可枚举(Function.length 属性描述符约定)。
|
||||
ctor.set_data_meta(2u32, PropAttributes::new(false, false, true));
|
||||
|
||||
|
||||
@@ -588,8 +588,10 @@ fn bind_error_subtype_global(
|
||||
}
|
||||
}
|
||||
|
||||
let function_proto_ptr = session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto_ptr)));
|
||||
// fallback 自建路径与 bind_error 的 Box 自建路径保持同一形态:[[Prototype]] 指向
|
||||
// Error 构造器(NativeError 构造器继承 Error 构造器),prototype/name 描述符按规范。
|
||||
let error_ctor_ptr = session.builtin_world().error_constructor.as_ptr() as *mut JsObject;
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(error_ctor_ptr)));
|
||||
ctor.set_function(true);
|
||||
configure_native_constructor(&mut ctor, ctor_fn, arg_count);
|
||||
|
||||
@@ -604,6 +606,9 @@ fn bind_error_subtype_global(
|
||||
ctor.ensure_hash_props()
|
||||
.push(JsValue::from_js_object(proto.as_ptr() as *mut JsObject));
|
||||
ctor.ensure_hash_props().push(JsValue::perm_string(sf.string_ptr(name_si)));
|
||||
// prototype/name 描述符与 Box 自建路径一致:{f,f,f} / {f,f,t}。
|
||||
ctor.set_data_meta(0u32, PropAttributes::new(false, false, false));
|
||||
ctor.set_data_meta(1u32, PropAttributes::new(false, false, true));
|
||||
|
||||
let ctor_ptr = Box::into_raw(ctor);
|
||||
bind_existing_global(core, global, name, JsValue::from_js_object(ctor_ptr));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use oxide_builtins::error;
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::mem::P;
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
@@ -575,3 +576,82 @@ fn create_suppressed_error_direct_call() {
|
||||
let meta = obj.prop_meta_at(err_pos).unwrap();
|
||||
assert!(!meta.attributes.enumerable(), "error 槽应非枚举");
|
||||
}
|
||||
|
||||
// ── 子类型构造器描述符与原型链测试 ──
|
||||
|
||||
#[test]
|
||||
fn error_subtype_constructor_descriptors() {
|
||||
let mut vm = make_vm();
|
||||
// 构造器 prototype 槽描述符 {f,f,f}(子类型与 SuppressedError 同规)。
|
||||
let r = eval_in(
|
||||
&mut vm,
|
||||
"var d = Object.getOwnPropertyDescriptor(TypeError, 'prototype'); [d.writable, d.enumerable, d.configurable].join(',')",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(vm.lookup_str(r), Some("false,false,false".to_string()));
|
||||
// name 槽描述符 {f,f,t}。
|
||||
let r = eval_in(
|
||||
&mut vm,
|
||||
"var d = Object.getOwnPropertyDescriptor(SuppressedError, 'name'); [d.writable, d.enumerable, d.configurable].join(',')",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(vm.lookup_str(r), Some("false,false,true".to_string()));
|
||||
// length 槽描述符 {f,f,t}。
|
||||
let r = eval_in(
|
||||
&mut vm,
|
||||
"var d = Object.getOwnPropertyDescriptor(TypeError, 'length'); [d.writable, d.enumerable, d.configurable].join(',')",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(vm.lookup_str(r), Some("false,false,true".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_subtype_constructor_proto_is_error_ctor() {
|
||||
let mut vm = make_vm();
|
||||
// NativeError 子类型构造器 [[Prototype]] === Error 构造器(instanceof 走 @@hasInstance 不受影响)。
|
||||
let r = eval_in(&mut vm, "Object.getPrototypeOf(TypeError) === Error").unwrap();
|
||||
assert_eq!(format!("{}", r), "true");
|
||||
let r = eval_in(&mut vm, "Object.getPrototypeOf(SuppressedError) === Error").unwrap();
|
||||
assert_eq!(format!("{}", r), "true");
|
||||
let r = eval_in(&mut vm, "new TypeError('x') instanceof TypeError").unwrap();
|
||||
assert_eq!(format!("{}", r), "true");
|
||||
}
|
||||
|
||||
// ── full_reset 重建路径测试 ──
|
||||
|
||||
#[test]
|
||||
fn suppressed_error_survives_error_family_reset() {
|
||||
let mut vm = make_vm();
|
||||
// error_family 脏:suppressed_error_proto 与构造器整体重建。
|
||||
let proto_ptr = P::as_ptr(&vm.session().builtin_world().suppressed_error_proto) as *mut JsObject;
|
||||
unsafe { (&mut *proto_ptr).bump_generation() };
|
||||
vm.full_reset();
|
||||
|
||||
// 重建后 length=3、构造器 [[Prototype]]=Error、原型链到 Error.prototype 均不回落。
|
||||
let r = eval_in(&mut vm, "SuppressedError.length").unwrap();
|
||||
assert_eq!(format!("{}", r), "3");
|
||||
let r = eval_in(&mut vm, "Object.getPrototypeOf(SuppressedError) === Error").unwrap();
|
||||
assert_eq!(format!("{}", r), "true");
|
||||
let r = eval_in(&mut vm, "Object.getPrototypeOf(SuppressedError.prototype) === Error.prototype").unwrap();
|
||||
assert_eq!(format!("{}", r), "true");
|
||||
let r = eval_in(&mut vm, "new SuppressedError('e','s','m').message").unwrap();
|
||||
assert_eq!(vm.lookup_str(r), Some("m".to_string()));
|
||||
assert!(!vm.session().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suppressed_error_length_kept_after_global_only_reset() {
|
||||
let mut vm = make_vm();
|
||||
// 仅 global 脏(error_family 干净):fallback 自建构造器分支须保留 length=3。
|
||||
let g_ptr = vm.session().global_object().as_ptr() as *mut JsObject;
|
||||
unsafe { (&mut *g_ptr).bump_generation() };
|
||||
vm.full_reset();
|
||||
|
||||
let r = eval_in(&mut vm, "SuppressedError.length").unwrap();
|
||||
assert_eq!(format!("{}", r), "3");
|
||||
let r = eval_in(&mut vm, "Object.getPrototypeOf(SuppressedError) === Error").unwrap();
|
||||
assert_eq!(format!("{}", r), "true");
|
||||
let r = eval_in(&mut vm, "new SuppressedError('e','s').error").unwrap();
|
||||
assert_eq!(vm.lookup_str(r), Some("e".to_string()));
|
||||
assert!(!vm.session().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user