Files
OxideJS-New/crates/oxide_vm/tests/boxed_string_exotic_tests.rs

323 lines
10 KiB
Rust
Raw Permalink 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.
//! 字符串盒(String exotic)构造期物化钉测:`boxed_value` 专属载荷与字符索引/
//! length 固有属性面。覆盖 test262 盒面红转绿的语义面——索引/length 读、in/
//! 枚举、for-in 原始值右值装箱、描述符、seal/freeze、defineProperty 同值改写
//! 放行/异值与属性面变更拒绝、写保护(strict/sloppy)、delete 返 false、
//! Object.assign/解构消费面、%StringPrototype% 自身盒面、迭代器/Array.from 载荷读、
//! @@toStringTag 覆盖。
//!
//! 语义对齐规范 String exotic:索引属性 writable:false / enumerable:true /
//! configurable:false,length writable:false / enumerable:false /
//! configurable:false;写失败 sloppy 返回被赋值(静默)、strict 抛 TypeError;
//! 同值重定义放行、异值或属性面变更抛 TypeError;delete 返 false;越界新键
//! (如 length 之后的下标)按普通数据属性定义成功。
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))
}
#[test]
fn boxed_value_roundtrip() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); return s.valueOf() === 'ab' && s.toString() === 'ab' && Object.prototype.toString.call(s) === '[object String]'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn index_and_length_read() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); return s[0] === 'a' && s[1] === 'b' && s[2] === undefined && s.length === 2; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn in_operator() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); return '0' in s && '1' in s && !('2' in s) && 'length' in s; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn forin_enumerates_indexes_only() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); const keys = []; for (const k in s) keys.push(k); return keys.length === 2 && keys[0] === '0' && keys[1] === '1'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn forin_primitive_string() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const keys = []; for (const k in 'ab') keys.push(k); return keys.length === 2 && keys[0] === '0' && keys[1] === '1'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn forin_primitive_no_keys() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const a = []; for (const k in 5) a.push(k); const b = []; for (const k in true) b.push(k); const c = []; for (const k in '') c.push(k); return a.length === 0 && b.length === 0 && c.length === 0; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn keys_and_own_keys() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); const k = Object.keys(s).join(','); const o = Object.getOwnPropertyNames(s).sort().join(','); return k === '0,1' && o === '0,1,length'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn gopd_index_and_length() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); const d = Object.getOwnPropertyDescriptor(s, '0'); const l = Object.getOwnPropertyDescriptor(s, 'length'); return d.value === 'a' && d.writable === false && d.enumerable === true && d.configurable === false && l.value === 2 && l.writable === false && l.enumerable === false && l.configurable === false; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn gopds_full_surface() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); const d = Object.getOwnPropertyDescriptors(s); const ks = Object.keys(d).sort(); return ks.length === 3 && ks[0] === '0' && ks[1] === '1' && ks[2] === 'length' && d['0'].value === 'a' && d['1'].value === 'b' && d.length.value === 2; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn seal_freeze_noop() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); Object.seal(s); Object.freeze(s); Object.preventExtensions(s); return s[0] === 'a' && s.length === 2 && s[1] === 'b'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn defineproperty_same_value_ok() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); const r = Object.defineProperty(s, '1', { value: 'b' }); return r === s && s[1] === 'b'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn defineproperty_diff_value_throws() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); let e = null; try { Object.defineProperty(s, '1', { value: 'x' }); } catch (err) { e = err; } return e instanceof TypeError && s[1] === 'b'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn defineproperty_attribute_change_throws() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); let e = null; try { Object.defineProperty(s, '1', { writable: true }); } catch (err) { e = err; } return e instanceof TypeError; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn defineproperty_new_key_ok() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); const r = Object.defineProperty(s, '2', { value: 'z' }); return r === s && s[2] === 'z'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn set_sloppy_returns_value() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); const r0 = s[0] = 'z'; const r1 = s.length = 9; return r0 === 'z' && r1 === 9 && s[0] === 'a' && s.length === 2; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn set_strict_throws() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"\"use strict\"; (function(){ const s = new String('ab'); let e1 = null; try { s[0] = 'z'; } catch (err) { e1 = err; } let e2 = null; try { s.length = 9; } catch (err) { e2 = err; } return e1 instanceof TypeError && e2 instanceof TypeError && s[0] === 'a' && s.length === 2; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn delete_returns_false() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('ab'); const d0 = delete s[0]; const dl = delete s.length; return d0 === false && dl === false && s[0] === 'a' && s.length === 2; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn object_assign_string_source() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const o1 = Object.assign({}, 'ab'); const o2 = Object.assign({}, new String('ab')); return o1[0] === 'a' && o1[1] === 'b' && o1.length === undefined && o2[0] === 'a' && o2[1] === 'b'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn object_assign_skips_null_undefined() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const o = Object.assign({}, null, undefined, 'cd'); return o[0] === 'c' && o[1] === 'd' && Object.keys(o).length === 2; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn assign_target_string_box_throws() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const t = new String('ab'); let e = null; try { Object.assign(t, 'cd'); } catch (err) { e = err; } return e instanceof TypeError && t[0] === 'a' && t[1] === 'b' && t.length === 2; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn rest_destructure_string_box() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const { 0: a, ...rest } = new String('abc'); return a === 'a' && rest[1] === 'b' && rest[2] === 'c' && rest[0] === undefined; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn string_prototype_box() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const p = String.prototype; return Object.prototype.toString.call(p) === '[object String]' && p.length === 0 && p.valueOf() === ''; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn symbol_tag_via_defineproperty() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('x'); Object.defineProperty(s, Symbol.toStringTag, { value: 'T' }); return Object.prototype.toString.call(s) === '[object T]'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn iterator_over_box() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const arr = [...new String('ab')]; return arr.length === 2 && arr[0] === 'a' && arr[1] === 'b'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn array_from_box() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const arr = Array.from(new String('ab')); return arr.length === 2 && arr[0] === 'a' && arr[1] === 'b'; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn empty_string_box() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const e = new String(''); return e.length === 0 && e.valueOf() === '' && Object.getOwnPropertyNames(e).length === 1 && e[0] === undefined; })()",
)
.unwrap();
assert!(r.as_bool());
}
#[test]
fn valueof_and_toststring_receiver() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"(function(){ const s = new String('z'); return String.prototype.valueOf.call(s) === 'z' && String.prototype.toString.call(s) === 'z'; })()",
)
.unwrap();
assert!(r.as_bool());
}