Skip to content

arr[i] = v never observes an inherited index accessor or non-writable inherited index — the strict store's fast lanes bypass array_spec_set #9220

Description

@proggeramlug

Summary

An indexed write to an array never consults the prototype chain. arr[i] = v lowers to js_array_set_f64_extend_strict, whose fast lanes go straight to the element storage; only array_spec_set (used by push/shift/reverse) does the [[Set]] walk.

const proto = {};
Object.defineProperty(proto, 9, { set(v) { calls.push(v); }, get() { return "acc9"; } });
const a = [1, 2, 3];
Object.setPrototypeOf(a, proto);
a[9] = 5;

// node:  the setter runs; a has NO own property "9"
// perry: no setter call; an own element 9 is created

The same gap applies to a non-writable inherited index, where ES requires the write to be rejected (silently in sloppy mode, TypeError in strict).

Not the setPrototypeOf bug

This is pre-existing and independent of #9192. It was found while fixing that issue, and the control that proves it is the point: the divergence reproduces identically when the prototype is itself an array, i.e. on the shape that has always been supported. #9192's fix (#9219) corrects the read side; this is the write side and is untouched by it.

Impact

Silent, and in the direction that matters: a program that installs an index setter to intercept writes sees nothing, and the array quietly gains an own property the author intended to be virtual. Accessor-backed array-likes and the ES5 Object.create(Array.prototype) idiom both hit it.

Fix direction

js_array_set_f64_extend_strict's fast lanes need the same recorded-prototype classification the read path now has — ArrayCustomProto::{Null, Array, Other} in crates/perry-runtime/src/array/indexing.rs, added by #9219. The cheap gate is the existing per-array "has a recorded prototype" check: an array with the default chain keeps today's lanes untouched, and only a retargeted array pays the [[Set]] walk. Note the ordering requirement — an inherited data property does not stop an own-property create, only an accessor or a non-writable one does.

There is a differential harness with controls for this in the #9219 branch (checks 085–087), so the regression instrument already exists.

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