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 sf = core.perm_interner().as_ref();
|
||||||
let sh = core.shape_forge().as_ref();
|
let sh = core.shape_forge().as_ref();
|
||||||
let function_proto_ptr = session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
// 子类型构造器 [[Prototype]] 指向 Error 构造器(规范:NativeError 构造器继承 Error
|
||||||
|
// 构造器,而非直接继承 Function.prototype;instanceof 走 @@hasInstance 不受影响)。
|
||||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto_ptr)));
|
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);
|
ctor.set_function(true);
|
||||||
// Error 子类型须标记可构造:new TypeError(...) 走 NEW_EXPRESSION 校验。
|
// Error 子类型须标记可构造:new TypeError(...) 走 NEW_EXPRESSION 校验。
|
||||||
ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR;
|
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::from_js_object(proto_ptr));
|
||||||
ctor.ensure_hash_props().push(JsValue::perm_string(sf.string_ptr(name_si)));
|
ctor.ensure_hash_props().push(JsValue::perm_string(sf.string_ptr(name_si)));
|
||||||
ctor.ensure_hash_props().push(JsValue::int(arg_count as i32));
|
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 属性描述符约定)。
|
// 构造器 length 按规范为不可写不可枚举(Function.length 属性描述符约定)。
|
||||||
ctor.set_data_meta(2u32, PropAttributes::new(false, false, true));
|
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;
|
// fallback 自建路径与 bind_error 的 Box 自建路径保持同一形态:[[Prototype]] 指向
|
||||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto_ptr)));
|
// 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);
|
ctor.set_function(true);
|
||||||
configure_native_constructor(&mut ctor, ctor_fn, arg_count);
|
configure_native_constructor(&mut ctor, ctor_fn, arg_count);
|
||||||
|
|
||||||
@@ -604,6 +606,9 @@ fn bind_error_subtype_global(
|
|||||||
ctor.ensure_hash_props()
|
ctor.ensure_hash_props()
|
||||||
.push(JsValue::from_js_object(proto.as_ptr() as *mut JsObject));
|
.push(JsValue::from_js_object(proto.as_ptr() as *mut JsObject));
|
||||||
ctor.ensure_hash_props().push(JsValue::perm_string(sf.string_ptr(name_si)));
|
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);
|
let ctor_ptr = Box::into_raw(ctor);
|
||||||
bind_existing_global(core, global, name, JsValue::from_js_object(ctor_ptr));
|
bind_existing_global(core, global, name, JsValue::from_js_object(ctor_ptr));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use oxide_builtins::error;
|
use oxide_builtins::error;
|
||||||
use oxide_compiler::compiler::Compiler;
|
use oxide_compiler::compiler::Compiler;
|
||||||
use oxide_types::mem::P;
|
use oxide_types::mem::P;
|
||||||
|
use oxide_types::object::JsObject;
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
use oxide_vm::vm::Vm;
|
use oxide_vm::vm::Vm;
|
||||||
|
|
||||||
@@ -575,3 +576,82 @@ fn create_suppressed_error_direct_call() {
|
|||||||
let meta = obj.prop_meta_at(err_pos).unwrap();
|
let meta = obj.prop_meta_at(err_pos).unwrap();
|
||||||
assert!(!meta.attributes.enumerable(), "error 槽应非枚举");
|
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