From fd2b290927e041d8c159b0f5d611f4aa2d2a852f Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 16 Aug 2026 13:49:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=A3=E6=9E=84=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E9=94=AE=E9=80=9A=E7=94=A8=20fallback=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=BB=BB=E6=84=8F=E9=94=AE=E8=A1=A8=E8=BE=BE=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/shared/chain.rs | 4 +- crates/oxide_vm/tests/destructuring_tests.rs | 90 ++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/crates/oxide_emit/src/shared/chain.rs b/crates/oxide_emit/src/shared/chain.rs index 584fe23..fd33b57 100644 --- a/crates/oxide_emit/src/shared/chain.rs +++ b/crates/oxide_emit/src/shared/chain.rs @@ -286,7 +286,9 @@ impl Emitter { ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx)); Ok(key_reg) } - _ => Err("computed destructuring key expression not supported".into()), + // 其余键形态(CallExpression/Template/BigInt/RegExp 等):转回 Expression + // 走通用 emit 分发,键值运行时求值后由 GET_PROP_DYNAMIC 做 ToPropertyKey。 + _ => self.emit_expression(key.to_expression(), ctx), } } diff --git a/crates/oxide_vm/tests/destructuring_tests.rs b/crates/oxide_vm/tests/destructuring_tests.rs index 62fc958..d70c880 100644 --- a/crates/oxide_vm/tests/destructuring_tests.rs +++ b/crates/oxide_vm/tests/destructuring_tests.rs @@ -160,3 +160,93 @@ fn object_rest_keeps_numeric_key_from_shape_source() { let s = unsafe { &*result.as_string_ptr() }.as_str().to_string(); assert_eq!(s, "1,2"); } + +// computed 解构键通用 fallback:CallExpression / Template / BigInt 等键表达式 +// 走既有 emit 全域,键值运行时求值后经 ToPropertyKey 读属性。 +#[test] +fn computed_key_call_expression_binding() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f() { return 'x'; } const { [f()]: a } = {x: 42}; a").unwrap(); + assert_num(result, 42.0); +} + +#[test] +fn computed_key_call_expression_throw_propagates() { + // 键求值抛错在属性读与绑定前抛出,错误透传不吞。 + let mut vm = Vm::new(); + let err = eval( + &mut vm, + "function thrower() { throw new TypeError('boom'); } const { [thrower()]: a } = {};", + ) + .unwrap_err(); + assert!(err.contains("boom"), "expected thrown error, got {err:?}"); +} + +#[test] +fn computed_key_template_literal_binding() { + // 无插值模板键折叠不可用,仍须正确求值(DYNAMIC 路径语义等价)。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "const { [`x`]: a } = {x: 5}; a").unwrap(); + assert_num(result, 5.0); + let result = eval(&mut vm, "const { [`x${'y'}`]: a } = {xy: 3}; a").unwrap(); + assert_num(result, 3.0); +} + +#[test] +fn computed_key_bigint_binding() { + // 1n 经 ToPropertyKey 转 "1",命中对象数字键。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "const { [1n]: a } = {1: 9}; a").unwrap(); + assert_num(result, 9.0); +} + +#[test] +fn computed_key_call_expression_rest_excludes() { + // rest 排除数组收集运行时键值,computed 键与 rest 语义正确。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "function k() { return 'x'; } const { [k()]: a, ...rest } = {x:1,y:2}; a + rest.y", + ) + .unwrap(); + assert_num(result, 3.0); +} + +#[test] +fn computed_key_assignment_target() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "let a; function k() { return 'x'; } ({ [k()]: a } = {x: 7}); a").unwrap(); + assert_num(result, 7.0); +} + +#[test] +fn computed_key_nested_pattern() { + // 嵌套解构的 computed 键逐层求值。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "function k1() { return 'x'; } function k2() { return 'y'; } const { [k1()]: { [k2()]: b } } = {x: {y: 3}}; b", + ) + .unwrap(); + assert_num(result, 3.0); +} + +#[test] +fn computed_key_evaluated_before_default_and_getter() { + // 求值序:键先于默认值(键未命中才走默认值);getter 副作用发生在键求值之后。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "const log = []; function k() { log.push('k'); return 'x'; } const { [k()]: a = (log.push('d'), 99) } = {}; log.join(',')", + ) + .unwrap(); + let s = unsafe { &*result.as_string_ptr() }.as_str().to_string(); + assert_eq!(s, "k,d"); + let result = eval( + &mut vm, + "let order=[]; function k() { order.push('key'); return 'x'; } const o = { get x() { order.push('get'); return 5; } }; const { [k()]: a } = o; a + ':' + order.join(',')", + ) + .unwrap(); + let s = unsafe { &*result.as_string_ptr() }.as_str().to_string(); + assert_eq!(s, "5:key,get"); +}