mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
feat: JsObject 新增 frozen/sealed 标志位,修复 freeze/seal/preventExtensions/isFrozen/isSealed/isExtensible 六个完整性方法
This commit is contained in:
@@ -339,62 +339,132 @@ macro_rules! native_try {
|
|||||||
|
|
||||||
pub fn object_freeze<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn object_freeze<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
return NativeResult::Ok(JsValue::undefined());
|
return NativeResult::Err(crate::error::create_type_error(vm, "Object.freeze called on non-object"));
|
||||||
}
|
}
|
||||||
let val = vm.reg(args[1]);
|
let val = vm.reg(args[1]);
|
||||||
if !val.is_object() || val.as_js_object_ptr().is_null() {
|
if !val.is_object() || val.as_js_object_ptr().is_null() {
|
||||||
return NativeResult::Ok(val);
|
return NativeResult::Ok(val);
|
||||||
}
|
}
|
||||||
|
let obj_ptr = val.as_js_object_ptr();
|
||||||
|
unsafe {
|
||||||
|
(*obj_ptr).set_frozen(true);
|
||||||
|
(*obj_ptr).set_extensible(false);
|
||||||
|
}
|
||||||
NativeResult::Ok(val)
|
NativeResult::Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn object_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
return NativeResult::Ok(JsValue::undefined());
|
return NativeResult::Err(crate::error::create_type_error(vm, "Object.seal called on non-object"));
|
||||||
}
|
}
|
||||||
let val = vm.reg(args[1]);
|
let val = vm.reg(args[1]);
|
||||||
if !val.is_object() || val.as_js_object_ptr().is_null() {
|
if !val.is_object() || val.as_js_object_ptr().is_null() {
|
||||||
return NativeResult::Ok(val);
|
return NativeResult::Ok(val);
|
||||||
}
|
}
|
||||||
|
let obj_ptr = val.as_js_object_ptr();
|
||||||
|
unsafe {
|
||||||
|
(*obj_ptr).set_sealed(true);
|
||||||
|
(*obj_ptr).set_extensible(false);
|
||||||
|
}
|
||||||
NativeResult::Ok(val)
|
NativeResult::Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_prevent_extensions<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn object_prevent_extensions<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
return NativeResult::Ok(JsValue::undefined());
|
return NativeResult::Err(crate::error::create_type_error(vm, "Object.preventExtensions called on non-object"));
|
||||||
}
|
}
|
||||||
let val = vm.reg(args[1]);
|
let val = vm.reg(args[1]);
|
||||||
if !val.is_object() || val.as_js_object_ptr().is_null() {
|
if !val.is_object() || val.as_js_object_ptr().is_null() {
|
||||||
return NativeResult::Ok(val);
|
return NativeResult::Ok(val);
|
||||||
}
|
}
|
||||||
|
let obj_ptr = val.as_js_object_ptr();
|
||||||
|
unsafe {
|
||||||
|
(*obj_ptr).set_extensible(false);
|
||||||
|
}
|
||||||
NativeResult::Ok(val)
|
NativeResult::Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_is_frozen<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn object_is_frozen<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
return NativeResult::Ok(JsValue::bool(true));
|
return NativeResult::Err(crate::error::create_type_error(vm, "Object.isFrozen called on non-object"));
|
||||||
}
|
}
|
||||||
let val = vm.reg(args[1]);
|
let val = vm.reg(args[1]);
|
||||||
if !val.is_object() || val.as_js_object_ptr().is_null() {
|
if !val.is_object() {
|
||||||
return NativeResult::Ok(JsValue::bool(true));
|
return NativeResult::Ok(JsValue::bool(true));
|
||||||
}
|
}
|
||||||
NativeResult::Ok(JsValue::bool(false))
|
let obj_ptr = val.as_js_object_ptr();
|
||||||
|
if obj_ptr.is_null() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(true));
|
||||||
|
}
|
||||||
|
let obj = unsafe { &*obj_ptr };
|
||||||
|
if obj.is_frozen() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(true));
|
||||||
|
}
|
||||||
|
if obj.is_extensible() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
let keys = walk_own_keys(vm, obj);
|
||||||
|
for (_si, offset) in keys {
|
||||||
|
if let Some(meta) = obj.prop_meta_at(offset) {
|
||||||
|
if meta.attributes.configurable() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
if !meta.is_accessor && meta.attributes.writable() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeResult::Ok(JsValue::bool(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_is_sealed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn object_is_sealed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
return NativeResult::Ok(JsValue::bool(true));
|
return NativeResult::Err(crate::error::create_type_error(vm, "Object.isSealed called on non-object"));
|
||||||
}
|
}
|
||||||
let val = vm.reg(args[1]);
|
let val = vm.reg(args[1]);
|
||||||
if !val.is_object() || val.as_js_object_ptr().is_null() {
|
if !val.is_object() {
|
||||||
return NativeResult::Ok(JsValue::bool(true));
|
return NativeResult::Ok(JsValue::bool(true));
|
||||||
}
|
}
|
||||||
NativeResult::Ok(JsValue::bool(false))
|
let obj_ptr = val.as_js_object_ptr();
|
||||||
|
if obj_ptr.is_null() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(true));
|
||||||
|
}
|
||||||
|
let obj = unsafe { &*obj_ptr };
|
||||||
|
if obj.is_sealed() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(true));
|
||||||
|
}
|
||||||
|
if obj.is_extensible() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
let keys = walk_own_keys(vm, obj);
|
||||||
|
for (_si, offset) in keys {
|
||||||
|
if let Some(meta) = obj.prop_meta_at(offset) {
|
||||||
|
if meta.attributes.configurable() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeResult::Ok(JsValue::bool(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_is_extensible<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn object_is_extensible<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
native_try!(require_obj_arg(vm, args, "isExtensible"));
|
if args.len() < 2 {
|
||||||
NativeResult::Ok(JsValue::bool(true))
|
return NativeResult::Err(crate::error::create_type_error(vm, "Object.isExtensible called on non-object"));
|
||||||
|
}
|
||||||
|
let val = vm.reg(args[1]);
|
||||||
|
if !val.is_object() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
let obj_ptr = val.as_js_object_ptr();
|
||||||
|
if obj_ptr.is_null() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
let obj = unsafe { &*obj_ptr };
|
||||||
|
NativeResult::Ok(JsValue::bool(obj.is_extensible()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_get_own_property_names<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn object_get_own_property_names<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
|||||||
@@ -99,3 +99,47 @@ fn test_assign_merges_two_sources() {
|
|||||||
let (_vm, result) = eval("var t = {a:1}; Object.assign(t, {b:2}); t.b").unwrap();
|
let (_vm, result) = eval("var t = {a:1}; Object.assign(t, {b:2}); t.b").unwrap();
|
||||||
assert!(result.is_int() || result.is_double(), "Object.assign should copy from source");
|
assert!(result.is_int() || result.is_double(), "Object.assign should copy from source");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integrity_freeze_returns_object() {
|
||||||
|
let (_vm, result) = eval("var o = {x:1}; Object.freeze(o) === o").unwrap();
|
||||||
|
assert!(result.is_bool() && result.as_bool(), "freeze should return the object");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integrity_freeze_sets_non_extensible() {
|
||||||
|
let (_vm, result) = eval("var o = {x:1}; Object.freeze(o); Object.isExtensible(o)").unwrap();
|
||||||
|
assert!(result.is_bool() && !result.as_bool(), "freeze should set non-extensible");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integrity_seal_non_extensible() {
|
||||||
|
let (_vm, result) = eval("var o = {x:1}; Object.seal(o); Object.isExtensible(o)").unwrap();
|
||||||
|
assert!(result.is_bool() && !result.as_bool(), "seal should set non-extensible");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integrity_prevent_extensions() {
|
||||||
|
let (_vm, result) = eval("var o = {x:1}; Object.preventExtensions(o); Object.isExtensible(o)").unwrap();
|
||||||
|
assert!(result.is_bool() && !result.as_bool(), "preventExtensions should set non-extensible");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_frozen_non_object_returns_true() {
|
||||||
|
let (_vm, result) = eval("Object.isFrozen(42)").unwrap();
|
||||||
|
assert!(result.is_bool() && result.as_bool(), "non-object should be frozen per ES spec");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_sealed_non_object_returns_true() {
|
||||||
|
let (_vm, result) = eval("Object.isSealed(42)").unwrap();
|
||||||
|
assert!(result.is_bool() && result.as_bool(), "non-object should be sealed per ES spec");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_extensible_non_object_returns_false() {
|
||||||
|
let (_vm, result) = eval("Object.isExtensible(42)").unwrap();
|
||||||
|
assert!(result.is_bool() && !result.as_bool(), "non-object should not be extensible per ES spec");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
use oxide_types::object::JsObject;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
fn new_obj() -> JsObject {
|
||||||
|
JsObject::new_empty(0, JsValue::null())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_frozen() {
|
||||||
|
let mut obj = new_obj();
|
||||||
|
assert!(!obj.is_frozen());
|
||||||
|
obj.set_frozen(true);
|
||||||
|
assert!(obj.is_frozen());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_sealed() {
|
||||||
|
let mut obj = new_obj();
|
||||||
|
assert!(!obj.is_sealed());
|
||||||
|
obj.set_sealed(true);
|
||||||
|
assert!(obj.is_sealed());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_frozen_and_sealed_independent() {
|
||||||
|
let mut obj = new_obj();
|
||||||
|
obj.set_frozen(true);
|
||||||
|
assert!(obj.is_frozen());
|
||||||
|
assert!(!obj.is_sealed());
|
||||||
|
obj.set_sealed(true);
|
||||||
|
assert!(obj.is_frozen());
|
||||||
|
assert!(obj.is_sealed());
|
||||||
|
obj.set_frozen(false);
|
||||||
|
assert!(!obj.is_frozen());
|
||||||
|
assert!(obj.is_sealed());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_defaults_are_false() {
|
||||||
|
let obj = new_obj();
|
||||||
|
assert!(!obj.is_frozen());
|
||||||
|
assert!(!obj.is_sealed());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clear_frozen() {
|
||||||
|
let mut obj = new_obj();
|
||||||
|
obj.set_frozen(true);
|
||||||
|
obj.set_frozen(false);
|
||||||
|
assert!(!obj.is_frozen());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clear_sealed() {
|
||||||
|
let mut obj = new_obj();
|
||||||
|
obj.set_sealed(true);
|
||||||
|
obj.set_sealed(false);
|
||||||
|
assert!(!obj.is_sealed());
|
||||||
|
}
|
||||||
@@ -574,6 +574,30 @@ impl JsObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_frozen(&self) -> bool {
|
||||||
|
(self._pad) & 1 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_frozen(&mut self, frozen: bool) {
|
||||||
|
if frozen {
|
||||||
|
self._pad |= 1 << 0;
|
||||||
|
} else {
|
||||||
|
self._pad &= !(1 << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_sealed(&self) -> bool {
|
||||||
|
(self._pad >> 1) & 1 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sealed(&mut self, sealed: bool) {
|
||||||
|
if sealed {
|
||||||
|
self._pad |= 1 << 1;
|
||||||
|
} else {
|
||||||
|
self._pad &= !(1 << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_set(&self) -> bool {
|
pub fn is_set(&self) -> bool {
|
||||||
(self.header >> 24) & 1 != 0
|
(self.header >> 24) & 1 != 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user