Repository navigation
runtime: complete Object.create descriptor and prototype validation semantics #2816
Description
Summary
Object.create currently only models Object.create(proto). Node also supports Object.create(proto, propertiesObject) and requires the prototype argument to be either an object or null; primitives and undefined throw TypeError.
Perry's current runtime/lowering ignores the descriptor argument and treats non-object prototypes like null/prototype-less objects, which hides invalid input and drops own properties from descriptor-form clones.
Expected Node behavior
Local Node v25.9.0 probe:
Object.getPrototypeOf(Object.create(null)) === null; // true
const p = { x: 1 };
const o = Object.create(p);
console.log(Object.getPrototypeOf(o) === p, o.x, Object.keys(o)); // true, 1, []
Object.create(1); // TypeError: Object prototype may only be an Object or null
Object.create("x"); // TypeError
Object.create(undefined); // TypeError
const d = Object.create(
{ p: 1 },
{
a: { value: 2, enumerable: true },
b: { value: 3, enumerable: false },
},
);
console.log(d.p, d.a, d.b, Object.keys(d)); // 1, 2, 3, ["a"]
console.log(Object.getOwnPropertyDescriptor(d, "b").enumerable); // falseCurrent Perry implementation
Source evidence from current tree:
crates/perry-hir/src/lower/expr_call/object_static.rslowers"create"toExpr::ObjectCreate(Box::new(iter.next().unwrap_or(Expr::Undefined))), discarding every argument after the first.crates/perry-hir/src/lower/expr_call/native_module.rsdoes the same for directObject.create(...)calls.crates/perry-hir/src/ir/expr.rsonly hasObjectCreate(Box<Expr>), so there is no HIR slot for the optional descriptor object.crates/perry-codegen/src/expr/array_methods.rslowersExpr::ObjectCreateto one runtime call:js_object_create(proto).crates/perry-codegen/src/runtime_decls/strings.rsdeclaresjs_object_createwith oneDOUBLEargument.crates/perry-runtime/src/object/object_ops.rs::js_object_create()comments thatObject.create(null)or a non-object proto falls back to a prototype-less object. Node only permitsnull; non-object, non-null prototypes must throw.docs/typescript-parity-gaps.mdalready notesObject.create()is limited and lacks the property descriptors argument, but there is no open issue tracking the concrete runtime/API work.
Suggested test surface
Add parity tests for:
const proto = { p: 1 };
const obj = Object.create(proto, {
a: { value: 2, enumerable: true },
b: { value: 3, enumerable: false },
});
assert.equal(obj.p, 1);
assert.equal(obj.a, 2);
assert.equal(obj.b, 3);
assert.deepEqual(Object.keys(obj), ["a"]);
assert.equal(Object.getOwnPropertyDescriptor(obj, "b")!.enumerable, false);
assert.throws(() => Object.create(1 as any), TypeError);
assert.throws(() => Object.create(undefined as any), TypeError);
assert.equal(Object.getPrototypeOf(Object.create(null)), null);Scope / non-goals
This is not about the already-fixed Object.create(proto) prototype-chain wiring or instanceof behavior (#1805 / PR #2244, #809 / PR #815). It tracks the remaining descriptor-form Object.create(proto, propertiesObject) behavior and prototype-argument validation.
Activity