details: https://hg.nginx.org/njs/rev/893fc436a363 branches: changeset: 1164:893fc436a363 user: Alexander Borisov <alexander.bori...@nginx.com> date: Thu Sep 19 10:19:02 2019 +0300 description: Fixed Array prototype functions according to the specification.
The following fuctions were fixed: pop, push, shift, unshift. diffstat: src/njs_array.c | 229 ++++++++++++++++++++++++++++++++++++++++++---- src/njs_object.h | 14 ++ src/test/njs_unit_test.c | 51 ++++++++++ 3 files changed, 273 insertions(+), 21 deletions(-) diffs (407 lines): diff -r a07722caaee3 -r 893fc436a363 src/njs_array.c --- a/src/njs_array.c Thu Sep 19 10:19:01 2019 +0300 +++ b/src/njs_array.c Thu Sep 19 10:19:02 2019 +0300 @@ -589,9 +589,19 @@ static njs_int_t njs_array_prototype_push(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { + uint32_t length; njs_int_t ret; njs_uint_t i; njs_array_t *array; + njs_value_t *value, index; + + value = njs_arg(args, nargs, 0); + length = 0; + + if (njs_slow_path(njs_is_null_or_undefined(value))) { + njs_type_error(vm, "Cannot convert undefined or null to object"); + return NJS_ERROR; + } if (njs_is_array(&args[0])) { array = njs_array(&args[0]); @@ -609,8 +619,31 @@ njs_array_prototype_push(njs_vm_t *vm, n } njs_set_number(&vm->retval, array->length); + + return NJS_OK; } + ret = njs_object_length(vm, value, &length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + for (i = 1; i < nargs; i++) { + njs_uint32_to_string(&index, length++); + + ret = njs_value_property_set(vm, value, &index, &args[i]); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + } + + ret = njs_object_length_set(vm, value, length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + njs_set_number(&vm->retval, length); + return NJS_OK; } @@ -619,25 +652,53 @@ static njs_int_t njs_array_prototype_pop(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_array_t *array; - const njs_value_t *retval, *value; - - retval = &njs_value_undefined; + uint32_t length; + njs_int_t ret; + njs_array_t *array; + njs_value_t *value, *entry, index; + + value = njs_arg(args, nargs, 0); + + if (njs_slow_path(njs_is_null_or_undefined(value))) { + njs_type_error(vm, "Cannot convert undefined or null to object"); + return NJS_ERROR; + } + + njs_set_undefined(&vm->retval); if (njs_is_array(&args[0])) { array = njs_array(&args[0]); if (array->length != 0) { array->length--; - value = &array->start[array->length]; - - if (njs_is_valid(value)) { - retval = value; + entry = &array->start[array->length]; + + if (njs_is_valid(entry)) { + vm->retval = *entry; } } + + return NJS_OK; } - vm->retval = *retval; + ret = njs_object_length(vm, value, &length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (length != 0) { + njs_uint32_to_string(&index, --length); + + ret = njs_value_property_delete(vm, value, &index, &vm->retval); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + ret = njs_object_length_set(vm, value, length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } return NJS_OK; } @@ -647,13 +708,28 @@ static njs_int_t njs_array_prototype_unshift(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { + uint32_t from, to, length; njs_int_t ret; njs_uint_t n; njs_array_t *array; - - if (njs_is_array(&args[0])) { - array = njs_array(&args[0]); - n = nargs - 1; + njs_value_t *value, entry, index; + + value = njs_arg(args, nargs, 0); + length = 0; + n = nargs - 1; + + if (njs_slow_path(njs_is_null_or_undefined(value))) { + njs_type_error(vm, "Cannot convert undefined or null to object"); + return NJS_ERROR; + } + + if (njs_is_array(value)) { + array = njs_array(value); + + if (array->length > (UINT32_MAX - n)) { + njs_type_error(vm, "Invalid length"); + return NJS_ERROR; + } if (n != 0) { ret = njs_array_expand(vm, array, n, 0); @@ -673,8 +749,66 @@ njs_array_prototype_unshift(njs_vm_t *vm } njs_set_number(&vm->retval, array->length); + + return NJS_OK; } + ret = njs_object_length(vm, value, &length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (n == 0) { + goto done; + } + + if (length > (UINT32_MAX - n)) { + njs_type_error(vm, "Invalid length"); + return NJS_ERROR; + } + + from = length; + length += n; + to = length; + + while (from > 0) { + njs_uint32_to_string(&index, --from); + + ret = njs_value_property_delete(vm, value, &index, &entry); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + to--; + + if (ret == NJS_OK) { + njs_uint32_to_string(&index, to); + + ret = njs_value_property_set(vm, value, &index, &entry); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + } + } + + for (n = 1; n < nargs; n++) { + njs_uint32_to_string(&index, n - 1); + + ret = njs_value_property_set(vm, value, &index, &args[n]); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + } + +done: + + ret = njs_object_length_set(vm, value, length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + njs_set_number(&vm->retval, length); + return NJS_OK; } @@ -683,10 +817,20 @@ static njs_int_t njs_array_prototype_shift(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_array_t *array; - const njs_value_t *retval, *value; - - retval = &njs_value_undefined; + uint32_t i, length; + njs_int_t ret; + njs_array_t *array; + njs_value_t *value, *item, entry, index; + + value = njs_arg(args, nargs, 0); + length = 0; + + if (njs_slow_path(njs_is_null_or_undefined(value))) { + njs_type_error(vm, "Cannot convert undefined or null to object"); + return NJS_ERROR; + } + + njs_set_undefined(&vm->retval); if (njs_is_array(&args[0])) { array = njs_array(&args[0]); @@ -694,16 +838,59 @@ njs_array_prototype_shift(njs_vm_t *vm, if (array->length != 0) { array->length--; - value = &array->start[0]; + item = &array->start[0]; array->start++; - if (njs_is_valid(value)) { - retval = value; + if (njs_is_valid(item)) { + vm->retval = *item; + } + } + + return NJS_OK; + } + + ret = njs_object_length(vm, value, &length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (length == 0) { + goto done; + } + + njs_uint32_to_string(&index, 0); + + ret = njs_value_property_delete(vm, value, &index, &vm->retval); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + for (i = 1; i < length; i++) { + njs_uint32_to_string(&index, i); + + ret = njs_value_property_delete(vm, value, &index, &entry); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (ret == NJS_OK) { + njs_uint32_to_string(&index, i - 1); + + ret = njs_value_property_set(vm, value, &index, &entry); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; } } } - vm->retval = *retval; + length--; + +done: + + ret = njs_object_length_set(vm, value, length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } return NJS_OK; } diff -r a07722caaee3 -r 893fc436a363 src/njs_object.h --- a/src/njs_object.h Thu Sep 19 10:19:01 2019 +0300 +++ b/src/njs_object.h Thu Sep 19 10:19:02 2019 +0300 @@ -86,4 +86,18 @@ extern const njs_object_init_t njs_obje extern const njs_object_init_t njs_object_prototype_init; +njs_inline njs_int_t +njs_object_length_set(njs_vm_t *vm, njs_value_t *value, uint32_t length) +{ + njs_value_t index; + + static const njs_value_t string_length = njs_string("length"); + + njs_value_number_set(&index, length); + + return njs_value_property_set(vm, value, njs_value_arg(&string_length), + &index); +} + + #endif /* _NJS_OBJECT_H_INCLUDED_ */ diff -r a07722caaee3 -r 893fc436a363 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Sep 19 10:19:01 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Sep 19 10:19:02 2019 +0300 @@ -3907,6 +3907,17 @@ static njs_unit_test_t njs_test[] = { njs_str("Array.prototype.pop()"), njs_str("undefined") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': 3};" + "Array.prototype.pop.call(o); var res = o.length;" + "Array.prototype.forEach.call(o, (v) => {res += ', ' + v}); res"), + njs_str("2, a, b") }, + + { njs_str("var obj = {}; obj.pop = Array.prototype.pop; obj.pop(); obj.length === 0"), + njs_str("true") }, + + { njs_str("var o = Object.freeze({0: 0, 1: 1, length: 2}); Array.prototype.pop.call(o)"), + njs_str("TypeError: Cannot delete property \"1\" of object") }, + { njs_str("Array.prototype.shift()"), njs_str("undefined") }, @@ -3939,9 +3950,22 @@ static njs_unit_test_t njs_test[] = "len +' '+ a.pop() +' '+ a"), njs_str("5 5 1,2,3,4") }, + { njs_str("var x = {'0': 'a', '1': 'b', '2': 'c', 'length': 3};" + "Array.prototype.push.call(x, 'x', 'y', 'z', 123) +' '+ x[0] +' '+ x.length"), + njs_str("7 a 7") }, + + { njs_str("var x = {'0': 'a', '1': 'b', '2': 'c', 'length': 3}; var a = [];" + "Array.prototype.push.call(x, 'x', 'y', 'z', 123);" + "Array.prototype.forEach.call(x, (v) => a.push(v)); a"), + njs_str("a,b,c,x,y,z,123") }, + { njs_str("var a = [1,2,3]; a.shift() +' '+ a[0] +' '+ a.length"), njs_str("1 2 2") }, + { njs_str("var x = {'0': 'x', '1': 'y', '2': 'z', 'length': 3};" + "Array.prototype.shift.call(x) +' '+ x[0] +' '+ x.length"), + njs_str("x y 2") }, + { njs_str("var a = [1,2], len = a.unshift(3);" "len +' '+ a +' '+ a.shift()"), njs_str("3 3,1,2 3") }, @@ -3950,6 +3974,33 @@ static njs_unit_test_t njs_test[] = "len +' '+ a +' '+ a.shift()"), njs_str("5 3,4,5,1,2 3") }, + { njs_str("var x = {'0': 'x', '1': 'y', '2': 'z', 'length': 3}; var a = [];" + "Array.prototype.unshift.call(x, 'a', 'b', 'c');" + "Array.prototype.forEach.call(x, (v) => a.push(v)); a + ', ' + x.length"), + njs_str("a,b,c,x,y,z, 6") }, + + { njs_str("var x = {}; x.length = 1; var a = [];" + "Array.prototype.unshift.call(x, 'a', 'b', 1025, 'c', 'oh my');" + "Array.prototype.forEach.call(x, (v) => a.push(v)); a + ', ' + x.length"), + njs_str("a,b,1025,c,oh my, 6") }, + + { njs_str("var x = {5: 1, 6: 2, length: 7}; var a = [];" + "Array.prototype.unshift.call(x, '0');" + "Array.prototype.forEach.call(x, (v) => a.push(v)); a + ', ' + x.length"), + njs_str("0,1,2, 8") }, + + { njs_str("var x = {5: 2, 10: 3, 11: 4, 12: 5, 20: 6, length: 21}; var a = [];" + "Array.prototype.unshift.call(x, '0', '1');" + "Array.prototype.forEach.call(x, (v, k) => a.push(k + ':' + v)); a + ', ' + x.length"), + njs_str("0:0,1:1,7:2,12:3,13:4,14:5,22:6, 23") }, + + { njs_str("var x = {0: 0, length: 4294967294};" + "Array.prototype.unshift.call(x, '0', '1');"), + njs_str("TypeError: Invalid length") }, + + { njs_str("var x = {0: 0}; Array.prototype.unshift.call(x); x.length"), + njs_str("0") }, + { njs_str("var a=[0], n = 64; while(--n) {a.push(n); a.shift()}; a"), njs_str("1") }, _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel