From: Dmitry Volyntsev Date: Wed, 3 Jun 2020 18:40:10 +0000 (+0000) Subject: Fixed Array.prototype.splice() according to the specification. X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=873dabafcf94eeb7aff8b602b55e2d18831b7105;p=njs.git Fixed Array.prototype.splice() according to the specification. --- diff --git a/src/njs_array.c b/src/njs_array.c index 519eba93..918103e0 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -1241,10 +1241,9 @@ static njs_int_t njs_array_prototype_splice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - int64_t n, start, length, items, delta, delete; + int64_t i, n, start, length, items, delta, delete; njs_int_t ret; - njs_uint_t i; - njs_value_t *this; + njs_value_t *this, value, del_object; njs_array_t *array, *deleted; this = njs_argument(args, 0); @@ -1254,74 +1253,81 @@ njs_array_prototype_splice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return ret; } - if (njs_slow_path(!njs_is_fast_array(this))) { - njs_internal_error(vm, "splice() is not implemented yet for objects"); - return NJS_ERROR; + ret = njs_object_length(vm, this, &length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; } - array = NULL; - start = 0; - delete = 0; + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (njs_is_fast_array(this)) { - array = njs_array(this); - length = array->length; + start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); - if (nargs > 1) { - ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + items = 0; + delete = 0; - if (start < 0) { - start += length; + if (nargs == 2) { + delete = length - start; - if (start < 0) { - start = 0; - } + } else if (nargs > 2) { + items = nargs - 3; - } else if (start > length) { - start = length; - } - - delete = length - start; + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &delete); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (nargs > 2) { - ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &n); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + delete = njs_min(njs_max(delete, 0), length - start); + } - if (n < 0) { - delete = 0; + delta = items - delete; - } else if (n < delete) { - delete = n; - } - } - } + if (njs_slow_path((length + delta) > NJS_MAX_LENGTH)) { + njs_type_error(vm, "Invalid length"); + return NJS_ERROR; } + /* TODO: ArraySpeciesCreate(). */ + deleted = njs_array_alloc(vm, 0, delete, 0); if (njs_slow_path(deleted == NULL)) { return NJS_ERROR; } - if (njs_slow_path(!deleted->object.fast_array)) { - njs_internal_error(vm, "deleted is not a fast_array"); - return NJS_ERROR; - } + if (njs_fast_path(njs_is_fast_array(this) && deleted->object.fast_array)) { + array = njs_array(this); + for (i = 0, n = start; i < delete; i++, n++) { + deleted->start[i] = array->start[n]; + } - if (array != NULL && (delete >= 0 || nargs > 3)) { + } else { + njs_set_array(&del_object, deleted); - /* Move deleted items to a new array to return. */ - for (i = 0, n = start; i < (njs_uint_t) delete; i++, n++) { - /* No retention required. */ - deleted->start[i] = array->start[n]; + for (i = 0, n = start; i < delete; i++, n++) { + ret = njs_value_property_i64(vm, this, n, &value); + if (njs_slow_path(ret == NJS_ERROR)) { + return NJS_ERROR; + } + + if (ret == NJS_OK) { + /* TODO: CreateDataPropertyOrThrow(). */ + ret = njs_value_property_i64_set(vm, &del_object, i, &value); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + } } - items = (nargs > 3) ? nargs - 3: 0; - delta = items - delete; + ret = njs_object_length_set(vm, &del_object, delete); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + + if (njs_fast_path(njs_is_fast_array(this))) { + array = njs_array(this); if (delta != 0) { /* @@ -1335,18 +1341,51 @@ njs_array_prototype_splice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } } - memmove(&array->start[start + items], &array->start[n], - (array->length - n) * sizeof(njs_value_t)); + ret = njs_array_copy_within(vm, this, start + items, start + delete, + array->length - (start + delete), 0); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } array->length += delta; } /* Copy new items. */ - n = start; - for (i = 3; i < nargs; i++) { - /* GC: njs_retain(&args[i]); */ - array->start[n++] = args[i]; + if (items > 0) { + memcpy(&array->start[start], &args[3], + items * sizeof(njs_value_t)); + } + + } else { + + if (delta != 0) { + ret = njs_array_copy_within(vm, this, start + items, start + delete, + length - (start + delete), delta < 0); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + for (i = length - 1; i >= length + delta; i--) { + ret = njs_value_property_i64_delete(vm, this, i, NULL); + if (njs_slow_path(ret == NJS_ERROR)) { + return NJS_ERROR; + } + } + } + + /* Copy new items. */ + + for (i = 3, n = start; items-- > 0; i++, n++) { + ret = njs_value_property_i64_set(vm, this, n, &args[i]); + if (njs_slow_path(ret == NJS_ERROR)) { + return NJS_ERROR; + } + } + + ret = njs_object_length_set(vm, this, length + delta); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; } } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 25a527c7..d408be13 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -4499,6 +4499,102 @@ static njs_unit_test_t njs_test[] = "a.splice(3, 2, 8, 9, 10, 11 ).join(':') + '|' + a"), njs_str("3:4|0,1,2,8,9,10,11,5,6,7") }, + { njs_str("[" + " []," + " [1]," + " [1, 2]," + " [1, 2, 'a']," + " [1, 2, 'a', 'b']," + " [1, 2, 'a', 'b', 'c']," + "]" + ".map(args=>{var a = [0,1,3,4,5]; a.splice.apply(a, args); return a})" + ".map(v=>v.join(''))"), + njs_str("01345,0,045,0a45,0ab45,0abc45") }, + + { njs_str("[" + " []," + " [1]," + " [1, 1, 'a']," + " [1, 2, 'a']," + " [1, 2, 'a', 'b']," + " [1, 2, 'a', 'b', 'c']," + "]" + ".map(args=>{var a = [0,1,3,4,5]; return a.splice.apply(a, args);})" + ".map(v=>v.join(''))"), + njs_str(",1345,1,13,13,13") }, + + { njs_str("Object.prototype.splice = Array.prototype.splice;" + "Object.prototype.join = Array.prototype.join;" + "[" + " []," + " [1]," + " [1, 2]," + " [1, 1, 'a']," + " [1, 2, 'a']," + " [1, 2, 'a', 'b']," + " [1, 2, 'a', 'b', 'c']," + "]" + ".map(args=>{var a = {0:0, 1:1, 2:3, 3:4, 4:5, length:5};" + " a.splice.apply(a, args); return a})" + ".map(v=>v.join(''))"), + njs_str("01345,0,045,0a345,0a45,0ab45,0abc45") }, + + { njs_str("Object.prototype.splice = Array.prototype.splice;" + "Object.prototype.join = Array.prototype.join;" + "[" + " []," + " [1]," + " [1, 0, 'a']," + " [1, 1, 'a']," + " [1, 2, 'a']," + " [1, 2, 'a', 'b']," + " [1, 2, 'a', 'b', 'c']," + "]" + ".map(args=>{var a = {0:0, 1:1, 2:3, 3:4, 4:5, length:5};" + " return a.splice.apply(a, args);})" + ".map(v=>v.join(''))"), + njs_str(",1345,,1,13,13,13") }, + + { njs_str("Array.prototype.splice.call({0:0,1:1,2:2,3:3,length:4},0,3,4,5)"), + njs_str("0,1,2") }, + + { njs_str("var obj = {0:0,1:1,2:2,3:3,length:4};" + "Array.prototype.splice.call(obj,0,3,4,5); obj[3]"), + njs_str("undefined") }, + + { njs_str("var obj = {4294967294: 'x', length:-1};" + "Array.prototype.splice.call(obj, 4294967294, 1); obj.length"), + njs_str("0") }, + + { njs_str("var obj = {0:0, 1:1, 2:2};" + "Object.defineProperty(obj, 'length', {value:3, writable:false});" + "Array.prototype.splice.call(obj, 1, 2, 4)"), + njs_str("TypeError: Cannot assign to read-only property \"length\" of object") }, + + { njs_str("var obj = {'9007199254740988': 'A', '9007199254740989': 'B'," + " '9007199254740990': 'C', '9007199254740991': 'D', " + " length: 2 ** 53 + 2};" + "Array.prototype.splice.call(obj, 2**53-3, 2 ** 53 + 4)"), + njs_str("B,C") }, + + { njs_str("var obj = {'9007199254740988': 'A', '9007199254740989': 'B'," + " '9007199254740990': 'C', '9007199254740991': 'D', " + " length: 2 ** 53 + 2};" + "Array.prototype.splice.call(obj, 2**53-3, 2 ** 53 + 4);" + "obj['9007199254740988'] == 'A' && obj['9007199254740991'] == 'D'"), + njs_str("true") }, + + { njs_str("var obj = {'9007199254740990': 'A', '9007199254740991': 'B'," + " length: 2 ** 53 - 1};" + "Array.prototype.splice.call(obj, 2**53-2, 1, 'C');" + "obj['9007199254740990'] == 'C' && obj['9007199254740991'] == 'B'"), + njs_str("true") }, + + { njs_str("var obj = {'9007199254740990': 'A', '9007199254740991': 'B'," + " length: 2 ** 53 - 1};" + "Array.prototype.splice.call(obj, 2**53-2, 0, 'C');"), + njs_str("TypeError: Invalid length") }, + { njs_str("var a = []; a.reverse()"), njs_str("") },