Skip to content

fix(runtime): link Object.create synthetic class_id to original class chain (#1805) - #2244

Merged
proggeramlug merged 1 commit into
mainfrom
worktree-fix-1805-object-create-instanceof
May 28, 2026
Merged

fix(runtime): link Object.create synthetic class_id to original class chain (#1805)#2244
proggeramlug merged 1 commit into
mainfrom
worktree-fix-1805-object-create-instanceof

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Fix #1805Object.create(Object.getPrototypeOf(instance)) produced
an object that wasn't instanceof the original class, even though
property reads and getter dispatch through the synthetic-prototype
chain worked correctly (the #711 / #809 wiring).

js_instanceof's chain walk keys off get_parent_class_id, but the
synthetic class id allocated by js_object_create had no parent edge
linking it to the original class — so the walk stopped at the
unregistered synthetic id and returned false.

Fix: at synthetic-cid registration time, also record (synthetic_cid → proto_ptr.class_id) in CLASS_REGISTRY when the proto pointer
carries a non-zero class_id. In Perry's model
Object.getPrototypeOf(instance) returns the instance itself (see
js_object_get_prototype_of), so proto_ptr here is a real class
instance whose class_id field IS the user class's id. Linking it as
the synthetic cid's parent lets the chain walk reach the original
class; chained Object.create cases keep working because the walk
transitively reaches the root via synthetic_2 → synthetic_1 → ClassId.

Surfaced by effect's SchemaAST.annotations, which clones AST nodes
via Object.create(Object.getPrototypeOf(ast), Object.getOwnPropertyDescriptors(ast)). Refs #1758, #809.

Test plan

  • test-files/test_issue_1805_object_create_instanceof.ts covers
    plain Object.create(proto), the descriptor form, the chained case,
    and the Object.create(null) negative — byte-identical with Node.
  • cargo fmt --all -- --check clean.
  • cargo test --release -p perry-runtime --lib — 2 flaky failures
    (gc::tests::copying::test_copying_minor_rewrites_exact_object_pointer_slot_only,
    object::tests::text_encoding_stream_globals_construct_readable_writable_shape)
    both pass in isolation; pre-existing thread-state flakiness.

… chain (#1805)

`Object.create(Object.getPrototypeOf(instance))` produced an object
that wasn't `instanceof` the original class, even though property
reads and getter dispatch through the synthetic-prototype chain worked
correctly (the #711 / #809 wiring). `js_instanceof`'s chain walk keys
off `get_parent_class_id`, but the synthetic class id allocated by
`js_object_create` had no parent edge linking it to the original
class — so the walk stopped at the unregistered synthetic id and
returned false.

Fix: at synthetic-cid registration time, also record
`(synthetic_cid → proto_ptr.class_id)` in CLASS_REGISTRY when the
proto pointer carries a non-zero class_id. In Perry's model
`Object.getPrototypeOf(instance)` returns the instance itself (see
`js_object_get_prototype_of`), so `proto_ptr` is a real class
instance whose `class_id` field IS the user class's id. Linking that
as the synthetic cid's parent lets the `js_instanceof` chain walk
reach the original class — chained `Object.create` cases also work
because the chain transitively walks `synthetic_2 → synthetic_1 →
ClassId`.

Surfaced by effect's `SchemaAST.annotations`, which clones AST nodes
via `Object.create(Object.getPrototypeOf(ast),
Object.getOwnPropertyDescriptors(ast))`. Refs #1758, #809.

## Test plan

- [x] `test-files/test_issue_1805_object_create_instanceof.ts` covers
  plain `Object.create(proto)`, the descriptor form, the chained
  case, and the `Object.create(null)` negative — byte-identical with
  Node.
- [x] `cargo fmt --all -- --check` clean.
- [x] `cargo test --release -p perry-runtime --lib` — 2 flaky failures
  (`gc::tests::copying::test_copying_minor_rewrites_exact_object_pointer_slot_only`,
  `object::tests::text_encoding_stream_globals_construct_readable_writable_shape`)
  both pass in isolation; pre-existing thread-state flakiness.
@proggeramlug
proggeramlug merged commit 7cace89 into main May 28, 2026
20 of 21 checks passed
@proggeramlug
proggeramlug deleted the worktree-fix-1805-object-create-instanceof branch May 28, 2026 15:59
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.

perry: Object.create(proto) result is not instanceof the prototype's class (class-id chain not linked)

1 participant