details: https://hg.nginx.org/njs/rev/398f4de34fe7 branches: changeset: 2134:398f4de34fe7 user: Dmitry Volyntsev <xei...@nginx.com> date: Wed May 24 22:04:38 2023 -0700 description: Implemented Array.prototype.toSorted().
diffstat: src/njs_array.c | 457 +++++++++++++++++++++++++++++++--------------- src/test/njs_unit_test.c | 30 +++ 2 files changed, 332 insertions(+), 155 deletions(-) diffs (547 lines): diff -r a0807bc0ec72 -r 398f4de34fe7 src/njs_array.c --- a/src/njs_array.c Tue May 23 23:47:43 2023 -0700 +++ b/src/njs_array.c Wed May 24 22:04:38 2023 -0700 @@ -2586,16 +2586,207 @@ exception: } +static njs_array_sort_slot_t * +njs_sort_indexed_properties(njs_vm_t *vm, njs_value_t *obj, int64_t length, + njs_function_t *compare, njs_bool_t skip_holes, int64_t *nslots, + int64_t *nunds) +{ + int64_t i, ilength, nlen; + njs_int_t ret; + njs_array_t *array, *keys; + njs_value_t *start, *strings, key; + njs_array_sort_ctx_t ctx; + njs_array_sort_slot_t *p, *end, *slots, *newslots; + + slots = NULL; + keys = NULL; + ctx.vm = vm; + ctx.function = compare; + ctx.strings.separate = 0; + ctx.strings.pointer = 0; + ctx.exception = 0; + + if (njs_fast_path(njs_is_fast_array(obj))) { + array = njs_array(obj); + start = array->start; + + slots = njs_mp_alloc(vm->mem_pool, + sizeof(njs_array_sort_slot_t) * length); + if (njs_slow_path(slots == NULL)) { + njs_memory_error(vm); + return NULL; + } + + *nunds = 0; + p = slots; + + for (i = 0; i < length; i++) { + if (njs_fast_path(njs_is_valid(&start[i]))) { + /* not an empty value at index i. */ + njs_value_assign(&p->value, &start[i]); + + } else { + njs_uint32_to_string(&key, i); + ret = njs_value_property(vm, obj, &key, &p->value); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; + } + + if (ret == NJS_DECLINED && skip_holes) { + continue; + } + } + + if (njs_slow_path(njs_is_undefined(&p->value))) { + (*nunds)++; + continue; + } + + p->pos = i; + p->str = NULL; + p++; + } + + *nslots = p - slots; + + } else { + if (skip_holes) { + keys = njs_array_indices(vm, obj); + if (njs_slow_path(keys == NULL)) { + return NULL; + } + + slots = njs_mp_alloc(vm->mem_pool, + sizeof(njs_array_sort_slot_t) * keys->length); + if (njs_slow_path(slots == NULL)) { + njs_memory_error(vm); + ret = NJS_ERROR; + goto exception; + } + + *nunds = 0; + p = slots; + + ilength = njs_min(keys->length, length); + + for (i = 0; i < ilength; i++) { + ret = njs_value_property(vm, obj, &keys->start[i], &p->value); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; + } + + if (ret == NJS_DECLINED) { + continue; + } + + if (njs_is_undefined(&p->value)) { + (*nunds)++; + continue; + } + + p->pos = i; + p->str = NULL; + p++; + } + + *nslots = p - slots; + + } else { + /* !skip_holes */ + + nlen = njs_min(length, 8); + slots = njs_mp_alloc(vm->mem_pool, + sizeof(njs_array_sort_slot_t) * nlen); + if (njs_slow_path(slots == NULL)) { + njs_memory_error(vm); + ret = NJS_ERROR; + goto exception; + } + + p = slots; + end = slots + nlen; + + for (i = 0; i < length; i++) { + if (p >= end) { + nlen = njs_min(njs_max((p - slots) * 2, 8), length); + newslots = njs_mp_alloc(vm->mem_pool, + sizeof(njs_array_sort_slot_t) * nlen); + if (njs_slow_path(newslots == NULL)) { + njs_memory_error(vm); + ret = NJS_ERROR; + goto exception; + } + + if (slots != NULL) { + p = (void *) njs_cpymem(newslots, slots, + sizeof(njs_array_sort_slot_t) * (p - slots)); + njs_mp_free(vm->mem_pool, slots); + + } else { + p = newslots; + } + + slots = newslots; + end = slots + nlen; + } + + ret = njs_value_property_i64(vm, obj, i, &p->value); + if (njs_slow_path(ret == NJS_ERROR)) { + ret = NJS_ERROR; + goto exception; + } + + if (njs_is_undefined(&p->value)) { + continue; + } + + p->pos = i; + p->str = NULL; + p++; + } + + *nslots = p - slots; + *nunds = length - *nslots; + } + } + + strings = njs_arr_init(vm->mem_pool, &ctx.strings, NULL, *nslots + 1, + sizeof(njs_value_t)); + if (njs_slow_path(strings == NULL)) { + njs_mp_free(vm->mem_pool, slots); + return NULL; + } + + njs_qsort(slots, *nslots, sizeof(njs_array_sort_slot_t), njs_array_compare, + &ctx); + + ret = NJS_OK; + njs_arr_destroy(&ctx.strings); + +exception: + + if (keys != NULL) { + njs_array_destroy(vm, keys); + } + + if ((ctx.exception || ret == NJS_ERROR) && slots != NULL) { + njs_mp_free(vm->mem_pool, slots); + return NULL; + } + + return slots; +} + + static njs_int_t njs_array_prototype_sort(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { - 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; - njs_array_sort_slot_t *p, *end, *slots, *nslots; + int64_t i, nslots, nunds, length; + njs_int_t ret; + njs_value_t *this, *comparefn; + njs_function_t *compare; + njs_array_sort_slot_t *slots; comparefn = njs_arg(args, nargs, 1); @@ -2605,10 +2796,10 @@ njs_array_prototype_sort(njs_vm_t *vm, n return NJS_ERROR; } - ctx.function = njs_function(comparefn); + compare = njs_function(comparefn); } else { - ctx.function = NULL; + compare = NULL; } this = njs_argument(args, 0); @@ -2623,158 +2814,34 @@ njs_array_prototype_sort(njs_vm_t *vm, n return ret; } - if (njs_slow_path(length < 2)) { - njs_value_assign(retval, this); - return NJS_OK; - } - - slots = NULL; - ctx.vm = vm; - ctx.strings.separate = 0; - ctx.strings.pointer = 0; - ctx.exception = 0; - - fast_path = njs_is_fast_array(this); - - if (njs_fast_path(fast_path)) { - array = njs_array(this); - start = array->start; - - 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; - 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; - } - - p->value = start[i]; - p->pos = i; - p->str = NULL; - p++; - } - - len = p - slots; - - } else { - -slow_path: - - und = 0; - p = NULL; - end = NULL; - - for (i = 0; i < length; i++) { - if (p >= end) { - nlen = njs_min(njs_max((p - slots) * 2, 8), length); - nslots = njs_mp_alloc(vm->mem_pool, - sizeof(njs_array_sort_slot_t) * nlen); - if (njs_slow_path(nslots == NULL)) { - njs_memory_error(vm); - return NJS_ERROR; - } - - if (slots != NULL) { - p = (void *) njs_cpymem(nslots, slots, - sizeof(njs_array_sort_slot_t) * (p - slots)); - njs_mp_free(vm->mem_pool, slots); - - } else { - p = nslots; - } - - slots = nslots; - end = slots + nlen; - } - - ret = njs_value_property_i64(vm, this, i, &p->value); - if (njs_slow_path(ret == NJS_ERROR)) { - ret = NJS_ERROR; - goto exception; - } - - if (ret == NJS_DECLINED) { - continue; - } - - if (njs_is_undefined(&p->value)) { - und++; - continue; - } - - p->pos = i; - p->str = NULL; - p++; - } - - len = p - slots; - } - - strings = njs_arr_init(vm->mem_pool, &ctx.strings, NULL, len + 1, - sizeof(njs_value_t)); - if (njs_slow_path(strings == NULL)) { + slots = njs_sort_indexed_properties(vm, this, length, compare, 1, &nslots, + &nunds); + if (njs_slow_path(slots == NULL)) { ret = NJS_ERROR; goto exception; } - njs_qsort(slots, len, sizeof(njs_array_sort_slot_t), njs_array_compare, - &ctx); - - if (ctx.exception) { - ret = NJS_ERROR; - goto exception; - } - - if (njs_fast_path(fast_path - && njs_is_fast_array(this) - && (njs_array(this)->length == length))) - { - 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; + njs_assert(length >= (nslots + nunds)); + + for (i = 0; i < nslots; i++) { + ret = njs_value_property_i64_set(vm, this, i, &slots[i].value); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; } - - } else { - for (i = 0; i < len; i++) { - ret = njs_value_property_i64_set(vm, this, i, &slots[i].value); - if (njs_slow_path(ret == NJS_ERROR)) { - goto exception; - } + } + + for (i = nslots; nunds-- > 0; i++) { + ret = njs_value_property_i64_set(vm, this, i, + njs_value_arg(&njs_value_undefined)); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; } - - for (i = len; und-- > 0; i++) { - ret = njs_value_property_i64_set(vm, this, i, - njs_value_arg(&njs_value_undefined)); - if (njs_slow_path(ret == NJS_ERROR)) { - goto exception; - } - } - - for (; i < length; i++) { - ret = njs_value_property_i64_delete(vm, this, i, NULL); - if (njs_slow_path(ret == NJS_ERROR)) { - goto exception; - } + } + + for (; i < length; i++) { + ret = njs_value_property_i64_delete(vm, this, i, NULL); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; } } @@ -2788,7 +2855,85 @@ exception: njs_mp_free(vm->mem_pool, slots); } - njs_arr_destroy(&ctx.strings); + return ret; +} + + +static njs_int_t +njs_array_prototype_to_sorted(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused, njs_value_t *retval) +{ + int64_t i, nslots, nunds, length; + njs_int_t ret; + njs_array_t *array; + njs_value_t *this, *comparefn; + njs_function_t *compare; + njs_array_sort_slot_t *slots; + + comparefn = njs_arg(args, nargs, 1); + + if (njs_is_defined(comparefn)) { + if (njs_slow_path(!njs_is_function(comparefn))) { + njs_type_error(vm, "comparefn must be callable or undefined"); + return NJS_ERROR; + } + + compare = njs_function(comparefn); + + } else { + compare = NULL; + } + + this = njs_argument(args, 0); + + ret = njs_value_to_object(vm, this); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_value_length(vm, this, &length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + array = njs_array_alloc(vm, 0, length, 0); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + slots = njs_sort_indexed_properties(vm, this, length, compare, 0, &nslots, + &nunds); + if (njs_slow_path(slots == NULL)) { + ret = NJS_ERROR; + goto exception; + } + + njs_assert(length == (nslots + nunds)); + + njs_set_array(retval, array); + + for (i = 0; i < nslots; i++) { + ret = njs_value_property_i64_set(vm, retval, i, &slots[i].value); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; + } + } + + for (; i < length; i++) { + ret = njs_value_property_i64_set(vm, retval, i, + njs_value_arg(&njs_value_undefined)); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; + } + } + + ret = NJS_OK; + +exception: + + if (slots != NULL) { + njs_mp_free(vm->mem_pool, slots); + } return ret; } @@ -2945,6 +3090,8 @@ static const njs_object_prop_t njs_arra NJS_DECLARE_PROP_NATIVE("splice", njs_array_prototype_splice, 2, 0), + NJS_DECLARE_PROP_NATIVE("toSorted", njs_array_prototype_to_sorted, 1, 0), + NJS_DECLARE_PROP_NATIVE("toString", njs_array_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE("unshift", njs_array_prototype_unshift, 1, 0), diff -r a0807bc0ec72 -r 398f4de34fe7 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue May 23 23:47:43 2023 -0700 +++ b/src/test/njs_unit_test.c Wed May 24 22:04:38 2023 -0700 @@ -7332,6 +7332,36 @@ static njs_unit_test_t njs_test[] = "[a.length, a[0].toString(), a[63].toString()]"), njs_str("64,00,63") }, + { njs_str("Object.prototype[2] = 4;" + "njs.dump([undefined, 3, /*hole*/, 2, undefined, /*hole*/, 1].sort())"), + njs_str("[1,2,3,4,undefined,undefined,<empty>]") }, + + { njs_str("var a = [3,2,1]; [a.toSorted(), a]"), + njs_str("1,2,3,3,2,1") }, + + { njs_str("var a = [3,,1]; njs.dump([a.toSorted(), a.sort()])"), + njs_str("[[1,3,undefined],[1,3,<empty>]]") }, + + { njs_str("var a = {length:3, 0:'Z', 2:'A'};" + "njs.dump([Array.prototype.toSorted.call(a), Array.prototype.sort.call(a)])"), + njs_str("[['A','Z',undefined],{length:3,0:'A',1:'Z'}]") }, + + { njs_str("var a = {length: 1}; a.__proto__ = {0:'A'};" + "njs.dump([Array.prototype.toSorted.call(a), Array.prototype.sort.call(a)])"), + njs_str("[['A'],{length:1}]") }, + + { njs_str("Array.prototype.toSorted.call(true)"), + njs_str("") }, + + { njs_str("Array.prototype.toSorted.call({length: -2})"), + njs_str("") }, + + { njs_str("Array.prototype.toSorted.call({length: NaN})"), + njs_str("") }, + + { njs_str("Array.prototype.toSorted.call({length: 2**32})"), + njs_str("RangeError: Invalid array length") }, + /* Array.prototype.keys() Array.prototype.values() _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org https://mailman.nginx.org/mailman/listinfo/nginx-devel