mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 删除 super 属性按规范报错并补 setPrototypeOf 内置
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user