From a1993e617dd8ffe9bd135dcdb7cdace624f9c561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Sat, 27 Jun 2026 19:18:31 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=A7=A3=E6=9E=84=E8=B5=8B=E5=80=BC=20r?= =?UTF-8?q?est=20=E7=9B=AE=E6=A0=87=E6=8C=89=20emitter=20=E9=80=92?= =?UTF-8?q?=E5=BD=92=E8=AE=A1=E6=95=B0=EF=BC=8C=E4=BF=AE=E5=B5=8C=E5=A5=97?= =?UTF-8?q?=20rest=20=E6=AD=BB=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/counter/helper/binding.rs | 14 ++++---- crates/oxide_vm/src/bindings/bind_date.rs | 24 +++----------- crates/oxide_vm/tests/builtins_date_tests.rs | 30 ++++++++++++++--- crates/oxide_vm/tests/destructuring_tests.rs | 33 +++++++++++++++++++ 4 files changed, 70 insertions(+), 31 deletions(-) diff --git a/crates/oxide_compiler/src/counter/helper/binding.rs b/crates/oxide_compiler/src/counter/helper/binding.rs index 9ac8356..975d783 100644 --- a/crates/oxide_compiler/src/counter/helper/binding.rs +++ b/crates/oxide_compiler/src/counter/helper/binding.rs @@ -77,10 +77,11 @@ impl Compiler { ctx.count_instr(); // STORE_VAR } } - if ap.rest.is_some() { + if let Some(rest) = &ap.rest { self.count_rest_array(ctx); - ctx.alloc_reg(); - ctx.count_instr(); // STORE_VAR + // Mirror the emitter (emit_array_assignment -> emit_assign_target): the rest + // target may itself be a nested pattern, not just an identifier. + self.count_assign_target(&rest.target, ctx); } 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.count_instr_with_ext(1); // REST_OBJECT + excluded-keys ext - ctx.alloc_reg(); - ctx.count_instr(); // STORE_VAR + // Mirror the emitter (emit_object_assignment -> emit_assign_target): the rest + // target may itself be a nested pattern, not just an identifier. + self.count_assign_target(&rest.target, ctx); } } diff --git a/crates/oxide_vm/src/bindings/bind_date.rs b/crates/oxide_vm/src/bindings/bind_date.rs index 2727eb6..ac5be31 100644 --- a/crates/oxide_vm/src/bindings/bind_date.rs +++ b/crates/oxide_vm/src/bindings/bind_date.rs @@ -88,21 +88,9 @@ pub fn bind_date(core: &Arc, session: &KernelSession, global: &mut J oxide_builtins::date::date_set_utc_full_year:: as *const (), 3, ), - ( - "setUTCMonth", - oxide_builtins::date::date_set_utc_month:: as *const (), - 2, - ), - ( - "setUTCDate", - oxide_builtins::date::date_set_utc_date:: as *const (), - 1, - ), - ( - "setUTCHours", - oxide_builtins::date::date_set_utc_hours:: as *const (), - 4, - ), + ("setUTCMonth", oxide_builtins::date::date_set_utc_month:: as *const (), 2), + ("setUTCDate", oxide_builtins::date::date_set_utc_date:: as *const (), 1), + ("setUTCHours", oxide_builtins::date::date_set_utc_hours:: as *const (), 4), ( "setUTCMinutes", oxide_builtins::date::date_set_utc_minutes:: as *const (), @@ -126,11 +114,7 @@ pub fn bind_date(core: &Arc, session: &KernelSession, global: &mut J ("toDateString", oxide_builtins::date::date_to_date_string:: as *const (), 0), ("toTimeString", oxide_builtins::date::date_to_time_string:: as *const (), 0), ("toUTCString", oxide_builtins::date::date_to_utc_string:: as *const (), 0), - ( - "toGMTString", - oxide_builtins::date::date_to_gmt_string:: as *const (), - 0, - ), + ("toGMTString", oxide_builtins::date::date_to_gmt_string:: as *const (), 0), ( "toLocaleDateString", oxide_builtins::date::date_to_locale_date_string:: as *const (), diff --git a/crates/oxide_vm/tests/builtins_date_tests.rs b/crates/oxide_vm/tests/builtins_date_tests.rs index 87b4519..aa3927e 100644 --- a/crates/oxide_vm/tests/builtins_date_tests.rs +++ b/crates/oxide_vm/tests/builtins_date_tests.rs @@ -237,20 +237,40 @@ fn date_local_setters_preserve_utc_offset() { #[test] fn date_set_utc_full_year_works() { 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); } #[test] fn date_set_utc_hours_optional_chain() { 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); - 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); - 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); - 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); } diff --git a/crates/oxide_vm/tests/destructuring_tests.rs b/crates/oxide_vm/tests/destructuring_tests.rs index 285f59d..5656ddc 100644 --- a/crates/oxide_vm/tests/destructuring_tests.rs +++ b/crates/oxide_vm/tests/destructuring_tests.rs @@ -87,3 +87,36 @@ fn function_and_method_parameter_destructuring() { .unwrap(); 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); +}