Refuse elements on Array.prototype

This commit is contained in:
Chris Tate
2026-10-21 17:24:17 -05:00
parent 75436e9c86
commit 0950725949
4 changed files with 71 additions and 0 deletions
+5
View File
@@ -997,6 +997,11 @@ ScrDyn *scr_dyn_get_own_property_descriptor(ScrDyn *target, ScrDyn *key);
ScrDyn *scr_dyn_array_proto_call(ScrDyn *target, ScrStr *method, ScrDyn *args);
ScrDyn *scr_dyn_array_prototype(void); /* +1, installs native method adapters */
ScrDyn *scr_dyn_array_prototype_base(void); /* +1, shared native array identity */
/* Native arrays read their holes as undefined without consulting
* Array.prototype, so storing an element on Array.prototype raises SC2020.
* True (with the error pending) when `recv` is Array.prototype. */
bool scr_dyn_array_prototype_element_fence(const ScrDyn *recv);
bool scr_dyn_is_array_prototype(const ScrDyn *d);
/* The checked-dynamic keyed WRITE (`h.k = v` on a dyn receiver): OBJ sets
* the member (JS: later writes win, insertion order); undefined/null and
* non-object kinds throw Node's catchable TypeErrors (strict-mode
+4
View File
@@ -811,6 +811,10 @@ static ScrDyn *scr_dyn_invoke_impl(
if (recv->kind == SCR_DYN_HANDLE) {
return scr_dynh_dispatch(recv, method, args, argc, what);
}
if (scr_dyn_is_array_prototype(recv) && dyn_arr_proto_mutates(method)) {
scr_dyn_array_prototype_element_fence(recv);
return NULL;
}
if (recv->kind == SCR_DYN_PROXY) {
+12
View File
@@ -674,6 +674,17 @@ void scr_dyn_from_leave(void) {
*/
static SCR_TL ScrDyn *scr_builtin_array_prototype;
bool scr_dyn_is_array_prototype(const ScrDyn *d) {
return d && d == scr_builtin_array_prototype;
}
bool scr_dyn_array_prototype_element_fence(const ScrDyn *recv) {
if (!scr_dyn_is_array_prototype(recv)) return false;
static const char message[] = "Elements on Array.prototype are not supported yet";
scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
return true;
}
/* Property names are immutable and often repeated across simultaneously live
* objects. Entries own references to their bytes; this fixed weak table owns
* none. Collisions only reduce sharing, and the final release removes a weak
@@ -8527,6 +8538,7 @@ void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
else idx = idx * 10 + (size_t)(key->data[i] - '0');
}
if (is_index && idx < 4294967295ULL) {
if (scr_dyn_array_prototype_element_fence(recv)) return;
if (recv->v.arr.frozen && idx < recv->v.arr.len) {
static const char message[] = "Cannot assign to read only property";
scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1);
+50
View File
@@ -1727,3 +1727,53 @@ console.log(util.inspect(extra));
);
});
});
describe("elements on Array.prototype", () => {
// Holes of native arrays read undefined without consulting
// Array.prototype, so storing an element there refuses instead of
// letting later hole reads diverge from Node. Named members stay
// writable.
test("an index write is a named fence (TS lane)", async () => {
const r = await compileAndRun(
"array-prototype-element-ts",
`const proto = Array.prototype as unknown as Record<string, unknown>;
proto.label = "named";
console.log(proto.label);
(Array.prototype as unknown as Record<number, unknown>)[1] = "proto";
console.log("unreachable");
`,
);
expect(r.exitCode).toBe(1);
expect(r.stdout).toBe("named\n");
expect(r.stderr).toMatch(
/^Uncaught Error: Elements on Array\.prototype are not supported yet \[SC2020[^\]]*\]\n$/,
);
});
test("an index write and a mutating method are named fences (JS lane)", async () => {
const write = await compileAndRun(
"array-prototype-element-js",
`Array.prototype[0] = "proto";
console.log("unreachable");
`,
"cjs",
);
expect(write.exitCode).toBe(1);
expect(write.stdout).toBe("");
expect(write.stderr).toMatch(
/^Uncaught Error: Elements on Array\.prototype are not supported yet \[SC2020[^\]]*\]\n$/,
);
const push = await compileAndRun(
"array-prototype-push-js",
`Array.prototype.push("proto");
console.log("unreachable");
`,
"cjs",
);
expect(push.exitCode).toBe(1);
expect(push.stdout).toBe("");
expect(push.stderr).toMatch(
/^Uncaught Error: Elements on Array\.prototype are not supported yet \[SC2020[^\]]*\]\n$/,
);
});
});