From a29a57d3511fe1d077121451c144ea379c7ab957 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 18 Oct 2026 07:47:39 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E4=BB=A3=E7=90=86=20g?= =?UTF-8?q?OPD=20=E4=B8=8D=E5=8F=98=E9=87=8F=E6=A3=80=E6=9F=A5=E7=9A=84=20?= =?UTF-8?q?writable=20=E4=BD=8D=E5=88=A4=E5=AE=9A=E6=96=B9=E5=90=91?= =?UTF-8?q?=E4=B8=8E=E5=80=BC=E4=B8=80=E8=87=B4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 42 ++++---- Cargo.toml | 2 +- crates/oxide_builtins/src/object.rs | 43 ++++++--- .../tests/proxy_gopd_invariant_tests.rs | 96 +++++++++++++++++++ 4 files changed, 146 insertions(+), 37 deletions(-) create mode 100644 crates/oxide_vm/tests/proxy_gopd_invariant_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 0f9ea981..7356917d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.20.11" +version = "0.20.12" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.20.11" +version = "0.20.12" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.20.11" +version = "0.20.12" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.20.11" +version = "0.20.12" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1272,7 +1272,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.20.11" +version = "0.20.12" dependencies = [ "num-bigint", "oxc_allocator", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "oxide_fold" -version = "0.20.11" +version = "0.20.12" dependencies = [ "num-bigint", "oxide_bytecode", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.20.11" +version = "0.20.12" dependencies = [ "bumpalo", "dashmap", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.20.11" +version = "0.20.12" dependencies = [ "tracing", "tracing-appender", @@ -1356,11 +1356,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.20.11" +version = "0.20.12" [[package]] name = "oxide_parser" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.20.11" +version = "0.20.12" dependencies = [ "num-bigint", "num-traits", @@ -1404,7 +1404,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.20.11" +version = "0.20.12" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.20.11" +version = "0.20.12" dependencies = [ "bumpalo", "dashmap", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.20.11" +version = "0.20.12" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 904bab6e..6e6a7979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.20.11" +version = "0.20.12" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index 632e3202..cbf3f7a9 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -1600,11 +1600,12 @@ fn proxy_is_extensible(vm: &mut H, obj: &JsObject) -> Result( vm: &mut H, target: &JsObject, key_si: u32, result_desc: JsValue, ) -> Result<(), JsValue> { @@ -1613,6 +1614,7 @@ fn proxy_gopd_invariant_check( let get_si = vm.perm_intern("get"); let set_si = vm.perm_intern("set"); let writable_si = vm.perm_intern("writable"); + let value_si = vm.perm_intern("value"); let extensible = proxy_is_extensible(vm, target).map_err(|msg| crate::error::create_from_text(vm, &msg))?; let target_desc = own_descriptor_of(vm, target, key_si)?; // IsCompatiblePropertyDescriptor:仅 target 不可扩展时执行。 @@ -1623,6 +1625,12 @@ fn proxy_gopd_invariant_check( "getOwnPropertyDescriptor trap reported a property that does not exist on a non-extensible target", )); }; + if desc_field_bool(vm, td, cfg_si)? { + return Err(crate::error::create_type_error( + vm, + "getOwnPropertyDescriptor trap reported a descriptor for a configurable target property of a non-extensible target", + )); + } if desc_field_bool(vm, result_desc, cfg_si)? { return Err(crate::error::create_type_error( vm, @@ -1672,16 +1680,27 @@ fn proxy_gopd_invariant_check( "getOwnPropertyDescriptor trap reported a setter for a non-configurable target data property", )); } - if !desc_field_bool(vm, result_desc, writable_si)? && desc_field_bool(vm, td, writable_si)? { + let target_writable = desc_field_bool(vm, td, writable_si)?; + let result_writable = desc_field_bool(vm, result_desc, writable_si)?; + if !target_writable && result_writable { return Err(crate::error::create_type_error( vm, - "getOwnPropertyDescriptor trap reported a non-writable descriptor for a writable target property", + "getOwnPropertyDescriptor trap reported a writable descriptor for a non-writable target property", )); } + if !result_writable && target_writable { + let result_value = own_field(vm, result_desc, value_si)?.unwrap_or(JsValue::undefined()); + let target_value = own_field(vm, td, value_si)?.unwrap_or(JsValue::undefined()); + if !oxide_runtime_api::same_value(result_value, target_value) { + return Err(crate::error::create_type_error( + vm, + "getOwnPropertyDescriptor trap reported a non-writable descriptor with a different value for a writable target property", + )); + } + } } } - // 结果描述符不可配置:target 自身属性必须存在且不可配置(与可扩展性无关); - // 数据描述符不得由可写放宽为不可写。 + // 结果描述符不可配置:target 自身属性必须存在且不可配置(与可扩展性无关)。 if !desc_field_bool(vm, result_desc, cfg_si)? { match target_desc { None => { @@ -1697,12 +1716,6 @@ fn proxy_gopd_invariant_check( "getOwnPropertyDescriptor trap reported a non-configurable descriptor for a configurable target property", )); } - if !desc_field_bool(vm, result_desc, writable_si)? && desc_field_bool(vm, td, writable_si)? { - return Err(crate::error::create_type_error( - vm, - "getOwnPropertyDescriptor trap reported a non-writable descriptor for a writable target property", - )); - } } } } diff --git a/crates/oxide_vm/tests/proxy_gopd_invariant_tests.rs b/crates/oxide_vm/tests/proxy_gopd_invariant_tests.rs new file mode 100644 index 00000000..db5c0d3a --- /dev/null +++ b/crates/oxide_vm/tests/proxy_gopd_invariant_tests.rs @@ -0,0 +1,96 @@ +use std::sync::Arc; + +use oxide_compiler::compiler::Compiler; +use oxide_types::value::JsValue; +use oxide_vm::vm::Vm; + +fn eval(vm: &mut Vm, source: &str) -> Result { + 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))?; + vm.run(&Arc::new(module)) +} + +// gOPD 不变量检查(writable 位方向与 value 一致性):target 不可写加 result +// 可写时抛 TypeError(规范 ValidateAndApplyPropertyDescriptor 步 5.5.1 口径)。 +#[test] +fn gopd_trap_writable_relaxation_throws() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { \ + var t = { a: 1 }; \ + Object.defineProperty(t, 'a', { writable: false, configurable: false, enumerable: false }); \ + Object.preventExtensions(t); \ + var p = new Proxy(t, { getOwnPropertyDescriptor: function (tt, k) { \ + return { value: 1, writable: true, enumerable: false, configurable: false }; \ + } }); \ + try { Object.getOwnPropertyDescriptor(p, 'a'); return 'no-throw'; } \ + catch (e) { return e.name; } \ + })()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap_or_default(), "TypeError"); +} + +// target 可配置时放宽:writable 交叉检查不执行,result 可写不抛。 +#[test] +fn gopd_trap_configurable_target_relaxed() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { \ + var t = { a: 1 }; \ + Object.defineProperty(t, 'a', { writable: false, enumerable: false }); \ + var p = new Proxy(t, { getOwnPropertyDescriptor: function (tt, k) { \ + return { value: 1, writable: true, enumerable: false, configurable: true }; \ + } }); \ + try { Object.getOwnPropertyDescriptor(p, 'a'); return 'ok'; } \ + catch (e) { return e.name; } \ + })()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap_or_default(), "ok"); +} + +// value 一致性:target 可写加 result 不可写且值不同抛 TypeError。 +#[test] +fn gopd_trap_value_mismatch_throws() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { \ + var t = { a: 1 }; \ + Object.defineProperty(t, 'a', { writable: true, configurable: false, enumerable: false }); \ + Object.preventExtensions(t); \ + var p = new Proxy(t, { getOwnPropertyDescriptor: function (tt, k) { \ + return { value: 2, writable: false, enumerable: false, configurable: false }; \ + } }); \ + try { Object.getOwnPropertyDescriptor(p, 'a'); return 'no-throw'; } \ + catch (e) { return e.name; } \ + })()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap_or_default(), "TypeError"); +} + +// value 一致性:target 可写加 result 不可写且值 SameValue 不抛。 +#[test] +fn gopd_trap_value_same_ok() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { \ + var t = { a: 1 }; \ + Object.defineProperty(t, 'a', { writable: true, configurable: false, enumerable: false }); \ + Object.preventExtensions(t); \ + var p = new Proxy(t, { getOwnPropertyDescriptor: function (tt, k) { \ + return { value: 1, writable: false, enumerable: false, configurable: false }; \ + } }); \ + try { Object.getOwnPropertyDescriptor(p, 'a'); return 'ok'; } \ + catch (e) { return e.name; } \ + })()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap_or_default(), "ok"); +}