mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
473 lines
16 KiB
Rust
473 lines
16 KiB
Rust
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());
|
||
}
|