Skip to content

Object.setPrototypeOf(array, non-array object) — the array inherits nothing (silent wrong values) #9192

Description

@proggeramlug

Found while investigating the process-global array-index latch (which turned out to be a non-issue for the workload — see the perf note at the bottom). This is a silent wrong-value bug, not a crash.

Repro

const a = [1, 2, 3];
Object.setPrototypeOf(a, { 7: "inherited", foo: "bar" });

a[7]      // node: "inherited"   perry: undefined
a.foo     // node: "bar"         perry: undefined
7 in a    // node: true          perry: false

The classic ES5 array-subclass idiom is broken the same way:

function MyList() {}
MyList.prototype = Object.create(Array.prototype);
MyList.prototype.tag = "mylist";
MyList.prototype.first = function () { return this[0]; };

const arr = [10, 20];
Object.setPrototypeOf(arr, MyList.prototype);

arr.tag        // node: "mylist"   perry: undefined
arr.first()    // node: 10         perry: TypeError / missing

Root cause

array_custom_array_prototype (crates/perry-runtime/src/array/indexing.rs:237-268) accepts the recorded [[Prototype]] only when that prototype is itself GC_TYPE_ARRAY. Retargeting an array's prototype to an ordinary object records the change (the process-wide deopt latch is set, so perry pays the cost) but the lookup path then declines to consult it — so the array inherits nothing.

perry therefore pays the deoptimisation for a case it does not implement.

Why it was never caught

The only fixture covering array prototype retargeting, test-files/test_gap_typed_arrays.ts:38, uses an array as the new prototype — the one shape that works. The object-prototype case has no coverage.

Suggested fixture

A test that sets an array's prototype to (a) a plain object with an indexed key and a named key, (b) an Object.create(Array.prototype)-derived prototype carrying methods, and (c) null, checking element reads, named reads, in, and method calls against node in each case.


Perf context, for completeness: the same investigation established that class X extends Array does not latch PERRY_ARRAY_INDEX_FAST_PATH_INVALIDATED on current main (subclass instances have been elements-backed GC_TYPE_OBJECT since #8974), that the claude-code and pi bundles never latch it, and that forcing it on for cc --help changes instructions by 0.001%. The latch is a genuine latent risk when it does fire (measured up to 14× on push, process-wide and permanent), but it is not a workload problem today.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions