[njs] Fixed Array functions according to specification.
Alexander Borisov
alexander.borisov at nginx.com
Wed Oct 9 15:56:20 UTC 2019
details: https://hg.nginx.org/njs/rev/6ed84cda7426
branches:
changeset: 1177:6ed84cda7426
user: Alexander Borisov <alexander.borisov at nginx.com>
date: Wed Oct 09 18:54:54 2019 +0300
description:
Fixed Array functions according to specification.
Length property should be queried before checking arguments for validity.
?ffected functions:
every, filter, find, findIndex, forEach, map, reduce, reduceRight, some.
This closes #228 issue on GitHub.
diffstat:
src/njs_array.c | 194 ++++++++++++++++++----------------------------
src/test/njs_unit_test.c | 81 +++++++++++++++++++
2 files changed, 159 insertions(+), 116 deletions(-)
diffs (518 lines):
diff -r 9f1ba171d81e -r 6ed84cda7426 src/njs_array.c
--- a/src/njs_array.c Tue Oct 08 15:56:58 2019 +0300
+++ b/src/njs_array.c Wed Oct 09 18:54:54 2019 +0300
@@ -2014,6 +2014,41 @@ njs_array_iterator_call(njs_vm_t *vm, nj
}
+njs_inline njs_int_t
+njs_array_validate_args(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_array_iterator_args_t *iargs)
+{
+ njs_int_t ret;
+
+ if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))) {
+ goto failed;
+ }
+
+ iargs->value = njs_argument(args, 0);
+
+ ret = njs_value_length(vm, iargs->value, &iargs->to);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) {
+ goto failed;
+ }
+
+ iargs->from = 0;
+ iargs->function = njs_function(njs_argument(args, 1));
+ iargs->argument = njs_arg(args, nargs, 2);
+
+ return NJS_OK;
+
+failed:
+
+ njs_type_error(vm, "unexpected iterator arguments");
+
+ return NJS_ERROR;
+}
+
+
static njs_int_t
njs_array_handler_for_each(njs_vm_t *vm, njs_array_iterator_args_t *args,
njs_value_t *entry, uint32_t n)
@@ -2033,20 +2068,7 @@ njs_array_prototype_for_each(njs_vm_t *v
njs_int_t ret;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
- }
-
- iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
- iargs.argument = njs_arg(args, nargs, 2);
-
- iargs.from = 0;
-
- ret = njs_value_length(vm, iargs.value, &iargs.to);
+ ret = njs_array_validate_args(vm, args, nargs, &iargs);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2092,20 +2114,7 @@ njs_array_prototype_some(njs_vm_t *vm, n
njs_int_t ret;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
- }
-
- iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
- iargs.argument = njs_arg(args, nargs, 2);
-
- iargs.from = 0;
-
- ret = njs_value_length(vm, iargs.value, &iargs.to);
+ ret = njs_array_validate_args(vm, args, nargs, &iargs);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2153,20 +2162,7 @@ njs_array_prototype_every(njs_vm_t *vm,
njs_int_t ret;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
- }
-
- iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
- iargs.argument = njs_arg(args, nargs, 2);
-
- iargs.from = 0;
-
- ret = njs_value_length(vm, iargs.value, &iargs.to);
+ ret = njs_array_validate_args(vm, args, nargs, &iargs);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2219,20 +2215,7 @@ njs_array_prototype_filter(njs_vm_t *vm,
njs_int_t ret;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
- }
-
- iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
- iargs.argument = njs_arg(args, nargs, 2);
-
- iargs.from = 0;
-
- ret = njs_value_length(vm, iargs.value, &iargs.to);
+ ret = njs_array_validate_args(vm, args, nargs, &iargs);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2289,20 +2272,7 @@ njs_array_prototype_find(njs_vm_t *vm, n
njs_int_t ret;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
- }
-
- iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
- iargs.argument = njs_arg(args, nargs, 2);
-
- iargs.from = 0;
-
- ret = njs_value_length(vm, iargs.value, &iargs.to);
+ ret = njs_array_validate_args(vm, args, nargs, &iargs);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2356,20 +2326,7 @@ njs_array_prototype_find_index(njs_vm_t
njs_int_t ret;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
- }
-
- iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
- iargs.argument = njs_arg(args, nargs, 2);
-
- iargs.from = 0;
-
- ret = njs_value_length(vm, iargs.value, &iargs.to);
+ ret = njs_array_validate_args(vm, args, nargs, &iargs);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2422,22 +2379,21 @@ njs_array_prototype_map(njs_vm_t *vm, nj
njs_array_t *array;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
+ if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))) {
+ goto unexpected_args;
}
iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
- iargs.argument = njs_arg(args, nargs, 2);
ret = njs_value_length(vm, iargs.value, &length);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
+ if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) {
+ goto unexpected_args;
+ }
+
iargs.array = njs_array_alloc(vm, length, 0);
if (njs_slow_path(iargs.array == NULL)) {
return NJS_ERROR;
@@ -2453,6 +2409,9 @@ njs_array_prototype_map(njs_vm_t *vm, nj
iargs.from = 0;
iargs.to = length;
+ iargs.function = njs_function(njs_argument(args, 1));
+ iargs.argument = njs_arg(args, nargs, 2);
+
ret = njs_array_iterator(vm, &iargs, njs_array_handler_map);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
@@ -2470,6 +2429,12 @@ njs_array_prototype_map(njs_vm_t *vm, nj
njs_set_array(&vm->retval, iargs.array);
return NJS_OK;
+
+unexpected_args:
+
+ njs_type_error(vm, "unexpected iterator arguments");
+
+ return NJS_ERROR;
}
@@ -2522,30 +2487,19 @@ njs_array_prototype_reduce(njs_vm_t *vm,
njs_value_t accumulator;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
+ ret = njs_array_validate_args(vm, args, nargs, &iargs);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
njs_set_invalid(&accumulator);
if (nargs > 2) {
- accumulator = *njs_argument(args, 2);
+ accumulator = *iargs.argument;
}
- iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
iargs.argument = &accumulator;
- iargs.from = 0;
-
- ret = njs_value_length(vm, iargs.value, &iargs.to);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
ret = njs_array_iterator(vm, &iargs, njs_array_handler_reduce);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
@@ -2570,25 +2524,27 @@ njs_array_prototype_reduce_right(njs_vm_
njs_value_t accumulator;
njs_array_iterator_args_t iargs;
- if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))
- || !njs_is_function(njs_arg(args, nargs, 1)))
- {
- njs_type_error(vm, "unexpected iterator arguments");
- return NJS_ERROR;
+ if (njs_is_null_or_undefined(njs_arg(args, nargs, 0))) {
+ goto unexpected_args;
}
- njs_set_invalid(&accumulator);
-
iargs.value = njs_argument(args, 0);
- iargs.function = njs_function(&args[1]);
- iargs.argument = &accumulator;
- iargs.to = 0;
ret = njs_value_length(vm, iargs.value, &iargs.from);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
+ if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) {
+ goto unexpected_args;
+ }
+
+ njs_set_invalid(&accumulator);
+
+ iargs.to = 0;
+ iargs.function = njs_function(njs_argument(args, 1));
+ iargs.argument = &accumulator;
+
if (nargs > 2) {
accumulator = *njs_argument(args, 2);
}
@@ -2623,6 +2579,12 @@ failed:
njs_type_error(vm, "Reduce of empty object with no initial value");
return NJS_ERROR;
+
+unexpected_args:
+
+ njs_type_error(vm, "unexpected iterator arguments");
+
+ return NJS_ERROR;
}
diff -r 9f1ba171d81e -r 6ed84cda7426 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Tue Oct 08 15:56:58 2019 +0300
+++ b/src/test/njs_unit_test.c Wed Oct 09 18:54:54 2019 +0300
@@ -4088,6 +4088,11 @@ static njs_unit_test_t njs_test[] =
{ 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("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.pop.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: Cannot set property \"length\" of object which has only a getter") },
+
{ njs_str("Array.prototype.shift()"),
njs_str("undefined") },
@@ -4129,6 +4134,11 @@ static njs_unit_test_t njs_test[] =
"Array.prototype.forEach.call(x, (v) => a.push(v)); a"),
njs_str("a,b,c,x,y,z,123") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.push.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: Cannot set property \"length\" of object which has only a getter") },
+
{ njs_str("var a = [1,2,3]; a.shift() +' '+ a[0] +' '+ a.length"),
njs_str("1 2 2") },
@@ -4136,6 +4146,11 @@ static njs_unit_test_t njs_test[] =
"Array.prototype.shift.call(x) +' '+ x[0] +' '+ x.length"),
njs_str("x y 2") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.shift.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: Cannot set property \"length\" of object which has only a getter") },
+
{ njs_str("var a = [1,2], len = a.unshift(3);"
"len +' '+ a +' '+ a.shift()"),
njs_str("3 3,1,2 3") },
@@ -4177,6 +4192,11 @@ static njs_unit_test_t njs_test[] =
"Array.prototype.forEach.call(obj, (v) => a.push(v)); a"),
njs_str("a,b,c,x,y,z")},
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.unshift.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: Cannot set property \"length\" of object which has only a getter") },
+
{ njs_str("var a=[0], n = 64; while(--n) {a.push(n); a.shift()}; a"),
njs_str("1") },
@@ -4257,6 +4277,10 @@ static njs_unit_test_t njs_test[] =
"Array.prototype.indexOf.call(o, 'd')"),
njs_str("3") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "Array.prototype.indexOf.call(o); i"),
+ njs_str("1") },
+
{ njs_str("[].lastIndexOf(1, -1)"),
njs_str("-1") },
@@ -4337,6 +4361,10 @@ static njs_unit_test_t njs_test[] =
"Array.prototype.lastIndexOf.call(obj, 'y');"),
njs_str("10000001")},
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "Array.prototype.lastIndexOf.call(o); i"),
+ njs_str("1") },
+
{ njs_str("[1,2,3,4].includes()"),
njs_str("false") },
@@ -4437,6 +4465,11 @@ static njs_unit_test_t njs_test[] =
"Array.prototype.forEach.call(s, function (a, b, c) {t = typeof c;}); [t, typeof s];"),
njs_str("object,string") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.forEach.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var a = [];"
"a.some(function(v, i, a) { return v > 1 })"),
njs_str("false") },
@@ -4467,6 +4500,11 @@ static njs_unit_test_t njs_test[] =
"var r = Array.prototype.some.call(o, function(v, i, a) { return v === 'd' }); r"),
njs_str("true") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.some.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var a = [];"
"a.every(function(v, i, a) { return v > 1 })"),
njs_str("true") },
@@ -4491,6 +4529,11 @@ static njs_unit_test_t njs_test[] =
"var r = Array.prototype.every.call(o, function(el, i, arr) {return el == 'c'}); r"),
njs_str("true") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.every.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var o = {0: 'x', 1: 'y', 2: 'z'};"
"Object.defineProperty(o, 'length', {get: () => 4});"
"Object.defineProperty(o, '3', {get: () => 'a'});"
@@ -4604,6 +4647,10 @@ static njs_unit_test_t njs_test[] =
njs_str("RangeError: Maximum call stack size exceeded") },
#endif
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "Array.prototype.fill.call(o); i"),
+ njs_str("1") },
+
{ njs_str("var a = [];"
"a.filter(function(v, i, a) { return v > 1 })"),
njs_str("") },
@@ -4646,6 +4693,11 @@ static njs_unit_test_t njs_test[] =
"var r = Array.prototype.filter.call(o, function(el, i, arr) { return el == 'c' }); r"),
njs_str("c,c") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.filter.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var a = [];"
"a.find(function(v, i, a) { return v > 1 })"),
njs_str("undefined") },
@@ -4703,6 +4755,11 @@ static njs_unit_test_t njs_test[] =
"var r = Array.prototype.find.call(o, function(el, i, arr) { return el == 'd' }); r"),
njs_str("d") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.find.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var a = [];"
"a.findIndex(function(v, i, a) { return v > 1 })"),
njs_str("-1") },
@@ -4757,6 +4814,11 @@ static njs_unit_test_t njs_test[] =
"var r = Array.prototype.findIndex.call(o, function(el, i, arr) { return el == 'd' }); r"),
njs_str("3") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.findIndex.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var a = [];"
"a.map(function(v, i, a) { return v + 1 })"),
njs_str("") },
@@ -4809,6 +4871,11 @@ static njs_unit_test_t njs_test[] =
"var res = Array.prototype.map.call(obj, callbackfn); typeof res[8000]"),
njs_str("undefined") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.map.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var a = [];"
"a.reduce(function(p, v, i, a) { return p + v })"),
njs_str("TypeError: Reduce of empty object with no initial value") },
@@ -4862,6 +4929,11 @@ static njs_unit_test_t njs_test[] =
"var r = Array.prototype.reduce.call(o, (a, b) => a + b); r"),
njs_str("abcd") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.reduce.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var a = [];"
"a.reduceRight(function(p, v, i, a) { return p + v })"),
njs_str("TypeError: Reduce of empty object with no initial value") },
@@ -4910,6 +4982,11 @@ static njs_unit_test_t njs_test[] =
"Array.prototype.reduceRight.call(o, (p, v) => p + v)"),
njs_str("dcba") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "try {Array.prototype.reduceRight.call(o);}"
+ "catch (e) {i += '; ' + e} i"),
+ njs_str("1; TypeError: unexpected iterator arguments") },
+
{ njs_str("var a = ['1','2','3','4','5','6']; a.sort()"),
njs_str("1,2,3,4,5,6") },
@@ -6053,6 +6130,10 @@ static njs_unit_test_t njs_test[] =
{ njs_str("'абв абв абвгдежз'.includes('абвгд', 9)"),
njs_str("false") },
+ { njs_str("var i = 0; var o = {get length() {i++}};"
+ "Array.prototype.includes.call(o); i"),
+ njs_str("1") },
+
{ njs_str("''.startsWith('')"),
njs_str("true") },
More information about the nginx-devel
mailing list