mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
271 lines
9.4 KiB
Rust
271 lines
9.4 KiB
Rust
//! native pack 实参区 × 恢复边界镜像重载的引擎钉:native pack 协议在飞
|
||
//! (`.call`/`.apply` 转发、直接成员调用)期间,pack 与实参读取之间若存在用户
|
||
//! 内联窗(length getter / 回调),窗口拷回后的调用方模块镜像重载会把顶层
|
||
//! 镜像名集(Array/Object 着落于 pack 区寄存器)重写回寄存器,覆写外层
|
||
//! builtin 尚未读取的实参值。期望值一律取 node v20.19.2 实测值。
|
||
|
||
use std::sync::Arc;
|
||
|
||
use oxide_compiler::compiler::Compiler;
|
||
use oxide_parser::Allocator;
|
||
use oxide_vm::vm::Vm;
|
||
|
||
fn compile(source: &str) -> oxide_bytecode::module::CompiledModule {
|
||
let allocator = Allocator::default();
|
||
let program = oxide_parser::parse(&allocator, source).expect("parse");
|
||
Compiler::new().compile(&program).expect("compile")
|
||
}
|
||
|
||
/// 运行脚本并取最终值(钉的期望值统一为字符串形态)。
|
||
fn eval_str(source: &str) -> String {
|
||
let module = compile(source);
|
||
let mut vm = Vm::new();
|
||
let result = vm.run(&Arc::new(module)).expect("run");
|
||
vm.lookup_str(result).expect("结果应为字符串").to_string()
|
||
}
|
||
|
||
// 共用的 length getter 装置:基元/对象 this 的 length 走用户 accessor,
|
||
// accessor 内联窗在 pack 与实参读取之间打开。
|
||
const LENGTH_GETTER: &str = "var savedLen = 1; \
|
||
Object.defineProperty(Number.prototype, 'length', { \
|
||
configurable: true, \
|
||
get: function () { return savedLen; }, \
|
||
set: function (v) { savedLen = v; } }); \
|
||
";
|
||
|
||
/// 在 body 脚本前拼入 length getter 装置,返回完整脚本。
|
||
fn with_length_getter(body: &str) -> String {
|
||
format!("{LENGTH_GETTER}{body}")
|
||
}
|
||
|
||
/// `.call` 转发:splice 的 deleteCount 落入 reg 1(顶层镜像着色域),
|
||
/// length getter 内联窗先于实参读取,窗口拷回后的镜像重载不得覆写实参区。
|
||
#[test]
|
||
fn splice_call_length_getter_keeps_delete_count() {
|
||
assert_eq!(
|
||
eval_str(&with_length_getter(
|
||
"var r = Array.prototype.splice.call(5, 0, 1, 'a', 'b'); \
|
||
delete Number.prototype.length; \
|
||
[r.length, savedLen].join('|')"
|
||
)),
|
||
"1|2"
|
||
);
|
||
}
|
||
|
||
/// 对象 this 同形态:装箱非触发条件,覆写对对象接收者同样成立。
|
||
#[test]
|
||
fn splice_object_this_length_getter_keeps_delete_count() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var savedLen = 1; \
|
||
var o = {}; \
|
||
Object.defineProperty(o, 'length', { \
|
||
configurable: true, \
|
||
get: function () { return savedLen; }, \
|
||
set: function (v) { savedLen = v; } }); \
|
||
var r = Array.prototype.splice.call(o, 0, 1, 'a', 'b'); \
|
||
[r.length, savedLen].join('|')"
|
||
),
|
||
"1|2"
|
||
);
|
||
}
|
||
|
||
/// 最小 getter 体(无副作用):覆写与 getter 复杂度无关,是最小判别形。
|
||
#[test]
|
||
fn splice_minimal_getter_keeps_delete_count() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var savedLen = 1; \
|
||
Object.defineProperty(Number.prototype, 'length', { \
|
||
configurable: true, \
|
||
get: function () { return 1; }, \
|
||
set: function (v) { savedLen = v; } }); \
|
||
var r = Array.prototype.splice.call(5, 0, 1, 'a', 'b'); \
|
||
delete Number.prototype.length; \
|
||
[r.length, savedLen].join('|')"
|
||
),
|
||
"1|2"
|
||
);
|
||
}
|
||
|
||
/// 2 实参 pack 区 [0, 2) 恰好覆盖第一个镜像寄存器:deleteCount 读值不得失真。
|
||
#[test]
|
||
fn splice_two_arg_pack_keeps_delete_count() {
|
||
assert_eq!(
|
||
eval_str(&with_length_getter(
|
||
"var r = Array.prototype.splice.call(5, 0, 1); \
|
||
delete Number.prototype.length; \
|
||
[r.length, savedLen].join('|')"
|
||
)),
|
||
"1|0"
|
||
);
|
||
}
|
||
|
||
/// 大 getter 体(字符串 churn、寄存器需求上升):覆写与窗宽无关,窗宽充分。
|
||
#[test]
|
||
fn splice_churn_getter_keeps_delete_count() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var savedLen = 1; \
|
||
Object.defineProperty(Number.prototype, 'length', { \
|
||
configurable: true, \
|
||
get: function () { \
|
||
var s = ''; \
|
||
for (var i = 0; i < 200; i++) { s = s + 'churn' + i; } \
|
||
globalThis.__churn = s; \
|
||
return savedLen; \
|
||
}, \
|
||
set: function (v) { savedLen = v; } }); \
|
||
var r = Array.prototype.splice.call(5, 0, 1, 'a', 'b'); \
|
||
delete Number.prototype.length; \
|
||
[r.length, savedLen].join('|')"
|
||
),
|
||
"1|2"
|
||
);
|
||
}
|
||
|
||
/// fill 的 start/end 落 pack 区 reg 1/2:区间两端读值不得折算失真,
|
||
/// 填充范围与未填充位保持 node 语义(length getter 开窗)。
|
||
#[test]
|
||
fn fill_arraylike_length_getter_keeps_range() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var o = {}; \
|
||
Object.defineProperty(o, 'length', { \
|
||
configurable: true, \
|
||
get: function () { return 2; }, \
|
||
set: function (v) {} }); \
|
||
var r = Array.prototype.fill.call(o, 'v', 0, 1); \
|
||
[r[0] === 'v' ? 1 : 0, r[1] === undefined ? 1 : 0].join('|')"
|
||
),
|
||
"1|1"
|
||
);
|
||
}
|
||
|
||
/// 5 实参 pack 区 [0, 5) 全量覆盖镜像着色集:全部实参存活,结果与插入区
|
||
/// 逐位保持。
|
||
#[test]
|
||
fn splice_five_arg_pack_keeps_all_args() {
|
||
assert_eq!(
|
||
eval_str(&with_length_getter(
|
||
"var r = Array.prototype.splice.call(5, 0, 1, 'a', 'b', 'c'); \
|
||
delete Number.prototype.length; \
|
||
[r.length, r[0], r[1], r[2], savedLen].join('|')"
|
||
)),
|
||
"1||||3"
|
||
);
|
||
}
|
||
|
||
/// getter 自读镜像(`Array === Array`):内联入口重载自愈,修后保真——
|
||
/// 入口重载语义不动,恢复边界 gate 不影响入口侧镜像可见性。
|
||
#[test]
|
||
fn splice_getter_mirror_self_read_stays_true() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var savedLen = 1; \
|
||
Object.defineProperty(Number.prototype, 'length', { \
|
||
configurable: true, \
|
||
get: function () { return Array === Array && Object === Object ? 1 : 0; }, \
|
||
set: function (v) { savedLen = v; } }); \
|
||
var r = Array.prototype.splice.call(5, 0, 1, 'a', 'b'); \
|
||
delete Number.prototype.length; \
|
||
[r.length, savedLen].join('|')"
|
||
),
|
||
"1|2"
|
||
);
|
||
}
|
||
|
||
/// getter 内嵌套用户 CALL(帧路径):帧返回后的恢复边界同门控,帧路径
|
||
/// 结果与无嵌套时逐位一致。
|
||
#[test]
|
||
fn splice_getter_nested_call_keeps_result() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var savedLen = 1; \
|
||
function probe() { return 42; } \
|
||
Object.defineProperty(Number.prototype, 'length', { \
|
||
configurable: true, \
|
||
get: function () { var x = probe(); return savedLen; }, \
|
||
set: function (v) { savedLen = v; } }); \
|
||
var r = Array.prototype.splice.call(5, 0, 1, 'a', 'b'); \
|
||
delete Number.prototype.length; \
|
||
[r.length, savedLen].join('|')"
|
||
),
|
||
"1|2"
|
||
);
|
||
}
|
||
|
||
/// indexOf 的 fromIndex 落 pack 区 reg 1:arraylike 接收者 length getter
|
||
/// 开窗后 fromIndex 读值不得折算为 0。
|
||
#[test]
|
||
fn index_of_arraylike_length_getter_keeps_from_index() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var savedLen = 3; \
|
||
var a = { 0: 10, 1: 20, 2: 10 }; \
|
||
Object.defineProperty(a, 'length', { \
|
||
configurable: true, \
|
||
get: function () { return savedLen; }, \
|
||
set: function (v) { savedLen = v; } }); \
|
||
String(Array.prototype.indexOf.call(a, 10, 1))"
|
||
),
|
||
"2"
|
||
);
|
||
}
|
||
|
||
/// reduce 的 init 落 pack 区 reg 2:arraylike length getter 开窗后
|
||
/// init 读值不得被镜像值(Object 内置函数)替换。
|
||
#[test]
|
||
fn reduce_arraylike_length_getter_keeps_init() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var savedLen = 1; \
|
||
var a = { 0: 12 }; \
|
||
Object.defineProperty(a, 'length', { \
|
||
configurable: true, \
|
||
get: function () { return savedLen; }, \
|
||
set: function (v) { savedLen = v; } }); \
|
||
String(Array.prototype.reduce.call(a, function (x, y) { return x + y; }, 1))"
|
||
),
|
||
"13"
|
||
);
|
||
}
|
||
|
||
/// `.apply` 转发同形态:TailCall 链 pack_end 嵌套存还后实参区保持。
|
||
#[test]
|
||
fn splice_apply_forward_keeps_delete_count() {
|
||
assert_eq!(
|
||
eval_str(&with_length_getter(
|
||
"var r = Array.prototype.splice.apply(5, [0, 1, 'a', 'b']); \
|
||
delete Number.prototype.length; \
|
||
[r.length, savedLen].join('|')"
|
||
)),
|
||
"1|2"
|
||
);
|
||
}
|
||
|
||
/// 无用户内联窗回归:无窗时 pack 区无覆写风险点,结果保持 node 语义。
|
||
#[test]
|
||
fn splice_no_window_result_unchanged() {
|
||
assert_eq!(
|
||
eval_str(
|
||
"var r = Array.prototype.splice.call(5, 0, 1, 'a', 'b'); \
|
||
[r.length, r[0], r[1]].join('|')"
|
||
),
|
||
"0||"
|
||
);
|
||
}
|
||
|
||
/// 1 实参 pack 区 [0, 1) 不与镜像着色集求交:reg 0 恒为结果槽,此形无风险。
|
||
#[test]
|
||
fn splice_one_arg_pack_stays_safe() {
|
||
assert_eq!(
|
||
eval_str(&with_length_getter(
|
||
"var r = Array.prototype.splice.call(5, 0); \
|
||
delete Number.prototype.length; \
|
||
[r.length, savedLen].join('|')"
|
||
)),
|
||
"1|0"
|
||
);
|
||
}
|