//! splice/reverse/copyWithin/at 四方法洞语义与原型链读语义引擎钉: //! 源洞保洞/换洞/删洞、重叠反向复制、元素读走 Get; //! push/pop/shift/unshift arraylike 接收者族:ToObject 通用入口、 //! ToLength 长度源、2^53-1 上限、DeletePropertyOrThrow 删位、末位显式删。 use std::sync::Arc; use oxide_compiler::compiler::Compiler; use oxide_types::value::JsValue; use oxide_vm::vm::Vm; fn eval_value(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(&Arc::new(module))?; Ok((vm, result)) } fn eval_str(source: &str) -> Result { let (vm, result) = eval_value(source)?; vm.lookup_str(result) .ok_or_else(|| "completion value is not a string".to_string()) } // splice 被删收集:源洞位在 removed 保洞,present 值原样落入。 #[test] fn test_splice_removed_keeps_holes() { let out = eval_str( "(() => { const a = new Array(3); a[1] = 1; a[2] = 3; \ const r = a.splice(0, 3); \ return r.length + '|' + (0 in r) + '|' + r[1]; })()", ) .unwrap(); assert_eq!(out, "3|false|1"); } // splice 被删收集走 Get:洞位落原型链,访问器值落入 removed 且该位为 present。 #[test] fn test_splice_removed_get_through_proto() { let out = eval_str( "(() => { Object.defineProperty(Array.prototype, 0, { get: () => 'G0' }); \ try { const a = new Array(2); a[1] = 1; const r = a.splice(0, 2); \ return r[0] + '|' + (0 in r) + '|' + r.length; } \ finally { delete Array.prototype[0]; } })()", ) .unwrap(); assert_eq!(out, "G0|true|2"); } // splice 扩容:洞洞源 removed 全洞;数组体洞位随搬移保留、插入位 present。 #[test] fn test_splice_grow_keeps_array_holes() { let out = eval_str( "(() => { const a = new Array(3); const r = a.splice(0, 2, 'a'); \ return (0 in r) + '|' + (1 in r) + '|' + r.length + '|' + \ (0 in a) + '|' + (1 in a) + '|' + a.length; })()", ) .unwrap(); assert_eq!(out, "false|false|2|true|false|2"); } // splice 中插:插入点之后的既有洞位随搬移落位,`3 in` 为 false。 #[test] fn test_splice_insert_mid_hole_moves() { let out = eval_str( "(() => { const a = new Array(4); a[0] = 1; a[1] = 2; a[3] = 4; \ a.splice(2, 0, 'x'); \ return (3 in a) + '|' + (2 in a) + '|' + a[2] + '|' + a.length; })()", ) .unwrap(); assert_eq!(out, "false|true|x|5"); } // reverse 洞对 present:值位互换、洞位互换,洞不物化。 #[test] fn test_reverse_hole_and_value_swap() { let out = eval_str( "(() => { const a = new Array(3); a[0] = 1; a[2] = 3; a.reverse(); \ return (1 in a) + '|' + a[0] + '|' + a[2]; })()", ) .unwrap(); assert_eq!(out, "false|3|1"); } // reverse 洞对洞:双缺失端不动,洞位互换后各归其位。 #[test] fn test_reverse_hole_to_hole() { let out = eval_str( "(() => { const a = new Array(3); a[1] = 2; a[2] = 3; a.reverse(); \ return (2 in a) + '|' + a[0] + '|' + a[1]; })()", ) .unwrap(); assert_eq!(out, "false|3|2"); } // reverse 原型链存在性:自身洞 + 原型 present 视同双存在,两端互写为 present。 #[test] fn test_reverse_proto_present_both_sides() { let out = eval_str( "(() => { Array.prototype[1] = 1; \ try { const x = [0]; x.length = 2; x.reverse(); \ return (0 in x) + '|' + (1 in x) + '|' + x[0] + '|' + x[1]; } \ finally { delete Array.prototype[1]; } })()", ) .unwrap(); assert_eq!(out, "true|true|1|0"); } // copyWithin 自拷贝:源洞位 DeleteProperty 自身,洞保洞。 #[test] fn test_copy_within_self_copy_hole_stays_hole() { let out = eval_str( "(() => { const a = new Array(3); a[0] = 1; a[2] = 3; a.copyWithin(0); \ return (1 in a) + '|' + a[0] + '|' + a[2]; })()", ) .unwrap(); assert_eq!(out, "false|1|3"); } // copyWithin 重叠正向陷阱:目标领先且重叠须反向迭代,源洞目标位删洞。 #[test] fn test_copy_within_overlap_reverse_direction() { let out = eval_str( "(() => { const a = new Array(4); a[1] = 1; a[2] = 3; a[3] = 4; \ a.copyWithin(1, 0, 2); \ return (0 in a) + '|' + (1 in a) + '|' + (2 in a) + '|' + a[2]; })()", ) .unwrap(); assert_eq!(out, "false|false|true|1"); } // copyWithin 重叠方向判别:密集数组反向复制结果与正向不同。 #[test] fn test_copy_within_dense_overlap() { let out = eval_str("(() => { const a = [0, 1, 2, 3]; a.copyWithin(1, 0, 2); return a.join(','); })()").unwrap(); assert_eq!(out, "0,0,1,3"); } // at 元素读走 Get:洞位落原型链,索引访问器触发。 #[test] fn test_at_get_through_proto() { let out = eval_str( "(() => { Object.defineProperty(Array.prototype, 0, { get: () => 'AG' }); \ try { const a = new Array(2); a[1] = 5; \ return a.at(0) + '|' + a.at(-2) + '|' + a.at(1); } \ finally { delete Array.prototype[0]; } })()", ) .unwrap(); assert_eq!(out, "AG|AG|5"); } // reverse 读值期 getter 截断元素区:上端写越界扩展不得物化被删端, // 终态被删端保洞、值端 present。 #[test] fn test_reverse_getter_truncation_pair() { let out = eval_str( "(() => { const a = ['first', 'second']; \ Object.defineProperty(a, 0, { get: () => { a.length = 0; return 'first'; } }); \ a.reverse(); \ return (0 in a) + '|' + (1 in a) + '|' + a[1] + '|' + a.length; })()", ) .unwrap(); assert_eq!(out, "false|true|first|2"); } // reverse 读值期 getter 中间截断:上端写越界扩展的整段间隙 [old_count, upper) // 补置洞,不只补被删端一洞。 #[test] fn test_reverse_getter_mid_truncation_gaps_holes() { let out = eval_str( "(() => { const a = new Array(5); a[0] = 1; \ Object.defineProperty(a, 0, { get: () => { a.length = 3; return 1; } }); \ a.reverse(); \ return (1 in a) + '|' + (3 in a) + '|' + a[4] + '|' + a.length; })()", ) .unwrap(); assert_eq!(out, "false|false|1|5"); } // 防过度修护栏:fill 对洞位无条件 Set,洞位变 present-0,不改。 #[test] fn test_fill_materializes_hole_unchanged() { let out = eval_str( "(() => { const a = new Array(3); a[0] = 1; a[2] = 3; a.fill(0); \ return (1 in a) + '|' + a[1]; })()", ) .unwrap(); assert_eq!(out, "true|0"); } // at 越界返回 undefined(无原型链参与,防误改读路径的回归护栏)。 #[test] fn test_at_out_of_bounds_undefined() { let (_vm, result) = eval_value("[1, 2].at(9)").unwrap(); assert!(result.is_undefined()); } // splice species:Ctor 返回体直接作结果,构造器恰收单参 actualDeleteCount,O 已搬移。 #[test] fn test_splice_species_ctor_returned_instance() { let out = eval_str( "(() => { let seen = null; const inst = { mark: 1 }; \ function Ctor(n) { seen = n; inst.len = n; return inst; } \ const a = [0,1,2,3]; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ const r = a.splice(2); \ return seen + '|' + (r === inst) + '|' + r.len + '|' + a.join(','); })()", ) .unwrap(); assert_eq!(out, "2|true|2|0,1"); } // splice species 普通 Ctor 无返回体:结果非数组、原型为 Ctor.prototype, // Set(A,"length") 扩长至删除数且值逐位填入。 #[test] fn test_splice_species_plain_ctor_extends_length() { let out = eval_str( "(() => { function Ctor() {} \ const a = [0,1,2,3]; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ const r = a.splice(0); \ return Array.isArray(r) + '|' + (Object.getPrototypeOf(r) === Ctor.prototype) + '|' + \ r.length + '|' + r[0] + ',' + r[1] + ',' + r[2] + ',' + r[3]; })()", ) .unwrap(); assert_eq!(out, "false|true|4|0,1,2,3"); } // splice species 为 null / 缺省:回退真数组(原型为 Array.prototype),值与长度正确。 #[test] fn test_splice_species_null_undef_plain_array() { let out = eval_str( "(() => { const a = [0,1,2]; a.constructor = {}; a.constructor[Symbol.species] = null; \ const r1 = a.splice(1); \ const b = [0,1,2]; b.constructor = {}; \ const r2 = b.splice(1); \ return Array.isArray(r1) + '|' + (Object.getPrototypeOf(r1) === Array.prototype) + '|' + \ r1.length + '|' + r1[0] + '|' + Array.isArray(r2) + '|' + \ (Object.getPrototypeOf(r2) === Array.prototype) + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "true|true|2|1|true|true|2"); } // splice species 构造器收 -0 折算的删除数时收到 +0(非 -0)。 #[test] fn test_splice_species_neg_zero() { let out = eval_str( "(() => { let isNeg = null; \ function Ctor(n) { isNeg = Object.is(n, -0); } \ const a = []; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ const r = a.splice(0, -0); \ return 'neg=' + isNeg + '|len=' + r.length; })()", ) .unwrap(); assert_eq!(out, "neg=false|len=0"); } // splice species 结果 "0" 不可写:CreateDataPropertyOrThrow 覆盖为 present, // 描述符恢复可写可配置。 #[test] fn test_splice_species_writable_false_overwritten() { let out = eval_str( "(() => { function Ctor(n) { const o = {}; o.length = n; \ Object.defineProperty(o, '0', { writable: false, configurable: true, enumerable: true, value: 99 }); \ return o; } \ const a = [0,1,2]; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ const r = a.splice(1); \ const d = Object.getOwnPropertyDescriptor(r, '0'); \ return r[0] + '|' + d.writable + '|' + d.configurable + '|' + r.length; })()", ) .unwrap(); assert_eq!(out, "1|true|true|2"); } // splice species 结果 "0" 不可配置:CreateDataPropertyOrThrow 抛 TypeError。 #[test] fn test_splice_species_non_configurable_throws() { let out = eval_str( "(() => { function Ctor(n) { const o = {}; o.length = n; \ Object.defineProperty(o, '0', { writable: true, configurable: false, enumerable: true, value: 99 }); \ return o; } \ const a = [0,1,2]; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ try { a.splice(1); return 'no-throw'; } catch (e) { return e.constructor.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // splice species 结果不可扩展:CreateDataPropertyOrThrow 抛 TypeError。 #[test] fn test_splice_species_non_extensible_throws() { let out = eval_str( "(() => { function Ctor(n) { const o = {}; o.length = n; Object.preventExtensions(o); return o; } \ const a = [0,1,2]; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ try { a.splice(1); return 'no-throw'; } catch (e) { return e.constructor.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // splice constructor 为非对象非 undefined(null/数字/字符串/布尔四值)均抛 TypeError。 #[test] fn test_splice_ctor_non_object_throws() { let out = eval_str( "(() => { let ok = 0; \ for (const cv of [null, 1, 'string', true]) { \ const a = [0,1,2]; a.constructor = cv; \ try { a.splice(1); } catch (e) { if (e instanceof TypeError) ok++; } } \ return String(ok); })()", ) .unwrap(); assert_eq!(out, "4"); } // 防过度修:无 species 时结果仍为真数组、原型为 Array.prototype,自定义 Ctor 不被调用。 #[test] fn test_splice_no_species_plain_array() { let out = eval_str( "(() => { let calls = 0; function Ctor() { calls++; } \ const a = [0,1,2]; \ const r = a.splice(1); \ return Array.isArray(r) + '|' + (Object.getPrototypeOf(r) === Array.prototype) + '|' + r.length + '|' + calls; })()", ) .unwrap(); assert_eq!(out, "true|true|2|0"); } // 步骤序:构造器在收集与搬移之前执行,读到未改动的 O。 #[test] fn test_splice_ctor_reads_o_before_mutation() { let out = eval_str( "(() => { let seenLen = null, seen0 = null; \ function Ctor(n) { seenLen = a.length; seen0 = a[0]; } \ const a = [0,1,2,3]; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ a.splice(0, 2, 9); \ return seenLen + '|' + seen0 + '|' + a.join(','); })()", ) .unwrap(); assert_eq!(out, "4|0|9,2,3"); } // @@species getter 抛错原样透传(非构造器 constructor 上的 species 读取异常上抛)。 #[test] fn test_splice_species_poisoned_propagates() { let out = eval_str( "(() => { const a = [0,1,2]; a.constructor = {}; \ Object.defineProperty(a.constructor, Symbol.species, { get() { throw new RangeError('species-get'); } }); \ try { a.splice(1); return 'no-throw'; } catch (e) { return e.constructor.name + ':' + e.message; } })()", ) .unwrap(); assert_eq!(out, "RangeError:species-get"); } // length 不可写(语料同形 defineProperty 仅收窄 writable 位):新旧长度相同的 // 空 splice 与收缩/插入 splice 均抛 TypeError。 #[test] fn test_splice_length_nonwritable_throws() { let out = eval_str( "(() => { const a = [0,1,2]; Object.defineProperty(a, 'length', { writable: false }); \ let k1; try { a.splice(1, 2, 4); return 'shrink-no-throw'; } catch (e) { k1 = e.constructor.name; } \ const b = [0,1,2]; Object.defineProperty(b, 'length', { writable: false }); \ try { b.splice(0, 0); return 'noop-no-throw'; } catch (e) { return k1 + '|' + e.constructor.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError|TypeError"); } // 冻结数组空删除也走 length 写:抛 TypeError(防空 splice 被特例放行)。 #[test] fn test_splice_frozen_empty_delete_throws_length() { let out = eval_str( "(() => { try { Object.freeze([0,1,2]).splice(0, 0); return 'no-throw'; } \ catch (e) { return e.constructor.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 冻结数组删除命中元素:元素严格 Set 面抛 TypeError(回归护栏)。 #[test] fn test_splice_frozen_element_set_throws() { let out = eval_str( "(() => { try { Object.freeze([0,1,2]).splice(1, 1); return 'no-throw'; } \ catch (e) { return e.constructor.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:splice 长度源按 ToLength 读取,2^53 及以上钳位到 2^53-1。 #[test] fn test_splice_length_clamped_to_integer_limit() { let out = eval_str( "(() => { const o = {}; o.length = 9007199254740992; \ Array.prototype.splice.call(o); return String(o.length); })()", ) .unwrap(); assert_eq!(out, "9007199254740991"); } // 引擎钉:splice 新长度(len + insertCount - actualDeleteCount)超 2^53-1 抛 TypeError。 #[test] fn test_splice_new_length_over_integer_limit_throws() { let out = eval_str( "(() => { const o = {}; o.length = 9007199254740991; \ try { Array.prototype.splice.call(o, 0, 0, null); return 'no-throw'; } \ catch (e) { return e.constructor.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:splice 极限扩容对——arraylike 上元素右移一位、插入位落值、长度扩到 2^53-1。 #[test] fn test_splice_integer_limit_grow_pair() { let out = eval_str( "(() => { const o = { '9007199254740985': '9007199254740985', \ '9007199254740986': '9007199254740986', '9007199254740987': '9007199254740987', \ '9007199254740989': '9007199254740989', '9007199254740991': '9007199254740991', \ length: 9007199254740990 }; \ const r = Array.prototype.splice.call(o, 9007199254740986, 0, 'new-value'); \ return r.length + '|' + o.length + '|' + o['9007199254740986'] + '|' + \ o['9007199254740987'] + '|' + o['9007199254740988'] + '|' + \ ('9007199254740989' in o) + '|' + o['9007199254740990'] + '|' + o['9007199254740991']; })()", ) .unwrap(); assert_eq!( out, "0|9007199254740991|new-value|9007199254740986|9007199254740987|false|9007199254740989|9007199254740991" ); } // 引擎钉:splice 极限收缩对——搬移 + 尾部删位 + 长度缩到 2^53-2。 #[test] fn test_splice_integer_limit_shrink_pair() { let out = eval_str( "(() => { const o = { '9007199254740986': '9007199254740986', \ '9007199254740987': '9007199254740987', '9007199254740988': '9007199254740988', \ '9007199254740990': '9007199254740990', '9007199254740991': '9007199254740991', \ length: 9007199254740992 }; \ const r = Array.prototype.splice.call(o, 9007199254740987, 1); \ return r.length + '|' + r[0] + '|' + o.length + '|' + o['9007199254740986'] + '|' + \ o['9007199254740987'] + '|' + ('9007199254740988' in o) + '|' + \ o['9007199254740989'] + '|' + ('9007199254740990' in o) + '|' + o['9007199254740991']; })()", ) .unwrap(); assert_eq!(out, "1|9007199254740987|9007199254740990|9007199254740986|9007199254740988|false|9007199254740990|false|9007199254740991"); } // 引擎钉:splice 无参——length getter 恰读一次、setter 恰写一次且值为 0。 #[test] fn test_splice_no_args_length_get_set_once() { let out = eval_str( "(() => { let g = 0, s = 0, v = null; \ const o = { get length() { g += 1; return '0'; }, \ set length(x) { s += 1; v = x; } }; \ Array.prototype.splice.call(o); return g + '|' + s + '|' + v; })()", ) .unwrap(); assert_eq!(out, "1|1|0"); } // 引擎钉:reverse 在超大 arraylike 上首对即触发上端 getter(StopReverse 上抛)。 #[test] fn test_reverse_integer_limit_object_throws_on_first_pair() { let out = eval_str( "(() => { function StopReverse() {} \ const o = { get '9007199254740990'() { throw new StopReverse(); }, \ length: 9007199254740994 }; \ try { Array.prototype.reverse.call(o); return 'no-throw'; } \ catch (e) { return String(e instanceof StopReverse); } })()", ) .unwrap(); assert_eq!(out, "true"); } // 引擎钉:copyWithin 极限区间——源缺失位目标删位、present 位跨极大下标拷贝。 #[test] fn test_copy_within_integer_limit_range() { let out = eval_str( "(() => { const si = 9007199254740988; \ const o = { 0: 0, 1: 1, 2: 2, length: 9007199254740992 }; \ o[si] = -3; o[si + 2] = -1; \ Array.prototype.copyWithin.call(o, 0, si, si + 3); \ return o[0] + '|' + (1 in o) + '|' + o[2]; })()", ) .unwrap(); assert_eq!(out, "-3|false|-1"); } // 引擎钉:fill 极限区间三格全落值;setter 抛错原样上抛(严格 Set 面)。 #[test] fn test_fill_integer_limit_range_and_setter_throws() { let out = eval_str( "(() => { const si = 9007199254740988; const v = { m: 1 }; \ const o = { length: 9007199254740992 }; \ Array.prototype.fill.call(o, v, si, si + 3); \ const a2 = { length: 1 }; \ Object.defineProperty(a2, '0', { set: function () { throw new RangeError('set'); } }); \ let th = null; try { Array.prototype.fill.call(a2); } catch (e) { th = e.constructor.name; } \ return (o[si] === v) + '|' + (o[si + 1] === v) + '|' + (o[si + 2] === v) + '|' + th; })()", ) .unwrap(); assert_eq!(out, "true|true|true|RangeError"); } // 引擎钉:at 负索引自逻辑长度(稀疏覆盖值)折算,越界返回 undefined。 #[test] fn test_at_negative_index_uses_logical_length() { let out = eval_str( "(() => { const a = new Array(1); a.length = 2147483653; a[0] = 'x'; \ const b = new Array(1); b.length = 2147483649; \ return a.at(-2147483653) + '|' + String(b.at(2147483648) === undefined); })()", ) .unwrap(); assert_eq!(out, "x|true"); } // 引擎钉:at 极负索引 k = len + relative < 0 返 undefined(不钳零回首元素); // at(-len) 为首元素、at(-len-1) 为 undefined。 #[test] fn test_at_extreme_negative_index_undefined() { let out = eval_str( "(() => { const a = [7, 8]; \ return String(a.at(-1e20) === undefined) + '|' + a.at(-2) + '|' + String(a.at(-3) === undefined); })()", ) .unwrap(); assert_eq!(out, "true|7|true"); } // 引擎钉:基元 this 经 ToObject 装箱——reverse 返回 Boolean 包装体、splice 返回真数组。 #[test] fn test_splice_reverse_primitive_this_boxed() { let out = eval_str( "(() => { const r1 = Array.prototype.reverse.call(true); \ const r2 = Array.prototype.splice.call(false); \ return (r1 instanceof Boolean) + '|' + Array.isArray(r2) + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "true|true|0"); } // 引擎钉:concat 空数组实参按 ConcatSteps 展开为零元素——不贡献元素也不占位。 #[test] fn test_concat_empty_array_arg_expands_zero_elements() { let out = eval_str( "(() => { return [1].concat([]).length + '|' + [1].concat([], []).length + '|' + \ [].concat([]).length + '|' + [1, 2].concat(new Array(0)).length; })()", ) .unwrap(); assert_eq!(out, "1|1|0|2"); } // 引擎钉:concat 多实参混合 present/空数组/非数组实参展开序与规范一致, // 空数组实参中间穿插不占位(length 观察面钉死);洞实参位保洞。 #[test] fn test_concat_mixed_args_order_and_holes() { let out = eval_str( "(() => { const mixed = [].concat([], 5, []); \ const ordered = [1].concat([2, 3], 0, []); \ const r = [].concat(new Array(1)); \ return mixed.join(',') + '|' + mixed.length + '|' + ordered.join(',') + '|' + \ ordered.length + '|' + r.length + '|' + (0 in r); })()", ) .unwrap(); assert_eq!(out, "5|1|1,2,3,0|4|1|false"); } // ── concat species 面收口钉(node v20.19.2 实测值钉死) ─────────────────── // 引擎钉:concat 基元 this 经 ToObject 装箱——数字/布尔接收者展开为单元素 // 真数组,包装体身份保留。 #[test] fn test_concat_primitive_this_boxed() { let out = eval_str( "(() => { const r1 = Array.prototype.concat.call(101); \ const r2 = Array.prototype.concat.call(true); \ return (r1[0] instanceof Number) + '|' + r1.length + '|' + \ (r2[0] instanceof Boolean) + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "true|1|true|1"); } // 引擎钉:spreadable 对象源按 HasProperty 门控展开——洞位保洞、present 位 // 按 Get 取值,length 决定展开宽度。 #[test] fn test_concat_spreadable_object_holes() { let out = eval_str( "(() => { const o = { length: 6 }; o[Symbol.isConcatSpreadable] = true; \ o[0] = 'A'; o[2] = 'C'; \ const r = [].concat(o); \ return r.length + '|' + (0 in r) + '|' + (1 in r) + '|' + r[0] + '|' + r[2]; })()", ) .unwrap(); assert_eq!(out, "6|true|false|A|C"); } // 引擎钉:spreadable 源 length 为负数 / 经 toString 折算 NaN 均 ToLength 归 0, // 不展开任何元素。 #[test] fn test_concat_spreadable_length_to_zero() { let out = eval_str( "(() => { const o = { length: -5, 0: 'A' }; o[Symbol.isConcatSpreadable] = true; \ const r1 = [].concat(o); \ const o2 = { 0: 'A' }; o2[Symbol.isConcatSpreadable] = true; \ o2.length = { toString() { return 'SIX'; }, valueOf: null }; \ return r1.length + '|' + [].concat(o2).length; })()", ) .unwrap(); assert_eq!(out, "0|0"); } // 引擎钉:spreadable 源 length getter 抛错 / 索引访问器抛错均原样透传。 #[test] fn test_concat_poisoned_length_and_index_propagate() { let out = eval_str( "(() => { const o = {}; o[Symbol.isConcatSpreadable] = true; \ Object.defineProperty(o, 'length', { get() { throw new Error('lc'); } }); \ let t1 = ''; try { [].concat(o); } catch (e) { t1 = e.message; } \ const a = [1, 2, 3]; \ Object.defineProperty(a, 1, { configurable: true, get() { throw new Error('ie'); } }); \ let t2 = ''; try { [].concat(a); } catch (e) { t2 = e.message; } \ return t1 + '|' + t2; })()", ) .unwrap(); assert_eq!(out, "lc|ie"); } // 引擎钉:isConcatSpreadable getter 抛错透传(读序上先于一切展开)。 #[test] fn test_concat_is_concat_spreadable_getter_throws() { let out = eval_str( "(() => { const o = {}; \ Object.defineProperty(o, Symbol.isConcatSpreadable, { get() { throw new Error('sg'); } }); \ let t = ''; try { [].concat(o); } catch (e) { t = e.message; } \ return t; })()", ) .unwrap(); assert_eq!(out, "sg"); } // 引擎钉:读序 constructor → isConcatSpreadable(this) → isConcatSpreadable(实参), // getter 返回值 undefined 时实参整值追加。 #[test] fn test_concat_read_order_constructor_then_spreadable() { let out = eval_str( "(() => { const calls = []; \ const arr = []; \ Object.defineProperty(arr, 'constructor', { configurable: true, get() { calls.push('c'); return Array; } }); \ Object.defineProperty(arr, Symbol.isConcatSpreadable, { configurable: true, get() { calls.push('s'); } }); \ const arg = {}; \ Object.defineProperty(arg, Symbol.isConcatSpreadable, { configurable: true, get() { calls.push('as'); } }); \ const r = arr.concat(arg); \ return calls.join(',') + '|' + (r[0] === arg) + '|' + r.length; })()", ) .unwrap(); assert_eq!(out, "c,s,as|true|1"); } // 引擎钉:isConcatSpreadable 值非 undefined 取 ToBoolean——truthy 展开(零元素 // length)、falsey 整值追加。 #[test] fn test_concat_spreadable_value_boolean_coercion() { let out = eval_str( "(() => { const o = {}; o[Symbol.isConcatSpreadable] = 86; \ const r1 = [].concat(o); \ o[Symbol.isConcatSpreadable] = false; \ const r2 = [].concat(o); \ return r1.length + '|' + (r2[0] === o) + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "0|true|1"); } // 引擎钉:函数 / 正则 / 装箱串默认整值,显式 spreadable 时按索引展开(含 // 原型链命中),实参侧真数组参数自有索引访问器按 Get 触发。 #[test] fn test_concat_spreadable_function_and_proto_chain() { let out = eval_str( "(() => { const fn = function (a, b, c) {}; \ const r0 = [].concat(fn); \ fn[Symbol.isConcatSpreadable] = true; fn[0] = 1; fn[1] = 2; fn[2] = 3; \ const r1 = [].concat(fn); \ const a = [1, 2]; \ Object.defineProperty(a, 1, { configurable: true, get() { return 99; } }); \ const r2 = [0].concat(a); \ return (r0[0] === fn) + '|' + r1.join(',') + '|' + r1.length + '|' + r2[2] + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "true|1,2,3|3|99|3"); } // 引擎钉:arguments 源 spreadable + 删位——删位保洞、余位按值展开。 #[test] fn test_concat_arguments_deleted_slot_hole() { let out = eval_str( "(() => { const args = (function (a, b, c) { return arguments; })(1, 2, 3); \ delete args[1]; args[Symbol.isConcatSpreadable] = true; \ const r = [].concat(args); \ return r.length + '|' + (1 in r) + '|' + r[0] + '|' + r[2]; })()", ) .unwrap(); assert_eq!(out, "3|false|1|3"); } // 引擎钉:装箱串源 spreadable 时按 UTF-16 单元展开(单元未物化,直读串数据)。 #[test] fn test_concat_string_wrapper_spreadable_units() { let out = eval_str( "(() => { const s = new String('ab'); s[Symbol.isConcatSpreadable] = true; \ const r = [].concat(s); \ return r.join(',') + '|' + r.length + '|' + (0 in r); })()", ) .unwrap(); assert_eq!(out, "a,b|2|true"); } // 引擎钉:非数组对象 this 整值追加且实参保留(结果真数组、proto 为 // Array.prototype,不读 constructor)。 #[test] fn test_concat_non_array_this_whole_value_args_kept() { let out = eval_str( "(() => { let ctorReads = 0; \ const o = { length: 2, 0: 'x' }; \ Object.defineProperty(o, 'constructor', { configurable: true, get() { ctorReads++; } }); \ const r = Array.prototype.concat.call(o, 4, 5, 6); \ return r.length + '|' + (r[0] === o) + '|' + r[1] + '|' + r[3] + '|' + \ Array.isArray(r) + '|' + ctorReads; })()", ) .unwrap(); assert_eq!(out, "4|true|4|6|true|0"); } // 引擎钉:species 目标自有不可写 length——收尾 Set 抛 TypeError。 #[test] fn test_concat_species_non_writable_length_throws() { let out = eval_str( "(() => { const A = function (_l) { Object.defineProperty(this, 'length', { value: 0, writable: false }); }; \ const arr = []; arr.constructor = {}; arr.constructor[Symbol.species] = A; \ let t = ''; try { arr.concat(1); } catch (e) { t = e.name; } \ return t; })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:Array.prototype 只读 "0" 下 CDO 写自有 writable:true 位。 #[test] fn test_concat_prototype_readonly_index_own_writable() { let out = eval_str( "(() => { Object.defineProperty(Array.prototype, '0', { value: 100, writable: false, configurable: true }); \ try { const r = [101].concat(); \ const d = Object.getOwnPropertyDescriptor(r, '0'); \ return d.value + '|' + d.writable + '|' + r.hasOwnProperty('0'); } \ finally { delete Array.prototype['0']; } })()", ) .unwrap(); assert_eq!(out, "101|true|true"); } // ── flat / flatMap 收口钉(node v20.19.2 实测值钉死) ────────────────────── // 引擎钉:reverse (S,N) 臂非可配置 lower——Delete 先于 Set,抛时零改动。 #[test] fn test_reverse_sn_non_configurable_lower_throws_untouched() { let out = eval_str( "(() => { const a = [1, 2]; delete a[1]; \ Object.defineProperty(a, '0', { value: 1, writable: true, enumerable: true, configurable: false }); \ let threw = false; try { a.reverse(); } catch (e) { threw = e.name === 'TypeError'; } \ return threw + '|' + (1 in a) + '|' + a[0] + '|' + a[1]; })()", ) .unwrap(); assert_eq!(out, "true|false|1|undefined"); } // 引擎钉:reverse (N,S) 臂非可配置 upper——Set(lower) 先成功、Delete(upper) 抛, // 部分改动形态保留(判别力核心)。 #[test] fn test_reverse_ns_non_configurable_upper_partial_mutation() { let out = eval_str( "(() => { const a = [undefined, 2, 3]; delete a[0]; \ Object.defineProperty(a, '2', { value: 3, writable: true, enumerable: true, configurable: false }); \ let threw = false; try { a.reverse(); } catch (e) { threw = e.name === 'TypeError'; } \ return threw + '|' + a[0] + '|' + a[1] + '|' + a[2]; })()", ) .unwrap(); assert_eq!(out, "true|3|2|3"); } // 引擎钉:reverse 读序 Hf,Gf,Ht,Gt 逐对交错 + (S,S) 臂写序 S0,S3,S1,S2; // getter 还原原值,终读经 getter 呈现原序列(读序钉位核心)。 // 访问器逐条显式定义(不走 for 循环闭包捕获,规避既有作用域面)。 #[test] fn test_reverse_get_set_order_and_final_values() { let out = eval_str( "(() => { const a = [10, 11, 22, 30]; const log = []; \ Object.defineProperty(a, 0, { configurable: true, get() { log.push('G0'); return 10; }, set(x) { log.push('S0=' + x); } }); \ Object.defineProperty(a, 1, { configurable: true, get() { log.push('G1'); return 11; }, set(x) { log.push('S1=' + x); } }); \ Object.defineProperty(a, 2, { configurable: true, get() { log.push('G2'); return 22; }, set(x) { log.push('S2=' + x); } }); \ Object.defineProperty(a, 3, { configurable: true, get() { log.push('G3'); return 30; }, set(x) { log.push('S3=' + x); } }); \ a.reverse(); return log.join(',') + '|' + a.join(','); })()", ) .unwrap(); assert_eq!(out, "G0,G3,S0=30,S3=10,G1,G2,S1=22,S2=11|10,11,22,30"); } // 引擎钉:reverse getter 内 length=0 截断(GID 形态)——(S,N) 臂 Delete(lower) // 后 upper 写越界扩长,`0 in` 假、`1 in` 真、a[1] 为下值。 #[test] fn test_reverse_getter_length_zero_in_check() { let out = eval_str( "(() => { const array = ['first', 'second']; \ Object.defineProperty(array, 0, { get() { array.length = 0; return 'first'; } }); \ array.reverse(); return (0 in array) + '|' + (1 in array) + '|' + array[1]; })()", ) .unwrap(); assert_eq!(out, "false|true|first"); } // 引擎钉:flat arraylike 接收者——ToObject 通用入口 + LengthOfArrayLike 长度源, // 嵌套数组元素展开一层。 #[test] fn test_flat_arraylike_receiver_nested() { let out = eval_str( "(() => { const r = Array.prototype.flat.call({ length: 2, 0: [1], 1: [2] }); \ return r.join(',') + '|' + r.length; })()", ) .unwrap(); assert_eq!(out, "1,2|2"); } // 引擎钉:flat arraylike 分数长度——ToLength 折算 2.9 → 2,第三元素不入。 #[test] fn test_flat_arraylike_fractional_length() { let out = eval_str( "(() => { const r = Array.prototype.flat.call({ length: 2.9, 0: 1, 1: 2, 2: 3 }); \ return r.join(',') + '|' + r.length; })()", ) .unwrap(); assert_eq!(out, "1,2|2"); } // 引擎钉:flat arraylike NaN 长度——ToLength(NaN) = 0,空结果。 #[test] fn test_flat_arraylike_nan_length() { let out = eval_str( "(() => { const r = Array.prototype.flat.call({ length: NaN, 0: 1 }); \ return r.join(',') + '|' + r.length; })()", ) .unwrap(); assert_eq!(out, "|0"); } // 引擎钉:flat arraylike undefined 长度——Get length 缺省 undefined,ToLength 0。 #[test] fn test_flat_arraylike_undefined_length() { let out = eval_str( "(() => { const r = Array.prototype.flat.call({ length: undefined, 0: [1] }); \ return r.join(',') + '|' + r.length; })()", ) .unwrap(); assert_eq!(out, "|0"); } // 引擎钉:flat 装箱基元 this——ToObject 后长度为 0,结果空真数组。 #[test] fn test_flat_boxed_primitive_this() { let out = eval_str( "(() => { const r = Array.prototype.flat.call(true); \ return r.length + '|' + Array.isArray(r); })()", ) .unwrap(); assert_eq!(out, "0|true"); } // 引擎钉:flat 负 depth——ToIntegerOrInfinity 后归 0,不展开。 #[test] fn test_flat_negative_depth_no_flatten() { assert_eq!(eval_str("(() => { return JSON.stringify([[1]].flat(-1)); })()").unwrap(), "[[1]]"); } // 引擎钉:flat NaN depth——ToIntegerOrInfinity(NaN) = 0,不展开(非默认深 1)。 #[test] fn test_flat_nan_depth_no_flatten() { assert_eq!(eval_str("(() => { return JSON.stringify([[1]].flat(NaN)); })()").unwrap(), "[[1]]"); } // 引擎钉:flat 0 depth——0 不展开(旧 (n as i32).max(1) 形态会误展开一层)。 #[test] fn test_flat_zero_depth_no_flatten() { assert_eq!(eval_str("(() => { return JSON.stringify([[1]].flat(0)); })()").unwrap(), "[[1]]"); } // 引擎钉:flat +∞ depth——无限深度臂,全嵌套展开。 #[test] fn test_flat_infinity_depth() { let out = eval_str("(() => { return [1, [2, [3, [4]]]].flat(Infinity).join(','); })()").unwrap(); assert_eq!(out, "1,2,3,4"); } // 引擎钉:flat 超大 depth(i32 回绕域)——f64 域无回绕,浅数组上等效深 1。 #[test] fn test_flat_huge_depth_no_wrap() { assert_eq!(eval_str("(() => { return JSON.stringify([[1]].flat(3000000000)); })()").unwrap(), "[1]",); } // 引擎钉:flat Symbol / null 原型对象 depth——ToPrimitive 转换异常按规范传播 // TypeError(旧 unwrap_or 吞错形态会静默返空)。 #[test] fn test_flat_symbol_null_proto_depth_throws() { let out = eval_str( "(() => { let t1 = '?'; let t2 = '?'; \ try { [1].flat(Symbol()); } catch (e) { t1 = e.name; } \ try { [1].flat(Object.create(null)); } catch (e) { t2 = e.name; } \ return t1 + '|' + t2; })()", ) .unwrap(); assert_eq!(out, "TypeError|TypeError"); } // 引擎钉:flat species frozen 目标——CreateDataPropertyOrThrow 抛 TypeError。 #[test] fn test_flat_species_frozen_target_throws() { let out = eval_str( "(() => { const a = [1, 2]; a.constructor = {}; \ a.constructor[Symbol.species] = (n) => Object.freeze([]); \ let t = '?'; try { a.flat(); } catch (e) { t = e.name; } return t; })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:flat species 构造器为 null——ArraySpeciesCreate 步 9 抛 TypeError。 #[test] fn test_flat_species_null_ctor_throws() { let out = eval_str( "(() => { const a = [1, 2]; a.constructor = null; \ let t = '?'; try { a.flat(); } catch (e) { t = e.name; } return t; })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:flat species 非可写 '0' 槽(configurable)——CDO 覆写并恢复可写描述符; // 普通对象目标 length 为普通属性,不随 CDO 扩长。 #[test] fn test_flat_species_non_writable_slot_overwritten() { let out = eval_str( "(() => { function Ctor(n) { const o = {}; o.length = n; \ Object.defineProperty(o, '0', { writable: false, configurable: true, enumerable: true, value: 99 }); \ return o; } \ const a = [0, 1]; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ const r = a.flat(); const d = Object.getOwnPropertyDescriptor(r, '0'); \ return r[0] + '|' + d.writable + '|' + d.configurable + '|' + r.length; })()", ) .unwrap(); assert_eq!(out, "0|true|true|0"); } // 引擎钉:flat 洞语义——HasProperty 门控丢洞,结果恒紧凑(防误修回归)。 #[test] fn test_flat_holes_dropped_compact() { let out = eval_str( "(() => { const r1 = [1, , 2].flat(); const r2 = [[1, , 2], [3]].flat(); \ return r1.join(',') + '|' + r1.length + '|' + r2.join(',') + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "1,2|2|1,2,3|3"); } // 引擎钉:flat 真数组 length 增/减后——长度源为 ToLength(Get length),洞位丢洞。 #[test] fn test_flat_logical_length_grow_shrink() { let out = eval_str( "(() => { const a = [1, 2, 3]; a.length = 5; const r1 = a.flat(); \ const b = [1, 2, 3]; b.length = 1; const r2 = b.flat(); \ return r1.join(',') + '|' + r1.length + '|' + r2.join(',') + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "1,2,3|3|1|1"); } // 引擎钉:flat 非数组嵌套元素原样保留(防过度展开:仅真数组递归)。 #[test] fn test_flat_non_array_elements_kept() { let out = eval_str( "(() => { const o = { a: 1 }; const r = [[o], 2].flat(); \ return r.length + '|' + (r[0] === o) + '|' + r[1]; })()", ) .unwrap(); assert_eq!(out, "2|true|2"); } // 引擎钉:flatMap ToObject 通用入口——装箱基元 this(旧无装箱形态对基元抛 // TypeError)长度为 0 返空真数组;arraylike 接收者形态同入口,回调结果 // 真数组展开一层。 #[test] fn test_flat_map_to_object_entry() { let out = eval_str( "(() => { const r1 = Array.prototype.flatMap.call(5, (v) => v); \ const r2 = Array.prototype.flatMap.call({ length: 1, 0: 5 }, (v) => [v, v + 1]); \ return r1.length + '|' + Array.isArray(r1) + '|' + r2.join(',') + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "0|true|5,6|2"); } // 引擎钉:flatMap species——ctor 传播结果构造(旧无物种裸构造形态不走 ctor, // tag 缺位、ctor 日志空)。 #[test] fn test_flat_map_species_ctor_propagates() { let out = eval_str( "(() => { const log = []; function Ctor(n) { log.push('c' + n); const a = []; a.tag = 'X'; return a; } \ const a = [1, 2]; a.constructor = {}; a.constructor[Symbol.species] = Ctor; \ const r = a.flatMap((v) => [v]); \ return r.join(',') + '|' + r.length + '|' + r.tag + '|' + log.join(','); })()", ) .unwrap(); assert_eq!(out, "1,2|2|X|c0"); } // 引擎钉:flatMap species frozen 目标——CreateDataPropertyOrThrow 抛 TypeError // (旧裸写不抛形态静默落空不抛)。 #[test] fn test_flat_map_species_frozen_target_throws() { let out = eval_str( "(() => { const a = [1, 2]; a.constructor = {}; \ a.constructor[Symbol.species] = (n) => Object.freeze([]); \ let t = '?'; try { a.flatMap((v) => [v]); } catch (e) { t = e.name; } return t; })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:flatMap 回调结果洞源——展开长度源为 LengthOfArrayLike,洞位不入 // 目标(旧 prop_count 长度源形态少读洞后存在位,结果短一元素)。 #[test] fn test_flat_map_hole_source_length_of_arraylike() { let out = eval_str( "(() => { const r = [1, 2].flatMap((v) => { const x = new Array(2); x[1] = v; return x; }); \ return r.join(',') + '|' + r.length + '|' + (0 in r) + '|' + (1 in r); })()", ) .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"); } // 引擎钉:reverse arraylike 单端缺失——(N,S) 臂 Delete 先于 Set,缺端不物化。 #[test] fn test_reverse_arraylike_single_side_missing() { let out = eval_str( "(() => { const o = {0:'a', length:2}; Array.prototype.reverse.call(o); \ return (0 in o) + '|' + (1 in o) + '|' + o[1] + '|' + o.length; })()", ) .unwrap(); assert_eq!(out, "false|true|a|2"); } // 引擎钉:reverse arraylike 洞位保持——洞不物化、length 不变。 #[test] fn test_reverse_arraylike_hole_kept() { let out = eval_str( "(() => { const o = {0:'a', 2:'b', length:3}; Array.prototype.reverse.call(o); \ return (0 in o) + '|' + (1 in o) + '|' + (2 in o) + '|' + o[0] + '|' + o[2] + '|' + o.length; })()", ) .unwrap(); assert_eq!(out, "true|false|true|b|a|3"); } // 引擎钉:reverse arraylike accessor length——getter 触发一次,抛错原值传播。 #[test] fn test_reverse_arraylike_length_accessor_throws() { let result = eval_value( "(() => { const o = {0:'x', 1:'y', length:2}; \ Object.defineProperty(o,'length',{get(){throw 99;},set(v){},enumerable:true,configurable:true}); \ Array.prototype.reverse.call(o); return 'no-throw'; })()", ); match result { Err(e) => assert!(e.contains("99"), "error should carry thrown 99, got: {}", e), Ok(_) => panic!("expected accessor length throw to propagate"), } } // 引擎钉:reverse arraylike length 值形态——ToLength 取整定界、原值不写回。 #[test] fn test_reverse_arraylike_length_not_written_back() { let out = eval_str( "(() => { const o = {0:'x', 1:'y', length:'2'}; Array.prototype.reverse.call(o); \ return o.length + '|' + o[0] + '|' + o[1]; })()", ) .unwrap(); assert_eq!(out, "2|y|x"); let out = eval_str( "(() => { const o = {0:'x', 1:'y', 2:'z', length:2.7}; Array.prototype.reverse.call(o); \ return o.length + '|' + o[0] + '|' + o[1]; })()", ) .unwrap(); assert_eq!(out, "2.7|y|x"); } // map species 实例型 Ctor:构造恰一次且收单参 n=5,返回体直接作结果, // 回调值经 CreateDataPropertyOrThrow 逐位写入。 #[test] fn test_map_species_instance_ctor() { let out = eval_str( "(() => { let cc = 0, seen = null; \ function C1(n) { cc++; seen = n; return { tag: 'inst' }; } \ const a = [1,2,3,4,5]; a.constructor = {}; a.constructor[Symbol.species] = C1; \ const r = a.map(x => x * 2); \ return cc + '|' + seen + '|' + r.tag + '|' + r[0] + '|' + r[4]; })()", ) .unwrap(); assert_eq!(out, "1|5|inst|2|10"); } // map species 普通 Ctor 无返回体:结果非数组、原型为 Ctor.prototype,且无收尾 // Set(length)(length 为 undefined,仅元素位 present)。 #[test] fn test_map_species_plain_ctor_no_length() { let out = eval_str( "(() => { let cc = 0; \ function C2(n) { cc++; } \ const a = [1,2,3]; a.constructor = {}; a.constructor[Symbol.species] = C2; \ const r = a.map(x => x + 10); \ return cc + '|' + Array.isArray(r) + '|' + (Object.getPrototypeOf(r) === C2.prototype) + '|' + \ r.length + '|' + r[0] + '|' + r[2]; })()", ) .unwrap(); assert_eq!(out, "1|false|true|undefined|11|13"); } // map constructor 访问器抛错:Get 异常原样传播。 #[test] fn test_map_ctor_accessor_throws() { let out = eval_str( "(() => { const a = [1]; \ Object.defineProperty(a, 'constructor', { get() { throw new Error('ctor-poison'); } }); \ try { a.map(x => x); return 'no-throw'; } catch (e) { return e.name + ':' + e.message; } })()", ) .unwrap(); assert_eq!(out, "Error:ctor-poison"); } // map constructor 为非对象非 undefined(null/数字/字符串/布尔四值)均抛 TypeError。 #[test] fn test_map_ctor_non_object_throws() { let out = eval_str( "(() => { let ok = 0; \ for (const cv of [null, 1, 'string', true]) { \ const a = [1,2]; a.constructor = cv; \ try { a.map(x => x); } catch (e) { if (e instanceof TypeError) ok++; } } \ return String(ok); })()", ) .unwrap(); assert_eq!(out, "4"); } // map @@species 访问器抛错原样透传。 #[test] fn test_map_species_accessor_throws() { let out = eval_str( "(() => { const a = [1]; a.constructor = { get [Symbol.species]() { throw new Error('species-poison'); } }; \ try { a.map(x => x); return 'no-throw'; } catch (e) { return e.name + ':' + e.message; } })()", ) .unwrap(); assert_eq!(out, "Error:species-poison"); } // map species 为非构造器对象(箭头函数)抛 TypeError。 #[test] fn test_map_species_non_ctor_throws() { let out = eval_str( "(() => { const a = [1]; a.constructor = {}; a.constructor[Symbol.species] = () => {}; \ try { a.map(x => x); return 'no-throw'; } catch (e) { return e.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // map 目标不可扩展:CreateDataPropertyOrThrow 抛 TypeError。 #[test] fn test_map_species_non_extensible_throws() { let out = eval_str( "(() => { function C3() { return Object.preventExtensions({}); } \ const a = [1]; a.constructor = {}; a.constructor[Symbol.species] = C3; \ try { a.map(x => x); return 'no-throw'; } catch (e) { return e.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // map 洞位输入:洞不写属性(结果该位缺失),length 与 present 位值正确。 #[test] fn test_map_hole_skipped_not_written() { let out = eval_str( "(() => { const r = [1,,3].map(x => x * 10); \ return (1 in r) + '|' + r.length + '|' + r[0] + '|' + r[2]; })()", ) .unwrap(); assert_eq!(out, "false|3|10|30"); } // map species 为 null / undefined:回退真数组(原型 Array.prototype),值正确。 #[test] fn test_map_species_null_undef_plain_array() { let out = eval_str( "(() => { const a = [1,2]; a.constructor = {}; a.constructor[Symbol.species] = null; \ const r1 = a.map(x => x); \ const b = [1,2]; b.constructor = {}; b.constructor[Symbol.species] = undefined; \ const r2 = b.map(x => x); \ return Array.isArray(r1) + '|' + (Object.getPrototypeOf(r1) === Array.prototype) + '|' + r1.length + '|' + \ Array.isArray(r2) + '|' + r2.length; })()", ) .unwrap(); assert_eq!(out, "true|true|2|true|2"); } // filter species 实例型 Ctor:构造恰一次且收单参 count=0,返回体直接作结果, // 保留元素逐位写入。 #[test] fn test_filter_species_instance_ctor() { let out = eval_str( "(() => { let cc = 0, seen = null; \ function F1(n) { cc++; seen = n; return { tag: 'inst' }; } \ const a = [1,2,3,4,5,6]; a.constructor = {}; a.constructor[Symbol.species] = F1; \ const r = a.filter(x => x % 2 === 0); \ return cc + '|' + seen + '|' + r.tag + '|' + r[0] + '|' + r[1]; })()", ) .unwrap(); assert_eq!(out, "1|0|inst|2|4"); } // filter species Ctor 预设 length=99:无收尾 Set(length),预设值原样保留, // 保留元素逐位写入。 #[test] fn test_filter_species_pre_length_kept() { let out = eval_str( "(() => { let cc = 0; \ function F2(n) { cc++; this.length = 99; } \ const a = [1,2,3,4]; a.constructor = {}; a.constructor[Symbol.species] = F2; \ const r = a.filter(x => x > 1); \ return cc + '|' + Object.prototype.hasOwnProperty.call(r, 'length') + '|' + r.length + '|' + \ r[0] + '|' + r[1] + '|' + Object.keys(r).join(','); })()", ) .unwrap(); assert_eq!(out, "1|true|99|2|3|0,1,2,length"); } // filter constructor 访问器抛错:Get 异常原样传播。 #[test] fn test_filter_ctor_accessor_throws() { let out = eval_str( "(() => { const a = [1]; \ Object.defineProperty(a, 'constructor', { get() { throw new Error('ctor-poison'); } }); \ try { a.filter(x => true); return 'no-throw'; } catch (e) { return e.name + ':' + e.message; } })()", ) .unwrap(); assert_eq!(out, "Error:ctor-poison"); } // filter constructor 为非对象非 undefined(null/数字/字符串/布尔四值)均抛 TypeError。 #[test] fn test_filter_ctor_non_object_throws() { let out = eval_str( "(() => { let ok = 0; \ for (const cv of [null, 1, 'string', true]) { \ const a = [1]; a.constructor = cv; \ try { a.filter(x => true); } catch (e) { if (e instanceof TypeError) ok++; } } \ return String(ok); })()", ) .unwrap(); assert_eq!(out, "4"); } // filter @@species 访问器抛错原样透传。 #[test] fn test_filter_species_accessor_throws() { let out = eval_str( "(() => { const a = [1]; a.constructor = { get [Symbol.species]() { throw new Error('species-poison'); } }; \ try { a.filter(x => true); return 'no-throw'; } catch (e) { return e.name + ':' + e.message; } })()", ) .unwrap(); assert_eq!(out, "Error:species-poison"); } // filter species 为非构造器对象(箭头函数)抛 TypeError。 #[test] fn test_filter_species_non_ctor_throws() { let out = eval_str( "(() => { const a = [1]; a.constructor = {}; a.constructor[Symbol.species] = () => {}; \ try { a.filter(x => true); return 'no-throw'; } catch (e) { return e.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // filter 目标不可扩展:CreateDataPropertyOrThrow 抛 TypeError。 #[test] fn test_filter_species_non_extensible_throws() { let out = eval_str( "(() => { function C3() { return Object.preventExtensions({}); } \ const a = [1,2,3,4]; a.constructor = {}; a.constructor[Symbol.species] = C3; \ try { a.filter(x => x > 0); return 'no-throw'; } catch (e) { return e.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // filter 非数组接收者(arraylike):结果恒为真数组,值正确。 #[test] fn test_filter_arraylike_receiver_plain() { let out = eval_str( "(() => { const r = Array.prototype.filter.call({ 0: 1, 1: 2, length: 2 }, x => x > 0); \ return Array.isArray(r) + '|' + r.length + '|' + r.join(','); })()", ) .unwrap(); assert_eq!(out, "true|2|1,2"); } // filter 普通数组无 species 覆写:结果真数组、值与长度正确(防过度修)。 #[test] fn test_filter_plain_array_plain_result() { let out = eval_str( "(() => { const r = [1,2,3,4].filter(x => x % 2 === 0); \ return Array.isArray(r) + '|' + r.length + '|' + r.join(','); })()", ) .unwrap(); assert_eq!(out, "true|2|2,4"); } // 可构造 C 覆写 @@species:结果经 species 构造(Other 实例),而非 C 本身。 #[test] fn test_slice_species_ctor_override_returns_other() { let out = eval_str( "(() => { function Base(n) { this.tag = 'base'; } \ function Other(n) { this.tag = 'other'; } \ Base[Symbol.species] = Other; \ const a = [1,2,3]; a.constructor = Base; \ const r = a.slice(0); \ return r.tag + '|' + Array.isArray(r); })()", ) .unwrap(); assert_eq!(out, "other|false"); } // 可构造 C 无 @@species:Get 查得 undefined,回退真数组(原型 Array.prototype)。 #[test] fn test_slice_ctor_plain_no_species_plain_array() { let out = eval_str( "(() => { function C(n) { this.tag = 'C'; } \ const a = [1,2,3]; a.constructor = C; \ const r = a.slice(0); \ return Array.isArray(r) + '|' + (Object.getPrototypeOf(r) === Array.prototype); })()", ) .unwrap(); assert_eq!(out, "true|true"); } // 可构造 C 的 @@species 为非构造器对象抛 TypeError。 #[test] fn test_slice_species_non_ctor_throws() { let out = eval_str( "(() => { function C3(n) { this.tag = 'C3'; } C3[Symbol.species] = () => {}; \ const a = [1,2,3]; a.constructor = C3; \ try { a.slice(0); return 'no-throw'; } catch (e) { return e.name; } })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 可构造 C 的 @@species 访问器抛错原样透传。 #[test] fn test_slice_species_accessor_throws() { let out = eval_str( "(() => { function C4(n) { this.tag = 'C4'; } \ Object.defineProperty(C4, Symbol.species, { get() { throw new Error('species-poison'); } }); \ const a = [1,2,3]; a.constructor = C4; \ try { a.slice(0); return 'no-throw'; } catch (e) { return e.name + ':' + e.message; } })()", ) .unwrap(); assert_eq!(out, "Error:species-poison"); } // 守卫:c={} 不可构造形态经 @@species 构造(136 系列保真,防 228 修点漂移)。 #[test] fn test_slice_ctor_braces_species_plain() { let out = eval_str( "(() => { function G(n) { this.tag = 'G'; } \ const a = [1,2,3]; a.constructor = {}; a.constructor[Symbol.species] = G; \ const r = a.slice(0); \ return r.tag; })()", ) .unwrap(); assert_eq!(out, "G"); } // 守卫:普通数组 slice 无 species 覆写,结果真数组、值正确。 #[test] fn test_slice_plain_array_guard() { let out = eval_str( "(() => { const r = [1,2,3].slice(0); \ return Array.isArray(r) + '|' + r.join(','); })()", ) .unwrap(); assert_eq!(out, "true|1,2,3"); } // ── push/pop/shift/unshift arraylike 接收者族收口钉(node v20.19.2 实测值钉死) ── // 引擎钉:push arraylike 接收者——length 经 ToLength 折算(1.5→1),尾部写值, // length 置新长度。 #[test] fn test_push_arraylike_receiver_length_coerce() { let out = eval_str( "(() => { const o = { length: 1.5, 0: 'a' }; \ const r = Array.prototype.push.call(o, -13); \ return r + '|' + o[1] + '|' + o.length; })()", ) .unwrap(); assert_eq!(out, "2|-13|2"); } // 引擎钉:push arraylike valueOf length——ToObject 后 ToLength 取整定界。 #[test] fn test_push_arraylike_value_of_length() { let out = eval_str( "(() => { const r = Array.prototype.push.call({ length: { valueOf() { return 3; } } }); \ return String(r); })()", ) .unwrap(); assert_eq!(out, "3"); } // 引擎钉:push ToObject 装箱基元 this——Boolean 包装体 length 0,返回 0。 #[test] fn test_push_primitive_this_boxed() { let out = eval_str("(() => { return String(Array.prototype.push.call(true)); })()").unwrap(); assert_eq!(out, "0"); } // 引擎钉:push 2^53-1 预检——len + 参数数越限抛 TypeError。 #[test] fn test_push_integer_limit_throws_type_error() { let out = eval_str( "(() => { const o = {}; o.length = 9007199254740991; \ let t = '?'; try { Array.prototype.push.call(o, 1); } catch (e) { t = e.name; } \ return t; })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:pop arraylike 2^53 长度——ToLength 钳 2^53-1,pop 末位(undefined), // length 收 2^53-2。 #[test] fn test_pop_arraylike_integer_limit() { let out = eval_str( "(() => { const o = { length: 9007199254740992 }; \ const r = Array.prototype.pop.call(o); \ return String(r === undefined) + '|' + o.length; })()", ) .unwrap(); assert_eq!(out, "true|9007199254740990"); } // 引擎钉:pop 末位非可配置自有索引——DeletePropertyOrThrow 抛 TypeError。 #[test] fn test_pop_last_slot_non_configurable_throws() { let out = eval_str( "(() => { const o = { length: 1, 0: 5 }; \ Object.defineProperty(o, '0', { value: 5, writable: true, enumerable: true, configurable: false }); \ let t = '?'; try { Array.prototype.pop.call(o); } catch (e) { t = e.name; } \ return t; })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:pop 原型链继承末位元素——Get 读继承值,末位非自有删位空操作, // 继承读持续可读。 #[test] fn test_pop_inherited_last_element() { let out = eval_str( "(() => { Array.prototype[1] = 1; \ try { const x = [0]; x.length = 2; const r = x.pop(); \ return r + '|' + x.length + '|' + x[1]; } \ finally { delete Array.prototype[1]; } })()", ) .unwrap(); assert_eq!(out, "1|1|1"); } // 引擎钉:shift arraylike 接收者——缺失中位删目标格、present 位前移、 // 末位显式删除。 #[test] fn test_shift_arraylike_receiver() { let out = eval_str( "(() => { const o = { 0: 0, 3: 3, length: 4 }; \ const r = Array.prototype.shift.call(o); \ return r + '|' + o.length + '|' + (3 in o) + '|' + o[2]; })()", ) .unwrap(); assert_eq!(out, "0|3|false|3"); } // 引擎钉:shift ToObject 装箱基元 this——Boolean 包装体 length 0,返回 undefined。 #[test] fn test_shift_primitive_this_boxed() { let out = eval_str("(() => { return String(Array.prototype.shift.call(true)); })()").unwrap(); assert_eq!(out, "undefined"); } // 引擎钉:unshift 无参 2^53-1 极限——argCount 0 不查限,返回 2^53-1。 #[test] fn test_unshift_no_args_integer_limit_no_check() { let out = eval_str( "(() => { const o = {}; o.length = 9007199254740991; \ const r = Array.prototype.unshift.call(o); \ return String(r === 9007199254740991) + '|' + o.length; })()", ) .unwrap(); assert_eq!(out, "true|9007199254740991"); } // 引擎钉:unshift 2^53-1 预检——len + 参数数越限抛 TypeError(现行规范文本, // 非 RangeError)。 #[test] fn test_unshift_integer_limit_throws_type_error() { let out = eval_str( "(() => { const o = {}; o.length = 9007199254740991; \ let t = '?'; try { Array.prototype.unshift.call(o, 1); } catch (e) { t = e.name; } \ return t; })()", ) .unwrap(); assert_eq!(out, "TypeError"); } // 引擎钉:unshift arraylike 接收者——已有元素右移、实参填头、length 扩容。 #[test] fn test_unshift_arraylike_receiver() { let out = eval_str( "(() => { const o = { length: 1, 0: 'a' }; \ const r = Array.prototype.unshift.call(o, 'x'); \ return r + '|' + o[0] + '|' + o[1]; })()", ) .unwrap(); assert_eq!(out, "2|x|a"); } // 引擎钉:unshift ToObject 装箱基元 this——Boolean 包装体 length 0,两实参返回 2。 #[test] fn test_unshift_primitive_this_boxed() { let out = eval_str("(() => { return String(Array.prototype.unshift.call(false, 7, 8)); })()").unwrap(); assert_eq!(out, "2"); } // 引擎钉:pop 末位 getter 返回冻结哨兵对象跨 delete/Set-length 用户窗口—— // 身份保持(钉位语义的可观察契约)。 #[test] fn test_pop_getter_sentinel_identity_across_window() { let out = eval_str( "(() => { const a = [10, 20]; const sentinel = Object.freeze({ tag: 'S' }); \ Object.defineProperty(a, 1, { get() { const junk = []; for (let i = 0; i < 100000; i++) junk.push({ i }); return sentinel; }, \ configurable: true }); \ const r = a.pop(); \ return String(r === sentinel) + '|' + a.length; })()", ) .unwrap(); assert_eq!(out, "true|1"); }