[njs] Ensuring Array.prototype.sort() stability.

Dmitry Volyntsev xeioex at nginx.com
Tue Jun 2 15:18:10 UTC 2020


details:   https://hg.nginx.org/njs/rev/c2fa0f18e58d
branches:  
changeset: 1418:c2fa0f18e58d
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Jun 02 12:08:04 2020 +0000
description:
Ensuring Array.prototype.sort() stability.

For fast arrays with empty or undefined elements.

This closes #312 issue on Github.

diffstat:

 src/njs_array.c          |  69 ++++++++++++++++++++++++-----------------------
 src/test/njs_unit_test.c |   6 ++++
 2 files changed, 41 insertions(+), 34 deletions(-)

diffs (128 lines):

diff -r 1ce9a0895630 -r c2fa0f18e58d src/njs_array.c
--- a/src/njs_array.c	Tue Jun 02 14:59:30 2020 +0000
+++ b/src/njs_array.c	Tue Jun 02 12:08:04 2020 +0000
@@ -3216,8 +3216,8 @@ static njs_int_t
 njs_array_prototype_sort(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    int64_t                i, und, inv, len, nlen, length;
-    njs_int_t              ret;
+    int64_t                i, und, len, nlen, length;
+    njs_int_t              ret, fast_path;
     njs_array_t            *array;
     njs_value_t            *this, *comparefn, *start, *strings;
     njs_array_sort_ctx_t   ctx;
@@ -3260,50 +3260,46 @@ njs_array_prototype_sort(njs_vm_t *vm, n
     ctx.strings.pointer = 0;
     ctx.exception = 0;
 
-    if (njs_fast_path(njs_is_fast_array(this))) {
+    fast_path = njs_is_fast_array(this);
+
+    if (njs_fast_path(fast_path)) {
         array = njs_array(this);
         start = array->start;
 
-        /**
-         * Moving undefined and invalid elements to the end.
-         * x x x | undefineds | invalids
-         */
+        slots = njs_mp_alloc(vm->mem_pool,
+                             sizeof(njs_array_sort_slot_t) * length);
+        if (njs_slow_path(slots == NULL)) {
+                return NJS_ERROR;
+        }
 
         und = 0;
-        inv = length;
-
-        for (i = length - 1; i >= 0; i--) {
-            if (njs_is_undefined(&start[i])) {
-                start[i] = start[inv - und - 1];
-                start[inv - und - 1] = njs_value_undefined;
+        p = slots;
+
+        for (i = 0; i < length; i++) {
+            if (njs_slow_path(!njs_is_valid(&start[i]))) {
+                fast_path = 0;
+                njs_mp_free(vm->mem_pool, slots);
+                slots = NULL;
+                goto slow_path;
+            }
+
+            if (njs_slow_path(njs_is_undefined(&start[i]))) {
                 und++;
                 continue;
             }
 
-            if (!njs_is_valid(&start[i])) {
-                start[i] = start[inv - und - 1];
-                start[inv - und - 1] = njs_value_undefined;
-                start[inv - 1] = njs_value_invalid;
-                inv--;
-                continue;
-            }
+            p->value = start[i];
+            p->pos = i;
+            p->str = NULL;
+            p++;
         }
 
-        len = inv - und;
-
-        slots = njs_mp_alloc(vm->mem_pool,
-                             sizeof(njs_array_sort_slot_t) * len);
-        if (njs_slow_path(slots == NULL)) {
-                return NJS_ERROR;
-        }
-
-        for (i = 0; i < len; i++) {
-            slots[i].value = start[i];
-            slots[i].pos = i;
-            slots[i].str = NULL;
-        }
+        len = p - slots;
 
     } else {
+
+slow_path:
+
         und = 0;
         p = NULL;
         end = NULL;
@@ -3369,13 +3365,18 @@ njs_array_prototype_sort(njs_vm_t *vm, n
         goto exception;
     }
 
-    if (njs_fast_path(njs_is_fast_array(this))) {
+    if (njs_fast_path(fast_path)) {
         array = njs_array(this);
         start = array->start;
+
         for (i = 0; i < len; i++) {
             start[i] = slots[i].value;
         }
 
+        for (i = len; und-- > 0; i++) {
+            start[i] = njs_value_undefined;
+        }
+
     } else {
         for (i = 0; i < len; i++) {
             if (slots[i].pos != i) {
diff -r 1ce9a0895630 -r c2fa0f18e58d src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Tue Jun 02 14:59:30 2020 +0000
+++ b/src/test/njs_unit_test.c	Tue Jun 02 12:08:04 2020 +0000
@@ -6104,6 +6104,12 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("[1,2,3].sort(()=>-1)"),
       njs_str("3,2,1") },
 
+    { njs_str("njs.dump([undefined,1,2,3].sort(()=>0))"),
+      njs_str("[1,2,3,undefined]") },
+
+    { njs_str("njs.dump([1,,2,3].sort(()=>0))"),
+      njs_str("[1,2,3,<empty>]") },
+
     { njs_str("var count = 0;"
               "[4,3,2,1].sort(function(x, y) { if (count++ == 2) {throw Error('Oops'); }; return x - y })"),
       njs_str("Error: Oops") },


More information about the nginx-devel mailing list