[njs] Handling non-array values in Array.prototype.slice.
Dmitry Volyntsev
xeioex at nginx.com
Tue Oct 23 17:40:18 UTC 2018
details: http://hg.nginx.org/njs/rev/de0974c3e90f
branches:
changeset: 629:de0974c3e90f
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Tue Oct 23 20:40:02 2018 +0300
description:
Handling non-array values in Array.prototype.slice.
diffstat:
njs/njs_array.c | 213 +++++++++++++++++++++++++++++++++++-----------
njs/njs_object_hash.h | 10 ++
njs/test/njs_unit_test.c | 84 ++++++++++++++++++
3 files changed, 254 insertions(+), 53 deletions(-)
diffs (386 lines):
diff -r 8ab908b0f226 -r de0974c3e90f njs/njs_array.c
--- a/njs/njs_array.c Fri Oct 19 20:55:38 2018 +0300
+++ b/njs/njs_array.c Tue Oct 23 20:40:02 2018 +0300
@@ -9,6 +9,19 @@
typedef struct {
+ union {
+ njs_continuation_t cont;
+ u_char padding[NJS_CONTINUATION_SIZE];
+ } u;
+ /*
+ * This retval value must be aligned so the continuation is padded
+ * to aligned size.
+ */
+ njs_value_t length;
+} njs_array_slice_t;
+
+
+typedef struct {
njs_continuation_t cont;
njs_value_t *values;
uint32_t max;
@@ -67,6 +80,10 @@ typedef struct {
} njs_array_sort_t;
+static njs_ret_t njs_array_prototype_slice_continuation(njs_vm_t *vm,
+ njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_array_prototype_slice_copy(njs_vm_t *vm,
+ njs_value_t *this, int64_t start, int64_t length);
static njs_ret_t njs_array_prototype_to_string_continuation(njs_vm_t *vm,
njs_value_t *args, nxt_uint_t nargs, njs_index_t retval);
static njs_ret_t njs_array_prototype_join_continuation(njs_vm_t *vm,
@@ -434,58 +451,95 @@ static njs_ret_t
njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
njs_index_t unused)
{
- int32_t start, end, length;
- uint32_t n;
- njs_array_t *array;
- njs_value_t *value;
-
- start = 0;
- length = 0;
-
- if (njs_is_array(&args[0])) {
- length = args[0].data.u.array->length;
-
- if (nargs > 1) {
- start = args[1].data.u.number;
-
- if (start < 0) {
- start += length;
-
- if (start < 0) {
- start = 0;
- }
- }
-
- if (start >= length) {
+ njs_ret_t ret;
+ njs_array_slice_t *slice;
+
+ static const njs_value_t njs_string_length = njs_string("length");
+
+ slice = njs_vm_continuation(vm);
+ slice->u.cont.function = njs_array_prototype_slice_continuation;
+
+ ret = njs_value_property(vm, &args[0], &njs_string_length, &slice->length);
+ if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_TRAP)) {
+ return ret;
+ }
+
+ return njs_array_prototype_slice_continuation(vm, args, nargs, unused);
+}
+
+
+static njs_ret_t
+njs_array_prototype_slice_continuation(njs_vm_t *vm, njs_value_t *args,
+ nxt_uint_t nargs, njs_index_t unused)
+{
+ int64_t start, end, length;
+ njs_array_slice_t *slice;
+
+ slice = njs_vm_continuation(vm);
+
+ if (nxt_slow_path(!njs_is_primitive(&slice->length))) {
+ njs_vm_trap_value(vm, &slice->length);
+ return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+ }
+
+ start = (int32_t) njs_primitive_value_to_integer(njs_arg(args, nargs, 1));
+ length = njs_primitive_value_to_integer(&slice->length);
+
+ if (start < 0) {
+ start += length;
+
+ if (start < 0) {
+ start = 0;
+ }
+ }
+
+ if (start >= length) {
+ start = 0;
+ length = 0;
+
+ } else {
+ if (!njs_is_void(njs_arg(args, nargs, 2))) {
+ end = (int32_t) njs_primitive_value_to_integer(&args[2]);
+
+ } else {
+ end = length;
+ }
+
+ if (end < 0) {
+ end += length;
+ }
+
+ if (length >= end) {
+ length = end - start;
+
+ if (length < 0) {
start = 0;
length = 0;
-
- } else {
- end = length;
-
- if (nargs > 2) {
- end = args[2].data.u.number;
-
- if (end < 0) {
- end += length;
- }
- }
-
- if (length >= end) {
- length = end - start;
-
- if (length < 0) {
- start = 0;
- length = 0;
- }
-
- } else {
- length -= start;
- }
}
+
+ } else {
+ length -= start;
}
}
+ return njs_array_prototype_slice_copy(vm, &args[0], start, length);
+}
+
+
+static njs_ret_t
+njs_array_prototype_slice_copy(njs_vm_t *vm, njs_value_t *this,
+ int64_t start, int64_t length)
+{
+ size_t size;
+ u_char *dst;
+ uint32_t n, len;
+ njs_ret_t ret;
+ njs_array_t *array;
+ njs_value_t *value, name;
+ const u_char *src, *end;
+ njs_slice_prop_t string_slice;
+ njs_string_prop_t string;
+
array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
if (nxt_slow_path(array == NULL)) {
return NXT_ERROR;
@@ -496,14 +550,66 @@ njs_array_prototype_slice(njs_vm_t *vm,
vm->retval.data.truth = 1;
if (length != 0) {
- value = args[0].data.u.array->start;
n = 0;
- do {
- /* GC: retain long string and object in values[start]. */
- array->start[n++] = value[start++];
- length--;
- } while (length != 0);
+ if (nxt_fast_path(njs_is_array(this))) {
+ value = this->data.u.array->start;
+
+ do {
+ /* GC: retain long string and object in values[start]. */
+ array->start[n++] = value[start++];
+ length--;
+ } while (length != 0);
+
+ } else if (njs_is_string(this) || this->type == NJS_OBJECT_STRING) {
+
+ if (this->type == NJS_OBJECT_STRING) {
+ this = &this->data.u.object_value->value;
+ }
+
+ string_slice.start = start;
+ string_slice.length = length;
+ string_slice.string_length = njs_string_prop(&string, this);
+
+ njs_string_slice_string_prop(&string, &string, &string_slice);
+
+ src = string.start;
+ end = src + string.size;
+
+ if (string.length == 0) {
+ /* Byte string. */
+ len = 0;
+
+ } else {
+ /* UTF-8 or ASCII string. */
+ len = 1;
+ }
+
+ do {
+ value = &array->start[n++];
+ dst = njs_string_short_start(value);
+ dst = nxt_utf8_copy(dst, &src, end);
+ size = dst - njs_string_short_start(value);
+ njs_string_short_set(value, size, len);
+
+ length--;
+ } while (length != 0);
+
+ } else if (njs_is_object(this)) {
+
+ do {
+ njs_uint32_to_string(&name, start++);
+
+ value = &array->start[n++];
+ ret = njs_value_property(vm, this, &name, value);
+
+ if (ret != NXT_OK) {
+ *value = njs_value_invalid;
+ }
+
+ length--;
+ } while (length != 0);
+ }
}
return NXT_OK;
@@ -2101,7 +2207,8 @@ static const njs_object_prop_t njs_arra
{
.type = NJS_METHOD,
.name = njs_string("slice"),
- .value = njs_native_function(njs_array_prototype_slice, 0,
+ .value = njs_native_function(njs_array_prototype_slice,
+ njs_continuation_size(njs_array_slice_t),
NJS_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
},
diff -r 8ab908b0f226 -r de0974c3e90f njs/njs_object_hash.h
--- a/njs/njs_object_hash.h Fri Oct 19 20:55:38 2018 +0300
+++ b/njs/njs_object_hash.h Tue Oct 23 20:40:02 2018 +0300
@@ -108,6 +108,16 @@
'j'), 'o'), 'i'), 'n')
+#define NJS_LENGTH_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 'l'), 'e'), 'n'), 'g'), 't'), 'h')
+
+
#define NJS_NAME_HASH \
nxt_djb_hash_add( \
nxt_djb_hash_add( \
diff -r 8ab908b0f226 -r de0974c3e90f njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c Fri Oct 19 20:55:38 2018 +0300
+++ b/njs/test/njs_unit_test.c Tue Oct 23 20:40:02 2018 +0300
@@ -3052,12 +3052,93 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("Array.prototype.slice(1,2)"),
nxt_string("") },
+ { nxt_string("Array.prototype.slice.call(undefined)"),
+ nxt_string("TypeError: cannot convert void to object") },
+
+ { nxt_string("Array.prototype.slice.call(1)"),
+ nxt_string("") },
+
+ { nxt_string("Array.prototype.slice.call(false)"),
+ nxt_string("") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:1})"),
+ nxt_string("a") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2})"),
+ nxt_string("a,b") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:4})"),
+ nxt_string("a,b,,") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1)"),
+ nxt_string("b") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1, 2)"),
+ nxt_string("b") },
+
+ { nxt_string("Array.prototype.slice.call({length:'2'})"),
+ nxt_string(",") },
+
+ { nxt_string("njs.dump(Array.prototype.slice.call({length: 3, 1: undefined }))"),
+ nxt_string("[<empty>,undefined,<empty>]") },
+
+ { nxt_string("Array.prototype.slice.call({length:new Number(3)})"),
+ nxt_string(",,") },
+
+ { nxt_string("Array.prototype.slice.call({length: { valueOf: function() { return 2; } }})"),
+ nxt_string(",") },
+
+ { nxt_string("Array.prototype.slice.call({ length: Object.create(null) })"),
+ nxt_string("TypeError: Cannot convert object to primitive value") },
+
+ { nxt_string("Array.prototype.slice.call({length:-1})"),
+ nxt_string("MemoryError") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ')"),
+ nxt_string("α,β,Z,γ") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 1)"),
+ nxt_string("β,Z,γ") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 2)"),
+ nxt_string("Z,γ") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 3)"),
+ nxt_string("γ") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 4)"),
+ nxt_string("") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 0, 1)"),
+ nxt_string("α") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 1, 2)"),
+ nxt_string("β") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ').length"),
+ nxt_string("4") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ')[1].length"),
+ nxt_string("1") },
+
+ { nxt_string("Array.prototype.slice.call(new String('αβZγ'))"),
+ nxt_string("α,β,Z,γ") },
+
{ nxt_string("Array.prototype.pop()"),
nxt_string("undefined") },
{ nxt_string("Array.prototype.shift()"),
nxt_string("undefined") },
+ { nxt_string("[0,1].slice()"),
+ nxt_string("0,1") },
+
+ { nxt_string("[0,1].slice(undefined)"),
+ nxt_string("0,1") },
+
+ { nxt_string("[0,1].slice(undefined, undefined)"),
+ nxt_string("0,1") },
+
{ nxt_string("[0,1,2,3,4].slice(1,4)"),
nxt_string("1,2,3") },
@@ -9667,6 +9748,9 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("njs.dump({a:1, b:[1,,2,{c:new Boolean(1)}]})"),
nxt_string("{a:1,b:[1,<empty>,2,{c:[Boolean: true]}]}") },
+ { nxt_string("njs.dump(Array.prototype.slice.call({'1':'b', length:2}))"),
+ nxt_string("[<empty>,'b']") },
+
{ nxt_string("njs.dump($r.props)"),
nxt_string("{a:{type:\"property\",props:[\"getter\"]},b:{type:\"property\",props:[\"getter\"]}}") },
More information about the nginx-devel
mailing list