diff --git a/quickjs.c b/quickjs.c index af401ae37..26904fd40 100644 --- a/quickjs.c +++ b/quickjs.c @@ -44800,17 +44800,11 @@ static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val, /* XXX: should special case fast arrays */ while (n < pos) { - if (array[n].str) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); - if (array[n].pos == n) { - JS_FreeValue(ctx, array[n].val); - } else { - if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { - n++; - goto exception; - } - } - n++; + i = n++; + if (array[i].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[i].str)); + if (JS_SetPropertyInt64(ctx, obj, i, array[i].val) < 0) + goto exception; } js_free(ctx, array); for (i = n; undefined_count-- > 0; i++) { diff --git a/tests/array-sort-write-back.js b/tests/array-sort-write-back.js new file mode 100644 index 000000000..dba0dc669 --- /dev/null +++ b/tests/array-sort-write-back.js @@ -0,0 +1,26 @@ +import { assert, assertThrows } from "./assert.js"; + +/* Array.prototype.sort writes every sorted value back with Set(..., true), + even ones that are already in place. */ + +/* a String's indices are read-only */ +assertThrows(TypeError, () => Array.prototype.sort.call("aa")); +assertThrows(TypeError, () => Array.prototype.sort.call("ab")); + +/* so are a frozen array's, even when it is already sorted */ +assertThrows(TypeError, () => Object.freeze([1, 2]).sort()); + +/* setters see every write */ +{ + const writes = []; + const o = { length: 2 }; + for (const i of [0, 1]) { + let v = i; + Object.defineProperty(o, i, { + get: () => v, + set: x => { writes.push(i); v = x; }, + }); + } + Array.prototype.sort.call(o); + assert(writes.join(), "0,1"); +}