mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: spread 字面量 elision 置洞并扩长
This commit is contained in:
@@ -58,6 +58,9 @@ impl Emitter {
|
|||||||
let one_reg = ctx.alloc_reg();
|
let one_reg = ctx.alloc_reg();
|
||||||
let one_idx = ctx.add_constant(Constant::Int(1));
|
let one_idx = ctx.add_constant(Constant::Int(1));
|
||||||
ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx));
|
ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx));
|
||||||
|
let undef_reg = ctx.alloc_reg();
|
||||||
|
let undef_idx = ctx.add_constant(Constant::Undefined);
|
||||||
|
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
|
||||||
|
|
||||||
for elem in &arr.elements {
|
for elem in &arr.elements {
|
||||||
if let Some(e) = elem.as_expression() {
|
if let Some(e) = elem.as_expression() {
|
||||||
@@ -75,7 +78,26 @@ impl Emitter {
|
|||||||
Operand::Reg(one_reg),
|
Operand::Reg(one_reg),
|
||||||
));
|
));
|
||||||
} else if elem.is_elision() {
|
} else if elem.is_elision() {
|
||||||
// hole:只推进 index,不写元素,length 由后续 SET_ELEM 越界扩容或初值覆盖。
|
// hole:先写 present-undefined 把 length 扩到 I+1,再删除置洞。
|
||||||
|
// 顺序不可换:DELETE 先跑对界外下标是空操作,随后的写会留下 present 槽。
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::SET_ELEM,
|
||||||
|
Operand::Reg(arr_reg),
|
||||||
|
Operand::Reg(index_reg),
|
||||||
|
Operand::Reg(undef_reg),
|
||||||
|
));
|
||||||
|
|
||||||
|
// 复制 arr 到 scratch 执行删除:DELETE_PROP_DYNAMIC 把结果写回 rd 槽,
|
||||||
|
// 直接用 arr_reg 会把数组寄存器覆盖成布尔值。
|
||||||
|
let scratch = ctx.alloc_reg();
|
||||||
|
ctx.inst(Inst::inst_mov(Operand::Reg(scratch), Operand::Reg(arr_reg)));
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::DELETE_PROP_DYNAMIC,
|
||||||
|
Operand::Reg(scratch),
|
||||||
|
Operand::Reg(scratch),
|
||||||
|
Operand::Reg(index_reg),
|
||||||
|
));
|
||||||
|
|
||||||
ctx.inst(Inst::new(
|
ctx.inst(Inst::new(
|
||||||
OpCode::ADD,
|
OpCode::ADD,
|
||||||
Operand::Reg(index_reg),
|
Operand::Reg(index_reg),
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
//! 数组字面量 elision(hole 元素)引擎钉:静态字面量 elision 由 emit 侧删除置洞,
|
||||||
|
//! spread 字面量 elision 由 SET_ELEM 扩长 + DELETE_PROP_DYNAMIC 置洞,
|
||||||
|
//! 行为对齐 `in`、`length`、`Object.keys`、`join`、for-in 键集与原型链读取。
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use oxide_compiler::compiler::Compiler;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
use oxide_vm::vm::Vm;
|
||||||
|
|
||||||
|
fn eval(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<String, String> {
|
||||||
|
let (vm, v) = eval(source)?;
|
||||||
|
// 字符串结果须在同一 VM 上读:perm 串指向 VM 私有内核,VM drop 后指针悬垂。
|
||||||
|
if v.is_string() {
|
||||||
|
Ok(vm.lookup_str(v).unwrap_or_default())
|
||||||
|
} else {
|
||||||
|
Ok(format!("{v}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 1:静态 elision 是真洞(`in` 为 false、`Object.keys` 不含洞下标)。
|
||||||
|
#[test]
|
||||||
|
fn static_elision_is_hole() {
|
||||||
|
assert_eq!(eval_str("''+(0 in [,,])+':'+Object.keys([,,]).length").unwrap(), "false:0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 2:界内静态 elision 读回为洞(length 含洞、`in` 为 false)。
|
||||||
|
#[test]
|
||||||
|
fn static_inner_elision_is_hole() {
|
||||||
|
assert_eq!(eval_str("''+[1,,3].length+':'+(1 in [1,,3])").unwrap(), "3:false");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 3:首元素 elision(length 含洞、0 下标为洞)。
|
||||||
|
#[test]
|
||||||
|
fn static_leading_elision_is_hole() {
|
||||||
|
assert_eq!(eval_str("''+[,1].length+':'+(0 in [,1])").unwrap(), "2:false");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 4:显式 undefined 元素是 present 属性(与 elision 区分)。
|
||||||
|
#[test]
|
||||||
|
fn explicit_undefined_is_present() {
|
||||||
|
assert_eq!(eval_str("''+(0 in [undefined])").unwrap(), "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 5:静态 elision 落原型链(原型有 0 下标时 `in` 为 true、读回原型上的值)。
|
||||||
|
#[test]
|
||||||
|
fn static_elision_falls_to_proto_chain() {
|
||||||
|
assert_eq!(eval_str("Array.prototype[0]='g'; ''+(0 in [,,])+':'+[,,][0]").unwrap(), "true:g",);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 6:join 跳洞(两个洞产生一个分隔符)。
|
||||||
|
#[test]
|
||||||
|
fn static_elision_join_skips_holes() {
|
||||||
|
assert_eq!(eval_str("[,,].join(\",\")").unwrap(), ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 7:for-in 键集不含洞下标。
|
||||||
|
#[test]
|
||||||
|
fn static_elision_for_in_no_keys() {
|
||||||
|
assert_eq!(eval_str("var c=0; for (var k in [,,]) c++; ''+c").unwrap(), "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 8:连续尾部 elision 计入 length。
|
||||||
|
#[test]
|
||||||
|
fn static_trailing_elisions_count_in_length() {
|
||||||
|
assert_eq!(eval_str("''+[4,5,,,,].length").unwrap(), "5");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 9:spread 后尾逗号不产生 elision 元素(length 不变)。
|
||||||
|
#[test]
|
||||||
|
fn spread_trailing_comma_not_elision() {
|
||||||
|
assert_eq!(eval_str("''+[...[1],].length").unwrap(), "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 10:spread 后尾逗号不产生 elision 元素(length = spread 长度)。
|
||||||
|
#[test]
|
||||||
|
fn spread_trailing_comma_not_elision_dense() {
|
||||||
|
assert_eq!(eval_str("''+[...[1,2,3],].length").unwrap(), "3");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 11:spread 中间 elision 置洞并计入 length(界内形)。
|
||||||
|
#[test]
|
||||||
|
fn spread_inner_elision_is_hole() {
|
||||||
|
assert_eq!(eval_str("const a=[0,...[1],,2]; ''+a.length+':'+(2 in a)").unwrap(), "4:false",);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钉 12:spread 空展开后界外 elision 扩长并置洞。
|
||||||
|
#[test]
|
||||||
|
fn spread_out_of_bounds_elision_extends_and_holes() {
|
||||||
|
assert_eq!(
|
||||||
|
eval_str("const b=[...[],,,]; ''+b.length+':'+(0 in b)+':'+(1 in b)").unwrap(),
|
||||||
|
"2:false:false",
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user