Skip to content

runtime/object-model: fix Object(...) boxing and Object.assign ToObject/copy semantics #3986

@andrewtdiz

Description

@andrewtdiz

Summary

The current c262 sample shows Object(...), new Object(...), and Object.assign(...) still diverging on core ToObject behavior. Perry often returns or reports primitive-ish values where Node exposes wrapper objects, loses constructor/prototype identity, and misses descriptor/getter/proxy behavior during assignment.

Evidence

Run from origin/main at 1f6d20e19 on 2026-06-01, Node v25.9.0, Test262 4249661388e5d3f92a85186213da140a6481490f:

scripts/test262_subset.py --root /tmp/perry-test262-4249661 --dir built-ins/Object --max 180 --timeout 10 --sample-cap 50 --report /tmp/perry-c262-builtins-object.json

Observed: 97 pass, 83 runtime-fail, 0 compile-fail, 53.9% sample parity.

Representative failures:

  • built-ins/Object/S9.9_A3.js: Object(true).valueOf() expected true, Perry returns an object-ish value.
  • built-ins/Object/S9.9_A4.js: Object(0).valueOf() expected 0, Perry returns an object-ish value.
  • built-ins/Object/S9.9_A5.js: Object("some string").valueOf() expected the original string, Perry returns an object-ish value.
  • built-ins/Object/assign/Target-String.js: Object.assign should box a string target and return an object; Perry reports typeof as string.
  • built-ins/Object/assign/source-get-attr-error.js: getter abrupt completion should propagate; Perry reports no expected Test262Error.
  • built-ins/Object/assign/source-own-prop-desc-missing.js: proxy/property descriptor path is not observed as Node does.

Product triage

User outcome: library code that uses object coercion, option merging, primitive wrappers, and descriptor-aware assignment behaves like Node instead of silently dropping values.

Shortest path: implement the shared ToObject/wrapper path and reuse it from Object(...), new Object(...), and Object.assign(...) before expanding into less-used Object static helpers.

Manual/narrow assumptions to test: boxed primitive probes for boolean/number/string/symbol plus a focused Object.assign fixture with primitive targets, non-enumerable source props, throwing getters, read-only target props, and proxies.

Core vs nice: core is ToObject, valueOf, constructor/prototype identity, enumerable own-property copy, and abrupt propagation. Nice is full Object static method descriptor polish already partially covered by #3655 and validation work in #3146/#3662.

Keep flexible: centralize ToObject so String wrappers, primitive receiver boxing (#3577), and future exotic wrappers can share it.

Suggested scope

Acceptance criteria

  • The representative rows above pass under the Test262 differential.
  • built-ins/Object --max 180 improves materially without moving cases to skip.
  • A small local probe covering Object(true/0/"x").valueOf() and primitive-target Object.assign matches Node output.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions