mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: splice removed 数组按 species 构造并严格化 length 写
This commit is contained in:
@@ -194,3 +194,194 @@ 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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user