From c5315ddaa2dd492f5e3b0099076db918cf15fcb9 Mon Sep 17 00:00:00 2001 From: Andrew DiZenzo Date: Wed, 3 Jun 2026 15:51:01 +0000 Subject: [PATCH] Align captured Object.assign value shape --- crates/perry-hir/src/lower/expr_member.rs | 20 +++++++++++++++++++ .../perry-runtime/src/object/global_this.rs | 9 ++++++++- .../object/captured-object-assign.ts | 13 ++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test-parity/node-suite/object/captured-object-assign.ts diff --git a/crates/perry-hir/src/lower/expr_member.rs b/crates/perry-hir/src/lower/expr_member.rs index fd4d25fa76..488b7c2acd 100644 --- a/crates/perry-hir/src/lower/expr_member.rs +++ b/crates/perry-hir/src/lower/expr_member.rs @@ -1768,9 +1768,29 @@ fn lower_member_inner(ctx: &mut LoweringContext, member: &ast::MemberExpr) -> Re ast::MemberProp::Computed(_) => true, _ => false, }; + let outer_is_reified_object_static_value = property == "Object" + && matches!( + &member.prop, + ast::MemberProp::Ident(p) if matches!( + p.sym.as_ref(), + "assign" + | "create" + | "defineProperty" + | "entries" + | "freeze" + | "fromEntries" + | "getOwnPropertyDescriptor" + | "getOwnPropertyNames" + | "getPrototypeOf" + | "hasOwn" + | "keys" + | "values" + ) + ); if !outer_is_prototype_or_proto && !receiver_is_namespace_value && !outer_is_websocket_static + && !outer_is_reified_object_static_value { object_expr = Expr::GlobalGet(0); } diff --git a/crates/perry-runtime/src/object/global_this.rs b/crates/perry-runtime/src/object/global_this.rs index 0d9c621145..ca7fc55458 100644 --- a/crates/perry-runtime/src/object/global_this.rs +++ b/crates/perry-runtime/src/object/global_this.rs @@ -3103,7 +3103,14 @@ fn install_builtin_constructor_statics(name: &str, ctor: *mut crate::closure::Cl 1, false, ); - install_constructor_static(ctor, "assign", object_assign_thunk as *const u8, 1, true); + install_constructor_static_with_call_arity( + ctor, + "assign", + object_assign_thunk as *const u8, + 2, + 1, + true, + ); install_constructor_static(ctor, "hasOwn", object_hasown_thunk as *const u8, 2, false); } "Array" => { diff --git a/test-parity/node-suite/object/captured-object-assign.ts b/test-parity/node-suite/object/captured-object-assign.ts new file mode 100644 index 0000000000..991f0cba78 --- /dev/null +++ b/test-parity/node-suite/object/captured-object-assign.ts @@ -0,0 +1,13 @@ +const assign = Object.assign; + +console.log("assign typeof:", typeof assign); +console.log("assign name:", assign.name); +console.log("assign length:", assign.length); + +const target: any = { base: 1 }; +const out: any = assign(target, { a: 2 }, { b: 3 }); + +console.log("same target:", out === target); +console.log("keys:", Object.keys(out).join(",")); +console.log("values:", [out.base, out.a, out.b].join(",")); +console.log("fresh copy:", JSON.stringify(assign({}, { x: 1 }, { y: 2 })));