From 484d9a9e113b5c4e6063e120c1ed4c456f5bd1b3 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 9 Oct 2026 09:21:49 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=81=8D=E5=8E=86=E6=97=8F=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20ToObject=20=E8=A3=85=E7=AE=B1=E5=9F=BA=E5=85=83?= =?UTF-8?q?=E6=8E=A5=E6=94=B6=E8=80=85=E5=B9=B6=E9=92=89=E4=BD=8D=20reg0?= =?UTF-8?q?=20=E4=BF=9D=20GC=20=E6=A0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 38 +++--- Cargo.toml | 2 +- crates/oxide_builtins/src/array/common.rs | 12 +- crates/oxide_builtins/src/array/iterate.rs | 60 +++------ .../tests/array_hole_semantics_tests.rs | 124 +++++++++++++++++- 5 files changed, 167 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1816f70a..3c78798a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.95" +version = "0.8.96" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.95" +version = "0.8.96" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.95" +version = "0.8.96" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.95" +version = "0.8.96" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.95" +version = "0.8.96" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.95" +version = "0.8.96" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.95" +version = "0.8.96" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.95" +version = "0.8.96" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.95" +version = "0.8.96" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.95" +version = "0.8.96" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.95" +version = "0.8.96" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index f50c6e89..24446070 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.8.95" +version = "0.8.96" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/array/common.rs b/crates/oxide_builtins/src/array/common.rs index d8acc95b..f4c41571 100644 --- a/crates/oxide_builtins/src/array/common.rs +++ b/crates/oxide_builtins/src/array/common.rs @@ -40,7 +40,15 @@ pub(crate) use array_ptr_len; macro_rules! array_ptr_len3 { ($vm:expr, $args:expr) => {{ - let this_val = $vm.reg($args[0]); + // ToObject 装箱加 reg0 钉位:装箱体跨回调窗保 GC 根。call 转发形态的实参 + // 寄存器集可占 reg0,占位时跳过钉位(装箱体按接收者值传递保活)。 + let this_val = match oxide_runtime_api::to_object($vm.reg($args[0]), $vm) { + Ok(v) => v, + Err(msg) => return NativeResult::Err(crate::array::from::from_engine_error($vm, &msg)), + }; + if !$args.contains(&0) { + $vm.set_reg(0, this_val); + } let (arr_ptr, len, is_arr) = match get_this_arraylike($vm, this_val) { Ok(v) => v, Err(err) => { @@ -48,7 +56,7 @@ macro_rules! array_ptr_len3 { return NativeResult::Err(err); } }; - (arr_ptr, len, is_arr) + (arr_ptr, len, is_arr, this_val) }}; } pub(crate) use array_ptr_len3; diff --git a/crates/oxide_builtins/src/array/iterate.rs b/crates/oxide_builtins/src/array/iterate.rs index 04ca47cf..6ae049fd 100644 --- a/crates/oxide_builtins/src/array/iterate.rs +++ b/crates/oxide_builtins/src/array/iterate.rs @@ -2,7 +2,7 @@ use oxide_types::value::JsValue; -use oxide_runtime_api::{to_object, NativeResult, VmHost}; +use oxide_runtime_api::{NativeResult, VmHost}; use crate::builtins_debug; use crate::builtins_error; @@ -12,13 +12,12 @@ use super::common::{ check_array_create_len, get_this_arraylike, invoke_native_callback, js_array_index, require_callback, unexpected_tail_call_error, }; -use super::from::{array_species_create, create_data_property_or_throw, from_engine_error}; +use super::from::{array_species_create, create_data_property_or_throw}; /// `Array.prototype.forEach(callback, thisArg)`:对每个元素调用 callback,返回 undefined。 pub fn array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.forEach called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, _is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); } @@ -57,8 +56,7 @@ pub fn array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { /// 可按规范抛 TypeError。 pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.map called with {} args", args.len()); - let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); } @@ -117,8 +115,7 @@ pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { /// 可按规范抛 TypeError。 pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.filter called with {} args", args.len()); - let (arr_ptr, n, is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.filter: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -168,8 +165,7 @@ pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { /// 空数组且无初始值抛 TypeError。 pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.reduce called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, _is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.reduce: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -231,8 +227,7 @@ pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { /// `Array.prototype.find(callback, thisArg)`:返回首个 callback 为真的元素,否则 undefined。 pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.find called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, _is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.find: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -269,8 +264,7 @@ pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { /// `Array.prototype.some(callback, thisArg)`:任一元素满足 callback 返回 true。 pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.some called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, _is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.some: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -311,8 +305,7 @@ pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { /// `Array.prototype.every(callback, thisArg)`:所有元素满足 callback 才返回 true。 pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.every called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, _is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.every: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -367,23 +360,8 @@ pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { /// 可按规范抛 TypeError。 pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.flatMap called with {} args", args.len()); - // ToObject:基元 this 装箱(null/undefined 抛 TypeError)。 - let this_val = match to_object(vm.reg(args[0]), vm) { - Ok(v) => v, - Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)), - }; - // 装箱体钉入返回寄存器:它只存于 Rust 局部,跨 length getter / species - // 用户窗口须保 GC 根。call 转发形态的实参寄存器集可占该槽,占位时跳过钉位 - // (装箱体按接收者值传递保活)。结果 A 钉入后此槽让位,循环期装箱体按 - // 接收者值传递保活。 - if !args.contains(&0) { - vm.set_reg(0, this_val); - } - let (arr_ptr, n, is_array) = match get_this_arraylike(vm, this_val) { - Ok(v) => v, - Err(err) => return NativeResult::Err(err), - }; - let o_val = this_val; + // 接收者 ToObject 装箱并钉位 reg0(宏内收口),第四返回值作 o_val。 + let (arr_ptr, n, is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.flatMap: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -471,8 +449,7 @@ pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { /// `Array.prototype.findIndex(callback, thisArg)`:返回首个 callback 为真的索引,否则 -1。 pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.findIndex called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, _is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.findIndex: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -509,11 +486,10 @@ pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { /// `Array.prototype.findLast(callback, thisArg)`:从后往前返回首个 callback 为真的元素。 pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.findLast called with {} args", args.len()); - let (arr_ptr, n) = { - let (arr_ptr, len, _is_array) = array_ptr_len3!(vm, args); - (arr_ptr, len as i32) + let (arr_ptr, n, o_val) = { + let (arr_ptr, len, _is_array, o_val) = array_ptr_len3!(vm, args); + (arr_ptr, len as i32, o_val) }; - let o_val = vm.reg(args[0]); if args.len() < 2 { builtins_error!("Array.prototype.findLast: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -550,8 +526,7 @@ pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { /// `Array.prototype.reduceRight(callback, initialValue)`:从右到左累计归约。 pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.reduceRight called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, _is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.reduceRight: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -612,8 +587,7 @@ pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { /// Array.prototype.findLastIndex(callback, thisArg):从后往前返回首个 callback 为真的下标。 pub fn array_find_last_index(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("Array.prototype.findLastIndex called with {} args", args.len()); - let (arr_ptr, n, _is_array) = array_ptr_len3!(vm, args); - let o_val = vm.reg(args[0]); + let (arr_ptr, n, _is_array, o_val) = array_ptr_len3!(vm, args); if args.len() < 2 { builtins_error!("Array.prototype.findLastIndex: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); diff --git a/crates/oxide_builtins/tests/array_hole_semantics_tests.rs b/crates/oxide_builtins/tests/array_hole_semantics_tests.rs index 2f985497..85790d1f 100644 --- a/crates/oxide_builtins/tests/array_hole_semantics_tests.rs +++ b/crates/oxide_builtins/tests/array_hole_semantics_tests.rs @@ -864,15 +864,131 @@ fn test_flat_arraylike_undefined_length() { assert_eq!(out, "|0"); } -// 引擎钉:flat 装箱基元 this——ToObject 后长度为 0,结果空真数组。 +// 引擎钉:flat 装箱基元 this——ToObject 后长度为 0,结果空真数组;装箱串 +// 长度 2,按单元遍历。 #[test] fn test_flat_boxed_primitive_this() { let out = eval_str( - "(() => { const r = Array.prototype.flat.call(true); \ - return r.length + '|' + Array.isArray(r); })()", + "(() => { const r1 = Array.prototype.flat.call(true); \ + const r2 = Array.prototype.flat.call('ab'); \ + return r1.length + '|' + Array.isArray(r1) + '|' + r2.join(',') + '|' + r2.length; })()", ) .unwrap(); - assert_eq!(out, "0|true"); + assert_eq!(out, "0|true|a,b|2"); +} + +// 引擎钉:map 装箱串 this——ToObject 装箱后按单元遍历,结果真数组。 +#[test] +fn test_map_boxed_string_this() { + let out = eval_str( + "(() => { const r = Array.prototype.map.call('ab', c => c); \ + return r.join(',') + '|' + r.length; })()", + ) + .unwrap(); + assert_eq!(out, "a,b|2"); +} + +// 引擎钉:forEach 装箱串 this——回调按序接收单元,返回 undefined。 +#[test] +fn test_for_each_boxed_string_this() { + let out = eval_str( + "(() => { const seen = []; \ + const r = Array.prototype.forEach.call('ab', c => seen.push(c)); \ + return seen.join(',') + '|' + (r === undefined); })()", + ) + .unwrap(); + assert_eq!(out, "a,b|true"); +} + +// 引擎钉:filter 装箱串 this——仅保留真值单元。 +#[test] +fn test_filter_boxed_string_this() { + let out = eval_str( + "(() => { const r = Array.prototype.filter.call('ab', c => c === 'a'); \ + return r.join(',') + '|' + r.length; })()", + ) + .unwrap(); + assert_eq!(out, "a|1"); +} + +// 引擎钉:reduce 装箱串 this——从左到右累计,初值空串。 +#[test] +fn test_reduce_boxed_string_this() { + let out = eval_str( + "(() => { return Array.prototype.reduce.call('ab', (a, b) => a + b, ''); })()", + ) + .unwrap(); + assert_eq!(out, "ab"); +} + +// 引擎钉:reduceRight 装箱串 this——从右到左累计。 +#[test] +fn test_reduce_right_boxed_string_this() { + let out = eval_str( + "(() => { return Array.prototype.reduceRight.call('ab', (a, b) => a + b, ''); })()", + ) + .unwrap(); + assert_eq!(out, "ba"); +} + +// 引擎钉:find 装箱串 this——返回首个真值单元。 +#[test] +fn test_find_boxed_string_this() { + let out = eval_str( + "(() => { return Array.prototype.find.call('ab', c => c === 'b'); })()", + ) + .unwrap(); + assert_eq!(out, "b"); +} + +// 引擎钉:findIndex 装箱串 this——返回首个真值下标。 +#[test] +fn test_find_index_boxed_string_this() { + let out = eval_str( + "(() => { return String(Array.prototype.findIndex.call('ab', c => c === 'a')); })()", + ) + .unwrap(); + assert_eq!(out, "0"); +} + +// 引擎钉:findLast 装箱串 this——返回末个真值单元。 +#[test] +fn test_find_last_boxed_string_this() { + let out = eval_str( + "(() => { return Array.prototype.findLast.call('ab', c => c === 'b'); })()", + ) + .unwrap(); + assert_eq!(out, "b"); +} + +// 引擎钉:findLastIndex 装箱串 this——返回末个真值下标。 +#[test] +fn test_find_last_index_boxed_string_this() { + let out = eval_str( + "(() => { return String(Array.prototype.findLastIndex.call('ab', c => c === 'b')); })()", + ) + .unwrap(); + assert_eq!(out, "1"); +} + +// 引擎钉:some 装箱串 this——任一单元真值即 true。 +#[test] +fn test_some_boxed_string_this() { + let out = eval_str( + "(() => { return String(Array.prototype.some.call('ab', c => c === 'b')); })()", + ) + .unwrap(); + assert_eq!(out, "true"); +} + +// 引擎钉:every 装箱串 this——全单元真值才 true。 +#[test] +fn test_every_boxed_string_this() { + let out = eval_str( + "(() => { return String(Array.prototype.every.call('ab', c => c !== 'x')); })()", + ) + .unwrap(); + assert_eq!(out, "true"); } // 引擎钉:flat 负 depth——ToIntegerOrInfinity 后归 0,不展开。