fix: 删除 super 属性按规范报错并补 setPrototypeOf 内置

This commit is contained in:
2026-09-18 01:06:48 +00:00
parent 75eb0294c4
commit ac6a48412b
7 changed files with 132 additions and 0 deletions
+36
View File
@@ -1250,6 +1250,42 @@ pub fn object_get_prototype_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
NativeResult::Ok(unsafe { (*obj_ptr).proto() })
}
/// `Object.setPrototypeOf(obj, proto)`:设置对象的 prototype,返回原值。
///
/// # 边界与前提
/// - `obj` 为 null/undefined 抛 TypeError(RequireObjectCoercible);原始值原样返回,
/// 不装箱。
/// - `proto` 既非对象也非 null 时抛 TypeError。
/// - 目标不可设置(原型环)时抛 TypeError;普通对象恒可设置,与 Reflect 版
/// 按布尔返回的语义不同。
///
/// # 副作用
/// - 目标为对象时改写其 proto 槽并递增 generation。
pub fn object_set_prototype_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target_val = args.get(1).map(|reg| vm.reg(*reg)).unwrap_or_else(JsValue::undefined);
if target_val.is_null() || target_val.is_undefined() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Object.setPrototypeOf called on null or undefined",
));
}
let proto = args.get(2).map(|reg| vm.reg(*reg)).unwrap_or_else(JsValue::undefined);
if !proto.is_object() && !proto.is_null() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Object.setPrototypeOf: prototype must be an object or null",
));
}
if !target_val.is_object() {
return NativeResult::Ok(target_val);
}
let target_ptr = target_val.as_js_object_ptr();
if unsafe { &mut *target_ptr }.set_proto(proto).is_err() {
return NativeResult::Err(crate::error::create_type_error(vm, "Object.setPrototypeOf: cannot set prototype"));
}
NativeResult::Ok(target_val)
}
/// `Object.hasOwn(obj, key)`:对象是否有指定自身属性。
pub fn object_has_own<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 3 {
@@ -245,6 +245,14 @@ impl Emitter {
ctx.inst(Inst::load_const(Operand::Reg(reg), idx));
Ok(reg)
}
Expression::StaticMemberExpression(member) if matches!(&member.object, Expression::Super(_)) => {
// super 属性引用不可删除:在求值引用之前直接抛 ReferenceError。
self.emit_throw_error("ReferenceError", "Cannot delete a super reference", ctx)
}
Expression::ComputedMemberExpression(member) if matches!(&member.object, Expression::Super(_)) => {
// super 属性引用不可删除:键表达式与基均不得先行求值。
self.emit_throw_error("ReferenceError", "Cannot delete a super reference", ctx)
}
Expression::StaticMemberExpression(member) => {
let obj_reg = self.emit_expression(&member.object, ctx)?;
let prop_name = member.property.name.as_str();
+1
View File
@@ -39,6 +39,7 @@ impl BuiltinWorld {
("defineProperties", methods.define_properties, 2),
("fromEntries", methods.from_entries, 1),
("getPrototypeOf", methods.get_prototype_of, 1),
("setPrototypeOf", methods.set_prototype_of, 2),
("hasOwn", methods.has_own, 2),
("entries", methods.entries, 1),
("values", methods.values, 1),
@@ -19,6 +19,7 @@ pub struct ObjectMethods {
pub define_properties: *const (),
pub from_entries: *const (),
pub get_prototype_of: *const (),
pub set_prototype_of: *const (),
pub has_own: *const (),
pub entries: *const (),
pub values: *const (),
@@ -28,6 +28,7 @@ pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
define_properties: oxide_builtins::object::object_define_properties::<crate::vm::Vm> as *const (),
from_entries: oxide_builtins::object::object_from_entries::<crate::vm::Vm> as *const (),
get_prototype_of: oxide_builtins::object::object_get_prototype_of::<crate::vm::Vm> as *const (),
set_prototype_of: oxide_builtins::object::object_set_prototype_of::<crate::vm::Vm> as *const (),
has_own: oxide_builtins::object::object_has_own::<crate::vm::Vm> as *const (),
entries: oxide_builtins::object::object_entries::<crate::vm::Vm> as *const (),
values: oxide_builtins::object::object_values::<crate::vm::Vm> as *const (),
@@ -316,6 +316,20 @@ fn assign_to_frozen_target_throws() {
// -- Symbol 键错位回归(shape 槽位计数含 symbol 键) --
// Object.setPrototypeOf:对象目标设置原型并返回原对象;proto 非对象/null 抛 TypeError。
#[test]
fn object_set_prototype_of_sets_prototype_and_validates() {
let (_vm, result) =
eval("var o = {}; (Object.setPrototypeOf(o, null) === o) && (Object.getPrototypeOf(o) === null)").unwrap();
assert!(result.is_bool() && result.as_bool());
let err = match eval("Object.setPrototypeOf({}, 1)") {
Ok(_) => panic!("non-object prototype should fail"),
Err(err) => err,
};
assert!(err.contains("TypeError"), "unexpected error: {err}");
}
#[test]
fn entries_values_with_interleaved_symbol_key() {
let (_vm, result) = eval(
+71
View File
@@ -862,3 +862,74 @@ fn class_declaration_outer_binding_is_mutable() {
let result = eval(&mut vm, "class A { m(){ return 1; } } A = 1; A").unwrap();
assert_num(result, 1.0);
}
// ── delete super 属性引用 ──
// 派生构造器内 delete super.x:运行期抛 ReferenceError,不落入成员删除。
#[test]
fn delete_super_property_in_derived_constructor_throws() {
let mut vm = Vm::new();
let err =
eval(&mut vm, "class C extends Object { constructor() { super(); delete super.x; } } new C()").unwrap_err();
assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}");
}
// 实例方法/getter/setter 三种方法体内 delete super.x 均抛 ReferenceError。
#[test]
fn delete_super_property_in_method_getter_and_setter_throws() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C extends Object { \
method() { delete super.x; } \
get g() { delete super.x; } \
set s(v) { delete super.x; } \
} \
var names = []; var c = new C(); \
try { c.method(); } catch (e) { names.push(e.name); } \
try { c.g; } catch (e) { names.push(e.name); } \
try { c.s = 1; } catch (e) { names.push(e.name); } \
names.join(',')",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "ReferenceError,ReferenceError,ReferenceError");
}
// 静态方法 + null 原型链基:基限制不得先于删除判定抛出 TypeError。
#[test]
fn delete_super_property_in_static_method_with_null_base_throws() {
let mut vm = Vm::new();
let err = eval(
&mut vm,
"class C { static m() { delete super.x; } } Object.setPrototypeOf(C, null); C.m()",
)
.unwrap_err();
assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}");
}
// 计算键 delete super[key]:键表达式不得求值(toString 不执行即抛 ReferenceError)。
#[test]
fn delete_super_property_computed_key_not_evaluated_throws() {
let mut vm = Vm::new();
let err = eval(
&mut vm,
"var key = { toString() { throw new Error('ToPropertyKey performed'); } }; \
var obj = { m() { delete super[key]; } }; obj.m()",
)
.unwrap_err();
assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}");
}
// this 未初始化时 delete super[键]:键表达式不得求值,基构造器也不得执行。
#[test]
fn delete_super_property_uninitialized_this_key_not_evaluated_throws() {
let mut vm = Vm::new();
let err = eval(
&mut vm,
"class Base { constructor() { throw new Error('base constructor called'); } } \
class Derived extends Base { constructor() { delete super[(super(), 0)]; } } new Derived()",
)
.unwrap_err();
assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}");
assert!(!err.contains("base constructor called"), "base constructor must not run: {err}");
}