Files
OxideJS-New/crates/oxide_vm/tests/property_receiver_tests.rs
T
2026-08-06 23:10:27 +08:00

96 lines
4.4 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 oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<(Vm, 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))?;
let mut vm = Vm::new();
let result = vm.run(&module)?;
Ok((vm, result))
}
fn eval_str(source: &str) -> Result<JsValue, String> {
eval(source).map(|(_vm, v)| v)
}
// 原型上的 getter 在 receiver 上设置标记,随后检查 receiver 持有标记(而非原型)。
#[test]
fn getter_on_proto_this_is_receiver() {
let r = eval_str(
"var proto={}; Object.defineProperty(proto,'x',{get:function(){ this._marker=1; return this._marker; },enumerable:true,configurable:true}); var child=Object.create(proto); child.x"
).unwrap();
assert!(r.is_int() && r.as_int() == 1, "getter should set marker on child (receiver), got {:?}", r);
}
// 深层原型链上的 getter:grandchild → child → proto,getter 定义在 proto 上。
#[test]
fn getter_on_proto_deep_chain_this_is_receiver() {
let r = eval_str(
"var proto={}; Object.defineProperty(proto,'x',{get:function(){ this._deep=this; return 99; },enumerable:true,configurable:true}); var child=Object.create(proto); var grandchild=Object.create(child); grandchild.x; grandchild._deep!==undefined"
).unwrap();
assert!(r.is_bool() && r.as_bool(), "grandchild should have _deep marker, got {:?}", r);
}
// 原型上的 setter 以 receiver 作为 this,在 receiver 而非原型上设置标记。
#[test]
fn setter_on_proto_this_is_receiver() {
let (_vm, r) = eval(
"var proto={}; Object.defineProperty(proto,'x',{set:function(v){ this._set_by_setter=v; },get:function(){ return this._set_by_setter; },enumerable:true,configurable:true}); var child=Object.create(proto); child.x='receiver_test'; child._set_by_setter"
).unwrap();
assert!(r.is_string(), "child should have _set_by_setter, got {:?}", r);
}
// 动态 getter(obj[expr])保持 receiver。
#[test]
fn dynamic_getter_on_proto_this_is_receiver() {
let r = eval_str(
"var proto={}; Object.defineProperty(proto,'x',{get:function(){ this._dyn=1; return this._dyn; },enumerable:true,configurable:true}); var child=Object.create(proto); child['x']"
).unwrap();
assert!(r.is_int() && r.as_int() == 1, "dynamic getter should set marker on child, got {:?}", r);
}
// 动态 setter(obj[expr]=val)保持 receiver。
#[test]
fn dynamic_setter_on_proto_this_is_receiver() {
let (_vm, r) = eval(
"var proto={}; Object.defineProperty(proto,'x',{set:function(v){ this._dyn_set=v; },get:function(){ return this._dyn_set; },enumerable:true,configurable:true}); var child=Object.create(proto); child['x']='dyn_test'; child._dyn_set"
).unwrap();
assert!(r.is_string(), "child should have _dyn_set after dynamic setter, got {:?}", r);
}
// 对象自身的 getter——this 即对象本身。
#[test]
fn own_getter_this_is_self() {
let r = eval_str(
"var obj={}; Object.defineProperty(obj,'x',{get:function(){ this._own=42; return this._own; },enumerable:true,configurable:true}); obj.x"
).unwrap();
assert!(r.is_int() && r.as_int() == 42, "own getter should set marker on self, got {:?}", r);
}
// 对象自身的 setter——this 即对象本身。
#[test]
fn own_setter_this_is_self() {
let (_vm, r) = eval(
"var obj={}; Object.defineProperty(obj,'x',{set:function(v){ this._own_set=v; },get:function(){ return this._own_set; },enumerable:true,configurable:true}); obj.x='own'; obj._own_set"
).unwrap();
assert!(r.is_string(), "own setter should set marker on self, got {:?}", r);
}
// 返回 undefined 的 getter 不崩溃。
#[test]
fn getter_returns_undefined_does_not_crash() {
let r = eval_str(
"var proto={}; Object.defineProperty(proto,'x',{get:function(){},enumerable:true,configurable:true}); var child=Object.create(proto); child.x"
).unwrap();
assert!(r.is_undefined(), "getter returning nothing should yield undefined, got {:?}", r);
}
// 定义原型 getter 后字符串 .length 仍正常(基本类型自动装箱未被破坏)。
#[test]
fn string_length_still_works_after_proto_getter() {
let r = eval_str("'hello'.length").unwrap();
assert!(r.is_int() && r.as_int() == 5, "string.length should be 5, got {:?}", r);
}