feat: 实现 Reflect 全局方法
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::bindings::{apply_binding_table, bind_global_value};
|
||||||
|
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||||
|
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||||
|
use oxide_types::object::JsObject;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
pub fn bind_reflect(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||||
|
let mut reflect = Box::new(JsObject::new_empty(
|
||||||
|
EMPTY_SHAPE_ID,
|
||||||
|
JsValue::from_js_object(session.builtin_world().object_proto.as_ptr() as *mut JsObject),
|
||||||
|
));
|
||||||
|
|
||||||
|
apply_binding_table(
|
||||||
|
session.builtin_world(),
|
||||||
|
&mut reflect,
|
||||||
|
core,
|
||||||
|
&[
|
||||||
|
("apply", crate::builtins::reflect::reflect_apply as *const (), 3),
|
||||||
|
("construct", crate::builtins::reflect::reflect_construct as *const (), 2),
|
||||||
|
("defineProperty", crate::builtins::reflect::reflect_define_property as *const (), 3),
|
||||||
|
("deleteProperty", crate::builtins::reflect::reflect_delete_property as *const (), 2),
|
||||||
|
("get", crate::builtins::reflect::reflect_get as *const (), 2),
|
||||||
|
(
|
||||||
|
"getOwnPropertyDescriptor",
|
||||||
|
crate::builtins::reflect::reflect_get_own_property_descriptor as *const (),
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
("getPrototypeOf", crate::builtins::reflect::reflect_get_prototype_of as *const (), 1),
|
||||||
|
("has", crate::builtins::reflect::reflect_has as *const (), 2),
|
||||||
|
("isExtensible", crate::builtins::reflect::reflect_is_extensible as *const (), 1),
|
||||||
|
("ownKeys", crate::builtins::reflect::reflect_own_keys as *const (), 1),
|
||||||
|
("preventExtensions", crate::builtins::reflect::reflect_prevent_extensions as *const (), 1),
|
||||||
|
("set", crate::builtins::reflect::reflect_set as *const (), 3),
|
||||||
|
("setPrototypeOf", crate::builtins::reflect::reflect_set_prototype_of as *const (), 2),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
bind_global_value(core, global, "Reflect", JsValue::from_js_object(Box::into_raw(reflect)));
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ pub mod bind_map;
|
|||||||
pub mod bind_math;
|
pub mod bind_math;
|
||||||
pub mod bind_number;
|
pub mod bind_number;
|
||||||
pub mod bind_object;
|
pub mod bind_object;
|
||||||
|
pub mod bind_reflect;
|
||||||
pub mod bind_regexp;
|
pub mod bind_regexp;
|
||||||
pub mod bind_set;
|
pub mod bind_set;
|
||||||
pub mod bind_string;
|
pub mod bind_string;
|
||||||
@@ -99,6 +100,7 @@ pub fn init_kernel_builtins(core: &Arc<KernelCore>, session: &mut KernelSession)
|
|||||||
bind_regexp::bind_regexp(core, session, global);
|
bind_regexp::bind_regexp(core, session, global);
|
||||||
bind_symbol::bind_symbol(core, session, global);
|
bind_symbol::bind_symbol(core, session, global);
|
||||||
bind_iterator::bind_iterator(core, session, global);
|
bind_iterator::bind_iterator(core, session, global);
|
||||||
|
bind_reflect::bind_reflect(core, session, global);
|
||||||
bind_global::bind_global(core, session, global);
|
bind_global::bind_global(core, session, global);
|
||||||
bind_stubs::bind_stubs(core, session, global);
|
bind_stubs::bind_stubs(core, session, global);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub mod map;
|
|||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod number;
|
pub mod number;
|
||||||
pub mod object;
|
pub mod object;
|
||||||
|
pub mod reflect;
|
||||||
pub mod regexp;
|
pub mod regexp;
|
||||||
pub mod set;
|
pub mod set;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
|||||||
@@ -0,0 +1,273 @@
|
|||||||
|
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||||
|
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
use crate::builtins::object::walk_own_keys;
|
||||||
|
use crate::coercion;
|
||||||
|
use crate::native::NativeResult;
|
||||||
|
use crate::vm::Vm;
|
||||||
|
|
||||||
|
pub fn reflect_apply(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target = arg(vm, args, 1);
|
||||||
|
let this_arg = arg(vm, args, 2);
|
||||||
|
let arg_list = arg(vm, args, 3);
|
||||||
|
if !is_callable(target) {
|
||||||
|
return type_error(vm, "Reflect.apply target is not callable");
|
||||||
|
}
|
||||||
|
let Some(call_args) = array_like_elements(arg_list) else {
|
||||||
|
return type_error(vm, "Reflect.apply argumentsList must be an array-like object");
|
||||||
|
};
|
||||||
|
match vm.call_function_sync(target, this_arg, &call_args) {
|
||||||
|
Ok(value) => NativeResult::Ok(value),
|
||||||
|
Err(err) => type_error(vm, &err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_construct(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||||
|
type_error(vm, "Reflect.construct not yet supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.defineProperty target is not an object");
|
||||||
|
};
|
||||||
|
let desc_val = arg(vm, args, 3);
|
||||||
|
let Some(desc_ptr) = object_ptr(desc_val) else {
|
||||||
|
return type_error(vm, "Reflect.defineProperty descriptor is not an object");
|
||||||
|
};
|
||||||
|
|
||||||
|
let key_si = vm.property_key_si(arg(vm, args, 2));
|
||||||
|
let desc = unsafe { &*desc_ptr };
|
||||||
|
let value_si = vm.kernel_core().string_forge().intern("value").0;
|
||||||
|
let get_si = vm.kernel_core().string_forge().intern("get").0;
|
||||||
|
let set_si = vm.kernel_core().string_forge().intern("set").0;
|
||||||
|
let writable_si = vm.kernel_core().string_forge().intern("writable").0;
|
||||||
|
let enumerable_si = vm.kernel_core().string_forge().intern("enumerable").0;
|
||||||
|
let configurable_si = vm.kernel_core().string_forge().intern("configurable").0;
|
||||||
|
|
||||||
|
let value_field = own_field(vm, desc, value_si);
|
||||||
|
let get_field = own_field(vm, desc, get_si);
|
||||||
|
let set_field = own_field(vm, desc, set_si);
|
||||||
|
let writable_field = own_field(vm, desc, writable_si);
|
||||||
|
let enumerable = own_field(vm, desc, enumerable_si)
|
||||||
|
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let configurable = own_field(vm, desc, configurable_si)
|
||||||
|
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let has_data = value_field.is_some() || writable_field.is_some();
|
||||||
|
let has_accessor = get_field.is_some() || set_field.is_some();
|
||||||
|
if has_data && has_accessor {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = unsafe { &mut *target_ptr };
|
||||||
|
let result = if has_accessor {
|
||||||
|
let get = get_field.unwrap_or(JsValue::undefined());
|
||||||
|
let set = set_field.unwrap_or(JsValue::undefined());
|
||||||
|
if (!get.is_undefined() && !is_callable(get)) || (!set.is_undefined() && !is_callable(set)) {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
vm.define_accessor_property(target, key_si, get, set, PropAttributes::new(false, enumerable, configurable))
|
||||||
|
} else {
|
||||||
|
let value = value_field.unwrap_or(JsValue::undefined());
|
||||||
|
let writable = writable_field
|
||||||
|
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
|
||||||
|
.unwrap_or(false);
|
||||||
|
vm.define_data_property(target, key_si, value, PropAttributes::new(writable, enumerable, configurable))
|
||||||
|
};
|
||||||
|
NativeResult::Ok(JsValue::bool(result.is_ok()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_delete_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.deleteProperty target is not an object");
|
||||||
|
};
|
||||||
|
let key_si = vm.property_key_si(arg(vm, args, 2));
|
||||||
|
let target = unsafe { &mut *target_ptr };
|
||||||
|
NativeResult::Ok(JsValue::bool(delete_own_property(vm, target, key_si)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_get(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.get target is not an object");
|
||||||
|
};
|
||||||
|
let key_si = vm.property_key_si(arg(vm, args, 2));
|
||||||
|
let receiver = if args.len() > 3 { vm.reg(args[3]) } else { target_val };
|
||||||
|
match vm.ordinary_get(unsafe { &*target_ptr }, key_si, receiver) {
|
||||||
|
Ok(value) => NativeResult::Ok(value),
|
||||||
|
Err(err) => type_error(vm, &err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(_) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.getOwnPropertyDescriptor target is not an object");
|
||||||
|
};
|
||||||
|
crate::builtins::object::object_get_own_property_descriptor(vm, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_get_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.getPrototypeOf target is not an object");
|
||||||
|
};
|
||||||
|
NativeResult::Ok(unsafe { &*target_ptr }.proto())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_has(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.has target is not an object");
|
||||||
|
};
|
||||||
|
let key_si = vm.property_key_si(arg(vm, args, 2));
|
||||||
|
NativeResult::Ok(JsValue::bool(vm.resolve_property(unsafe { &*target_ptr }, key_si).is_some()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_is_extensible(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.isExtensible target is not an object");
|
||||||
|
};
|
||||||
|
NativeResult::Ok(JsValue::bool(unsafe { &*target_ptr }.is_extensible()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_own_keys(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.ownKeys target is not an object");
|
||||||
|
};
|
||||||
|
let target = unsafe { &*target_ptr };
|
||||||
|
let key_names: Vec<String> = walk_own_keys(vm, target)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(si, _)| vm.kernel_core().string_forge().lookup(si).unwrap_or_default())
|
||||||
|
.collect();
|
||||||
|
NativeResult::Ok(make_string_array(vm, &key_names))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_prevent_extensions(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.preventExtensions target is not an object");
|
||||||
|
};
|
||||||
|
unsafe { &mut *target_ptr }.set_extensible(false);
|
||||||
|
NativeResult::Ok(JsValue::bool(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_set(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.set target is not an object");
|
||||||
|
};
|
||||||
|
let key_si = vm.property_key_si(arg(vm, args, 2));
|
||||||
|
let value = arg(vm, args, 3);
|
||||||
|
let receiver = if args.len() > 4 { vm.reg(args[4]) } else { target_val };
|
||||||
|
let target = unsafe { &mut *target_ptr };
|
||||||
|
if !target.is_extensible() && vm.get_own_property_slot(target, key_si).is_none() {
|
||||||
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
|
}
|
||||||
|
NativeResult::Ok(JsValue::bool(vm.ordinary_set(target, key_si, value, receiver).is_ok()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_set_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let target_val = arg(vm, args, 1);
|
||||||
|
let Some(target_ptr) = object_ptr(target_val) else {
|
||||||
|
return type_error(vm, "Reflect.setPrototypeOf target is not an object");
|
||||||
|
};
|
||||||
|
let proto = arg(vm, args, 2);
|
||||||
|
if !proto.is_object() && !proto.is_null() {
|
||||||
|
return type_error(vm, "Reflect.setPrototypeOf prototype must be an object or null");
|
||||||
|
}
|
||||||
|
NativeResult::Ok(JsValue::bool(unsafe { &mut *target_ptr }.set_proto(proto).is_ok()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg(vm: &Vm, args: &[u8], idx: usize) -> JsValue {
|
||||||
|
args.get(idx).map(|reg| vm.reg(*reg)).unwrap_or_else(JsValue::undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn object_ptr(value: JsValue) -> Option<*mut JsObject> {
|
||||||
|
if !value.is_object() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ptr = value.as_js_object_ptr();
|
||||||
|
(!ptr.is_null()).then_some(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_callable(value: JsValue) -> bool {
|
||||||
|
object_ptr(value).is_some_and(|ptr| unsafe { &*ptr }.is_function())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_error(vm: &mut Vm, message: &str) -> NativeResult {
|
||||||
|
NativeResult::Err(crate::builtins::error::create_type_error(vm, message))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_like_elements(value: JsValue) -> Option<Vec<JsValue>> {
|
||||||
|
let ptr = object_ptr(value)?;
|
||||||
|
let obj = unsafe { &*ptr };
|
||||||
|
Some((0..obj.prop_count() as usize).map(|idx| obj.get_prop_at(idx)).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn own_field(vm: &Vm, obj: &JsObject, prop_si: u32) -> Option<JsValue> {
|
||||||
|
vm.kernel_core()
|
||||||
|
.shape_forge()
|
||||||
|
.lookup_position(obj.shape_id(), prop_si)
|
||||||
|
.and_then(|pos| (obj.prop_vec_len() > pos as usize).then(|| obj.get_prop_at(pos)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
|
||||||
|
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||||
|
let arr = vm.epoch().alloc(JsObject::new_array(
|
||||||
|
EMPTY_SHAPE_ID,
|
||||||
|
JsValue::from_js_object(proto),
|
||||||
|
parts.len(),
|
||||||
|
vm.epoch().bump(),
|
||||||
|
));
|
||||||
|
for (idx, part) in parts.iter().enumerate() {
|
||||||
|
let value = vm.intern(part);
|
||||||
|
unsafe { &mut *arr }.set_prop_at(idx, value);
|
||||||
|
}
|
||||||
|
unsafe { &mut *arr }.set_prop_count(parts.len());
|
||||||
|
JsValue::from_js_object(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_own_property(vm: &mut Vm, obj: &mut JsObject, key_si: u32) -> bool {
|
||||||
|
let keys = walk_own_keys(vm, obj);
|
||||||
|
let Some((_, delete_pos)) = keys.iter().find(|(si, _)| *si == key_si).copied() else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if obj
|
||||||
|
.prop_meta_at(delete_pos)
|
||||||
|
.map(|meta| !meta.attributes.configurable())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let retained: Vec<(u32, JsValue, Option<PropMetaEntry>)> = keys
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, pos)| *pos != delete_pos)
|
||||||
|
.map(|(si, pos)| (si, obj.get_prop_at(pos), obj.prop_meta_at(pos)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
obj.set_shape_id(EMPTY_SHAPE_ID);
|
||||||
|
obj.set_prop_count(0usize);
|
||||||
|
for (si, value, meta) in retained {
|
||||||
|
let shape = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si);
|
||||||
|
obj.set_shape_id(shape);
|
||||||
|
let pos = obj.push_prop(value);
|
||||||
|
if let Some(meta) = meta {
|
||||||
|
if meta.is_accessor {
|
||||||
|
obj.set_accessor_meta(pos, meta.get, meta.set, meta.attributes);
|
||||||
|
} else {
|
||||||
|
obj.set_data_meta(pos, meta.attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.bump_generation();
|
||||||
|
true
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
use oxide_compiler::compiler::Compiler;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
use oxide_vm::vm::Vm;
|
||||||
|
|
||||||
|
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||||
|
let allocator = oxide_parser::Allocator::default();
|
||||||
|
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||||
|
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||||
|
vm.run(&module)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||||
|
vm.kernel_core()
|
||||||
|
.string_forge()
|
||||||
|
.lookup(val.as_string_index())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflect_global_and_methods_exist() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"typeof Reflect === 'object' && typeof Reflect.get === 'function' && typeof Reflect.construct === 'function'",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflect_get_set_has_delete_property() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var obj = {a: 1, b: 2}; \
|
||||||
|
var getOk = Reflect.get(obj, 'a') === 1; \
|
||||||
|
var setOk = Reflect.set(obj, 'c', 3) === true && obj.c === 3; \
|
||||||
|
var hasOk = Reflect.has(obj, 'a') === true && Reflect.has(obj, 'z') === false; \
|
||||||
|
var delOk = Reflect.deleteProperty(obj, 'a') === true && Reflect.has(obj, 'a') === false; \
|
||||||
|
getOk && setOk && hasOk && delOk",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflect_own_keys_returns_own_property_names() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var obj = {a: 1, b: 2}; Reflect.set(obj, 'c', 3); Reflect.ownKeys(obj).join(',')",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "a,b,c");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflect_get_own_property_descriptor_returns_descriptor() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var obj = {}; Reflect.defineProperty(obj, 'x', { value: 9, writable: true, enumerable: true, configurable: true }); \
|
||||||
|
var d = Reflect.getOwnPropertyDescriptor(obj, 'x'); d.value === 9 && d.writable === true",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflect_prototype_and_extensible_methods() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var proto = {p: 1}; var obj = {}; \
|
||||||
|
var setProto = Reflect.setPrototypeOf(obj, proto); \
|
||||||
|
var getProto = Reflect.getPrototypeOf(obj) === proto; \
|
||||||
|
var ext1 = Reflect.isExtensible(obj); \
|
||||||
|
var prevent = Reflect.preventExtensions(obj); \
|
||||||
|
var ext2 = Reflect.isExtensible(obj); \
|
||||||
|
setProto && getProto && ext1 === true && prevent === true && ext2 === false",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflect_apply_calls_function() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"function add(a, b) { return this.base + a + b; } var receiver = {base: 10}; var args = [1, 2]; Reflect.apply(add, receiver, args)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result.as_double(), 13.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflect_construct_throws_clear_type_error() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"try { Reflect.construct(function(){}, []) } catch (e) { e instanceof TypeError }",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflect_non_object_target_throws_type_error() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(&mut vm, "try { Reflect.get(1, 'x') } catch (e) { e instanceof TypeError }").unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user