fix: 修复数组回调与原始值属性访问
This commit is contained in:
@@ -12,6 +12,18 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
|
||||
Ok((vm, result))
|
||||
}
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel_core()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn assert_num_eq(val: JsValue, expected: f64) {
|
||||
let actual = if val.is_int() { val.as_int() as f64 } else { val.as_double() };
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_push_adds_element() {
|
||||
let (_vm, result) = eval("var a = [1,2]; a.push(3); a").unwrap();
|
||||
@@ -246,6 +258,66 @@ fn array_sort_default() {
|
||||
assert_eq!(obj.prop_count(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_map_filter_reduce_accept_bytecode_callbacks() {
|
||||
let (vm, result) = eval("[1,2,3].map(function(x){return x+1}).join(',')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "2,3,4");
|
||||
|
||||
let (vm, result) = eval("[1,2,3].map(x => x * 2).join(',')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "2,4,6");
|
||||
|
||||
let (vm, result) = eval("[1,2,3,4].filter(x => x % 2 === 0).join(',')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "2,4");
|
||||
|
||||
let (_vm, result) = eval("[1,2,3].reduce((a,b)=>a+b,0)").unwrap();
|
||||
assert_num_eq(result, 6.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_iteration_predicates_accept_bytecode_callbacks() {
|
||||
let (_vm, result) = eval("[1,2,3].forEach(function(x){return x+1})").unwrap();
|
||||
assert!(result.is_undefined());
|
||||
|
||||
let (_vm, result) = eval("[1,2,3].find(x => x > 1)").unwrap();
|
||||
assert_num_eq(result, 2.0);
|
||||
|
||||
let (_vm, result) = eval("[1,2,3].some(x => x === 2)").unwrap();
|
||||
assert_eq!(result, JsValue::bool(true));
|
||||
|
||||
let (_vm, result) = eval("[1,2,3].every(function(x){return x > 0})").unwrap();
|
||||
assert_eq!(result, JsValue::bool(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_callback_exceptions_propagate() {
|
||||
let (vm, result) = eval("try{[1].forEach(()=>{throw new TypeError('boom')})}catch(e){e.message}").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "boom");
|
||||
|
||||
let (vm, result) = eval("try{[1].map(()=>{throw new Error('x')})}catch(e){e.message}").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "x");
|
||||
|
||||
let (vm, result) = eval("try{[1].filter(()=>{throw new Error('f')})}catch(e){e.message}").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_sort_uses_user_comparator() {
|
||||
let (vm, result) = eval("[3,1,2].sort((a,b)=>a-b).join(',')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "1,2,3");
|
||||
|
||||
let (vm, result) = eval("[3,1,2].sort((a,b)=>b-a).join(',')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "3,2,1");
|
||||
|
||||
let (vm, result) = eval("[10,9,1].sort().join(',')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "1,10,9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_sort_propagates_comparator_exception() {
|
||||
let (vm, result) = eval("try{[1,2,3].sort(()=>{throw new Error('cmp')})}catch(e){e.message}").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "cmp");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_copy_within_copies() {
|
||||
let (_vm, result) = eval("[1,2,3,4,5].copyWithin(0,3)").unwrap();
|
||||
|
||||
@@ -106,3 +106,23 @@ fn boolean_new_to_string() {
|
||||
let r = eval(&mut vm, "new Boolean(false).toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_primitive_to_string_autoboxes() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "true.toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "true");
|
||||
|
||||
let r = eval(&mut vm, "false.toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_primitive_value_of_autoboxes() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "false.valueOf()").unwrap();
|
||||
assert_eq!(r, JsValue::bool(false));
|
||||
|
||||
let r = eval(&mut vm, "(true).constructor === Boolean").unwrap();
|
||||
assert_eq!(r, JsValue::bool(true));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@ fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn assert_num_eq(val: JsValue, expected: f64) {
|
||||
let actual = if val.is_int() { val.as_int() as f64 } else { val.as_double() };
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_index_of_found() {
|
||||
let mut vm = Vm::new();
|
||||
@@ -23,6 +28,13 @@ fn string_index_of_found() {
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_from_char_code_static() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "String.fromCharCode(65,66,67)").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "ABC");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_index_of_not_found() {
|
||||
let mut vm = Vm::new();
|
||||
@@ -86,6 +98,26 @@ fn string_to_upper_case() {
|
||||
assert_eq!(to_str(&vm, s), "HELLO");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_primitive_length_autoboxes() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "'abc'.length").unwrap();
|
||||
assert_num_eq(result, 3.0);
|
||||
|
||||
let result = eval(&mut vm, "''.length").unwrap();
|
||||
assert_num_eq(result, 0.0);
|
||||
|
||||
let result = eval(&mut vm, "'abc'.length + 'de'.length").unwrap();
|
||||
assert_num_eq(result, 5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_length_does_not_break_methods() {
|
||||
let mut vm = Vm::new();
|
||||
let s = eval(&mut vm, "'hello'.toUpperCase()").unwrap();
|
||||
assert_eq!(to_str(&vm, s), "HELLO");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_to_lower_case() {
|
||||
let mut vm = Vm::new();
|
||||
|
||||
@@ -105,3 +105,17 @@ fn test_to_int32_and_to_uint32() {
|
||||
assert_eq!(coercion::to_int32(JsValue::float(f64::INFINITY), sf), 0);
|
||||
assert_eq!(coercion::to_uint32(JsValue::int(-1), sf), 4_294_967_295);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn primitive_property_write_throws_clean_type_error() {
|
||||
for source in ["'abc'.x = 1", "(5).foo = 1", "true.foo = 1"] {
|
||||
let err = eval(source);
|
||||
assert!(err.contains("TypeError"), "expected TypeError for {source}, got {err}");
|
||||
assert!(
|
||||
!err.contains("IC_SET_PROP on non-object")
|
||||
&& !err.contains("SET_PROP on non-object")
|
||||
&& !err.contains("SET_PROP_DYNAMIC on non-object"),
|
||||
"unexpected internal opcode message for {source}: {err}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user