From: Dmitry Volyntsev Date: Thu, 25 May 2023 05:04:38 +0000 (-0700) Subject: Implemented Array.prototype.toSorted(). X-Git-Url: http://www.kaiwu.me/postgresql/commit/static/gitweb.js?a=commitdiff_plain;h=1a2a65f7d18aa7e45ef763d2f2457350f30b1da2;p=njs.git Implemented Array.prototype.toSorted(). --- diff --git a/src/njs_array.c b/src/njs_array.c index b45dbfa5..9fa2785e 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -2586,195 +2586,262 @@ exception: } -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) +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, und, len, nlen, length; - njs_int_t ret, fast_path; - njs_array_t *array; - njs_value_t *this, *comparefn, *start, *strings; + 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, *nslots; - - 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; - } - - ctx.function = njs_function(comparefn); - - } else { - ctx.function = 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; - } - - if (njs_slow_path(length < 2)) { - njs_value_assign(retval, this); - return NJS_OK; - } + 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; - fast_path = njs_is_fast_array(this); - - if (njs_fast_path(fast_path)) { - array = njs_array(this); + 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)) { - return NJS_ERROR; + njs_memory_error(vm); + return NULL; } - und = 0; + *nunds = 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_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(&start[i]))) { - und++; + if (njs_slow_path(njs_is_undefined(&p->value))) { + (*nunds)++; continue; } - p->value = start[i]; p->pos = i; p->str = NULL; p++; } - len = p - slots; + *nslots = p - slots; } else { + if (skip_holes) { + keys = njs_array_indices(vm, obj); + if (njs_slow_path(keys == NULL)) { + return NULL; + } -slow_path: + 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; + } - und = 0; - p = NULL; - end = NULL; + *nunds = 0; + p = slots; - 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; + 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 (slots != NULL) { - p = (void *) njs_cpymem(nslots, slots, - sizeof(njs_array_sort_slot_t) * (p - slots)); - njs_mp_free(vm->mem_pool, slots); + if (ret == NJS_DECLINED) { + continue; + } - } else { - p = nslots; + if (njs_is_undefined(&p->value)) { + (*nunds)++; + continue; } - slots = nslots; - end = slots + nlen; + p->pos = i; + p->str = NULL; + p++; } - ret = njs_value_property_i64(vm, this, i, &p->value); - if (njs_slow_path(ret == NJS_ERROR)) { + *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; } - if (ret == NJS_DECLINED) { - continue; - } + p = slots; + end = slots + nlen; - if (njs_is_undefined(&p->value)) { - und++; - continue; + 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++; } - p->pos = i; - p->str = NULL; - p++; + *nslots = p - slots; + *nunds = length - *nslots; } - - len = p - slots; } - strings = njs_arr_init(vm->mem_pool, &ctx.strings, NULL, len + 1, + strings = njs_arr_init(vm->mem_pool, &ctx.strings, NULL, *nslots + 1, sizeof(njs_value_t)); if (njs_slow_path(strings == NULL)) { - ret = NJS_ERROR; - goto exception; + njs_mp_free(vm->mem_pool, slots); + return NULL; } - njs_qsort(slots, len, sizeof(njs_array_sort_slot_t), njs_array_compare, + njs_qsort(slots, *nslots, sizeof(njs_array_sort_slot_t), njs_array_compare, &ctx); - if (ctx.exception) { - ret = NJS_ERROR; - goto exception; + ret = NJS_OK; + njs_arr_destroy(&ctx.strings); + +exception: + + if (keys != NULL) { + njs_array_destroy(vm, keys); } - if (njs_fast_path(fast_path - && njs_is_fast_array(this) - && (njs_array(this)->length == length))) - { - array = njs_array(this); - start = array->start; + if ((ctx.exception || ret == NJS_ERROR) && slots != NULL) { + njs_mp_free(vm->mem_pool, slots); + return NULL; + } + + return slots; +} - for (i = 0; i < len; i++) { - start[i] = slots[i].value; - } - for (i = len; und-- > 0; i++) { - start[i] = njs_value_undefined; +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, 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); + + 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 { - 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; - } + 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; + } + + slots = njs_sort_indexed_properties(vm, this, length, compare, 1, &nslots, + &nunds); + if (njs_slow_path(slots == NULL)) { + ret = NJS_ERROR; + goto exception; + } + + 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; } + } - 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 = 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 < 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_array_prototype_properties[] = 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 --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 5669e348..2c8cfb11 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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,]") }, + + { 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,]]") }, + + { 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()