From 13685efc9a04371c0e61f8c09e363975dd323e94 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 19 Sep 2026 20:24:22 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20species=20=E6=9E=84=E9=80=A0=E5=99=A8=20?= =?UTF-8?q?undefined=20=E5=9B=9E=E9=80=80=E5=86=85=E7=BD=AE=20ArrayCreate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array/from.rs | 4 ++ .../tests/array_hole_semantics_tests.rs | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/crates/oxide_builtins/src/array/from.rs b/crates/oxide_builtins/src/array/from.rs index 7d4067da..37d8fad4 100644 --- a/crates/oxide_builtins/src/array/from.rs +++ b/crates/oxide_builtins/src/array/from.rs @@ -339,6 +339,10 @@ pub(crate) fn array_species_create( Ok(v) => v, Err(msg) => return Err(from_engine_error(vm, &msg)), }; + // C 为 undefined 时不查 species,直接回退内置 ArrayCreate。 + if c.is_undefined() { + return Ok(create_new_array(vm, count)); + } if !is_constructor_value(c) { if !c.is_object() { return Err(crate::error::create_type_error(vm, "Species constructor not a constructor")); diff --git a/crates/oxide_builtins/tests/array_hole_semantics_tests.rs b/crates/oxide_builtins/tests/array_hole_semantics_tests.rs index 32ed81ed..56f3f23b 100644 --- a/crates/oxide_builtins/tests/array_hole_semantics_tests.rs +++ b/crates/oxide_builtins/tests/array_hole_semantics_tests.rs @@ -850,3 +850,40 @@ fn test_flat_map_hole_source_length_of_arraylike() { .unwrap(); assert_eq!(out, "1,2|2|true|true"); } + +// 引擎钉:species 构造器为 undefined 时不查 species,四挂载点(slice/splice/ +// flat/flatMap)均回退内置 ArrayCreate——纯数组、原型 Array.prototype、元素逐值。 +#[test] +fn test_species_ctor_undefined_plain_array_four_mounts() { + let out = eval_str( + "(() => { const mk = () => { const a = [1, 2, 3]; a.constructor = undefined; return a; }; \ + const s = mk().slice(); const sp = mk().splice(1, 1); \ + const f = mk().flat(); const fm = mk().flatMap((x) => x); \ + return Array.isArray(s) + '|' + (Object.getPrototypeOf(s) === Array.prototype) + '|' + \ + s.join(',') + '|' + Array.isArray(sp) + '|' + sp.join(',') + '|' + \ + Array.isArray(f) + '|' + f.join(',') + '|' + Array.isArray(fm) + '|' + fm.join(','); })()", + ) + .unwrap(); + assert_eq!(out, "true|true|1,2,3|true|2|true|1,2,3|true|1,2,3"); +} + +// 防过度修护栏:species 构造器为 null/数字/字符串/布尔四值时,四挂载点均保持 +// 抛 TypeError(仅 undefined 判定前移,null/基元臂不得回退)。 +#[test] +fn test_species_ctor_null_primitive_throws_four_mounts() { + let out = eval_str( + "(() => { let ok = 0; \ + for (const cv of [null, 1, 'string', true]) { \ + const a = [1, 2, 3]; a.constructor = cv; \ + try { a.slice(); } catch (e) { if (e instanceof TypeError) ok++; } \ + const b = [1, 2, 3]; b.constructor = cv; \ + try { b.splice(1); } catch (e) { if (e instanceof TypeError) ok++; } \ + const c = [1, 2, 3]; c.constructor = cv; \ + try { c.flat(); } catch (e) { if (e instanceof TypeError) ok++; } \ + const d = [1, 2, 3]; d.constructor = cv; \ + try { d.flatMap((x) => x); } catch (e) { if (e instanceof TypeError) ok++; } } \ + return String(ok); })()", + ) + .unwrap(); + assert_eq!(out, "16"); +}