Files
OxideJS-New/crates/oxide_vm/tests/builtins_reflect_tests.rs
T

473 lines
16 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::sync::Arc;
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(&Arc::new(module))
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.lookup_str(val).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());
}
// 不可扩展目标:新旧不同返回 false,新旧相同返回 true,且原值不被改写。
#[test]
fn reflect_set_prototype_of_non_extensible_returns_false() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var a = {}; Object.preventExtensions(a); \
var diff = Reflect.setPrototypeOf(a, null); \
var kept = Object.getPrototypeOf(a) === Object.prototype; \
var same = Reflect.setPrototypeOf(a, Object.prototype); \
diff === false && kept && same === true",
)
.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_int(), 13, "this.base 10 + 1 + 2 = 13");
}
#[test]
fn reflect_construct_returns_new_instance() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var o = Reflect.construct(function(){ this.x = 5; }, []); typeof o === 'object' && o.x === 5",
)
.unwrap();
assert!(result.as_bool());
}
#[test]
fn reflect_construct_non_callable_target_throws_type_error() {
let mut vm = Vm::new();
let result = eval(&mut vm, "try { Reflect.construct(123, []) } 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());
}
// 派生类(extends Array)经 Reflect.construct 产出真实例:isArray 与 length 就位。
#[test]
fn reflect_construct_derived_class_instance() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class B extends Array { constructor(x) { super(x); } } \
var inst = Reflect.construct(B, [2]); \
inst instanceof B && Array.isArray(inst) && inst.length === 2",
)
.unwrap();
assert!(result.as_bool());
}
// 派生类经 Reflect.construct 正常走 super 链构造,实例值判别。
#[test]
fn reflect_construct_derived_extends_base_class() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class Base { constructor(x) { this.x = x; } } \
class D extends Base { constructor(x) { super(x); } } \
var inst = Reflect.construct(D, [9]); \
inst instanceof D && inst.x === 9",
)
.unwrap();
assert!(result.as_bool());
}
// 无 newTarget 时构造帧内 new.target = target。
#[test]
fn reflect_construct_new_target_defaults_to_target() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var newTarget = null; function f() { newTarget = new.target; } \
Reflect.construct(f, []); newTarget === f",
)
.unwrap();
assert!(result.as_bool());
}
// newTarget 覆写臂:构造帧内 new.target = newTarget,实例 proto 取 newTarget.prototype。
#[test]
fn reflect_construct_new_target_override() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var newTarget = null; function f() { newTarget = new.target; } \
var NT = function() {}; \
Reflect.construct(f, [], NT); \
newTarget === NT && Object.getPrototypeOf(Reflect.construct(f, [], NT)) === NT.prototype",
)
.unwrap();
assert!(result.as_bool());
}
// 派生类 + newTarget 覆写全形:super 实参逐位、new.target = NT、this = 实例。
#[test]
fn reflect_construct_derived_with_new_target() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var expectedNewTarget = function() {}; \
var thisValue, instance, args, actualNewTarget; \
function Parent() { thisValue = this; args = arguments; actualNewTarget = new.target; } \
class Child extends Parent { constructor() { super(1, 2, 3); } } \
instance = Reflect.construct(Child, [4, 5, 6], expectedNewTarget); \
thisValue === instance && args.length === 3 && args[0] === 1 && args[1] === 2 && args[2] === 3 \
&& actualNewTarget === expectedNewTarget \
&& Object.getPrototypeOf(instance) === expectedNewTarget.prototype",
)
.unwrap();
assert!(result.as_bool());
}
// 基类 + newTarget 覆写:实例 proto 取 NT.prototype,new.target = NT。
#[test]
fn reflect_construct_base_class_with_new_target() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class CBase { constructor(x) { this.x = x; this.nt = new.target; } } \
var NT = function() {}; \
var inst = Reflect.construct(CBase, [5], NT); \
Object.getPrototypeOf(inst) === NT.prototype && inst.nt === NT && inst.x === 5",
)
.unwrap();
assert!(result.as_bool());
}
// 入参面:target 非构造器(Date.now / 箭头 / 基元)均 TypeError。
#[test]
fn reflect_construct_non_constructor_target_throws() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var t1 = false, t2 = false, t3 = false; \
try { Reflect.construct(Date.now, [], Array); } catch (e) { t1 = e instanceof TypeError; } \
try { Reflect.construct((a) => a, []); } catch (e) { t2 = e instanceof TypeError; } \
try { Reflect.construct(1, []); } catch (e) { t3 = e instanceof TypeError; } \
t1 && t2 && t3",
)
.unwrap();
assert!(result.as_bool());
}
// 入参面:newTarget 非构造器与 argumentsList 非对象均 TypeError。
#[test]
fn reflect_construct_invalid_new_target_or_arguments_list_throws() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var t1 = false, t2 = false; \
try { Reflect.construct(function(){}, [], 1); } catch (e) { t1 = e instanceof TypeError; } \
try { Reflect.construct(function(){}, 1); } catch (e) { t2 = e instanceof TypeError; } \
t1 && t2",
)
.unwrap();
assert!(result.as_bool());
}
// length 访问器抛出的异常原值传播(不降级、不吞没)。
#[test]
fn reflect_construct_length_getter_exception_propagates() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var o = {}; \
Object.defineProperty(o, 'length', { get: function() { throw new RangeError('len'); } }); \
try { Reflect.construct(function(a){ return a; }, o); false } catch (e) { e instanceof RangeError }",
)
.unwrap();
assert!(result.as_bool());
}
// array-like 对象按规范 Get 逐位取值(非位序直读),值与实参个数就位。
#[test]
fn reflect_construct_array_like_elements_read_by_get() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var captured; \
function C(a, b) { captured = [typeof a, arguments.length]; } \
Reflect.construct(C, { length: 2, 0: 'a', 1: 'b' }); \
captured[0] === 'string' && captured[1] === 2",
)
.unwrap();
assert!(result.as_bool());
}
// 守卫面:native 构造器(Array 填槽)、构造帧 arguments 完整、显式返回对象
// 优先、绑定元数据(isConstructor(Reflect.construct) === false)。
#[test]
fn reflect_construct_guard_face() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var arr = Reflect.construct(Array, [3]); \
function fn() { this.args = arguments; } \
var result = Reflect.construct(fn, [42, 'Mike', 'Leo']); \
var retObj = Reflect.construct(function() { return { z: 9 }; }, []); \
var meta = false; \
try { Reflect.construct(Reflect.construct, []); } catch (e) { meta = e instanceof TypeError; } \
arr instanceof Array && arr.length === 3 \
&& result.args.length === 3 && result.args[0] === 42 && result.args[1] === 'Mike' && result.args[2] === 'Leo' \
&& retObj.z === 9 && !(retObj instanceof Function) && meta",
)
.unwrap();
assert!(result.as_bool());
}
// 键转换异常原值传播:Reflect.has 的键 ToPropertyKey 抛 "X" → 捕获值 === "X"。
#[test]
fn reflect_has_propagates_key_conversion_original_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var key = { toString: function() { throw 'X'; } }; \
var caught; \
try { Reflect.has({}, key); caught = 'none'; } catch (e) { caught = e; } \
caught === 'X'",
)
.unwrap();
assert!(result.as_bool());
}
// 同族探针:Reflect.get 的键转换异常同式原值传播。
#[test]
fn reflect_get_propagates_key_conversion_original_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var key = { toString: function() { throw 'X'; } }; \
var caught; \
try { Reflect.get({}, key); caught = 'none'; } catch (e) { caught = e; } \
caught === 'X'",
)
.unwrap();
assert!(result.as_bool());
}
// 守卫序钉:defineProperty 键转换先于 descriptor 类型检——descriptor 缺省且
// 键抛 "X" 时捕获的必须是键异常原值,不是 descriptor TypeError。
#[test]
fn reflect_define_property_key_evaluates_before_descriptor_check() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var key = { toString: function() { throw 'X'; } }; \
var caught; \
try { Reflect.defineProperty({}, key, undefined); caught = 'none'; } catch (e) { caught = e; } \
caught === 'X'",
)
.unwrap();
assert!(result.as_bool());
}
// 227.1 回归守卫:隐式 super 面经 `new` 直接构造不回归。
#[test]
fn construct_direct_new_derived_array_guard() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class B extends Array { constructor(x) { super(x); } } \
var sub = new B(2); \
class Sub extends Array { constructor(x) { super(x); } } \
var s7 = new Sub(7); \
sub instanceof B && sub.length === 2 && s7.length === 7",
)
.unwrap();
assert!(result.as_bool());
}
// 数组 length 是虚拟自身属性:ownKeys 注入字符串组首(创建序第一枚字符串键)。
#[test]
fn reflect_own_keys_array_includes_virtual_length() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var k1 = Reflect.ownKeys([]); \
var k2 = Reflect.ownKeys([,,2]); \
var a = []; a.x = 1; var k3 = Reflect.ownKeys(a); \
k1.join(',') === 'length' && k2.join(',') === '2,length' && k3.join(',') === 'length,x'",
)
.unwrap();
assert!(result.as_bool());
}
// array index 上限 2^32-2:4294967294 落索引组(整数升序在前),
// 4294967295 落字符串组(创建序)。
#[test]
fn reflect_own_keys_large_index_boundary() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var o = { b: 1, a: 2, 4294967294: 3, 4294967295: 4 }; \
Reflect.ownKeys(o).join(',')",
)
.unwrap();
assert_eq!(to_str(&vm, result), "4294967294,b,a,4294967295");
}
// Reflect.get 的 own 与 inherited 访问器异常两臂均原值传播。
#[test]
fn reflect_get_propagates_accessor_exception_original_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var boom = { value: 1 }; \
var own = { get p() { throw boom; } }; \
var caughtOwn; \
try { Reflect.get(own, 'p'); caughtOwn = 'none'; } catch (e) { caughtOwn = e; } \
var inherited = { q: 1 }; \
Object.defineProperty(inherited, 'q', { get() { throw boom; }, configurable: true }); \
var sub = Object.create(inherited); \
var caughtInh; \
try { Reflect.get(sub, 'q'); caughtInh = 'none'; } catch (e) { caughtInh = e; } \
caughtOwn === boom && caughtInh === boom",
)
.unwrap();
assert!(result.as_bool());
}
// apply 的 argumentsList 按 CreateListFromArrayLike 读取:length 经 getter,
// 逐索引规范 Get;函数对象命名槽(prototype/length/name)不被误读。
#[test]
fn reflect_apply_arguments_list_array_like() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function fn() { return [arguments.length, arguments[0]]; } \
var list = Object.create(null); \
Object.defineProperty(list, 'length', { get: function() { return 1; } }); \
var r = Reflect.apply(fn, null, list); \
r[0] === 1 && r[1] === undefined",
)
.unwrap();
assert!(result.as_bool());
}
// apply 的 length getter 异常原值传播;非对象 argumentsList 抛 TypeError。
#[test]
fn reflect_apply_propagates_length_getter_original_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var boom = 'X'; \
var list = { get length() { throw boom; } }; \
var caught; \
try { Reflect.apply(function(){}, null, list); caught = 'none'; } catch (e) { caught = e; } \
var thrown; \
try { Reflect.apply(function(){}, null, 42); thrown = 'none'; } catch (e) { thrown = (e instanceof TypeError); } \
caught === boom && thrown === true",
)
.unwrap();
assert!(result.as_bool());
}
// defineProperty 描述符字段 getter 抛出的异常原值传播(Reflect 与 Object 两侧同通道)。
#[test]
fn define_property_propagates_attribute_getter_original_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var boom = 'X'; \
var desc = { get enumerable() { throw boom; } }; \
var caught; \
try { Reflect.defineProperty({}, 'a', desc); caught = 'none'; } catch (e) { caught = e; } \
var caughtObj; \
try { Object.defineProperty({}, 'a', desc); caughtObj = 'none'; } catch (e) { caughtObj = e; } \
caught === boom && caughtObj === boom",
)
.unwrap();
assert!(result.as_bool());
}