[njs] Fixed Array prototype functions according to the specification.
Alexander Borisov
alexander.borisov at nginx.com
Thu Sep 19 07:20:13 UTC 2019
details: https://hg.nginx.org/njs/rev/893fc436a363
branches:
changeset: 1164:893fc436a363
user: Alexander Borisov <alexander.borisov at 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") },
More information about the nginx-devel
mailing list