fix: 解构赋值 rest 目标按 emitter 递归计数,修嵌套 rest 死循环

This commit is contained in:
爱特每羊队
2026-06-27 19:18:31 +08:00
parent a502782d04
commit a1993e617d
4 changed files with 70 additions and 31 deletions
@@ -77,10 +77,11 @@ impl Compiler {
ctx.count_instr(); // STORE_VAR ctx.count_instr(); // STORE_VAR
} }
} }
if ap.rest.is_some() { if let Some(rest) = &ap.rest {
self.count_rest_array(ctx); self.count_rest_array(ctx);
ctx.alloc_reg(); // Mirror the emitter (emit_array_assignment -> emit_assign_target): the rest
ctx.count_instr(); // STORE_VAR // target may itself be a nested pattern, not just an identifier.
self.count_assign_target(&rest.target, ctx);
} }
ctx.count_instr(); // FOR_OF_CLOSE ctx.count_instr(); // FOR_OF_CLOSE
} }
@@ -108,11 +109,12 @@ impl Compiler {
} }
} }
} }
if op.rest.is_some() { if let Some(rest) = &op.rest {
ctx.alloc_reg(); ctx.alloc_reg();
ctx.count_instr_with_ext(1); // REST_OBJECT + excluded-keys ext ctx.count_instr_with_ext(1); // REST_OBJECT + excluded-keys ext
ctx.alloc_reg(); // Mirror the emitter (emit_object_assignment -> emit_assign_target): the rest
ctx.count_instr(); // STORE_VAR // target may itself be a nested pattern, not just an identifier.
self.count_assign_target(&rest.target, ctx);
} }
} }
+4 -20
View File
@@ -88,21 +88,9 @@ pub fn bind_date(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
oxide_builtins::date::date_set_utc_full_year::<crate::vm::Vm> as *const (), oxide_builtins::date::date_set_utc_full_year::<crate::vm::Vm> as *const (),
3, 3,
), ),
( ("setUTCMonth", oxide_builtins::date::date_set_utc_month::<crate::vm::Vm> as *const (), 2),
"setUTCMonth", ("setUTCDate", oxide_builtins::date::date_set_utc_date::<crate::vm::Vm> as *const (), 1),
oxide_builtins::date::date_set_utc_month::<crate::vm::Vm> as *const (), ("setUTCHours", oxide_builtins::date::date_set_utc_hours::<crate::vm::Vm> as *const (), 4),
2,
),
(
"setUTCDate",
oxide_builtins::date::date_set_utc_date::<crate::vm::Vm> as *const (),
1,
),
(
"setUTCHours",
oxide_builtins::date::date_set_utc_hours::<crate::vm::Vm> as *const (),
4,
),
( (
"setUTCMinutes", "setUTCMinutes",
oxide_builtins::date::date_set_utc_minutes::<crate::vm::Vm> as *const (), oxide_builtins::date::date_set_utc_minutes::<crate::vm::Vm> as *const (),
@@ -126,11 +114,7 @@ pub fn bind_date(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
("toDateString", oxide_builtins::date::date_to_date_string::<crate::vm::Vm> as *const (), 0), ("toDateString", oxide_builtins::date::date_to_date_string::<crate::vm::Vm> as *const (), 0),
("toTimeString", oxide_builtins::date::date_to_time_string::<crate::vm::Vm> as *const (), 0), ("toTimeString", oxide_builtins::date::date_to_time_string::<crate::vm::Vm> as *const (), 0),
("toUTCString", oxide_builtins::date::date_to_utc_string::<crate::vm::Vm> as *const (), 0), ("toUTCString", oxide_builtins::date::date_to_utc_string::<crate::vm::Vm> as *const (), 0),
( ("toGMTString", oxide_builtins::date::date_to_gmt_string::<crate::vm::Vm> as *const (), 0),
"toGMTString",
oxide_builtins::date::date_to_gmt_string::<crate::vm::Vm> as *const (),
0,
),
( (
"toLocaleDateString", "toLocaleDateString",
oxide_builtins::date::date_to_locale_date_string::<crate::vm::Vm> as *const (), oxide_builtins::date::date_to_locale_date_string::<crate::vm::Vm> as *const (),
+25 -5
View File
@@ -237,20 +237,40 @@ fn date_local_setters_preserve_utc_offset() {
#[test] #[test]
fn date_set_utc_full_year_works() { fn date_set_utc_full_year_works() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let r = eval(&mut vm, "var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCFullYear(2025); d.getUTCFullYear()").unwrap(); let r = eval(
&mut vm,
"var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCFullYear(2025); d.getUTCFullYear()",
)
.unwrap();
assert_eq!(r.as_double(), 2025.0); assert_eq!(r.as_double(), 2025.0);
} }
#[test] #[test]
fn date_set_utc_hours_optional_chain() { fn date_set_utc_hours_optional_chain() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let r = eval(&mut vm, "var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCHours(23, 59, 59, 999); d.getUTCHours()").unwrap(); let r = eval(
&mut vm,
"var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCHours(23, 59, 59, 999); d.getUTCHours()",
)
.unwrap();
assert_eq!(r.as_double(), 23.0); assert_eq!(r.as_double(), 23.0);
let r = eval(&mut vm, "var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCHours(23, 59, 59, 999); d.getUTCMinutes()").unwrap(); let r = eval(
&mut vm,
"var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCHours(23, 59, 59, 999); d.getUTCMinutes()",
)
.unwrap();
assert_eq!(r.as_double(), 59.0); assert_eq!(r.as_double(), 59.0);
let r = eval(&mut vm, "var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCHours(23, 59, 59, 999); d.getUTCSeconds()").unwrap(); let r = eval(
&mut vm,
"var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCHours(23, 59, 59, 999); d.getUTCSeconds()",
)
.unwrap();
assert_eq!(r.as_double(), 59.0); assert_eq!(r.as_double(), 59.0);
let r = eval(&mut vm, "var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCHours(23, 59, 59, 999); d.getUTCMilliseconds()").unwrap(); let r = eval(
&mut vm,
"var d = new Date(2020, 0, 1, 0, 0, 0); d.setUTCHours(23, 59, 59, 999); d.getUTCMilliseconds()",
)
.unwrap();
assert_eq!(r.as_double(), 999.0); assert_eq!(r.as_double(), 999.0);
} }
@@ -87,3 +87,36 @@ fn function_and_method_parameter_destructuring() {
.unwrap(); .unwrap();
assert_num(result, 10.0); assert_num(result, 10.0);
} }
// Regression: a for-of whose assignment target has a NESTED rest pattern (the rest
// element is itself a pattern, e.g. `[...[x]]`) used to loop forever / error, because the
// counter undercounted the rest target's instructions vs the emitter, corrupting the loop's
// jump offsets. (Object rest with a nested target is a syntax error per spec, so untested.)
#[test]
fn for_of_assignment_nested_rest_target_runs_once() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var x; var c=0; for([...[x]] of [[1,2,3]]){ c=c+1; } c").unwrap();
assert_num(result, 1.0);
}
#[test]
fn for_of_assignment_nested_rest_empty_body_binds_inner() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var x; for([...[x]] of [[1,2,3]]){} x").unwrap();
assert_num(result, 1.0);
}
#[test]
fn for_of_assignment_nested_rest_with_leading_element() {
let mut vm = Vm::new();
// outer iterates once; a=1, rest=[2,3,4], then [x,y] from rest -> x=2, y=3
let result = eval(&mut vm, "var a,x,y; for([a,...[x,y]] of [[1,2,3,4]]){} a*100+x*10+y").unwrap();
assert_num(result, 123.0);
}
#[test]
fn standalone_nested_rest_assignment() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var x; [...[x]]=[1,2,3]; x").unwrap();
assert_num(result, 1.0);
}