Skip to content

fix(object): validate defineProperty descriptors, invariants, and Object.create - #3376

Merged
proggeramlug merged 3 commits into
mainfrom
fix-object-defineproperty-2817-2843-2816
May 30, 2026
Merged

proggeramlug merged 3 commits into
mainfrom
fix-object-defineproperty-2817-2843-2816

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Closes #2817
Closes #2843
Closes #2816

Implementation

#2817 — defineProperty / defineProperties descriptor validation

js_object_define_property now runs the ES descriptor-validation path before any mutation, throwing Node's exact TypeErrors:

  • Non-object / nullish target -> Object.defineProperty called on non-object
  • Non-object descriptor -> Property description must be an object: <desc>
  • Mixing accessor (get/set) and data (value/writable) fields -> Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
  • A present, non-callable get -> Getter must be a function: <v>; likewise for set -> Setter must be a function: <v>

js_object_define_properties now throws Object.defineProperties called on non-object for primitive targets and Cannot convert undefined or null to object for a nullish properties bag; per-key non-object descriptors flow through the same validation (Property description must be an object: 1).

#2843 — frozen / sealed / non-extensible invariants

js_object_define_property enforces mutation invariants before touching the object:

  • Adding a new key to a non-extensible object -> Cannot define property <k>, object is not extensible
  • Redefining a non-configurable (frozen/sealed) key -> Cannot redefine property: <k>
  • Sealed-but-not-frozen objects still allow rewriting an existing writable data property's value (matching Node).

#2816 — Object.create descriptor-form + prototype validation

  • HIR Expr::ObjectCreate now carries an optional second operand (the properties bag); lowering, walkers, collectors, stable-hash and both codegen backends updated.
  • New runtime js_object_create_with_props(proto, props) validates the prototype (Object prototype may only be an Object or null for primitives/undefined; null and objects accepted) and applies the descriptor bag via js_object_define_properties. Codegen routes all Object.create calls through it. A #[used] keepalive anchor retains the symbol through the auto-optimize bitcode pipeline, and it is declared in the runtime decls.

Validation

  • New gap test test-files/test_gap_2817_2843_2816_object_defineproperty.ts covers every case in all three issues. Compiled with the DEFAULT auto-optimize flow, its output is byte-identical to node --experimental-strip-types.
  • cargo test --release -p perry-runtime object — green (82 passed). One unit test (test_unboxed_object_accessor_define_property_falls_back) used { get: 1 }, now invalid per runtime: validate Object.defineProperty descriptors and throw TypeErrors #2817; updated it to use a real callable getter so it still exercises the accessor shape-invalidation path.
  • cargo fmt --all -- --check — clean.
  • Regression-checked 8 existing object/defineProperty/create test files (test_issue_450_defineProperty_accessor, test_gap_2159_defineproperty_class_prototype, test_issue_1805_object_create_instanceof, test_gap_object_create_*, test_gap_object_methods, test_gap_object_get_own_property_descriptors, test_gap_2442_object_literal_accessors) — all stay byte-identical to Node.

@proggeramlug
proggeramlug merged commit a78deb7 into main May 30, 2026
11 checks passed
@proggeramlug
proggeramlug deleted the fix-object-defineproperty-2817-2843-2816 branch May 30, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant