Non-extensible holey array: a failed `a[i] = v`, `push()` or `defineProperty` past the end still sets `length` (regression for assignment since fce112d)
Maintainers usually reply within 2 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 74/100
Research direction
Start in quickjs.c at JS_CreateProperty() and the generic_array: label; trace when array length is updated relative to the extensibility check. Add a regression case to tests/test_builtin.js for a failed property addition past the end of a non-extensible holey array, then run make test. Done means the operation fails without changing length and the test suite passes.
Written by the indexing model from the issue text.
Description
Environment
- QuickJS master 712e8c0 (2026-11-02): every case below
- QuickJS 2026-06-04 (release): only the
defineProperty/Reflect.definePropertycase - The assignment and
pushcases bisect to fce112d ("fix super property assignment on non-extensible receiver with existing property", 2026-09-29) make qjs, Debian 12 x86-64, gcc 12.2. Reference: node v26.13.0- Checked on 2026-11-03
Reproduction
"use strict";
var b = [1, , 3]; // a hole: not a fast array
Object.preventExtensions(b);
try { b[3] = 9; } catch (e) { console.log(e.name); }
console.log(b.length); // expected: TypeError, 3 master: TypeError, 4
var c = [1, , 3]; Object.preventExtensions(c);
try { c.push(9); } catch (e) { console.log(e.name); }
console.log(c.length); // expected: TypeError, 3 master: TypeError, 4
var s = [1, , 3]; Object.seal(s);
try { s[5] = 1; } catch (e) { console.log(e.name); }
console.log(s.length); // expected: TypeError, 3 master: TypeError, 6
var o = [1, , 3]; Object.preventExtensions(o);
console.log(Reflect.defineProperty(o, 7, { value: 1 }), o.length);
// expected: false 3 master and 2026-06-04: false 8
The element itself is not created (Object.getOwnPropertyNames(b) stays 0,2,length); only length changes. Arrays
without holes are not affected.
Expected (spec)
Array [[DefineOwnProperty]] for an array
index: Let succeeded be ! OrdinaryDefineOwnProperty(A, P, Desc). If succeeded is false, return false. Only after that:
If index ≥ length, then Set lengthDesc.[[Value]] to index + 1 …. A new property on a non-extensible object is rejected,
so length must not change. b[3] = 9 and push reach the same method through OrdinarySet / CreateDataProperty.
Cause and patch
JS_CreateProperty(), Array case, label generic_array:, updates length (XXX: should update the length after defining the property) before the if (!p->extensible) goto not_extensible check further down. Before fce112d,
JS_SetPropertyInternal() rejected non-extensible receivers before calling JS_CreateProperty(), so only the define path
showed the bug. fce112d moved that test ("the extensibility test is included in JS_CreateProperty()"), which exposes the
early length update to ordinary assignment and push.
diff --git a/quickjs.c b/quickjs.c
--- a/quickjs.c
+++ b/quickjs.c
@@ -10374,6 +10374,9 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
JSProperty *plen;
JSShapeProperty *pslen;
generic_array:
+ /* the length must not change if the property cannot be added */
+ if (!p->extensible)
+ goto not_extensible;
/* update the length field */
plen = &p->prop[0];
JS_ToUint32(ctx, &len, plen->u.value);
With the patch the cases above match node, make test passes (a test is added to tests/test_builtin.js), and test262
shows no new failures against test262_errors.txt.
Related
- quickjs-ng has the
definePropertyhalf of this: quickjs-ng/quickjs#1882
- Dominant language
- C
- Stars
- 11.1k
- Forks
- 1.2k
- Avg merge
- 11h 20m
- Merged PRs (30d)
- 1
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from bellard/quickjs
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
Maintainers usually reply within 2 days
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
Maintainers usually reply within 2 days
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
Maintainers usually reply within 2 days
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
Maintainers usually reply within 2 days
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Maintainers usually reply within 2 days
Similar issues
-
5700模组网络优先级问题Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Maintainers usually reply within 1 day
-
severity: low
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
luainkernel/lunatik#2372 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 75/100
ly05010419/MacNativeGames#1796 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Maintainers usually reply within 1 day