Summary
Two small, independent ES5/core-semantics correctness bugs found during the Test262/parity dive. Both are self-contained; an agent can fix both in one PR (clearly separate root causes/files).
Bug 1 — Object(null) / Object() / Object(undefined) returns undefined instead of a fresh {}
typeof Object(null) // Perry "undefined" Node "object"
typeof Object() // Perry "undefined" Node "object"
typeof Object(undefined) // Perry "undefined" Node "object"
// (new Object(null) also affected)
Per spec, the Object coercion function called with null/undefined/no args returns a new ordinary object. Perry returns undefined.
Where: the Object(...) call path (not new). The new path in crates/perry-runtime/src/object/class_registry.rs::js_new_function_construct already has an "Object" => js_object_alloc(0,0) arm under identify_global_builtin_constructor; the plain-call path (codegen call dispatch for the Object global / global_this.rs Object thunk) needs the same nullish→{} handling. Verify both Object(null) and new Object(null).
Bug 2 — reassigning a function-declaration binding is ignored
function G(m){ this.v = "OLD"; }
G = function(m){ this.v = "NEW"; };
new G(1).v; // Perry "OLD" Node "NEW"
// also: var f = G; G = otherFn; f and G should reflect the reassignment per binding semantics
A top-level function declaration's binding is treated as an immutable FuncRef at use sites, so a later G = ... reassignment doesn't take effect — new G() / G() still calls the original. Function-declaration bindings are mutable var-like bindings in JS.
Where: HIR lowering resolves an identifier that names a function to Expr::FuncRef(func_id) (see crates/perry-hir/src/lower/ — lookup_func resolution in lower_expr.rs / call + new lowering). When the name has been reassigned, the binding must read the current value (a mutable slot), not the static FuncRef. Likely needs the function name to also have a mutable local/global slot that assignment writes and reads go through once reassigned.
Acceptance criteria
typeof Object(null) === "object", Object()/Object(undefined) return {}, new Object(null) too.
- After
G = fn2, both new G() and G() invoke fn2; unre-assigned function declarations keep working (no perf/behavior regression for the common case).
- Add gap tests; differential vs
node --experimental-strip-types.
Refs: Test262 radar #799, Node+TS roadmap #793.
Summary
Two small, independent ES5/core-semantics correctness bugs found during the Test262/parity dive. Both are self-contained; an agent can fix both in one PR (clearly separate root causes/files).
Bug 1 —
Object(null)/Object()/Object(undefined)returnsundefinedinstead of a fresh{}Per spec, the
Objectcoercion function called withnull/undefined/no args returns a new ordinary object. Perry returnsundefined.Where: the
Object(...)call path (notnew). Thenewpath incrates/perry-runtime/src/object/class_registry.rs::js_new_function_constructalready has an"Object" => js_object_alloc(0,0)arm underidentify_global_builtin_constructor; the plain-call path (codegen call dispatch for theObjectglobal /global_this.rsObject thunk) needs the same nullish→{}handling. Verify bothObject(null)andnew Object(null).Bug 2 — reassigning a function-declaration binding is ignored
A top-level
functiondeclaration's binding is treated as an immutableFuncRefat use sites, so a laterG = ...reassignment doesn't take effect —new G()/G()still calls the original. Function-declaration bindings are mutablevar-like bindings in JS.Where: HIR lowering resolves an identifier that names a function to
Expr::FuncRef(func_id)(seecrates/perry-hir/src/lower/—lookup_funcresolution inlower_expr.rs/ call +newlowering). When the name has been reassigned, the binding must read the current value (a mutable slot), not the staticFuncRef. Likely needs the function name to also have a mutable local/global slot that assignment writes and reads go through once reassigned.Acceptance criteria
typeof Object(null) === "object",Object()/Object(undefined)return{},new Object(null)too.G = fn2, bothnew G()andG()invokefn2; unre-assigned function declarations keep working (no perf/behavior regression for the common case).node --experimental-strip-types.Refs: Test262 radar #799, Node+TS roadmap #793.