Skip to content

fix(codemode): coerce enumeration sources like JS ToObject - #48257

Merged
rekram1-node merged 3 commits into
v2from
codemode-object-coercion
Sep 10, 2026
Merged

fix(codemode): coerce enumeration sources like JS ToObject#48257
rekram1-node merged 3 commits into
v2from
codemode-object-coercion

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Object.keys/values/entries/hasOwn/assign, object spread, and for...in rejected anything that was not a plain object or array. JS applies ToObject first, so they should behave like this:

Object.entries("abc")            // [["0","a"],["1","b"],["2","c"]]   was InvalidDataValue
Object.keys(42)                  // []                                 was InvalidDataValue
Object.keys(null)                // TypeError                          was InvalidDataValue
Object.assign({}, "12", [3])     // { 0: 3, 1: "2" }                   was "expects data objects"
Object.assign([7,8,9], [1])      // [1, 8, 9]                          was "expects a data object target"
{ ...[1, 2], ..."ab", ...5 }     // { 0: "a", 1: "b" }                 was InvalidDataValue
for (const k in "ab")            // "0", "1"                            was a for...in error
for (const k in null)            // nothing                             was a for...in error

One ToObject step for seven sites

enumerableSource(label, value)                 stdlib/object.ts
  null / undefined  → TypeError                ← spread and for...in skip nullish before calling
  Promise           → "await it first" hint
  tools.x           → "not plain data" hint
  "abc"             → "abc"                    ← the string itself: host Object.keys("abc") is ["0","1","2"]
  5 / true / Date / Map / fn → {}              ← nothing enumerable, like JS
  {…} / […]         → the value
Object.keys / values / entries / hasOwn      enumerableSource → host Object.*(...)
Object.assign(target, ...sources)            per source: skip nullish → enumerableSource → copy pairs (+ iterator symbols, insertion order)
{ ...spread }                                skip nullish → enumerableSource → copy pairs (+ iterator symbols)
for (k in value)                             tools? catalog names · nullish? [] · else Object.keys(enumerableSource(value))

The un-awaited-Promise and tool-reference hints move into the shared step, so they fire uniformly — for (k in tools.x.y()) gets the await hint too.

Deviations kept

case JS here why
Object.assign("a", …) boxed String TypeError no wrapper objects
Object.keys(promise) [] await hint almost always a missing await

Tests

test/object-coercion-test262.test.ts ports 23 Test262 files: Object/keys ×5, entries ×3, values ×3, hasOwn ×3, assign ×9. Only the boxed-primitive files are omitted; Override.js asserts through Object.keys instead of getOwnPropertyNames.

  • bun typecheck
  • bun test — 1170 pass

@rekram1-node
rekram1-node merged commit 5ec7dd9 into v2 Sep 10, 2026
8 checks passed
@rekram1-node
rekram1-node deleted the codemode-object-coercion branch September 10, 2026 03:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant