From: Dmitry Volyntsev Date: Mon, 16 Dec 2019 12:18:51 +0000 (+0300) Subject: Fixed Array.prototype.join(). X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=27ceb88646f1ebe21f7756c3e08a15b96ff5cbd0;p=njs.git 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. --- diff --git a/src/njs_array.c b/src/njs_array.c index cd2bad3b..e6016e8c 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -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, njs_value_t *args, njs_uint_t nargs, 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); - length += string.length; + 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, njs_value_t *args, njs_uint_t nargs, njs_chb_drop(&chain, separator.size); size = njs_chb_size(&chain); + length -= separator.length; - if (length != 0) { - length -= separator.length; - } - - p = njs_string_alloc(vm, &vm->retval, size, length); + p = njs_string_alloc(vm, &vm->retval, size, utf8 ? length : 0); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } diff --git a/src/njs_chb.h b/src/njs_chb.h index 065f6280..3a8a24f7 100644 --- a/src/njs_chb.h +++ b/src/njs_chb.h @@ -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 --git a/src/njs_number.c b/src/njs_number.c index 82fbcfdd..8b4476c1 100644 --- a/src/njs_number.c +++ b/src/njs_number.c @@ -236,37 +236,39 @@ njs_number_to_string(njs_vm_t *vm, njs_value_t *string, } -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 --git a/src/njs_number.h b/src/njs_number.h index 22d9140e..f73bf7c0 100644 --- a/src/njs_number.h +++ b/src/njs_number.h @@ -17,8 +17,8 @@ int64_t njs_number_radix_parse(const u_char **start, const u_char *end, 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 --git a/src/njs_string.h b/src/njs_string.h index edf78f4f..32204009 100644 --- a/src/njs_string.h +++ b/src/njs_string.h @@ -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 --git a/src/njs_value.c b/src/njs_value.c index 9f3d2736..a861889d 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -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 --git a/src/njs_value_conversion.h b/src/njs_value_conversion.h index 7c706b15..581a29c6 100644 --- a/src/njs_value_conversion.h +++ b/src/njs_value_conversion.h @@ -199,6 +199,9 @@ njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) } +/* + * 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 --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 9d0ae40b..a7fc052e 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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("[" + " 'α'.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 = ['β', 'γ']; var out= a.join('α'); [out, out.length]"), - njs_str("βαγ,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") },