[njs] Fixed Array.prototype.join().
Dmitry Volyntsev
xeioex at nginx.com
Mon Dec 23 15:53:26 UTC 2019
details: https://hg.nginx.org/njs/rev/a0adc224673d
branches:
changeset: 1285:a0adc224673d
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Mon Dec 16 15:18:51 2019 +0300
description:
Fixed Array.prototype.join().
Resulting string length of Array.prototype.join() was invalid
when joined values are not strings.
The issue was introduced in 5bf71dfc052f.
diffstat:
src/njs_array.c | 33 ++++++++++++++++++++++++---------
src/njs_chb.h | 7 +++++++
src/njs_number.c | 34 ++++++++++++++++++----------------
src/njs_number.h | 4 ++--
src/njs_string.h | 7 +++++++
src/njs_value.c | 21 +++++++++------------
src/njs_value_conversion.h | 3 +++
src/test/njs_unit_test.c | 30 +++++++++++++++++++++++++-----
8 files changed, 95 insertions(+), 44 deletions(-)
diffs (292 lines):
diff -r 9c1ef5ab7a1f -r a0adc224673d src/njs_array.c
--- a/src/njs_array.c Thu Dec 12 18:50:27 2019 +0300
+++ b/src/njs_array.c Mon Dec 16 15:18:51 2019 +0300
@@ -1122,11 +1122,12 @@ static njs_int_t
njs_array_prototype_join(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
- u_char *p;
+ u_char *p, *last;
size_t size;
ssize_t length;
njs_int_t ret;
njs_chb_t chain;
+ njs_utf8_t utf8;
njs_uint_t i;
njs_array_t *array;
njs_value_t *value;
@@ -1163,20 +1164,37 @@ njs_array_prototype_join(njs_vm_t *vm, n
njs_chb_init(&chain, vm->mem_pool);
length = 0;
+ utf8 = njs_is_byte_string(&separator) ? NJS_STRING_BYTE : NJS_STRING_UTF8;
for (i = 0; i < array->length; i++) {
value = &array->start[i];
+
if (njs_is_valid(value) && !njs_is_null_or_undefined(value)) {
if (!njs_is_string(value)) {
+ last = njs_chb_current(&chain);
+
ret = njs_value_to_chain(vm, &chain, value);
- if (njs_slow_path(ret != NJS_OK)) {
+ if (njs_slow_path(ret < NJS_OK)) {
return ret;
}
+ if (last != njs_chb_current(&chain) && ret == 0) {
+ /*
+ * Appended values was a byte string.
+ */
+ utf8 = NJS_STRING_BYTE;
+ }
+
+ length += ret;
+
} else {
(void) njs_string_prop(&string, value);
+
+ if (njs_is_byte_string(&string)) {
+ utf8 = NJS_STRING_BYTE;
+ }
+
length += string.length;
-
njs_chb_append(&chain, string.start, string.size);
}
}
@@ -1188,12 +1206,9 @@ njs_array_prototype_join(njs_vm_t *vm, n
njs_chb_drop(&chain, separator.size);
size = njs_chb_size(&chain);
-
- if (length != 0) {
- length -= separator.length;
- }
-
- p = njs_string_alloc(vm, &vm->retval, size, length);
+ length -= separator.length;
+
+ p = njs_string_alloc(vm, &vm->retval, size, utf8 ? length : 0);
if (njs_slow_path(p == NULL)) {
return NJS_ERROR;
}
diff -r 9c1ef5ab7a1f -r a0adc224673d src/njs_chb.h
--- a/src/njs_chb.h Thu Dec 12 18:50:27 2019 +0300
+++ b/src/njs_chb.h Mon Dec 16 15:18:51 2019 +0300
@@ -104,6 +104,13 @@ njs_chb_utf8_length(njs_chb_t *chain)
}
+njs_inline u_char *
+njs_chb_current(njs_chb_t *chain)
+{
+ return (chain->last != NULL) ? chain->last->pos : NULL;
+}
+
+
njs_inline void
njs_chb_written(njs_chb_t *chain, size_t bytes)
{
diff -r 9c1ef5ab7a1f -r a0adc224673d src/njs_number.c
--- a/src/njs_number.c Thu Dec 12 18:50:27 2019 +0300
+++ b/src/njs_number.c Mon Dec 16 15:18:51 2019 +0300
@@ -236,37 +236,39 @@ njs_number_to_string(njs_vm_t *vm, njs_v
}
-void
-njs_number_to_chain(njs_vm_t *vm, njs_chb_t *chain, const njs_value_t *number)
+njs_int_t
+njs_number_to_chain(njs_vm_t *vm, njs_chb_t *chain, double num)
{
- double num;
- size_t size;
- u_char *p;
-
- num = njs_number(number);
+ size_t size;
+ u_char *p;
if (isnan(num)) {
njs_chb_append_literal(chain, "NaN");
+ return njs_length("NaN");
- } else if (isinf(num)) {
+ }
+ if (isinf(num)) {
if (num < 0) {
njs_chb_append_literal(chain, "-Infinity");
+ return njs_length("-Infinity");
} else {
njs_chb_append_literal(chain, "Infinity");
+ return njs_length("Infinity");
}
+ }
- } else {
- p = njs_chb_reserve(chain, 64);
- if (njs_slow_path(p == NULL)) {
- return;
- }
+ p = njs_chb_reserve(chain, 64);
+ if (njs_slow_path(p == NULL)) {
+ return NJS_ERROR;
+ }
- size = njs_dtoa(num, (char *) p);
+ size = njs_dtoa(num, (char *) p);
- njs_chb_written(chain, size);
- }
+ njs_chb_written(chain, size);
+
+ return size;
}
diff -r 9c1ef5ab7a1f -r a0adc224673d src/njs_number.h
--- a/src/njs_number.h Thu Dec 12 18:50:27 2019 +0300
+++ b/src/njs_number.h Mon Dec 16 15:18:51 2019 +0300
@@ -17,8 +17,8 @@ int64_t njs_number_radix_parse(const u_c
uint8_t radix);
njs_int_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
const njs_value_t *number);
-void njs_number_to_chain(njs_vm_t *vm, njs_chb_t *chain,
- const njs_value_t *number);
+njs_int_t njs_number_to_chain(njs_vm_t *vm, njs_chb_t *chain,
+ double number);
njs_int_t njs_number_global_is_nan(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused);
njs_int_t njs_number_global_is_finite(njs_vm_t *vm, njs_value_t *args,
diff -r 9c1ef5ab7a1f -r a0adc224673d src/njs_string.h
--- a/src/njs_string.h Thu Dec 12 18:50:27 2019 +0300
+++ b/src/njs_string.h Mon Dec 16 15:18:51 2019 +0300
@@ -102,6 +102,13 @@ typedef enum {
} njs_utf8_t;
+njs_inline njs_bool_t
+njs_is_byte_string(njs_string_prop_t *string)
+{
+ return (string->length == 0 && string->size != 0);
+}
+
+
njs_inline uint32_t
njs_string_calc_length(njs_utf8_t utf8, const u_char *start, size_t size)
{
diff -r 9c1ef5ab7a1f -r a0adc224673d src/njs_value.c
--- a/src/njs_value.c Thu Dec 12 18:50:27 2019 +0300
+++ b/src/njs_value.c Mon Dec 16 15:18:51 2019 +0300
@@ -1270,46 +1270,43 @@ njs_int_t
njs_primitive_value_to_chain(njs_vm_t *vm, njs_chb_t *chain,
const njs_value_t *src)
{
- njs_str_t string;
+ njs_string_prop_t string;
switch (src->type) {
case NJS_NULL:
njs_chb_append_literal(chain, "null");
- break;
+ return njs_length("null");
case NJS_UNDEFINED:
njs_chb_append_literal(chain, "undefined");
- break;
+ return njs_length("undefined");
case NJS_BOOLEAN:
if (njs_is_true(src)) {
njs_chb_append_literal(chain, "true");
+ return njs_length("true");
} else {
njs_chb_append_literal(chain, "false");
+ return njs_length("false");
}
- break;
-
case NJS_NUMBER:
- njs_number_to_chain(vm, chain, src);
- break;
+ return njs_number_to_chain(vm, chain, njs_number(src));
case NJS_SYMBOL:
njs_symbol_conversion_failed(vm, 1);
return NJS_ERROR;
case NJS_STRING:
- njs_string_get(src, &string);
- njs_chb_append_str(chain, &string);
- break;
+ (void) njs_string_prop(&string, src);
+ njs_chb_append(chain, string.start, string.size);
+ return string.length;
default:
return NJS_ERROR;
}
-
- return NJS_OK;
}
diff -r 9c1ef5ab7a1f -r a0adc224673d src/njs_value_conversion.h
--- a/src/njs_value_conversion.h Thu Dec 12 18:50:27 2019 +0300
+++ b/src/njs_value_conversion.h Mon Dec 16 15:18:51 2019 +0300
@@ -199,6 +199,9 @@ njs_value_to_string(njs_vm_t *vm, njs_va
}
+/*
+ * retval >= 0 is length (UTF8 characters) value of appended string.
+ */
njs_inline njs_int_t
njs_value_to_chain(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *value)
{
diff -r 9c1ef5ab7a1f -r a0adc224673d src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Thu Dec 12 18:50:27 2019 +0300
+++ b/src/test/njs_unit_test.c Mon Dec 16 15:18:51 2019 +0300
@@ -3893,11 +3893,31 @@ static njs_unit_test_t njs_test[] =
{ njs_str("var a = [1,2,3]; a.join(':')"),
njs_str("1:2:3") },
- { njs_str("var a = ['#', '@']; var out= a.join('α'.repeat(33)); [out, out.length]"),
- njs_str("#ααααααααααααααααααααααααααααααααα@,35") },
-
- { njs_str("var a = ['β', 'γ']; var out= a.join('α'); [out, out.length]"),
- njs_str("βαγ,3") },
+ { njs_str("["
+ " 'α'.repeat(33),"
+ " String.bytesFrom(Array(16).fill(0x9d)),"
+ "]"
+ ".map(v=>{var out = ['β', 'γ'].join(v); return out.length})"),
+ njs_str("35,20") },
+
+ { njs_str("["
+ " [],"
+ " ['β', 'γ'],"
+ " [NaN, Math.pow(2,123.2), Infinity, -1],"
+ " [new String('β'),{toString(){return 'γ'}}],"
+ "]"
+ ".map(v=>{var out = v.join('α'); return [out, out[out.length - 1],out.length]})"
+ ".map(v=>njs.dump(v))"),
+ njs_str("['',undefined,0],"
+ "['βαγ','γ',3],"
+ "['NaNα1.2215056097393134e+37αInfinityα-1','1',38],"
+ "['βαγ','γ',3]") },
+
+ { njs_str("var a = ['β','γ']; a.join('').length"),
+ njs_str("2") },
+
+ { njs_str("var a = ['β', String.bytesFrom([0x9d]),'γ']; a.join('').length"),
+ njs_str("5") },
{ njs_str("var a = []; a[5] = 5; a.join()"),
njs_str(",,,,,5") },
More information about the nginx-devel
mailing list