From xeioex at nginx.com Fri Nov 1 11:49:01 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 01 Nov 2019 11:49:01 +0000 Subject: [njs] Fixed getter/setter proto. Message-ID: details: https://hg.nginx.org/njs/rev/151ac933c8f3 branches: changeset: 1216:151ac933c8f3 user: Dmitry Volyntsev date: Thu Oct 31 19:53:21 2019 +0300 description: Fixed getter/setter proto. This closes #238 issue on Github. diffstat: src/njs_function.c | 7 +++++++ src/njs_object_prop.c | 37 +++++++++++++++++++++++++++---------- src/test/njs_unit_test.c | 8 ++++++++ 3 files changed, 42 insertions(+), 10 deletions(-) diffs (96 lines): diff -r 297f7dd356c0 -r 151ac933c8f3 src/njs_function.c --- a/src/njs_function.c Thu Oct 31 18:17:34 2019 +0300 +++ b/src/njs_function.c Thu Oct 31 19:53:21 2019 +0300 @@ -90,6 +90,13 @@ njs_function_value_copy(njs_vm_t *vm, nj return NULL; } + if (copy->ctor) { + copy->object.shared_hash = vm->shared->function_instance_hash; + + } else { + copy->object.shared_hash = vm->shared->arrow_instance_hash; + } + value->data.u.function = copy; return copy; diff -r 297f7dd356c0 -r 151ac933c8f3 src/njs_object_prop.c --- a/src/njs_object_prop.c Thu Oct 31 18:17:34 2019 +0300 +++ b/src/njs_object_prop.c Thu Oct 31 19:53:21 2019 +0300 @@ -381,8 +381,6 @@ njs_prop_private_copy(njs_vm_t *vm, njs_ njs_object_prop_t *prop, *shared, *name; njs_lvlhsh_query_t lhq; - static const njs_value_t name_string = njs_string("name"); - prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), sizeof(njs_object_prop_t)); if (njs_slow_path(prop == NULL)) { @@ -403,6 +401,32 @@ njs_prop_private_copy(njs_vm_t *vm, njs_ return NJS_ERROR; } + if (njs_is_accessor_descriptor(prop)) { + if (njs_is_function(&prop->getter)) { + function = njs_function_value_copy(vm, &prop->getter); + if (njs_slow_path(function == NULL)) { + return NJS_ERROR; + } + + if (njs_is_function(&prop->setter) + && function->native && njs_function(&prop->setter)->native + && function->u.native == njs_function(&prop->setter)->u.native) + { + prop->setter = prop->getter; + return NJS_OK; + } + } + + if (njs_is_function(&prop->setter)) { + function = njs_function_value_copy(vm, &prop->setter); + if (njs_slow_path(function == NULL)) { + return NJS_ERROR; + } + } + + return NJS_OK; + } + if (!njs_is_function(&prop->value)) { return NJS_OK; } @@ -412,14 +436,7 @@ njs_prop_private_copy(njs_vm_t *vm, njs_ return NJS_ERROR; } - if (function->ctor) { - function->object.shared_hash = vm->shared->function_instance_hash; - - } else { - function->object.shared_hash = vm->shared->arrow_instance_hash; - } - - name = njs_object_prop_alloc(vm, &name_string, &prop->name, 0); + name = njs_object_prop_alloc(vm, &njs_string_name, &prop->name, 0); if (njs_slow_path(name == NULL)) { return NJS_ERROR; } diff -r 297f7dd356c0 -r 151ac933c8f3 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Oct 31 18:17:34 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Oct 31 19:53:21 2019 +0300 @@ -8111,6 +8111,14 @@ static njs_unit_test_t njs_test[] = { njs_str("(function() {}).arguments"), njs_str("TypeError: \"caller\", \"callee\", \"arguments\" properties may not be accessed") }, + { njs_str("var desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Math.min), 'caller');" + "desc.get === desc.set"), + njs_str("true") }, + + { njs_str("var desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Math.min), 'caller');" + "1/desc.get"), + njs_str("NaN") }, + { njs_str("var p = Object.getPrototypeOf(function() {});" "var d = Object.getOwnPropertyDescriptor(p, 'caller');" "typeof d.get == 'function' && typeof d.get == typeof d.set" From xeioex at nginx.com Fri Nov 1 13:58:42 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 01 Nov 2019 13:58:42 +0000 Subject: [njs] Making custom top-level objects enumerable. Message-ID: details: https://hg.nginx.org/njs/rev/9054169baaaf branches: changeset: 1218:9054169baaaf user: Dmitry Volyntsev date: Fri Nov 01 16:02:21 2019 +0300 description: Making custom top-level objects enumerable. diffstat: src/njs_builtin.c | 4 +++- src/test/njs_unit_test.c | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diffs (52 lines): diff -r 3e3b00388d69 -r 9054169baaaf src/njs_builtin.c --- a/src/njs_builtin.c Fri Nov 01 15:48:21 2019 +0300 +++ b/src/njs_builtin.c Fri Nov 01 16:02:21 2019 +0300 @@ -889,7 +889,7 @@ njs_top_level_object(njs_vm_t *vm, njs_o /* GC */ prop->value = *retval; - prop->enumerable = 0; + prop->enumerable = self->enumerable; lhq.value = prop; njs_string_get(&self->name, &lhq.key); @@ -1097,6 +1097,7 @@ static const njs_object_prop_t njs_glob .value = njs_prop_handler2(njs_top_level_object, NJS_OBJECT_NJS, NJS_NJS_HASH), .writable = 1, + .enumerable = 1, .configurable = 1, }, @@ -1106,6 +1107,7 @@ static const njs_object_prop_t njs_glob .value = njs_prop_handler2(njs_top_level_object, NJS_OBJECT_PROCESS, NJS_PROCESS_HASH), .writable = 1, + .enumerable = 1, .configurable = 1, }, diff -r 3e3b00388d69 -r 9054169baaaf src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Nov 01 15:48:21 2019 +0300 +++ b/src/test/njs_unit_test.c Fri Nov 01 16:02:21 2019 +0300 @@ -9249,6 +9249,9 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.getOwnPropertyNames(this).includes('NaN')"), njs_str("true") }, + { njs_str("Object.keys(this)"), + njs_str("njs,process") }, + { njs_str("this.a = 1; this.a"), njs_str("1") }, @@ -13259,7 +13262,9 @@ static njs_unit_test_t njs_test[] = { njs_str("var global = this;" "function isMutableObject(v) {" " var d = Object.getOwnPropertyDescriptor(global, v);" - " return d.writable && !d.enumerable && d.configurable;" + " /* Custom top-level objects are enumerable. */" + " var enumerable = (v in {'njs':1, 'process':1}) ^ !d.enumerable;" + " return d.writable && enumerable && d.configurable;" "};" "['njs', 'process', 'Math', 'JSON'].every((v)=>isMutableObject(v))"), njs_str("true") }, From xeioex at nginx.com Fri Nov 1 13:58:42 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 01 Nov 2019 13:58:42 +0000 Subject: [njs] Fixed typo introduced in c75a8fc6d534. Message-ID: details: https://hg.nginx.org/njs/rev/3e3b00388d69 branches: changeset: 1217:3e3b00388d69 user: Dmitry Volyntsev date: Fri Nov 01 15:48:21 2019 +0300 description: Fixed typo introduced in c75a8fc6d534. This closes #242 issue on Github. diffstat: src/njs_object_hash.h | 3 +-- src/test/njs_unit_test.c | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diffs (33 lines): diff -r 151ac933c8f3 -r 3e3b00388d69 src/njs_object_hash.h --- a/src/njs_object_hash.h Thu Oct 31 19:53:21 2019 +0300 +++ b/src/njs_object_hash.h Fri Nov 01 15:48:21 2019 +0300 @@ -538,9 +538,8 @@ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ - njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ - 'U'), 'R'), 'I'), 'e'), 'E'), 'r'), 'r'), 'o'), 'r') + 'U'), 'R'), 'I'), 'E'), 'r'), 'r'), 'o'), 'r') #endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */ diff -r 151ac933c8f3 -r 3e3b00388d69 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Oct 31 19:53:21 2019 +0300 +++ b/src/test/njs_unit_test.c Fri Nov 01 15:48:21 2019 +0300 @@ -9240,6 +9240,15 @@ static njs_unit_test_t njs_test[] = { njs_str("this"), njs_str("[object Object]") }, + { njs_str("Object.getOwnPropertyDescriptor(this, 'NaN').value"), + njs_str("NaN") }, + + { njs_str("Object.getOwnPropertyDescriptors(this).NaN.value"), + njs_str("NaN") }, + + { njs_str("Object.getOwnPropertyNames(this).includes('NaN')"), + njs_str("true") }, + { njs_str("this.a = 1; this.a"), njs_str("1") }, From xeioex at nginx.com Fri Nov 1 16:27:05 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 01 Nov 2019 16:27:05 +0000 Subject: [njs] Fixed "Date" object string formatting. Message-ID: details: https://hg.nginx.org/njs/rev/ea70e6e7a0b8 branches: changeset: 1219:ea70e6e7a0b8 user: Dmitry Volyntsev date: Fri Nov 01 19:26:42 2019 +0300 description: Fixed "Date" object string formatting. 1) Getting rid of strftime() as it is locale dependent whereas output should always be in english. 2) Unifying all formatters in a single function. diffstat: src/njs_date.c | 331 +++++++++++++++++++++++++--------------------- src/test/njs_unit_test.c | 34 ++++- 2 files changed, 213 insertions(+), 152 deletions(-) diffs (489 lines): diff -r 9054169baaaf -r ea70e6e7a0b8 src/njs_date.c --- a/src/njs_date.c Fri Nov 01 16:02:21 2019 +0300 +++ b/src/njs_date.c Fri Nov 01 19:26:42 2019 +0300 @@ -14,14 +14,19 @@ * FreeBSD and MacOSX timegm() cannot handle years before 1900. */ -#define NJS_ISO_DATE_TIME_LEN sizeof("+001970-09-28T12:00:00.000Z") - -#define NJS_HTTP_DATE_TIME_LEN sizeof("Mon, 28 Sep 1970 12:00:00 GMT") - #define NJS_DATE_TIME_LEN \ sizeof("Mon Sep 28 1970 12:00:00 GMT+0600 (XXXXX)") +typedef enum { + NJS_DATE_FMT_TO_TIME_STRING, + NJS_DATE_FMT_TO_DATE_STRING, + NJS_DATE_FMT_TO_STRING, + NJS_DATE_FMT_TO_UTC_STRING, + NJS_DATE_FMT_TO_ISO_STRING, +} njs_date_fmt_t; + + static double njs_date_string_parse(njs_value_t *date); static double njs_date_rfc2822_string_parse(struct tm *tm, const u_char *p, const u_char *end); @@ -36,7 +41,8 @@ static njs_int_t njs_date_gmtoff_parse(c static const u_char *njs_date_number_parse(int *value, const u_char *p, const u_char *end, size_t size); static int64_t njs_timegm(struct tm *tm); -static njs_int_t njs_date_string(njs_vm_t *vm, const char *fmt, double time); +static njs_int_t njs_date_string(njs_vm_t *vm, njs_value_t *retval, + njs_date_fmt_t fmt, double time); static double njs_date_time(struct tm *tm, int64_t ms); static double njs_date_utc_time(struct tm *tm, double time); @@ -179,94 +185,94 @@ njs_date_constructor(njs_vm_t *vm, njs_v njs_date_t *date; int64_t values[8]; - if (vm->top_frame->ctor) { - - if (nargs == 1) { - time = njs_gettime(); - - } else if (nargs == 2) { - if (njs_is_object(&args[1])) { - if (!njs_is_date(&args[1])) { - ret = njs_value_to_primitive(vm, &args[1], &args[1], 0); - if (ret != NJS_OK) { - return ret; - } + if (!vm->top_frame->ctor) { + return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_STRING, + njs_gettime()); + } + + if (nargs == 1) { + time = njs_gettime(); + + } else if (nargs == 2) { + if (njs_is_object(&args[1])) { + if (!njs_is_date(&args[1])) { + ret = njs_value_to_primitive(vm, &args[1], &args[1], 0); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } } - - if (njs_is_date(&args[1])) { - time = njs_date(&args[1])->time; - - } else if (njs_is_string(&args[1])) { - time = njs_date_string_parse(&args[1]); - - } else { - time = njs_number(&args[1]); - } + } + + if (njs_is_date(&args[1])) { + time = njs_date(&args[1])->time; + + } else if (njs_is_string(&args[1])) { + time = njs_date_string_parse(&args[1]); } else { - - time = NAN; - - njs_memzero(values, 8 * sizeof(int64_t)); - - /* Day. */ - values[3] = 1; - - n = njs_min(8, nargs); - - for (i = 1; i < n; i++) { - if (!njs_is_numeric(&args[i])) { - ret = njs_value_to_numeric(vm, &args[i], &args[i]); - if (ret != NJS_OK) { - return ret; - } + time = njs_number(&args[1]); + } + + } else { + + time = NAN; + + njs_memzero(values, 8 * sizeof(int64_t)); + + /* Day. */ + values[3] = 1; + + n = njs_min(8, nargs); + + for (i = 1; i < n; i++) { + if (!njs_is_numeric(&args[i])) { + ret = njs_value_to_numeric(vm, &args[i], &args[i]); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } - - num = njs_number(&args[i]); - - if (isnan(num) || isinf(num)) { - goto done; - } - - values[i] = num; } - /* Year. */ - if (values[1] >= 0 && values[1] < 100) { - values[1] += 1900; + num = njs_number(&args[i]); + + if (isnan(num) || isinf(num)) { + goto done; } - day = njs_make_day(values[1], values[2], values[3]); - - tm = njs_make_time(values[4], values[5], values[6], values[7]); - - time = njs_make_date(day, tm, 1); + values[i] = num; } - done: - - date = njs_mp_alloc(vm->mem_pool, sizeof(njs_date_t)); - if (njs_slow_path(date == NULL)) { - njs_memory_error(vm); - return NJS_ERROR; + /* Year. */ + if (values[1] >= 0 && values[1] < 100) { + values[1] += 1900; } - njs_lvlhsh_init(&date->object.hash); - njs_lvlhsh_init(&date->object.shared_hash); - date->object.type = NJS_DATE; - date->object.shared = 0; - date->object.extensible = 1; - date->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_DATE].object; - - date->time = njs_timeclip(time); - - njs_set_date(&vm->retval, date); - - return NJS_OK; + day = njs_make_day(values[1], values[2], values[3]); + + tm = njs_make_time(values[4], values[5], values[6], values[7]); + + time = njs_make_date(day, tm, 1); + } + +done: + + date = njs_mp_alloc(vm->mem_pool, sizeof(njs_date_t)); + if (njs_slow_path(date == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; } - return njs_date_string(vm, "%a %b %d %Y %T GMT%z (%Z)", njs_gettime()); + njs_lvlhsh_init(&date->object.hash); + njs_lvlhsh_init(&date->object.shared_hash); + date->object.type = NJS_DATE; + date->object.shared = 0; + date->object.extensible = 1; + date->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_DATE].object; + + date->time = njs_timeclip(time); + + njs_set_date(&vm->retval, date); + + return NJS_OK; } @@ -1081,7 +1087,7 @@ njs_date_prototype_to_string(njs_vm_t *v return NJS_ERROR; } - return njs_date_string(vm, "%a %b %d %Y %T GMT%z (%Z)", + return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_STRING, njs_date(&args[0])->time); } @@ -1097,7 +1103,8 @@ njs_date_prototype_to_date_string(njs_vm return NJS_ERROR; } - return njs_date_string(vm, "%a %b %d %Y", njs_date(&args[0])->time); + return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_DATE_STRING, + njs_date(&args[0])->time); } @@ -1112,77 +1119,108 @@ njs_date_prototype_to_time_string(njs_vm return NJS_ERROR; } - return njs_date_string(vm, "%T GMT%z (%Z)", njs_date(&args[0])->time); + return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_TIME_STRING, + njs_date(&args[0])->time); } static njs_int_t -njs_date_string(njs_vm_t *vm, const char *fmt, double time) +njs_date_string(njs_vm_t *vm, njs_value_t *retval, njs_date_fmt_t fmt, + double time) { - size_t size; + u_char *p, sign; + int32_t year, tz; time_t clock; u_char buf[NJS_DATE_TIME_LEN]; struct tm tm; - if (!isnan(time)) { - clock = time / 1000; - localtime_r(&clock, &tm); - - size = strftime((char *) buf, NJS_DATE_TIME_LEN, fmt, &tm); - - return njs_string_new(vm, &vm->retval, buf, size, size); - } - - vm->retval = njs_string_invalid_date; - - return NJS_OK; -} - - -static njs_int_t -njs_date_prototype_to_utc_string(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - double time; - time_t clock; - u_char buf[NJS_HTTP_DATE_TIME_LEN], *p; - struct tm tm; - static const char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; + if (njs_slow_path(isnan(time))) { + vm->retval = njs_string_invalid_date; + return NJS_OK; } - time = njs_date(&args[0])->time; - - if (!isnan(time)) { + p = buf; + + switch (fmt) { + case NJS_DATE_FMT_TO_ISO_STRING: + case NJS_DATE_FMT_TO_UTC_STRING: clock = time / 1000; gmtime_r(&clock, &tm); - - p = njs_sprintf(buf, buf + NJS_HTTP_DATE_TIME_LEN, - "%s, %02d %s %4d %02d:%02d:%02d GMT", - week[tm.tm_wday], tm.tm_mday, month[tm.tm_mon], - tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); - - return njs_string_new(vm, &vm->retval, buf, p - buf, p - buf); + year = tm.tm_year + 1900; + + if (fmt == NJS_DATE_FMT_TO_UTC_STRING) { + p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, + "%s, %02d %s %04d %02d:%02d:%02d GMT", + week[tm.tm_wday], tm.tm_mday, month[tm.tm_mon], + year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + break; + } + + if (year >= 0 && year <= 9999) { + p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%04d", year); + + } else { + if (year > 0) { + *p++ = '+'; + } + + p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%06d", year); + } + + p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, + "-%02d-%02dT%02d:%02d:%02d.%03dZ", + tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec, + (int) ((int64_t) time % 1000)); + + break; + + case NJS_DATE_FMT_TO_TIME_STRING: + case NJS_DATE_FMT_TO_DATE_STRING: + case NJS_DATE_FMT_TO_STRING: + default: + clock = time / 1000; + localtime_r(&clock, &tm); + + if (fmt != NJS_DATE_FMT_TO_TIME_STRING) { + p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, + "%s %s %02d %04d", + week[tm.tm_wday], month[tm.tm_mon], tm.tm_mday, + tm.tm_year + 1900); + } + + if (fmt != NJS_DATE_FMT_TO_DATE_STRING) { + tz = -njs_tz_offset(time); + sign = (tz < 0) ? '-' : '+'; + + if (tz < 0) { + tz = -tz; + } + + if (p != buf) { + *p++ = ' '; + } + + p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, + "%02d:%02d:%02d GMT%c%02d%02d", + tm.tm_hour, tm.tm_min, tm.tm_sec, + sign, tz / 60, tz % 60); + } } - vm->retval = njs_string_invalid_date; - - return NJS_OK; + return njs_string_new(vm, retval, buf, p - buf, p - buf); } static njs_int_t -njs_date_prototype_to_iso_string(njs_vm_t *vm, njs_value_t *args, +njs_date_prototype_to_utc_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { if (njs_slow_path(!njs_is_date(&args[0]))) { @@ -1192,6 +1230,15 @@ njs_date_prototype_to_iso_string(njs_vm_ return NJS_ERROR; } + return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_UTC_STRING, + njs_date(&args[0])->time); +} + + +static njs_int_t +njs_date_prototype_to_iso_string(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ return njs_date_to_string(vm, &vm->retval, &args[0]); } @@ -1199,33 +1246,15 @@ njs_date_prototype_to_iso_string(njs_vm_ njs_int_t njs_date_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *date) { - int32_t year; - double time; - time_t clock; - u_char buf[NJS_ISO_DATE_TIME_LEN], *p; - struct tm tm; - - time = njs_date(date)->time; - - if (!isnan(time)) { - clock = time / 1000; - - gmtime_r(&clock, &tm); - - year = tm.tm_year + 1900; - - p = njs_sprintf(buf, buf + NJS_ISO_DATE_TIME_LEN, - (year < 0) ? "%07d-%02d-%02dT%02d:%02d:%02d.%03dZ" - : "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", - year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, - tm.tm_sec, (int) ((int64_t) time % 1000)); - - return njs_string_new(vm, retval, buf, p - buf, p - buf); + if (njs_slow_path(!njs_is_date(date))) { + njs_type_error(vm, "cannot convert %s to date", + njs_type_string(date->type)); + + return NJS_ERROR; } - *retval = njs_string_invalid_date; - - return NJS_OK; + return njs_date_string(vm, retval, NJS_DATE_FMT_TO_ISO_STRING, + njs_date(date)->time); } diff -r 9054169baaaf -r ea70e6e7a0b8 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Nov 01 16:02:21 2019 +0300 +++ b/src/test/njs_unit_test.c Fri Nov 01 19:26:42 2019 +0300 @@ -11572,6 +11572,9 @@ static njs_unit_test_t njs_test[] = { njs_str("(new Date(86400)).getTime()"), njs_str("86400") }, + { njs_str("Date().split(' ')[0] in {'Mon':1, 'Tue':1, 'Wed':1, 'Thu':1, 'Fri':1, 'Sat':1, 'Sun':1}"), + njs_str("true") }, + { njs_str("var d = new Date(''); d +' '+ d.getTime()"), njs_str("Invalid Date NaN") }, @@ -11619,9 +11622,38 @@ static njs_unit_test_t njs_test[] = "local.toISOString()"), njs_str("1999-10-10T10:10:10.010Z") }, -#if 0 +#if 0 /* FIXME: implement own gmtime_r(). */ /* These tests fail on Solaris: gmtime_r() returns off by one day. */ + { njs_str("[" + "'-010000-01-01T00:00:00.000Z'," + "'+010000-01-01T00:00:00.000Z'," + "'0002-01-01T00:00:00.000Z'," + "'0123-01-01T00:00:00.000Z'," + "].every((iso)=> (new Date(iso)).toISOString() === iso)"), + njs_str("true") }, + + { njs_str("new Date('0020-01-01T00:00:00Z').toUTCString()"), + njs_str("Wed, 01 Jan 0020 00:00:00 GMT") }, + + { njs_str("new Date('0020-01-01T00:00:00Z').toString().slice(0, 15)"), + njs_str("Wed Jan 01 0020") }, + + { njs_str("(new Date('-000001-07-01T00:00Z')).toUTCString()"), + njs_str("Thu, 01 Jul -0001 00:00:00 GMT") }, + + { njs_str("(new Date('-000012-07-01T00:00Z')).toUTCString()"), + njs_str("Fri, 01 Jul -0012 00:00:00 GMT") }, + + { njs_str("(new Date('-000123-07-01T00:00Z')).toUTCString()"), + njs_str("Sun, 01 Jul -0123 00:00:00 GMT") }, + + { njs_str("(new Date('-001234-07-01T00:00Z')).toUTCString()"), + njs_str("Fri, 01 Jul -1234 00:00:00 GMT") }, + + { njs_str("(new Date('-012345-07-01T00:00Z')).toUTCString()"), + njs_str("Thu, 01 Jul -12345 00:00:00 GMT") }, + { njs_str("var d = new Date(-62167219200000); d.toISOString()"), njs_str("0000-01-01T00:00:00.000Z") }, From xeioex at nginx.com Tue Nov 5 17:07:31 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 05 Nov 2019 17:07:31 +0000 Subject: [njs] Added Object.assign(). Message-ID: details: https://hg.nginx.org/njs/rev/fc6aa7e27d57 branches: changeset: 1220:fc6aa7e27d57 user: Alexander Mazyrin date: Sat Oct 12 16:23:25 2019 +0300 description: Added Object.assign(). diffstat: src/njs_object.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ src/njs_value.c | 3 +- src/test/njs_unit_test.c | 65 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) diffs (176 lines): diff -r ea70e6e7a0b8 -r fc6aa7e27d57 src/njs_object.c --- a/src/njs_object.c Fri Nov 01 19:26:42 2019 +0300 +++ b/src/njs_object.c Sat Oct 12 16:23:25 2019 +0300 @@ -1637,6 +1637,67 @@ njs_object_is_extensible(njs_vm_t *vm, n } +static njs_int_t +njs_object_assign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + uint32_t i, j, length; + njs_int_t ret; + njs_array_t *names; + njs_value_t *key, *source, *value, setval; + njs_object_prop_t *prop; + njs_property_query_t pq; + + value = njs_arg(args, nargs, 1); + + ret = njs_value_to_object(vm, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + for (i = 2; i < nargs; i++) { + source = &args[i]; + + names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS, 1); + if (njs_slow_path(names == NULL)) { + return NJS_ERROR; + } + + length = names->length; + + for (j = 0; j < length; j++) { + key = &names->start[j]; + + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1); + + ret = njs_property_query(vm, &pq, source, key); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + prop = pq.lhq.value; + if (!prop->enumerable) { + continue; + } + + ret = njs_value_property(vm, source, key, &setval); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + ret = njs_value_property_set(vm, value, key, &setval); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + } + + vm->retval = *value; + + return NJS_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -1908,6 +1969,15 @@ static const njs_object_prop_t njs_obje .writable = 1, .configurable = 1, }, + + /* Object.assign(). */ + { + .type = NJS_PROPERTY, + .name = njs_string("assign"), + .value = njs_native_function(njs_object_assign, 2), + .writable = 1, + .configurable = 1, + }, }; diff -r ea70e6e7a0b8 -r fc6aa7e27d57 src/njs_value.c --- a/src/njs_value.c Fri Nov 01 19:26:42 2019 +0300 +++ b/src/njs_value.c Sat Oct 12 16:23:25 2019 +0300 @@ -1186,9 +1186,8 @@ njs_value_to_object(njs_vm_t *vm, njs_va return NJS_ERROR; } - if (njs_is_object(value)) { + if (njs_fast_path(njs_is_object(value))) { return NJS_OK; - } if (njs_is_primitive(value)) { diff -r ea70e6e7a0b8 -r fc6aa7e27d57 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Nov 01 19:26:42 2019 +0300 +++ b/src/test/njs_unit_test.c Sat Oct 12 16:23:25 2019 +0300 @@ -14531,6 +14531,71 @@ static njs_unit_test_t njs_test[] = { njs_str("export"), njs_str("SyntaxError: Illegal export statement in 1") }, + { njs_str("Object.assign(undefined)"), + njs_str("TypeError: cannot convert null or undefined to object") }, + + { njs_str("Object.assign(null)"), + njs_str("TypeError: cannot convert null or undefined to object") }, + + { njs_str("Object.assign({x:123}).toString()"), + njs_str("[object Object]") }, + + { njs_str("Object.assign({x:123}).x"), + njs_str("123") }, + + { njs_str("Object.assign(true)"), + njs_str("true") }, + + { njs_str("Object.assign(123)"), + njs_str("123") }, + + { njs_str("var o1 = {a:1, b:1, c:1}; var o2 = {b:2, c:2}; " + "var o3 = {c:3}; var obj = Object.assign({}, o1, o2, o3); " + "Object.values(obj);"), + njs_str("1,2,3") }, + + { njs_str("var v1 = 'abc'; var v2 = true; var v3 = 10; " + "var obj = Object.assign({}, v1, null, v2, undefined, v3); " + "Object.values(obj);"), + njs_str("a,b,c") }, + + { njs_str("Object.assign(true, {a:123})"), + njs_str("true") }, + + { njs_str("Object.assign(true, {a:123}).a"), + njs_str("123") }, + + { njs_str("var y = Object.create({s:123}); y.z = 456;" + "Object.assign({}, y).s;"), + njs_str("undefined") }, + + { njs_str("var obj = {s:123}; Object.defineProperty(obj," + "'p1', {value:12, enumerable:false});" + "Object.assign({}, obj).p1"), + njs_str("undefined") }, + + { njs_str("var obj = {s:123}; Object.defineProperty(obj," + "'x', {value:12, writable:false});" + "Object.assign(obj, {x:4})"), + njs_str("TypeError: Cannot assign to read-only property \"x\" of object") }, + + { njs_str("var obj = {foo:1, get bar() {return 2;}};" + "var copy = Object.assign({}, obj);" + "Object.getOwnPropertyDescriptor(copy, 'bar').get"), + njs_str("undefined") }, + + { njs_str("try{var x = Object.defineProperty({}, 'foo'," + "{value:1, writable:false});" + "Object.assign(x, {bar:2}, {foo:2});}catch(error){};" + "x.bar"), + njs_str("2") }, + + { njs_str("var a = Object.defineProperty({}, 'a'," + "{get(){Object.defineProperty(this, 'b'," + "{value:2,enumerable:false});" + "return 1}, enumerable:1}); a.b =1;" + "var x = Object.assign({}, a);x.b;"), + njs_str("undefined") }, }; From alexander.borisov at nginx.com Tue Nov 5 17:25:34 2019 From: alexander.borisov at nginx.com (Alexander Borisov) Date: Tue, 05 Nov 2019 17:25:34 +0000 Subject: [njs] Added implementation of Array.prototype.copyWithin(). Message-ID: details: https://hg.nginx.org/njs/rev/ea588c413548 branches: changeset: 1221:ea588c413548 user: Alexander Borisov date: Tue Nov 05 20:24:48 2019 +0300 description: Added implementation of Array.prototype.copyWithin(). diffstat: src/njs_array.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++ src/test/njs_unit_test.c | 34 +++++++++++++ 2 files changed, 156 insertions(+), 0 deletions(-) diffs (183 lines): diff -r fc6aa7e27d57 -r ea588c413548 src/njs_array.c --- a/src/njs_array.c Sat Oct 12 16:23:25 2019 +0300 +++ b/src/njs_array.c Tue Nov 05 20:24:48 2019 +0300 @@ -2770,6 +2770,120 @@ start: } +static njs_int_t +njs_array_prototype_copy_within(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ + int8_t direction; + int64_t count, to, from, end; + uint32_t length; + njs_int_t ret; + njs_array_t *array; + njs_value_t *this, *value, from_key, to_key, prop; + + this = njs_arg(args, nargs, 0); + + ret = njs_value_to_object(vm, this); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_value_length(vm, this, &length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &to); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + to = (to < 0) ? njs_max(length + to, 0) : njs_min(to, length); + + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + from = (from < 0) ? njs_max(length + from, 0) : njs_min(from, length); + + value = njs_arg(args, nargs, 3); + + if (njs_is_undefined(value)) { + end = length; + + } else { + ret = njs_value_to_integer(vm, value, &end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); + + count = njs_min(end - from, length - to); + + if (from < to && from + count) { + direction = -1; + from = from + count - 1; + to = to + count - 1; + + } else { + direction = 1; + } + + njs_vm_retval_set(vm, this); + + if (njs_is_array(this)) { + if (njs_slow_path(!njs_object_hash_is_empty(this))) { + goto process_object; + } + + array = njs_array(this); + + while (count-- > 0) { + array->start[to] = array->start[from]; + + from = from + direction; + to = to + direction; + } + + return NJS_OK; + } + +process_object: + + while (count-- > 0) { + /* FIXME: largest index is 2**53-1. */ + + njs_uint32_to_string(&from_key, (uint32_t) from); + njs_uint32_to_string(&to_key, (uint32_t) to); + + ret = njs_value_property(vm, this, &from_key, &prop); + + if (ret == NJS_OK) { + ret = njs_value_property_set(vm, this, &to_key, &prop); + + } else { + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + ret = njs_value_property_delete(vm, this, &to_key, NULL); + } + + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + from = from + direction; + to = to + direction; + } + + return NJS_OK; +} + + static const njs_object_prop_t njs_array_prototype_properties[] = { { @@ -2982,6 +3096,14 @@ static const njs_object_prop_t njs_arra .writable = 1, .configurable = 1, }, + + { + .type = NJS_PROPERTY, + .name = njs_string("copyWithin"), + .value = njs_native_function(njs_array_prototype_copy_within, 2), + .writable = 1, + .configurable = 1, + }, }; diff -r fc6aa7e27d57 -r ea588c413548 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Sat Oct 12 16:23:25 2019 +0300 +++ b/src/test/njs_unit_test.c Tue Nov 05 20:24:48 2019 +0300 @@ -4019,6 +4019,40 @@ static njs_unit_test_t njs_test[] = "Object.defineProperty(a, 'length', {writable:true})"), njs_str("TypeError: Cannot redefine property: \"length\"") }, + { njs_str("[1, 2, 3, 4, 5].copyWithin(0, 3)"), + njs_str("4,5,3,4,5") }, + + { njs_str("[1, 2, 3, 4, 5].copyWithin(0, 3, 4)"), + njs_str("4,2,3,4,5") }, + + { njs_str("[1, 2, 3, 4, 5].copyWithin(0, -2, -1)"), + njs_str("4,2,3,4,5") }, + + { njs_str("[1, 2, 3, 4, 5].copyWithin(100, 200, 500)"), + njs_str("1,2,3,4,5") }, + + { njs_str("[0, 1, , , 1].copyWithin(0, 1, 4)"), + njs_str("1,,,,1") }, + + { njs_str("var o = [0, 1, , , 1].copyWithin(0, 1, 4); typeof o"), + njs_str("object") }, + + { njs_str("[].copyWithin.call({length: 5, 3: 1}, 0, 3)"), + njs_str("[object Object]") }, + + { njs_str("var o = [1, 2, 3, 4]; Object.defineProperties(o, { 5: {value: 'abc'}});" + "[].copyWithin.call(o, 0, 3, 4);"), + njs_str("4,2,3,4,,abc") }, + + { njs_str("var obj = {length: 5, 3: 1}; [].copyWithin.call(obj, 0, 3);" + "Object.keys(obj)"), + njs_str("length,3,0") }, + + { njs_str("var obj = {length: 5, 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'};" + "[].copyWithin.call(obj, 0, -2, -1);" + "Object.keys(obj) + '|' + Object.values(obj)"), + njs_str("length,1,2,3,4,5,0|5,a,b,c,d,e,c") }, + { njs_str("Array.prototype.slice(1)"), njs_str("") }, From xeioex at nginx.com Tue Nov 5 17:54:40 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 05 Nov 2019 17:54:40 +0000 Subject: [njs] auto/cc cleanup. Message-ID: details: https://hg.nginx.org/njs/rev/bb0bac71da3f branches: changeset: 1222:bb0bac71da3f user: Dmitry Volyntsev date: Tue Nov 05 20:49:56 2019 +0300 description: auto/cc cleanup. diffstat: auto/cc | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-) diffs (13 lines): diff -r ea588c413548 -r bb0bac71da3f auto/cc --- a/auto/cc Tue Nov 05 20:24:48 2019 +0300 +++ b/auto/cc Tue Nov 05 20:49:56 2019 +0300 @@ -156,9 +156,3 @@ esac # Stop on error exit status again. set -e - -cat << END >> $NJS_MAKEFILE - -NJS_CC = ${CC} -NJS_CFLAGS = ${NJS_CFLAGS} ${CFLAGS} -END From xeioex at nginx.com Tue Nov 5 17:54:40 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 05 Nov 2019 17:54:40 +0000 Subject: [njs] Added explanation for why njs CLI cannot be built. Message-ID: details: https://hg.nginx.org/njs/rev/44e41970a160 branches: changeset: 1223:44e41970a160 user: Dmitry Volyntsev date: Tue Nov 05 20:49:56 2019 +0300 description: Added explanation for why njs CLI cannot be built. diffstat: auto/make | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-) diffs (32 lines): diff -r bb0bac71da3f -r 44e41970a160 auto/make --- a/auto/make Tue Nov 05 20:49:56 2019 +0300 +++ b/auto/make Tue Nov 05 20:49:56 2019 +0300 @@ -91,6 +91,8 @@ done # njs cli. +if [ $NJS_HAVE_READLINE = YES ]; then + cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/njs: \\ @@ -104,6 +106,19 @@ cat << END >> $NJS_MAKEFILE END +else + +cat << END >> $NJS_MAKEFILE + +$NJS_BUILD_DIR/njs: + @echo + @echo " error: to make njs CLI \"readline\" library is required." + @echo + @exit 1 + +END + +fi # lib tests. From xeioex at nginx.com Tue Nov 5 17:54:41 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 05 Nov 2019 17:54:41 +0000 Subject: [njs] Added arguments validation for configure script. Message-ID: details: https://hg.nginx.org/njs/rev/4d33ea223de0 branches: changeset: 1224:4d33ea223de0 user: Dmitry Volyntsev date: Tue Nov 05 20:49:57 2019 +0300 description: Added arguments validation for configure script. diffstat: auto/help | 15 +++++++++++++++ auto/make | 9 +++++---- auto/options | 41 +++++++++++++++++++++++++++++++++++++++++ auto/summary | 25 +++++++++++++++++++++++++ configure | 5 +++-- 5 files changed, 89 insertions(+), 6 deletions(-) diffs (152 lines): diff -r 44e41970a160 -r 4d33ea223de0 auto/help --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/auto/help Tue Nov 05 20:49:57 2019 +0300 @@ -0,0 +1,15 @@ + +# Copyright (C) Igor Sysoev +# Copyright (C) NGINX, Inc. + + +cat << END + +./configure options: + + --cc=FILE set C compiler filename, default: "$CC" + --cc-opt=OPTIONS set additional C compiler options + --ld-opt=OPTIONS set additional linker options + --ar=FILE set static linking program, default: "$AR" + +END diff -r 44e41970a160 -r 4d33ea223de0 auto/make --- a/auto/make Tue Nov 05 20:49:56 2019 +0300 +++ b/auto/make Tue Nov 05 20:49:57 2019 +0300 @@ -15,8 +15,9 @@ cat << END > $NJS_MAKEFILE # This file is auto-generated by configure NJS_CC = ${CC} -NJS_AR = ${AR} -NJS_CFLAGS = ${NJS_CFLAGS} ${CFLAGS} +NJS_STATIC_LINK = ${AR} -r -c +NJS_LINK = ${CC} ${NJS_LD_OPT} +NJS_CFLAGS = ${NJS_CFLAGS} ${NJS_CC_OPT} ${CFLAGS} default: $NJS_DEFAULT_TARGET END @@ -58,7 +59,7 @@ libnjs: $NJS_BUILD_DIR/libnjs.a $NJS_BUILD_DIR/njs_auto_config.h \\ \$(NJS_LIB_OBJS) - \$(NJS_AR) -r -c $NJS_BUILD_DIR/libnjs.a \\ + \$(NJS_STATIC_LINK) $NJS_BUILD_DIR/libnjs.a \\ \$(NJS_LIB_OBJS) END @@ -98,7 +99,7 @@ cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/njs: \\ $NJS_BUILD_DIR/libnjs.a \\ src/njs_shell.c - \$(NJS_CC) -o $NJS_BUILD_DIR/njs \$(NJS_CFLAGS) \\ + \$(NJS_LINK) -o $NJS_BUILD_DIR/njs \$(NJS_CFLAGS) \\ $NJS_LIB_AUX_CFLAGS \$(NJS_LIB_INCS) -Injs \\ src/njs_shell.c \\ $NJS_BUILD_DIR/libnjs.a \\ diff -r 44e41970a160 -r 4d33ea223de0 auto/options --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/auto/options Tue Nov 05 20:49:57 2019 +0300 @@ -0,0 +1,41 @@ + +# Copyright (C) Igor Sysoev +# Copyright (C) NGINX, Inc. + + +NJS_CONFIGURE_OPTIONS= +NJS_CC_OPT=${NJS_CC_OPT:--O} +NJS_LD_OPT=${NJS_CC_OPT:--O} + +for njs_option +do + case "$njs_option" in + -*=*) value=`echo "$njs_option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) value="" ;; + esac + + case "$njs_option" in + --cc=*) CC="$value" ;; + --cc-opt=*) NJS_CC_OPT="$value" ;; + --ld-opt=*) NJS_LD_OPT="$value" ;; + --ar=*) AR="$value" ;; + + --help) + . auto/help + exit 0 + ;; + + *) + echo + echo $0: error: invalid option \"$njs_option\". + echo Run \"$0 --help\" to see available options. + echo + exit 1 + ;; + esac + + njs_opt=`echo $njs_option | sed -e "s/\(--[^=]*=\)\(.* .*\)/\1'\2'/"` + + NJS_CONFIGURE_OPTIONS="$NJS_CONFIGURE_OPTIONS $njs_opt" + +done diff -r 44e41970a160 -r 4d33ea223de0 auto/summary --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/auto/summary Tue Nov 05 20:49:57 2019 +0300 @@ -0,0 +1,25 @@ + +# Copyright (C) Igor Sysoev +# Copyright (C) NGINX, Inc. + + +echo +echo "NJS configuration summary:" +echo +echo " + using CC: \"$CC\"" +echo " + using CFLAGS: \"$NJS_CFLAGS $NJS_CC_OPT $CFLAGS\"" +echo +echo " + using PCRE library: $NJS_PCRE_LIB" + +if [ $NJS_HAVE_READLINE = YES ]; then + echo " + using readline library: $NJS_READLINE_LIB" +fi + +echo +echo " njs build dir: $NJS_BUILD_DIR" + +if [ $NJS_HAVE_READLINE = YES ]; then + echo " njs CLI: $NJS_BUILD_DIR/njs" +fi + +echo diff -r 44e41970a160 -r 4d33ea223de0 configure --- a/configure Tue Nov 05 20:49:56 2019 +0300 +++ b/configure Tue Nov 05 20:49:57 2019 +0300 @@ -24,8 +24,6 @@ NJS_TEST_LIBS=${NJS_TEST_LIBS=} CC=${CC:-cc} AR=${AR:-ar} NJS_CFLAGS=${NJS_CFLAGS=} -NJS_CC_OPT=${NJS_CC_OPT:--O} -NJS_LD_OPT=${NJS_CC_OPT:--O} NJS_BUILD_DIR=${NJS_BUILD_DIR:-build} @@ -47,6 +45,7 @@ END NJS_LIBRT= . auto/os +. auto/options . auto/cc . auto/clang . auto/time @@ -65,3 +64,5 @@ NJS_LIB_AUX_LIBS="$NJS_PCRE_LIB" . auto/make . auto/expect + +. auto/summary From arut at nginx.com Wed Nov 6 16:06:19 2019 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 06 Nov 2019 16:06:19 +0000 Subject: [nginx] Limit req: $limit_req_status variable. Message-ID: details: https://hg.nginx.org/nginx/rev/776d1bebdca2 branches: changeset: 7592:776d1bebdca2 user: Roman Arutyunyan date: Wed Nov 06 19:03:18 2019 +0300 description: Limit req: $limit_req_status variable. The variable takes one of the values: PASSED, DELAYED, REJECTED, DELAYED_DRY_RUN or REJECTED_DRY_RUN. diffstat: src/http/modules/ngx_http_limit_req_module.c | 79 ++++++++++++++++++++++++++- src/http/ngx_http_request.h | 2 +- 2 files changed, 76 insertions(+), 5 deletions(-) diffs (184 lines): diff -r 89adf49fe76a -r 776d1bebdca2 src/http/modules/ngx_http_limit_req_module.c --- a/src/http/modules/ngx_http_limit_req_module.c Mon Oct 21 20:22:30 2019 +0300 +++ b/src/http/modules/ngx_http_limit_req_module.c Wed Nov 06 19:03:18 2019 +0300 @@ -10,6 +10,13 @@ #include +#define NGX_HTTP_LIMIT_REQ_PASSED 1 +#define NGX_HTTP_LIMIT_REQ_DELAYED 2 +#define NGX_HTTP_LIMIT_REQ_REJECTED 3 +#define NGX_HTTP_LIMIT_REQ_DELAYED_DRY_RUN 4 +#define NGX_HTTP_LIMIT_REQ_REJECTED_DRY_RUN 5 + + typedef struct { u_char color; u_char dummy; @@ -65,6 +72,8 @@ static ngx_msec_t ngx_http_limit_req_acc static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n); +static ngx_int_t ngx_http_limit_req_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf); static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -72,6 +81,7 @@ static char *ngx_http_limit_req_zone(ngx void *conf); static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_limit_req_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf); @@ -131,7 +141,7 @@ static ngx_command_t ngx_http_limit_req static ngx_http_module_t ngx_http_limit_req_module_ctx = { - NULL, /* preconfiguration */ + ngx_http_limit_req_add_variables, /* preconfiguration */ ngx_http_limit_req_init, /* postconfiguration */ NULL, /* create main configuration */ @@ -161,6 +171,24 @@ ngx_module_t ngx_http_limit_req_module }; +static ngx_http_variable_t ngx_http_limit_req_vars[] = { + + { ngx_string("limit_req_status"), NULL, + ngx_http_limit_req_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + ngx_http_null_variable +}; + + +static ngx_str_t ngx_http_limit_req_status[] = { + ngx_string("PASSED"), + ngx_string("DELAYED"), + ngx_string("REJECTED"), + ngx_string("DELAYED_DRY_RUN"), + ngx_string("REJECTED_DRY_RUN") +}; + + static ngx_int_t ngx_http_limit_req_handler(ngx_http_request_t *r) { @@ -173,7 +201,7 @@ ngx_http_limit_req_handler(ngx_http_requ ngx_http_limit_req_conf_t *lrcf; ngx_http_limit_req_limit_t *limit, *limits; - if (r->main->limit_req_set) { + if (r->main->limit_req_status) { return NGX_DECLINED; } @@ -232,8 +260,6 @@ ngx_http_limit_req_handler(ngx_http_requ return NGX_DECLINED; } - r->main->limit_req_set = 1; - if (rc == NGX_BUSY || rc == NGX_ERROR) { if (rc == NGX_BUSY) { @@ -261,9 +287,12 @@ ngx_http_limit_req_handler(ngx_http_requ } if (lrcf->dry_run) { + r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED_DRY_RUN; return NGX_DECLINED; } + r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED; + return lrcf->status_code; } @@ -276,6 +305,7 @@ ngx_http_limit_req_handler(ngx_http_requ delay = ngx_http_limit_req_account(limits, n, &excess, &limit); if (!delay) { + r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_PASSED; return NGX_DECLINED; } @@ -285,9 +315,12 @@ ngx_http_limit_req_handler(ngx_http_requ excess / 1000, excess % 1000, &limit->shm_zone->shm.name); if (lrcf->dry_run) { + r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_DELAYED_DRY_RUN; return NGX_DECLINED; } + r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_DELAYED; + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -711,6 +744,25 @@ ngx_http_limit_req_init_zone(ngx_shm_zon } +static ngx_int_t +ngx_http_limit_req_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->main->limit_req_status == 0) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ngx_http_limit_req_status[r->main->limit_req_status - 1].len; + v->data = ngx_http_limit_req_status[r->main->limit_req_status - 1].data; + + return NGX_OK; +} + + static void * ngx_http_limit_req_create_conf(ngx_conf_t *cf) { @@ -996,6 +1048,25 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_c static ngx_int_t +ngx_http_limit_req_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_limit_req_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; diff -r 89adf49fe76a -r 776d1bebdca2 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Mon Oct 21 20:22:30 2019 +0300 +++ b/src/http/ngx_http_request.h Wed Nov 06 19:03:18 2019 +0300 @@ -513,7 +513,7 @@ struct ngx_http_request_s { * we use the single bits in the request structure */ unsigned limit_conn_set:1; - unsigned limit_req_set:1; + unsigned limit_req_status:3; unsigned limit_rate_set:1; unsigned limit_rate_after_set:1; From xeioex at nginx.com Fri Nov 8 13:30:16 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 08 Nov 2019 13:30:16 +0000 Subject: [njs] Passing to native function additional magic argument. Message-ID: details: https://hg.nginx.org/njs/rev/6df48738a043 branches: changeset: 1225:6df48738a043 user: Dmitry Volyntsev date: Fri Nov 08 16:29:24 2019 +0300 description: Passing to native function additional magic argument. This allows to make more generic function handlers. diffstat: src/njs_function.c | 17 +++++++++++------ src/njs_value.h | 17 +++++++++++++---- 2 files changed, 24 insertions(+), 10 deletions(-) diffs (123 lines): diff -r 4d33ea223de0 -r 6df48738a043 src/njs_function.c --- a/src/njs_function.c Tue Nov 05 20:49:57 2019 +0300 +++ b/src/njs_function.c Fri Nov 08 16:29:24 2019 +0300 @@ -591,8 +591,7 @@ njs_function_native_call(njs_vm_t *vm) function = native->function; ret = function->u.native(vm, native->arguments, native->nargs, - frame->retval); - + function->magic); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } @@ -919,9 +918,10 @@ njs_function_instance_length(njs_vm_t *v static njs_int_t njs_function_prototype_call(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t retval) + njs_index_t unused) { njs_int_t ret; + njs_frame_t *frame; njs_function_t *function; const njs_value_t *this; @@ -939,6 +939,8 @@ njs_function_prototype_call(njs_vm_t *vm nargs = 0; } + frame = (njs_frame_t *) vm->top_frame; + function = njs_function(&args[0]); /* Skip the "call" method frame. */ @@ -949,7 +951,7 @@ njs_function_prototype_call(njs_vm_t *vm return ret; } - ret = njs_function_frame_invoke(vm, retval); + ret = njs_function_frame_invoke(vm, frame->retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -960,10 +962,11 @@ njs_function_prototype_call(njs_vm_t *vm static njs_int_t njs_function_prototype_apply(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t retval) + njs_index_t unused) { uint32_t i, length; njs_int_t ret; + njs_frame_t *frame; njs_value_t name, *this, *arr_like; njs_array_t *arr; njs_function_t *func; @@ -1021,12 +1024,14 @@ activate: /* Skip the "apply" method frame. */ vm->top_frame->skip = 1; + frame = (njs_frame_t *) vm->top_frame; + ret = njs_function_frame(vm, func, this, args, length, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } - ret = njs_function_frame_invoke(vm, retval); + ret = njs_function_frame_invoke(vm, frame->retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } diff -r 4d33ea223de0 -r 6df48738a043 src/njs_value.h --- a/src/njs_value.h Tue Nov 05 20:49:57 2019 +0300 +++ b/src/njs_value.h Fri Nov 08 16:29:24 2019 +0300 @@ -234,14 +234,14 @@ struct njs_function_s { njs_object_t object; uint8_t args_offset; - uint8_t args_count; - /* Function is a closure. */ + uint8_t args_count:5; uint8_t closure:1; - uint8_t native:1; uint8_t ctor:1; + uint8_t magic; + union { njs_function_lambda_t *lambda; njs_function_native_t native; @@ -391,12 +391,13 @@ typedef struct { } -#define njs_native_function(_function, _args_count) { \ +#define _njs_native_function(_function, _args_count, _magic) { \ .data = { \ .type = NJS_FUNCTION, \ .truth = 1, \ .u.function = & (njs_function_t) { \ .native = 1, \ + .magic = _magic, \ .args_count = _args_count, \ .args_offset = 1, \ .u.native = _function, \ @@ -408,6 +409,14 @@ typedef struct { } +#define njs_native_function(_function, _args_count) \ + _njs_native_function(_function, _args_count, 0) + + +#define njs_native_function2(_function, _args_count, _magic) \ + _njs_native_function(_function, _args_count, _magic) + + #define njs_prop_handler(_handler) { \ .data = { \ .type = NJS_INVALID, \ From xeioex at nginx.com Fri Nov 8 13:30:17 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 08 Nov 2019 13:30:17 +0000 Subject: [njs] Refactored working with Date object. Message-ID: details: https://hg.nginx.org/njs/rev/f770bdea7c85 branches: changeset: 1226:f770bdea7c85 user: Dmitry Volyntsev date: Fri Nov 08 16:29:29 2019 +0300 description: Refactored working with Date object. 1) Replacing gmtime_r() and localtime_r() with builtin function. 2) Custom getters and setters for each field (like hours, minutes, seconds) are replaced with generic functions. diffstat: auto/time | 16 + src/njs_date.c | 1730 +++++++++------------------------------------ src/test/njs_unit_test.c | 49 +- 3 files changed, 408 insertions(+), 1387 deletions(-) diffs (truncated from 2507 to 1000 lines): diff -r 6df48738a043 -r f770bdea7c85 auto/time --- a/auto/time Fri Nov 08 16:29:24 2019 +0300 +++ b/auto/time Fri Nov 08 16:29:29 2019 +0300 @@ -33,6 +33,22 @@ if [ $njs_found = no ]; then fi fi + +njs_feature="sizeof(time_t)" +njs_feature_name=NJS_TIME_T_SIZE +njs_feature_run=value +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#include + #include + + int main(void) { + printf(\"%zu\", sizeof(time_t)); + return 0; + }" +. auto/feature + + # Linux, FreeBSD, MacOSX. njs_feature="struct tm.tm_gmtoff" diff -r 6df48738a043 -r f770bdea7c85 src/njs_date.c --- a/src/njs_date.c Fri Nov 08 16:29:24 2019 +0300 +++ b/src/njs_date.c Fri Nov 08 16:29:29 2019 +0300 @@ -8,15 +8,27 @@ #include -/* - * njs_timegm() is used because - * Solaris lacks timegm(), - * FreeBSD and MacOSX timegm() cannot handle years before 1900. - */ - #define NJS_DATE_TIME_LEN \ sizeof("Mon Sep 28 1970 12:00:00 GMT+0600 (XXXXX)") +#define NJS_DATE_MAX_FIELDS 8 +#define NJS_DATE_WDAY 0 +#define NJS_DATE_YR 1 +#define NJS_DATE_MON 2 +#define NJS_DATE_DAY 3 +#define NJS_DATE_HR 4 +#define NJS_DATE_MIN 5 +#define NJS_DATE_SEC 6 +#define NJS_DATE_MSEC 7 + + +#define njs_date_magic(field, local) \ + ((local << 6) + field) + + +#define njs_date_magic2(since, len, local) \ + ((local << 6) + ((len & 7) << 3) + since) + typedef enum { NJS_DATE_FMT_TO_TIME_STRING, @@ -28,23 +40,20 @@ typedef enum { static double njs_date_string_parse(njs_value_t *date); -static double njs_date_rfc2822_string_parse(struct tm *tm, const u_char *p, +static double njs_date_rfc2822_string_parse(int64_t tm[], const u_char *p, const u_char *end); -static double njs_date_js_string_parse(struct tm *tm, const u_char *p, +static double njs_date_js_string_parse(int64_t tm[], const u_char *p, const u_char *end); static const u_char *njs_date_skip_week_day(const u_char *p, const u_char *end); static const u_char *njs_date_skip_spaces(const u_char *p, const u_char *end); static njs_int_t njs_date_month_parse(const u_char *p, const u_char *end); -static const u_char *njs_date_time_parse(struct tm *tm, const u_char *p, +static const u_char *njs_date_time_parse(int64_t tm[], const u_char *p, const u_char *end); -static njs_int_t njs_date_gmtoff_parse(const u_char *start, const u_char *end); -static const u_char *njs_date_number_parse(int *value, const u_char *p, +static int64_t njs_date_gmtoff_parse(const u_char *start, const u_char *end); +static const u_char *njs_date_number_parse(int64_t *value, const u_char *p, const u_char *end, size_t size); -static int64_t njs_timegm(struct tm *tm); static njs_int_t njs_date_string(njs_vm_t *vm, njs_value_t *retval, njs_date_fmt_t fmt, double time); -static double njs_date_time(struct tm *tm, int64_t ms); -static double njs_date_utc_time(struct tm *tm, double time); static const njs_value_t njs_string_invalid_date = njs_string("Invalid Date"); @@ -95,13 +104,6 @@ njs_timeclip(double time) njs_inline int64_t -njs_make_time(int64_t h, int64_t min, int64_t s, int64_t milli) -{ - return ((h * 60 + min) * 60 + s) * 1000 + milli; -} - - -njs_inline int64_t njs_days_in_year(int64_t y) { return 365 + !(y % 4) - !(y % 100) + !(y % 400); @@ -152,25 +154,151 @@ njs_tz_offset(int64_t time) time /= 1000; +#if (NJS_TIME_T_SIZE < 8) + + /* Smart truncation. */ + + if ((time_t) -1 < 0) { + if (time < INT32_MIN) { + time = INT32_MIN; + + } else if (time > INT32_MAX) { + time = INT32_MAX; + } + + } else { + if (time < 0) { + time = 0; + + } else if (time > UINT32_MAX) { + time = UINT32_MAX; + } + } + +#endif + ti = time; localtime_r(&ti, &tm); + /* + * As njs_timezone(&tm) may return value which is not a multiple of 60 + * secs (see "zdump -v /etc/localtime" for MSK zone) rounding it to + * minutes precision here to ensure: + * var date = new Date() + * date.valueOf() - date.getTimezoneOffset() * 60000 == Date.UTC() + * which is expected by test262. + */ + return -njs_timezone(&tm) / 60; } njs_inline int64_t -njs_make_date(int64_t days, int64_t time, njs_bool_t local) +njs_year_from_days(int64_t *days) { - int64_t date; + int64_t y, d1, nd, d; + + d = *days; + + y = njs_floor_div(d * 10000, 3652425) + 1970; + + for ( ;; ) { + d1 = d - njs_days_from_year(y); + + if (d1 < 0) { + y--; + d1 += njs_days_in_year(y); + + } else { + nd = njs_days_in_year(y); + + if (d1 < nd) { + break; + } - date = days * 86400000 + time; + d1 -= nd; + y++; + } + } + + *days = d1; + + return y; +} + + +njs_inline double +njs_make_date(int64_t tm[], njs_bool_t local) +{ + int64_t days, time; + + days = njs_make_day(tm[NJS_DATE_YR], tm[NJS_DATE_MON], + tm[NJS_DATE_DAY]); + + time = ((tm[NJS_DATE_HR] * 60 + tm[NJS_DATE_MIN]) * 60 + + tm[NJS_DATE_SEC]) * 1000 + tm[NJS_DATE_MSEC]; + + time += days * 86400000; if (local) { - date += njs_tz_offset(date) * 60000; + time += njs_tz_offset(time) * 60000; + } + + return njs_timeclip(time); +} + + +njs_inline int64_t +njs_destruct_date(double time, int64_t tm[], int index, njs_bool_t local) +{ + int64_t days, wd, y, i, md, h, m, s, ms; + + static const int month_days[] = { 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 }; + + if (njs_slow_path(isnan(time))) { + time = 0; + + } else if (local) { + time -= njs_tz_offset(time) * 60000; } - return date; + h = njs_mod(time, 86400000); + days = (time - h) / 86400000; + ms = h % 1000; + h = (h - ms) / 1000; + s = h % 60; + h = (h - s) / 60; + m = h % 60; + h = (h - m) / 60; + wd = njs_mod(days + 4, 7); + y = njs_year_from_days(&days); + + for (i = 0; i < 11; i++) { + md = month_days[i]; + + if (i == 1) { + /* Leap day. */ + md += njs_days_in_year(y) - 365; + } + + if (days < md) { + break; + } + + days -= md; + } + + tm[NJS_DATE_YR] = y; + tm[NJS_DATE_MON] = i; + tm[NJS_DATE_DAY] = days + 1; + tm[NJS_DATE_HR] = h; + tm[NJS_DATE_MIN] = m; + tm[NJS_DATE_SEC] = s; + tm[NJS_DATE_MSEC] = ms; + tm[NJS_DATE_WDAY] = wd; + + return tm[index]; } @@ -179,11 +307,10 @@ njs_date_constructor(njs_vm_t *vm, njs_v njs_index_t unused) { double num, time; - int64_t day, tm; njs_int_t ret; njs_uint_t i, n; njs_date_t *date; - int64_t values[8]; + int64_t tm[NJS_DATE_MAX_FIELDS]; if (!vm->top_frame->ctor) { return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_STRING, @@ -210,17 +337,16 @@ njs_date_constructor(njs_vm_t *vm, njs_v time = njs_date_string_parse(&args[1]); } else { - time = njs_number(&args[1]); + time = njs_timeclip(njs_number(&args[1])); } } else { time = NAN; - njs_memzero(values, 8 * sizeof(int64_t)); + njs_memzero(tm, NJS_DATE_MAX_FIELDS * sizeof(int64_t)); - /* Day. */ - values[3] = 1; + tm[NJS_DATE_DAY] = 1; n = njs_min(8, nargs); @@ -238,19 +364,14 @@ njs_date_constructor(njs_vm_t *vm, njs_v goto done; } - values[i] = num; + tm[i] = num; } - /* Year. */ - if (values[1] >= 0 && values[1] < 100) { - values[1] += 1900; + if (tm[NJS_DATE_YR] >= 0 && tm[NJS_DATE_YR] < 100) { + tm[NJS_DATE_YR] += 1900; } - day = njs_make_day(values[1], values[2], values[3]); - - tm = njs_make_time(values[4], values[5], values[6], values[7]); - - time = njs_make_date(day, tm, 1); + time = njs_make_date(tm, 1); } done: @@ -268,7 +389,7 @@ done: date->object.extensible = 1; date->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_DATE].object; - date->time = njs_timeclip(time); + date->time = time; njs_set_date(&vm->retval, date); @@ -280,19 +401,17 @@ static njs_int_t njs_date_utc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - int64_t day, tm; double num, time; njs_int_t ret; njs_uint_t i, n; - int64_t values[8]; + int64_t tm[NJS_DATE_MAX_FIELDS]; time = NAN; if (nargs > 1) { - njs_memzero(values, 8 * sizeof(int64_t)); + njs_memzero(tm, NJS_DATE_MAX_FIELDS * sizeof(int64_t)); - /* Day. */ - values[3] = 1; + tm[NJS_DATE_DAY] = 1; n = njs_min(8, nargs); @@ -310,18 +429,15 @@ njs_date_utc(njs_vm_t *vm, njs_value_t * goto done; } - values[i] = num; + tm[i] = num; } /* Year. */ - if (values[1] >= 0 && values[1] < 100) { - values[1] += 1900; + if (tm[NJS_DATE_YR] >= 0 && tm[NJS_DATE_YR] < 100) { + tm[NJS_DATE_YR] += 1900; } - day = njs_make_day(values[1], values[2], values[3]); - tm = njs_make_time(values[4], values[5], values[6], values[7]); - - time = njs_timeclip(njs_make_date(day, tm, 0)); + time = njs_make_date(tm, 0); } done: @@ -332,55 +448,6 @@ done: } -static int64_t -njs_timegm(struct tm *tm) -{ - int32_t year, month, days; - - year = tm->tm_year + 1900; - - /* - * Shift new year to March 1 and start months - * from 1 (not 0), as required for Gauss' formula. - */ - - month = tm->tm_mon - 1; - - if (month <= 0) { - month += 12; - year -= 1; - } - - /* Gauss' formula for Gregorian days since March 1, 1 BCE. */ - - /* Days in years including leap years since March 1, 1 BCE. */ - days = 365 * year + year / 4 - year / 100 + year / 400; - - /* Days before the month. */ - days += 367 * month / 12 - 30; - - /* Days before the day. */ - if (year >= 0) { - days += tm->tm_mday - 1; - - } else { - /* 1 BCE was a leap year. */ - days += tm->tm_mday - 2; - } - - /* - * 719527 days were between March 1, 1 BCE and March 1, 1970, - * 31 and 28 days were in January and February 1970. - */ - days = days - 719527 + 31 + 28; - - return (int64_t) days * 86400 - + tm->tm_hour * 3600 - + tm->tm_min * 60 - + tm->tm_sec; -} - - static njs_int_t njs_date_now(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) @@ -421,12 +488,12 @@ njs_date_parse(njs_vm_t *vm, njs_value_t static double njs_date_string_parse(njs_value_t *date) { - int ext, ms, ms_length, skipped; - double time; - njs_str_t string; - struct tm tm; - njs_bool_t sign, week, utc; - const u_char *p, *next, *end; + size_t ms_length; + int64_t ext, skipped; + njs_str_t string; + njs_bool_t sign, week, utc; + const u_char *p, *next, *end; + int64_t tm[NJS_DATE_MAX_FIELDS]; njs_string_get(date, &string); @@ -445,19 +512,20 @@ njs_date_string_parse(njs_value_t *date) sign = 0; } - tm.tm_mon = 0; - tm.tm_mday = 1; - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; + tm[NJS_DATE_MON] = 0; + tm[NJS_DATE_DAY] = 1; + tm[NJS_DATE_HR] = 0; + tm[NJS_DATE_MIN] = 0; + tm[NJS_DATE_SEC] = 0; + tm[NJS_DATE_MSEC] = 0; - next = njs_date_number_parse(&tm.tm_year, p, end, 4); + next = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (next != NULL) { /* ISO-8601 format: "1970-09-28T06:00:00.000Z" */ if (next == end) { - goto year; + goto done; } if (*next != '-') { @@ -468,18 +536,19 @@ njs_date_string_parse(njs_value_t *date) return NAN; } - tm.tm_year = tm.tm_year * 100 + ext; + tm[NJS_DATE_YR] *= 100; + tm[NJS_DATE_YR] += ext; if (string.start[0] == '-') { - if (tm.tm_year == 0) { + if (tm[NJS_DATE_YR] == 0) { return NAN; } - tm.tm_year = -tm.tm_year; + tm[NJS_DATE_YR] = -tm[NJS_DATE_YR]; } if (next == end) { - goto year; + goto done; } if (*next != '-') { @@ -487,14 +556,12 @@ njs_date_string_parse(njs_value_t *date) } } - tm.tm_year -= 1900; - - p = njs_date_number_parse(&tm.tm_mon, next + 1, end, 2); + p = njs_date_number_parse(&tm[NJS_DATE_MON], next + 1, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } - tm.tm_mon--; + tm[NJS_DATE_MON]--; if (p == end) { goto done; @@ -504,7 +571,7 @@ njs_date_string_parse(njs_value_t *date) return NAN; } - p = njs_date_number_parse(&tm.tm_mday, p + 1, end, 2); + p = njs_date_number_parse(&tm[NJS_DATE_DAY], p + 1, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } @@ -525,7 +592,7 @@ njs_date_string_parse(njs_value_t *date) end++; } - p = njs_date_time_parse(&tm, p + 1, end); + p = njs_date_time_parse(tm, p + 1, end); if (njs_slow_path(p == NULL)) { return NAN; } @@ -542,7 +609,7 @@ njs_date_string_parse(njs_value_t *date) ms_length = (end - p < 3) ? end - p : 3; - p = njs_date_number_parse(&ms, p, end, ms_length); + p = njs_date_number_parse(&tm[NJS_DATE_MSEC], p, end, ms_length); if (njs_slow_path(p == NULL)) { return NAN; } @@ -555,21 +622,13 @@ njs_date_string_parse(njs_value_t *date) } if (ms_length == 1) { - ms *= 100; + tm[NJS_DATE_MSEC] *= 100; } else if (ms_length == 2) { - ms *= 10; + tm[NJS_DATE_MSEC] *= 10; } - if (utc) { - time = njs_timegm(&tm); - - } else { - tm.tm_isdst = -1; - time = mktime(&tm); - } - - return time * 1000 + ms; + return njs_make_date(tm, !utc); } if (sign) { @@ -579,7 +638,7 @@ njs_date_string_parse(njs_value_t *date) week = 1; for ( ;; ) { - next = njs_date_number_parse(&tm.tm_mday, p, end, 2); + next = njs_date_number_parse(&tm[NJS_DATE_DAY], p, end, 2); if (next != NULL) { /* @@ -588,15 +647,15 @@ njs_date_string_parse(njs_value_t *date) * "Mon, 28 Sep 1970 06:00:00 UTC", * "Mon, 28 Sep 1970 12:00:00 +0600". */ - return njs_date_rfc2822_string_parse(&tm, next, end); + return njs_date_rfc2822_string_parse(tm, next, end); } - tm.tm_mon = njs_date_month_parse(p, end); + tm[NJS_DATE_MON] = njs_date_month_parse(p, end); - if (tm.tm_mon >= 0) { + if (tm[NJS_DATE_MON] >= 0) { /* Date.toString() format: "Mon Sep 28 1970 12:00:00 GMT+0600". */ - return njs_date_js_string_parse(&tm, p + 3, end); + return njs_date_js_string_parse(tm, p + 3, end); } if (!week) { @@ -616,28 +675,24 @@ njs_date_string_parse(njs_value_t *date) week = 0; } -year: - - tm.tm_year -= 1900; - done: - return njs_timegm(&tm) * 1000; + return njs_make_date(tm, 0); } static double -njs_date_rfc2822_string_parse(struct tm *tm, const u_char *p, const u_char *end) +njs_date_rfc2822_string_parse(int64_t tm[], const u_char *p, const u_char *end) { - int gmtoff; + int64_t gmtoff; p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } - tm->tm_mon = njs_date_month_parse(p, end); - if (njs_slow_path(tm->tm_mon < 0)) { + tm[NJS_DATE_MON] = njs_date_month_parse(p, end); + if (njs_slow_path(tm[NJS_DATE_MON] < 0)) { return NAN; } @@ -646,12 +701,12 @@ njs_date_rfc2822_string_parse(struct tm return NAN; } - p = njs_date_number_parse(&tm->tm_year, p, end, 4); + p = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (njs_slow_path(p == NULL)) { return NAN; } - tm->tm_year -= 1900; + gmtoff = 0; if (p == end) { goto done; @@ -701,25 +756,25 @@ njs_date_rfc2822_string_parse(struct tm } } - return (njs_timegm(tm) - gmtoff * 60) * 1000; - done: - return njs_timegm(tm) * 1000; + tm[NJS_DATE_MSEC] = -gmtoff * 60000; + + return njs_make_date(tm, 0); } static double -njs_date_js_string_parse(struct tm *tm, const u_char *p, const u_char *end) +njs_date_js_string_parse(int64_t tm[], const u_char *p, const u_char *end) { - int gmtoff; + int64_t gmtoff; p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } - p = njs_date_number_parse(&tm->tm_mday, p, end, 2); + p = njs_date_number_parse(&tm[NJS_DATE_DAY], p, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } @@ -729,13 +784,11 @@ njs_date_js_string_parse(struct tm *tm, return NAN; } - p = njs_date_number_parse(&tm->tm_year, p, end, 4); + p = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (njs_slow_path(p == NULL)) { return NAN; } - tm->tm_year -= 1900; - if (p == end) { goto done; } @@ -768,11 +821,11 @@ njs_date_js_string_parse(struct tm *tm, } if (p + 2 < end && p[0] == 'G' && p[1] == 'M' && p[2] == 'T') { - gmtoff = njs_date_gmtoff_parse(&p[3], end); if (njs_fast_path(gmtoff != -1)) { - return (njs_timegm(tm) - gmtoff * 60) * 1000; + tm[NJS_DATE_MSEC] = -gmtoff * 60000; + return njs_make_date(tm, 0); } } @@ -780,7 +833,7 @@ njs_date_js_string_parse(struct tm *tm, done: - return njs_timegm(tm) * 1000; + return njs_make_date(tm, 0); } @@ -908,9 +961,9 @@ njs_date_month_parse(const u_char *p, co static const u_char * -njs_date_time_parse(struct tm *tm, const u_char *p, const u_char *end) +njs_date_time_parse(int64_t tm[], const u_char *p, const u_char *end) { - p = njs_date_number_parse(&tm->tm_hour, p, end, 2); + p = njs_date_number_parse(&tm[NJS_DATE_HR], p, end, 2); if (njs_slow_path(p == NULL)) { return p; } @@ -919,7 +972,7 @@ njs_date_time_parse(struct tm *tm, const return NULL; } - p = njs_date_number_parse(&tm->tm_min, p + 1, end, 2); + p = njs_date_number_parse(&tm[NJS_DATE_MIN], p + 1, end, 2); if (njs_slow_path(p == NULL)) { return p; } @@ -932,14 +985,14 @@ njs_date_time_parse(struct tm *tm, const return NULL; } - return njs_date_number_parse(&tm->tm_sec, p + 1, end, 2); + return njs_date_number_parse(&tm[NJS_DATE_SEC], p + 1, end, 2); } -static njs_int_t +static int64_t njs_date_gmtoff_parse(const u_char *start, const u_char *end) { - int gmtoff, hour, min; + int64_t gmtoff, hour, min; const u_char *p; if (njs_fast_path(start + 4 < end && (*start == '+' || *start == '-'))) { @@ -968,7 +1021,7 @@ njs_date_gmtoff_parse(const u_char *star static const u_char * -njs_date_number_parse(int *value, const u_char *p, const u_char *end, +njs_date_number_parse(int64_t *value, const u_char *p, const u_char *end, size_t size) { u_char c; @@ -1128,11 +1181,10 @@ static njs_int_t njs_date_string(njs_vm_t *vm, njs_value_t *retval, njs_date_fmt_t fmt, double time) { - u_char *p, sign; - int32_t year, tz; - time_t clock; - u_char buf[NJS_DATE_TIME_LEN]; - struct tm tm; + int year, tz; + u_char *p, sign; + u_char buf[NJS_DATE_TIME_LEN]; + int64_t tm[NJS_DATE_MAX_FIELDS]; static const char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; @@ -1150,15 +1202,15 @@ njs_date_string(njs_vm_t *vm, njs_value_ switch (fmt) { case NJS_DATE_FMT_TO_ISO_STRING: case NJS_DATE_FMT_TO_UTC_STRING: - clock = time / 1000; - gmtime_r(&clock, &tm); - year = tm.tm_year + 1900; + njs_destruct_date(time, tm, 0, 0); + year = tm[NJS_DATE_YR]; if (fmt == NJS_DATE_FMT_TO_UTC_STRING) { p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, - "%s, %02d %s %04d %02d:%02d:%02d GMT", - week[tm.tm_wday], tm.tm_mday, month[tm.tm_mon], - year, tm.tm_hour, tm.tm_min, tm.tm_sec); + "%s, %02L %s %04d %02L:%02L:%02L GMT", + week[tm[NJS_DATE_WDAY]], tm[NJS_DATE_DAY], + month[tm[NJS_DATE_MON]], year, tm[NJS_DATE_HR], + tm[NJS_DATE_MIN], tm[NJS_DATE_SEC]); break; } @@ -1175,10 +1227,9 @@ njs_date_string(njs_vm_t *vm, njs_value_ } p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, - "-%02d-%02dT%02d:%02d:%02d.%03dZ", - tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, - tm.tm_min, tm.tm_sec, - (int) ((int64_t) time % 1000)); + "-%02L-%02LT%02L:%02L:%02L.%03LZ", + tm[NJS_DATE_MON] + 1, tm[NJS_DATE_DAY], tm[NJS_DATE_HR], + tm[NJS_DATE_MIN], tm[NJS_DATE_SEC], tm[NJS_DATE_MSEC]); break; @@ -1186,14 +1237,13 @@ njs_date_string(njs_vm_t *vm, njs_value_ case NJS_DATE_FMT_TO_DATE_STRING: case NJS_DATE_FMT_TO_STRING: default: - clock = time / 1000; - localtime_r(&clock, &tm); + njs_destruct_date(time, tm, 0, 1); if (fmt != NJS_DATE_FMT_TO_TIME_STRING) { p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, - "%s %s %02d %04d", - week[tm.tm_wday], month[tm.tm_mon], tm.tm_mday, - tm.tm_year + 1900); + "%s %s %02L %04L", + week[tm[NJS_DATE_WDAY]], month[tm[NJS_DATE_MON]], + tm[NJS_DATE_DAY], tm[NJS_DATE_YR]); } if (fmt != NJS_DATE_FMT_TO_DATE_STRING) { @@ -1209,8 +1259,8 @@ njs_date_string(njs_vm_t *vm, njs_value_ } p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, - "%02d:%02d:%02d GMT%c%02d%02d", - tm.tm_hour, tm.tm_min, tm.tm_sec, + "%02L:%02L:%02L GMT%c%02d%02d", + tm[NJS_DATE_HR], tm[NJS_DATE_MIN], tm[NJS_DATE_SEC], sign, tz / 60, tz % 60); } } @@ -1259,193 +1309,11 @@ njs_date_to_string(njs_vm_t *vm, njs_val static njs_int_t -njs_date_prototype_get_full_year(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - double value; - time_t clock; - struct tm tm; - - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; - } - - value = njs_date(&args[0])->time; - - if (njs_fast_path(!isnan(value))) { - clock = value / 1000; - localtime_r(&clock, &tm); - - value = tm.tm_year + 1900; - } - - njs_set_number(&vm->retval, value); - - return NJS_OK; -} - - -static njs_int_t -njs_date_prototype_get_utc_full_year(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - double value; - time_t clock; - struct tm tm; - - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; - } - - value = njs_date(&args[0])->time; - - if (njs_fast_path(!isnan(value))) { - clock = value / 1000; - gmtime_r(&clock, &tm); - - value = tm.tm_year + 1900; - } - - njs_set_number(&vm->retval, value); - - return NJS_OK; -} - - -static njs_int_t -njs_date_prototype_get_month(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - double value; - time_t clock; - struct tm tm; - - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; - } - - value = njs_date(&args[0])->time; - - if (njs_fast_path(!isnan(value))) { - clock = value / 1000; - localtime_r(&clock, &tm); - - value = tm.tm_mon; - } - - njs_set_number(&vm->retval, value); - - return NJS_OK; -} - - -static njs_int_t -njs_date_prototype_get_utc_month(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) +njs_date_prototype_get_field(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t magic) { - double value; - time_t clock; - struct tm tm; - - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; - } - - value = njs_date(&args[0])->time; - - if (njs_fast_path(!isnan(value))) { - clock = value / 1000; - - gmtime_r(&clock, &tm); - - value = tm.tm_mon; - } - - njs_set_number(&vm->retval, value); - - return NJS_OK; -} - - -static njs_int_t -njs_date_prototype_get_date(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - double value; - time_t clock; - struct tm tm; - - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; - } - - value = njs_date(&args[0])->time; - - if (njs_fast_path(!isnan(value))) { - clock = value / 1000; - localtime_r(&clock, &tm); - - value = tm.tm_mday; - } - From xeioex at nginx.com Fri Nov 8 13:30:18 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 08 Nov 2019 13:30:18 +0000 Subject: [njs] Unifying Date.prototype.*ToString() function into single handler. Message-ID: details: https://hg.nginx.org/njs/rev/de6c3c68e18e branches: changeset: 1227:de6c3c68e18e user: Dmitry Volyntsev date: Fri Nov 08 16:29:34 2019 +0300 description: Unifying Date.prototype.*ToString() function into single handler. diffstat: src/njs_date.c | 85 ++++++++++++--------------------------------------------- 1 files changed, 18 insertions(+), 67 deletions(-) diffs (165 lines): diff -r f770bdea7c85 -r de6c3c68e18e src/njs_date.c --- a/src/njs_date.c Fri Nov 08 16:29:29 2019 +0300 +++ b/src/njs_date.c Fri Nov 08 16:29:34 2019 +0300 @@ -1131,7 +1131,7 @@ njs_date_prototype_value_of(njs_vm_t *vm static njs_int_t njs_date_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) + njs_index_t fmt) { if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", @@ -1140,40 +1140,7 @@ njs_date_prototype_to_string(njs_vm_t *v return NJS_ERROR; } - return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_STRING, - njs_date(&args[0])->time); -} - - -static njs_int_t -njs_date_prototype_to_date_string(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; - } - - return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_DATE_STRING, - njs_date(&args[0])->time); -} - - -static njs_int_t -njs_date_prototype_to_time_string(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; - } - - return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_TIME_STRING, - njs_date(&args[0])->time); + return njs_date_string(vm, &vm->retval, fmt, njs_date(&args[0])->time); } @@ -1269,30 +1236,6 @@ njs_date_string(njs_vm_t *vm, njs_value_ } -static njs_int_t -njs_date_prototype_to_utc_string(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - if (njs_slow_path(!njs_is_date(&args[0]))) { - njs_type_error(vm, "cannot convert %s to date", - njs_type_string(args[0].type)); - - return NJS_ERROR; - } - - return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_UTC_STRING, - njs_date(&args[0])->time); -} - - -static njs_int_t -njs_date_prototype_to_iso_string(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - return njs_date_to_string(vm, &vm->retval, &args[0]); -} - - njs_int_t njs_date_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *date) { @@ -1512,7 +1455,8 @@ static const njs_object_prop_t njs_date { .type = NJS_PROPERTY, .name = njs_string("toString"), - .value = njs_native_function(njs_date_prototype_to_string, 0), + .value = njs_native_function2(njs_date_prototype_to_string, 0, + NJS_DATE_FMT_TO_STRING), .writable = 1, .configurable = 1, }, @@ -1520,7 +1464,8 @@ static const njs_object_prop_t njs_date { .type = NJS_PROPERTY, .name = njs_string("toDateString"), - .value = njs_native_function(njs_date_prototype_to_date_string, 0), + .value = njs_native_function2(njs_date_prototype_to_string, 0, + NJS_DATE_FMT_TO_DATE_STRING), .writable = 1, .configurable = 1, }, @@ -1528,7 +1473,8 @@ static const njs_object_prop_t njs_date { .type = NJS_PROPERTY, .name = njs_string("toTimeString"), - .value = njs_native_function(njs_date_prototype_to_time_string, 0), + .value = njs_native_function2(njs_date_prototype_to_string, 0, + NJS_DATE_FMT_TO_TIME_STRING), .writable = 1, .configurable = 1, }, @@ -1536,7 +1482,8 @@ static const njs_object_prop_t njs_date { .type = NJS_PROPERTY, .name = njs_string("toLocaleString"), - .value = njs_native_function(njs_date_prototype_to_string, 0), + .value = njs_native_function2(njs_date_prototype_to_string, 0, + NJS_DATE_FMT_TO_STRING), .writable = 1, .configurable = 1, }, @@ -1544,7 +1491,8 @@ static const njs_object_prop_t njs_date { .type = NJS_PROPERTY, .name = njs_long_string("toLocaleDateString"), - .value = njs_native_function(njs_date_prototype_to_date_string, 0), + .value = njs_native_function2(njs_date_prototype_to_string, 0, + NJS_DATE_FMT_TO_DATE_STRING), .writable = 1, .configurable = 1, }, @@ -1552,7 +1500,8 @@ static const njs_object_prop_t njs_date { .type = NJS_PROPERTY, .name = njs_long_string("toLocaleTimeString"), - .value = njs_native_function(njs_date_prototype_to_time_string, 0), + .value = njs_native_function2(njs_date_prototype_to_string, 0, + NJS_DATE_FMT_TO_TIME_STRING), .writable = 1, .configurable = 1, }, @@ -1560,7 +1509,8 @@ static const njs_object_prop_t njs_date { .type = NJS_PROPERTY, .name = njs_string("toUTCString"), - .value = njs_native_function(njs_date_prototype_to_utc_string, 0), + .value = njs_native_function2(njs_date_prototype_to_string, 0, + NJS_DATE_FMT_TO_UTC_STRING), .writable = 1, .configurable = 1, }, @@ -1568,7 +1518,8 @@ static const njs_object_prop_t njs_date { .type = NJS_PROPERTY, .name = njs_string("toISOString"), - .value = njs_native_function(njs_date_prototype_to_iso_string, 0), + .value = njs_native_function2(njs_date_prototype_to_string, 0, + NJS_DATE_FMT_TO_ISO_STRING), .writable = 1, .configurable = 1, }, From webmaster at numerica.cl Fri Nov 8 23:34:04 2019 From: webmaster at numerica.cl (Roberto Soto) Date: Fri, 8 Nov 2019 15:34:04 -0800 Subject: changeset 7591 Message-ID: Greetings, I have a question regarding the release life-cycle. For a specific use-case (which can be expanded on later), I'm relying on variable $proxy_protocol_server_addr, that was just introduced recently in changeset 7591. I see version was bumped to 1.17.6, but there is no tag in mercurial for that yet, so I supposed it hasn't been released? The question is rather simple: can I expect this version to be available soon? Otherwise I suppose one can always compile it from source into a new docker image. Reading the official Dockerfile, I guess compiling would be easier if the tag existed already, and even might need that a debian package was available for 1.17.6 version too, isn't it? --- Why do I need this variable? Explanation below: I use Nginx as a reverse proxy in a docker network. In this case, I'm experiencing with proxying radio streaming. I've found it is easy to do with the stream module. However, with it, I can not longer load balance based on the Host header. (Because stream assumes it is plain TCP, so server_name directive is not available. However, the stream in matter of fact is HTTP and does carry the header when reaching for the proxy) So instead the solution would be to use proxy_protocol, combining this variable with the map module, so that the upstream can be decided based on the requested host. So if the $proxy_protocol_server_addr doesn't actually do that, please let me know XD Thank you in advance Roberto From maxim at nginx.com Sat Nov 9 16:40:31 2019 From: maxim at nginx.com (Maxim Konovalov) Date: Sat, 9 Nov 2019 19:40:31 +0300 Subject: changeset 7591 In-Reply-To: References: Message-ID: <868e8a03-ba06-886e-4adf-31c248a855c1@nginx.com> Hi Roberto, On 09/11/2019 02:34, Roberto Soto wrote: > Greetings, I have a question regarding the release life-cycle. > > For a specific use-case (which can be expanded on later), I'm relying on > variable $proxy_protocol_server_addr, that was just introduced recently > in changeset 7591. > > I see version was bumped to 1.17.6, but there is no tag in mercurial for > that yet, so I supposed it hasn't been released? > > The question is rather simple: can I expect this version to be available > soon? > > Otherwise I suppose one can always compile it from source into a new > docker image. > > Reading the official Dockerfile, I guess compiling would be easier if > the tag existed already, and even might need that a debian package was > available for 1.17.6 version too, isn't it? > [...] We are planning to release 1.17.6 with these vars on 19th of November, see https://trac.nginx.org/nginx/roadmap -- Maxim Konovalov From zhangshaokun at hisilicon.com Mon Nov 11 03:07:02 2019 From: zhangshaokun at hisilicon.com (Zhangshaokun) Date: Mon, 11 Nov 2019 03:07:02 +0000 Subject: [PATCH] Optimal performance when use http non-persistent connection Message-ID: <07890E72BF5A6C44976E3EB9981B18780435D11E@dggeml527-mbx.china.huawei.com> # HG changeset patch # User Rui Sun > # Date 1572848389 -28800 # Mon Nov 04 14:19:49 2019 +0800 # Branch local # Node ID a5ae6e9e99f747fcb45082bac8795622938184f1 # Parent 89adf49fe76ada86d84e2af8f5cee9ca8c3dca19 Optimal performance when use http non-persistent connection diff -r 89adf49fe76a -r a5ae6e9e99f7 src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c Mon Oct 21 20:22:30 2019 +0300 +++ b/src/core/ngx_cycle.c Mon Nov 04 14:19:49 2019 +0800 @@ -35,6 +35,40 @@ /* STUB */ +void +ngx_change_pid_core(ngx_cycle_t *cycle) +{ + ngx_pid_t setpid; + ngx_cpuset_t *setaffinity=NULL; + setpid = ngx_getpid(); + { +#if (NGX_HAVE_CPU_AFFINITY) + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ccf->cpu_affinity == NULL) { + setaffinity = NULL; + } + + if (ccf->cpu_affinity_auto) { + setaffinity = NULL; + } + + setaffinity = &ccf->cpu_affinity[0]; + +#else + + setaffinity = NULL; + +#endif + } + + if (setaffinity) + // set new mask + sched_setaffinity(setpid, sizeof(ngx_cpuset_t), setaffinity); +} + ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) { @@ -278,6 +312,8 @@ return NULL; } + ngx_change_pid_core(cycle); + if (ngx_test_config && !ngx_quiet_mode) { ngx_log_stderr(0, "the configuration file %s syntax is ok", cycle->conf_file.data); ???????? Huawei Technologies Co., Ltd. [Company_logo] ________________________________ ???????????????????????????????????????? ???????????????????????????????????????? ??????????????????????????????????? This e-mail and its attachments contain confidential information from HUAWEI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it! -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: patch-Optimal_performance_when_use_http_non-persistent_connection.patch Type: application/octet-stream Size: 1540 bytes Desc: patch-Optimal_performance_when_use_http_non-persistent_connection.patch URL: From xeioex at nginx.com Mon Nov 11 12:37:55 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 11 Nov 2019 12:37:55 +0000 Subject: [njs] Style. Message-ID: details: https://hg.nginx.org/njs/rev/71decc0d345c branches: changeset: 1228:71decc0d345c user: Dmitry Volyntsev date: Fri Nov 08 20:33:46 2019 +0300 description: Style. diffstat: nginx/ngx_http_js_module.c | 2 +- src/njs_object_prop.c | 4 ++-- src/njs_value.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diffs (39 lines): diff -r de6c3c68e18e -r 71decc0d345c nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Fri Nov 08 16:29:34 2019 +0300 +++ b/nginx/ngx_http_js_module.c Fri Nov 08 20:33:46 2019 +0300 @@ -329,7 +329,7 @@ static njs_external_t ngx_http_js_ext_r NULL, NULL, NULL, - offsetof(ngx_http_request_t, headers_out.status) }, + 0 }, { njs_str("headersOut"), NJS_EXTERN_OBJECT, diff -r de6c3c68e18e -r 71decc0d345c src/njs_object_prop.c --- a/src/njs_object_prop.c Fri Nov 08 16:29:34 2019 +0300 +++ b/src/njs_object_prop.c Fri Nov 08 20:33:46 2019 +0300 @@ -585,9 +585,9 @@ static const njs_value_t njs_object_val static const njs_value_t njs_object_get_string = njs_string("get"); static const njs_value_t njs_object_set_string = njs_string("set"); static const njs_value_t njs_object_writable_string = - njs_string("writable"); + njs_string("writable"); static const njs_value_t njs_object_enumerable_string = - njs_string("enumerable"); + njs_string("enumerable"); static const njs_value_t njs_object_configurable_string = njs_string("configurable"); diff -r de6c3c68e18e -r 71decc0d345c src/njs_value.c --- a/src/njs_value.c Fri Nov 08 16:29:34 2019 +0300 +++ b/src/njs_value.c Fri Nov 08 20:33:46 2019 +0300 @@ -120,7 +120,7 @@ njs_value_release(njs_vm_t *vm, njs_valu njs_int_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, - njs_uint_t hint) + njs_uint_t hint) { njs_int_t ret; njs_uint_t tries; From xeioex at nginx.com Mon Nov 11 12:37:56 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 11 Nov 2019 12:37:56 +0000 Subject: [njs] Fixed sharing of externals between main and cloned VMs. Message-ID: details: https://hg.nginx.org/njs/rev/984520294a3c branches: changeset: 1229:984520294a3c user: Dmitry Volyntsev date: Mon Nov 11 15:37:08 2019 +0300 description: Fixed sharing of externals between main and cloned VMs. The issue was introduced in 7e7d0dac4572. This closes #245 issue on Github. diffstat: src/njs_extern.c | 17 ++++++++++++++--- src/test/njs_unit_test.c | 3 +++ 2 files changed, 17 insertions(+), 3 deletions(-) diffs (53 lines): diff -r 71decc0d345c -r 984520294a3c src/njs_extern.c --- a/src/njs_extern.c Fri Nov 08 20:33:46 2019 +0300 +++ b/src/njs_extern.c Mon Nov 11 15:37:08 2019 +0300 @@ -172,22 +172,33 @@ njs_vm_external_create(njs_vm_t *vm, njs const njs_extern_t *proto, njs_external_ptr_t object) { void *obj; + uint32_t n; njs_arr_t *externals; if (njs_slow_path(proto == NULL)) { return NJS_ERROR; } - if (njs_slow_path(vm->external_objects == NULL)) { - externals = njs_arr_create(vm->mem_pool, 4, sizeof(void *)); + if (vm->external_objects->mem_pool != vm->mem_pool) { + + /* Making a local copy of externals in shared VM. */ + + n = vm->external_objects->items; + + externals = njs_arr_create(vm->mem_pool, n + 4, sizeof(void *)); if (njs_slow_path(externals == NULL)) { return NJS_ERROR; } + if (n > 0) { + memcpy(externals->start, vm->external_objects->start, + n * sizeof(void *)); + externals->items = n; + } + vm->external_objects = externals; } - obj = njs_arr_add(vm->external_objects); if (njs_slow_path(obj == NULL)) { return NJS_ERROR; diff -r 71decc0d345c -r 984520294a3c src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Nov 08 20:33:46 2019 +0300 +++ b/src/test/njs_unit_test.c Mon Nov 11 15:37:08 2019 +0300 @@ -14694,6 +14694,9 @@ static njs_unit_test_t njs_shared_test[ { njs_str("isNaN(function(){})"), njs_str("true") }, + + { njs_str("var r; for (var i = 0; i < 2**10; i++) {r = $r.create('XXX').uri;}"), + njs_str("undefined") }, }; From xeioex at nginx.com Mon Nov 11 12:37:56 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 11 Nov 2019 12:37:56 +0000 Subject: [njs] Fixed a dead store. Message-ID: details: https://hg.nginx.org/njs/rev/b18f8180e6da branches: changeset: 1230:b18f8180e6da user: Dmitry Volyntsev date: Mon Nov 11 15:37:39 2019 +0300 description: Fixed a dead store. Found by Clang static analyzer. diffstat: src/njs_date.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-) diffs (19 lines): diff -r 984520294a3c -r b18f8180e6da src/njs_date.c --- a/src/njs_date.c Mon Nov 11 15:37:08 2019 +0300 +++ b/src/njs_date.c Mon Nov 11 15:37:39 2019 +0300 @@ -207,7 +207,6 @@ njs_year_from_days(int64_t *days) if (d1 < 0) { y--; - d1 += njs_days_in_year(y); } else { nd = njs_days_in_year(y); @@ -216,7 +215,6 @@ njs_year_from_days(int64_t *days) break; } - d1 -= nd; y++; } } From xeioex at nginx.com Mon Nov 11 17:24:01 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 11 Nov 2019 17:24:01 +0000 Subject: [njs] Fixed handling of NaN and -0 arguments in Math.min(), Math.max(). Message-ID: details: https://hg.nginx.org/njs/rev/0d119e8ce71a branches: changeset: 1231:0d119e8ce71a user: Artem S. Povalyukhin date: Thu Oct 31 22:18:41 2019 +0300 description: Fixed handling of NaN and -0 arguments in Math.min(), Math.max(). This closes #241 issue on Github. diffstat: src/njs_math.c | 82 +++++++++++++++++------------------------------ src/test/njs_unit_test.c | 24 ++++++++++++++ 2 files changed, 54 insertions(+), 52 deletions(-) diffs (178 lines): diff -r b18f8180e6da -r 0d119e8ce71a src/njs_math.c --- a/src/njs_math.c Mon Nov 11 15:37:39 2019 +0300 +++ b/src/njs_math.c Thu Oct 31 22:18:41 2019 +0300 @@ -628,75 +628,53 @@ njs_object_math_log2(njs_vm_t *vm, njs_v } -static njs_int_t -njs_object_math_max(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) +njs_inline double +njs_fmax(double x, double y) { - double num; - njs_int_t ret; - njs_uint_t i; - - if (nargs > 1) { - for (i = 1; i < nargs; i++) { - if (njs_is_undefined(&args[i])) { - num = NAN; - goto done; - - } else if (!njs_is_numeric(&args[i])) { - ret = njs_value_to_numeric(vm, &args[i], &args[i]); - if (ret != NJS_OK) { - return ret; - } - } - } - - num = njs_number(&args[1]); - - for (i = 2; i < nargs; i++) { - num = fmax(num, njs_number(&args[i])); - } - - } else { - num = -INFINITY; + if (x == 0 && y == 0) { + return signbit(x) ? y : x; } -done: + return fmax(x, y); +} + - njs_set_number(&vm->retval, num); +njs_inline double +njs_fmin(double x, double y) +{ + if (x == 0 && y == 0) { + return signbit(x) ? x : y; + } - return NJS_OK; + return fmin(x, y); } static njs_int_t -njs_object_math_min(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) +njs_object_math_min_max(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t max) { - double num; + double num, value; njs_int_t ret; njs_uint_t i; - if (nargs > 1) { - for (i = 1; i < nargs; i++) { - if (!njs_is_numeric(&args[i])) { - ret = njs_value_to_numeric(vm, &args[i], &args[i]); - if (ret != NJS_OK) { - return ret; - } - } + value = max ? -INFINITY : INFINITY; + + for (i = 1; i < nargs; i++) { + ret = njs_value_to_number(vm, &args[i], &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } - num = njs_number(&args[1]); - - for (i = 2; i < nargs; i++) { - num = fmin(num, njs_number(&args[i])); + if (njs_slow_path(isnan(num))) { + value = num; + break; } - } else { - num = INFINITY; + value = max ? njs_fmax(value, num) : njs_fmin(value, num); } - njs_set_number(&vm->retval, num); + njs_set_number(&vm->retval, value); return NJS_OK; } @@ -1221,7 +1199,7 @@ static const njs_object_prop_t njs_math { .type = NJS_PROPERTY, .name = njs_string("max"), - .value = njs_native_function(njs_object_math_max, 2), + .value = njs_native_function2(njs_object_math_min_max, 2, 1), .writable = 1, .configurable = 1, }, @@ -1229,7 +1207,7 @@ static const njs_object_prop_t njs_math { .type = NJS_PROPERTY, .name = njs_string("min"), - .value = njs_native_function(njs_object_math_min, 2), + .value = njs_native_function2(njs_object_math_min_max, 2, 0), .writable = 1, .configurable = 1, }, diff -r b18f8180e6da -r 0d119e8ce71a src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Mon Nov 11 15:37:39 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Oct 31 22:18:41 2019 +0300 @@ -12828,12 +12828,24 @@ static njs_unit_test_t njs_test[] = { njs_str("Math.max()"), njs_str("-Infinity") }, + { njs_str("Math.max(0, -0)"), + njs_str("0") }, + + { njs_str("Math.max(-0, 0)"), + njs_str("0") }, + { njs_str("Math.max(null)"), njs_str("0") }, { njs_str("Math.max(undefined)"), njs_str("NaN") }, + { njs_str("Math.max(1, 2, 3, undefined)"), + njs_str("NaN") }, + + { njs_str("Math.max(1, 2, 3, NaN)"), + njs_str("NaN") }, + { njs_str("Math.max('1', '2', '5')"), njs_str("5") }, @@ -12852,12 +12864,24 @@ static njs_unit_test_t njs_test[] = { njs_str("Math.min()"), njs_str("Infinity") }, + { njs_str("Math.min(0, -0)"), + njs_str("-0") }, + + { njs_str("Math.min(-0, 0)"), + njs_str("-0") }, + { njs_str("Math.min(null)"), njs_str("0") }, { njs_str("Math.min(undefined)"), njs_str("NaN") }, + { njs_str("Math.min(1, 2, 3, undefined)"), + njs_str("NaN") }, + + { njs_str("Math.min(1, 2, 3, NaN)"), + njs_str("NaN") }, + { njs_str("Math.min('1', '2', '5')"), njs_str("1") }, From xeioex at nginx.com Mon Nov 11 17:24:01 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 11 Nov 2019 17:24:01 +0000 Subject: [njs] Fixed Math.round() according to the specification. Message-ID: details: https://hg.nginx.org/njs/rev/fdd56914c9ca branches: changeset: 1232:fdd56914c9ca user: Artem S. Povalyukhin date: Sun Nov 10 01:35:02 2019 +0300 description: Fixed Math.round() according to the specification. This closes #246 issue on Github. diffstat: src/njs_diyfp.h | 31 ++++++++++++++++++------------- src/njs_main.h | 1 + src/njs_math.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- src/njs_number.c | 1 - src/test/njs_unit_test.c | 18 ++++++++++++++++++ 5 files changed, 81 insertions(+), 17 deletions(-) diffs (193 lines): diff -r 0d119e8ce71a -r fdd56914c9ca src/njs_diyfp.h --- a/src/njs_diyfp.h Thu Oct 31 22:18:41 2019 +0300 +++ b/src/njs_diyfp.h Sun Nov 10 01:35:02 2019 +0300 @@ -18,25 +18,34 @@ typedef struct { } njs_diyfp_t; +typedef union { + double d; + uint64_t u64; +} njs_diyfp_conv_t; + + #define njs_diyfp(_s, _e) (njs_diyfp_t) \ { .significand = (_s), .exp = (_e) } #define njs_uint64(h, l) (((uint64_t) (h) << 32) + (l)) #define NJS_DBL_SIGNIFICAND_SIZE 52 -#define NJS_DBL_EXPONENT_BIAS (0x3FF + NJS_DBL_SIGNIFICAND_SIZE) +#define NJS_DBL_EXPONENT_OFFSET ((int64_t) 0x3ff) +#define NJS_DBL_EXPONENT_BIAS (NJS_DBL_EXPONENT_OFFSET \ + + NJS_DBL_SIGNIFICAND_SIZE) #define NJS_DBL_EXPONENT_MIN (-NJS_DBL_EXPONENT_BIAS) -#define NJS_DBL_EXPONENT_MAX (0x7FF - NJS_DBL_EXPONENT_BIAS) +#define NJS_DBL_EXPONENT_MAX (0x7ff - NJS_DBL_EXPONENT_BIAS) #define NJS_DBL_EXPONENT_DENORMAL (-NJS_DBL_EXPONENT_BIAS + 1) #define NJS_DBL_SIGNIFICAND_MASK njs_uint64(0x000FFFFF, 0xFFFFFFFF) #define NJS_DBL_HIDDEN_BIT njs_uint64(0x00100000, 0x00000000) #define NJS_DBL_EXPONENT_MASK njs_uint64(0x7FF00000, 0x00000000) +#define NJS_DBL_SIGN_MASK njs_uint64(0x80000000, 0x00000000) #define NJS_DIYFP_SIGNIFICAND_SIZE 64 #define NJS_SIGNIFICAND_SIZE 53 -#define NJS_SIGNIFICAND_SHIFT (NJS_DIYFP_SIGNIFICAND_SIZE \ +#define NJS_SIGNIFICAND_SHIFT (NJS_DIYFP_SIGNIFICAND_SIZE \ - NJS_DBL_SIGNIFICAND_SIZE) #define NJS_DECIMAL_EXPONENT_OFF 348 @@ -78,13 +87,9 @@ njs_d2diyfp(double d) njs_inline double njs_diyfp2d(njs_diyfp_t v) { - int exp; - uint64_t significand, biased_exp; - - union { - double d; - uint64_t u64; - } u; + int exp; + uint64_t significand, biased_exp; + njs_diyfp_conv_t conv; exp = v.exp; significand = v.significand; @@ -118,10 +123,10 @@ njs_diyfp2d(njs_diyfp_t v) biased_exp = (uint64_t) (exp + NJS_DBL_EXPONENT_BIAS); } - u.u64 = (significand & NJS_DBL_SIGNIFICAND_MASK) - | (biased_exp << NJS_DBL_SIGNIFICAND_SIZE); + conv.u64 = (significand & NJS_DBL_SIGNIFICAND_MASK) + | (biased_exp << NJS_DBL_SIGNIFICAND_SIZE); - return u.d; + return conv.d; } diff -r 0d119e8ce71a -r fdd56914c9ca src/njs_main.h --- a/src/njs_main.h Thu Oct 31 22:18:41 2019 +0300 +++ b/src/njs_main.h Sun Nov 10 01:35:02 2019 +0300 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff -r 0d119e8ce71a -r fdd56914c9ca src/njs_math.c --- a/src/njs_math.c Thu Oct 31 22:18:41 2019 +0300 +++ b/src/njs_math.c Sun Nov 10 01:35:02 2019 +0300 @@ -746,21 +746,62 @@ static njs_int_t njs_object_math_round(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_int_t ret; + uint8_t sign; + uint64_t one, fraction_mask; + njs_int_t ret, biased_exp; + njs_diyfp_conv_t conv; if (njs_slow_path(nargs < 2)) { njs_set_number(&vm->retval, NAN); return NJS_OK; } - if (njs_slow_path(!njs_is_number(&args[1]))) { + if (njs_slow_path(!njs_is_numeric(&args[1]))) { ret = njs_value_to_numeric(vm, &args[1], &args[1]); if (njs_slow_path(ret != NJS_OK)) { return ret; } } - njs_set_number(&vm->retval, round(njs_number(&args[1]))); + conv.d = njs_number(&args[1]); + biased_exp = (conv.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE; + + if (biased_exp < NJS_DBL_EXPONENT_OFFSET) { + + /* |v| < 1. */ + + if (biased_exp == (NJS_DBL_EXPONENT_OFFSET - 1) + && conv.u64 != njs_uint64(0xbfe00000, 0x00000000)) + { + /* (|v| > 0.5 || v == 0.5) => +-1.0 */ + + conv.u64 = (conv.u64 & NJS_DBL_SIGN_MASK) + | (NJS_DBL_EXPONENT_OFFSET << NJS_DBL_SIGNIFICAND_SIZE); + + } else { + + /* (|v| < 0.5 || v == -0.5) => +-0. */ + + conv.u64 &= ((uint64_t) 1) << 63; + } + + } else if (biased_exp < NJS_DBL_EXPONENT_BIAS) { + + /* |v| <= 2^52 - 1 (largest safe integer). */ + + one = ((uint64_t) 1) << (NJS_DBL_EXPONENT_BIAS - biased_exp); + fraction_mask = one - 1; + + /* truncation. */ + + sign = conv.u64 >> 63; + conv.u64 += (one >> 1) - sign; + conv.u64 &= ~fraction_mask; + } + + /* |v| >= 2^52, Infinity and NaNs => v. */ + + njs_set_number(&vm->retval, conv.d); return NJS_OK; } diff -r 0d119e8ce71a -r fdd56914c9ca src/njs_number.c --- a/src/njs_number.c Thu Oct 31 22:18:41 2019 +0300 +++ b/src/njs_number.c Sun Nov 10 01:35:02 2019 +0300 @@ -6,7 +6,6 @@ #include -#include /* diff -r 0d119e8ce71a -r fdd56914c9ca src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Oct 31 22:18:41 2019 +0300 +++ b/src/test/njs_unit_test.c Sun Nov 10 01:35:02 2019 +0300 @@ -12995,8 +12995,26 @@ static njs_unit_test_t njs_test[] = njs_str("-0") }, { njs_str("Math.round(-0.5)"), + njs_str("-0") }, + + { njs_str("Math.round(-0.50000000000000001)"), + njs_str("-0") }, + + { njs_str("Math.round(-0.5000000000000001)"), njs_str("-1") }, + { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round((2**32) + v) - 2**32)"), + njs_str("1,1,1") }, + + { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round(-(2**32) + v) + 2**32)"), + njs_str("1,1,1") }, + + { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round((2**32) - v) - 2**32)"), + njs_str("-1,0,0") }, + + { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round(-(2**32) - v) + 2**32)"), + njs_str("-1,0,0") }, + { njs_str("Math.sign(5)"), njs_str("1") }, From webmaster at numerica.cl Mon Nov 11 20:55:42 2019 From: webmaster at numerica.cl (Roberto Soto) Date: Mon, 11 Nov 2019 12:55:42 -0800 Subject: changeset 7591 | accessing SNI from Stream module In-Reply-To: <868e8a03-ba06-886e-4adf-31c248a855c1@nginx.com> References: <868e8a03-ba06-886e-4adf-31c248a855c1@nginx.com> Message-ID: <482eeaf8-9ec0-24ec-bafe-86edbd9740d4@numerica.cl> Thank you for your answers, however, i'm still in doubt: What does the variable $proxy_protocol_server_addr carry exactly? I've been reading the code a bit, but cannot figure out if it is indeed what I'm looking for: Does it carry the server_name (SNI) requested by the client? Checking the Proxy Protocol preference, it seems that it does support SNI: "2.2.2 PP2_TYPE_AUTHORITY Contains the host name value passed by the client, as an UTF8-encoded string..." https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt Is it possible to access such information from Nginx's stream module? Best regards, Roberto On 11/9/19 8:40 AM, Maxim Konovalov wrote: > Hi Roberto, > > On 09/11/2019 02:34, Roberto Soto wrote: >> Greetings, I have a question regarding the release life-cycle. >> >> For a specific use-case (which can be expanded on later), I'm relying on >> variable $proxy_protocol_server_addr, that was just introduced recently >> in changeset 7591. >> >> I see version was bumped to 1.17.6, but there is no tag in mercurial for >> that yet, so I supposed it hasn't been released? >> >> The question is rather simple: can I expect this version to be available >> soon? >> >> Otherwise I suppose one can always compile it from source into a new >> docker image. >> >> Reading the official Dockerfile, I guess compiling would be easier if >> the tag existed already, and even might need that a debian package was >> available for 1.17.6 version too, isn't it? >> > [...] > > We are planning to release 1.17.6 with these vars on 19th of > November, see https://trac.nginx.org/nginx/roadmap > From xeioex at nginx.com Tue Nov 12 12:23:08 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 12 Nov 2019 12:23:08 +0000 Subject: [njs] Avoiding dynamic allocation in JSON methods. Message-ID: details: https://hg.nginx.org/njs/rev/6d1d0369b041 branches: changeset: 1233:6d1d0369b041 user: Dmitry Volyntsev date: Tue Nov 12 15:09:48 2019 +0300 description: Avoiding dynamic allocation in JSON methods. Before 540f03725df2 the parse/stringify state had to be allocated from dynamic memory because function calls to JS functions were asynchronous. diffstat: src/njs_json.c | 492 +++++++++++++++++++++++++------------------------------- 1 files changed, 216 insertions(+), 276 deletions(-) diffs (931 lines): diff -r fdd56914c9ca -r 6d1d0369b041 src/njs_json.c --- a/src/njs_json.c Sun Nov 10 01:35:02 2019 +0300 +++ b/src/njs_json.c Tue Nov 12 15:09:48 2019 +0300 @@ -42,8 +42,11 @@ typedef struct { typedef struct { njs_value_t retval; - njs_arr_t stack; + njs_uint_t depth; njs_json_state_t *state; +#define NJS_JSON_MAX_DEPTH 32 + njs_json_state_t states[NJS_JSON_MAX_DEPTH]; + njs_function_t *function; } njs_json_parse_t; @@ -66,11 +69,14 @@ typedef struct { njs_mp_t *pool; njs_chb_node_t *nodes; njs_chb_node_t *last; - njs_arr_t stack; + + njs_uint_t depth; njs_json_state_t *state; + njs_json_state_t states[NJS_JSON_MAX_DEPTH]; njs_value_t replacer; njs_str_t space; + u_char space_buf[16]; } njs_json_stringify_t; @@ -88,17 +94,15 @@ njs_inline uint32_t njs_json_unicode(con static const u_char *njs_json_skip_space(const u_char *start, const u_char *end); -static njs_int_t njs_json_parse_iterator(njs_vm_t *vm, njs_json_parse_t *parse); +static njs_int_t njs_json_parse_iterator(njs_vm_t *vm, njs_json_parse_t *parse, + njs_value_t *value); static njs_int_t njs_json_parse_iterator_call(njs_vm_t *vm, njs_json_parse_t *parse); -static njs_json_state_t *njs_json_push_parse_state(njs_vm_t *vm, - njs_json_parse_t *parse, njs_value_t *value); -static njs_json_state_t *njs_json_pop_parse_state(njs_json_parse_t *parse); static void njs_json_parse_exception(njs_json_parse_ctx_t *ctx, const char *msg, const u_char *pos); static njs_int_t njs_json_stringify_iterator(njs_vm_t *vm, - njs_json_stringify_t *stringify); + njs_json_stringify_t *stringify, njs_value_t *value); static njs_function_t *njs_object_to_json_function(njs_vm_t *vm, njs_value_t *value); static njs_int_t njs_json_stringify_to_json(njs_vm_t *vm, @@ -108,10 +112,6 @@ static njs_int_t njs_json_stringify_repl njs_json_stringify_t* stringify, njs_value_t *key, njs_value_t *value); static njs_int_t njs_json_stringify_array(njs_vm_t *vm, njs_json_stringify_t *stringify); -static njs_json_state_t *njs_json_push_stringify_state(njs_vm_t *vm, - njs_json_stringify_t *stringify, const njs_value_t *value); -static njs_json_state_t *njs_json_pop_stringify_state( - njs_json_stringify_t *stringify); static njs_int_t njs_json_append_value(njs_json_stringify_t *stringify, const njs_value_t *value); @@ -120,7 +120,8 @@ static njs_int_t njs_json_append_string( static njs_int_t njs_json_append_number(njs_json_stringify_t *stringify, const njs_value_t *value); -static njs_value_t *njs_json_wrap_value(njs_vm_t *vm, const njs_value_t *value); +static njs_object_t *njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper, + const njs_value_t *value); #define NJS_JSON_BUF_MIN_SIZE 128 @@ -147,7 +148,7 @@ njs_json_parse(njs_vm_t *vm, njs_value_t njs_index_t unused) { njs_int_t ret; - njs_value_t *text, *value, *wrapper, lvalue; + njs_value_t *text, value, lvalue; const u_char *p, *end; njs_json_parse_t *parse, json_parse; const njs_value_t *reviver; @@ -156,12 +157,6 @@ njs_json_parse(njs_vm_t *vm, njs_value_t parse = &json_parse; - value = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); - if (njs_slow_path(value == NULL)) { - njs_memory_error(vm); - return NJS_ERROR; - } - text = njs_lvalue_arg(&lvalue, args, nargs, 1); if (njs_slow_path(!njs_is_string(text))) { @@ -178,7 +173,7 @@ njs_json_parse(njs_vm_t *vm, njs_value_t ctx.vm = vm; ctx.pool = vm->mem_pool; - ctx.depth = 32; + ctx.depth = NJS_JSON_MAX_DEPTH; ctx.start = string.start; ctx.end = end; @@ -188,7 +183,7 @@ njs_json_parse(njs_vm_t *vm, njs_value_t return NJS_ERROR; } - p = njs_json_parse_value(&ctx, value, p); + p = njs_json_parse_value(&ctx, &value, p); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } @@ -201,37 +196,16 @@ njs_json_parse(njs_vm_t *vm, njs_value_t reviver = njs_arg(args, nargs, 2); - if (njs_is_function(reviver) && njs_is_object(value)) { - wrapper = njs_json_wrap_value(vm, value); - if (njs_slow_path(wrapper == NULL)) { - goto memory_error; - } - + if (njs_is_function(reviver) && njs_is_object(&value)) { parse->function = njs_function(reviver); - - if (njs_arr_init(vm->mem_pool, &parse->stack, NULL, 4, - sizeof(njs_json_state_t)) - == NULL) - { - goto memory_error; - } - - if (njs_json_push_parse_state(vm, parse, wrapper) == NULL) { - goto memory_error; - } - - return njs_json_parse_iterator(vm, parse); + parse->depth = 0; + + return njs_json_parse_iterator(vm, parse, &value); } - vm->retval = *value; + vm->retval = value; return NJS_OK; - -memory_error: - - njs_memory_error(vm); - - return NJS_ERROR; } @@ -253,13 +227,14 @@ njs_json_stringify(njs_vm_t *vm, njs_val double num; njs_int_t i; njs_int_t ret; - njs_value_t *wrapper, *replacer, *space; + njs_value_t *replacer, *space; njs_json_stringify_t *stringify, json_stringify; stringify = &json_stringify; stringify->vm = vm; stringify->pool = vm->mem_pool; + stringify->depth = 0; stringify->nodes = NULL; stringify->last = NULL; @@ -289,15 +264,12 @@ njs_json_stringify(njs_vm_t *vm, njs_val } else { num = njs_number(space); + if (!isnan(num) && !isinf(num) && num > 0) { num = njs_min(num, 10); stringify->space.length = (size_t) num; - stringify->space.start = njs_mp_alloc(vm->mem_pool, - (size_t) num + 1); - if (njs_slow_path(stringify->space.start == NULL)) { - goto memory_error; - } + stringify->space.start = stringify->space_buf; for (i = 0; i < (int) num; i++) { stringify->space.start[i] = ' '; @@ -306,23 +278,7 @@ njs_json_stringify(njs_vm_t *vm, njs_val } } - if (njs_arr_init(vm->mem_pool, &stringify->stack, NULL, 4, - sizeof(njs_json_state_t)) - == NULL) - { - goto memory_error; - } - - wrapper = njs_json_wrap_value(vm, njs_arg(args, nargs, 1)); - if (njs_slow_path(wrapper == NULL)) { - goto memory_error; - } - - if (njs_json_push_stringify_state(vm, stringify, wrapper) == NULL) { - goto memory_error; - } - - return njs_json_stringify_iterator(vm, stringify); + return njs_json_stringify_iterator(vm, stringify, njs_arg(args, nargs, 1)); memory_error: @@ -403,7 +359,7 @@ njs_json_parse_object(njs_json_parse_ctx { njs_int_t ret; njs_object_t *object; - njs_value_t *prop_name, *prop_value; + njs_value_t prop_name, prop_value; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; @@ -438,12 +394,7 @@ njs_json_parse_object(njs_json_parse_ctx goto error_token; } - prop_name = njs_mp_alloc(ctx->pool, sizeof(njs_value_t)); - if (njs_slow_path(prop_name == NULL)) { - goto memory_error; - } - - p = njs_json_parse_string(ctx, prop_name, p); + p = njs_json_parse_string(ctx, &prop_name, p); if (njs_slow_path(p == NULL)) { /* The exception is set by the called function. */ return NULL; @@ -459,23 +410,18 @@ njs_json_parse_object(njs_json_parse_ctx goto error_end; } - prop_value = njs_mp_alloc(ctx->pool, sizeof(njs_value_t)); - if (njs_slow_path(prop_value == NULL)) { - goto memory_error; - } - - p = njs_json_parse_value(ctx, prop_value, p); + p = njs_json_parse_value(ctx, &prop_value, p); if (njs_slow_path(p == NULL)) { /* The exception is set by the called function. */ return NULL; } - prop = njs_object_prop_alloc(ctx->vm, prop_name, prop_value, 1); + prop = njs_object_prop_alloc(ctx->vm, &prop_name, &prop_value, 1); if (njs_slow_path(prop == NULL)) { goto memory_error; } - njs_string_get(prop_name, &lhq.key); + njs_string_get(&prop_name, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.value = prop; lhq.replace = 1; @@ -533,8 +479,9 @@ njs_json_parse_array(njs_json_parse_ctx_ const u_char *p) { njs_int_t ret; + njs_bool_t empty; njs_array_t *array; - njs_value_t *element; + njs_value_t element; if (njs_slow_path(--ctx->depth == 0)) { njs_json_parse_exception(ctx, "Nested too deep", p); @@ -546,7 +493,7 @@ njs_json_parse_array(njs_json_parse_ctx_ return NULL; } - element = NULL; + empty = 1; for ( ;; ) { p = njs_json_skip_space(p + 1, ctx->end); @@ -555,7 +502,7 @@ njs_json_parse_array(njs_json_parse_ctx_ } if (*p == ']') { - if (njs_slow_path(element != NULL)) { + if (njs_slow_path(!empty)) { njs_json_parse_exception(ctx, "Trailing comma", p - 1); return NULL; } @@ -563,22 +510,18 @@ njs_json_parse_array(njs_json_parse_ctx_ break; } - element = njs_mp_alloc(ctx->pool, sizeof(njs_value_t)); - if (njs_slow_path(element == NULL)) { - njs_memory_error(ctx->vm); - return NULL; - } - - p = njs_json_parse_value(ctx, element, p); + p = njs_json_parse_value(ctx, &element, p); if (njs_slow_path(p == NULL)) { return NULL; } - ret = njs_array_add(ctx->vm, array, element); + ret = njs_array_add(ctx->vm, array, &element); if (njs_slow_path(ret != NJS_OK)) { return NULL; } + empty = 0; + p = njs_json_skip_space(p, ctx->end); if (njs_slow_path(p == ctx->end)) { goto error_end; @@ -913,6 +856,52 @@ njs_json_skip_space(const u_char *start, } +static njs_json_state_t * +njs_json_push_parse_state(njs_vm_t *vm, njs_json_parse_t *parse, + njs_value_t *value) +{ + njs_json_state_t *state; + + if (njs_slow_path(parse->depth >= NJS_JSON_MAX_DEPTH)) { + njs_type_error(vm, "Nested too deep or a cyclic structure"); + return NULL; + } + + state = &parse->states[parse->depth++]; + state->value = *value; + state->index = 0; + + if (njs_is_array(value)) { + state->type = NJS_JSON_ARRAY_START; + + } else { + state->type = NJS_JSON_OBJECT_START; + state->prop_value = NULL; + state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0); + if (state->keys == NULL) { + return NULL; + } + } + + parse->state = state; + + return state; +} + + +njs_inline njs_json_state_t * +njs_json_pop_parse_state(njs_json_parse_t *parse) +{ + if (parse->depth > 1) { + parse->depth--; + parse->state = &parse->states[parse->depth - 1]; + return parse->state; + } + + return NULL; +} + + #define njs_json_is_non_empty(_value) \ (((_value)->type == NJS_OBJECT) \ && !njs_lvlhsh_is_empty(njs_object_hash(_value))) \ @@ -920,14 +909,26 @@ njs_json_skip_space(const u_char *start, static njs_int_t -njs_json_parse_iterator(njs_vm_t *vm, njs_json_parse_t *parse) +njs_json_parse_iterator(njs_vm_t *vm, njs_json_parse_t *parse, + njs_value_t *value) { njs_int_t ret; - njs_value_t *key, *value; + njs_value_t *key, *retval, wrapper; + njs_object_t *object; njs_json_state_t *state; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; + object = njs_json_wrap_value(vm, &wrapper, value); + if (njs_slow_path(object == NULL)) { + goto memory_error; + } + + state = njs_json_push_parse_state(vm, parse, &wrapper); + if (njs_slow_path(state == NULL)) { + goto memory_error; + } + start: state = parse->state; @@ -969,7 +970,6 @@ start: state = njs_json_pop_parse_state(parse); if (state == NULL) { vm->retval = parse->retval; - return NJS_OK; } } @@ -1014,10 +1014,10 @@ start: case NJS_JSON_ARRAY_START: if (state->index < njs_array_len(&state->value)) { - value = &njs_array_start(&state->value)[state->index]; - - if (njs_json_is_non_empty(value)) { - state = njs_json_push_parse_state(vm, parse, value); + retval = &njs_array_start(&state->value)[state->index]; + + if (njs_json_is_non_empty(retval)) { + state = njs_json_push_parse_state(vm, parse, retval); if (state == NULL) { goto memory_error; } @@ -1037,8 +1037,8 @@ start: goto start; case NJS_JSON_ARRAY_REPLACED: - value = &njs_array_start(&state->value)[state->index]; - *value = parse->retval; + retval = &njs_array_start(&state->value)[state->index]; + *retval = parse->retval; state->index++; state->type = NJS_JSON_ARRAY_START; @@ -1098,50 +1098,6 @@ njs_json_parse_iterator_call(njs_vm_t *v } -static njs_json_state_t * -njs_json_push_parse_state(njs_vm_t *vm, njs_json_parse_t *parse, - njs_value_t *value) -{ - njs_json_state_t *state; - - state = njs_arr_add(&parse->stack); - if (state != NULL) { - state = njs_arr_last(&parse->stack); - state->value = *value; - state->index = 0; - - if (njs_is_array(value)) { - state->type = NJS_JSON_ARRAY_START; - - } else { - state->type = NJS_JSON_OBJECT_START; - state->prop_value = NULL; - state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0); - if (state->keys == NULL) { - return NULL; - } - } - } - - parse->state = state; - - return state; -} - - -static njs_json_state_t * -njs_json_pop_parse_state(njs_json_parse_t *parse) -{ - if (parse->stack.items > 1) { - parse->stack.items--; - parse->state = njs_arr_last(&parse->stack); - return parse->state; - } - - return NULL; -} - - static void njs_json_parse_exception(njs_json_parse_ctx_t *ctx, const char *msg, const u_char *pos) @@ -1157,6 +1113,67 @@ njs_json_parse_exception(njs_json_parse_ } +static njs_json_state_t * +njs_json_push_stringify_state(njs_vm_t *vm, njs_json_stringify_t *stringify, + const njs_value_t *value) +{ + njs_json_state_t *state; + + if (njs_slow_path(stringify->depth >= NJS_JSON_MAX_DEPTH)) { + njs_type_error(vm, "Nested too deep or a cyclic structure"); + return NULL; + } + + state = &stringify->states[stringify->depth++]; + state->value = *value; + state->index = 0; + state->written = 0; + + if (njs_is_array(value)) { + state->type = NJS_JSON_ARRAY_START; + + } else { + state->type = NJS_JSON_OBJECT_START; + state->prop_value = NULL; + + if (njs_is_array(&stringify->replacer)) { + state->keys = njs_array(&stringify->replacer); + + } else { + if (njs_is_external(value)) { + state->keys = njs_extern_keys_array(vm, value->external.proto); + + } else { + state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + 0); + } + + if (njs_slow_path(state->keys == NULL)) { + return NULL; + } + } + } + + stringify->state = state; + + return state; +} + + +njs_inline njs_json_state_t * +njs_json_pop_stringify_state(njs_json_stringify_t *stringify) +{ + if (stringify->depth > 1) { + stringify->depth--; + stringify->state = &stringify->states[stringify->depth - 1]; + stringify->state->written = 1; + return stringify->state; + } + + return NULL; +} + + #define njs_json_is_object(value) \ (((value)->type == NJS_OBJECT) \ || ((value)->type == NJS_ARRAY) \ @@ -1183,7 +1200,7 @@ njs_json_parse_exception(njs_json_parse_ #define njs_json_stringify_append_key(key) \ if (state->written) { \ njs_json_stringify_append(",", 1); \ - njs_json_stringify_indent(stringify->stack.items); \ + njs_json_stringify_indent(stringify->depth); \ } \ \ state->written = 1; \ @@ -1207,7 +1224,8 @@ njs_json_parse_exception(njs_json_parse_ static njs_int_t -njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify) +njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify, + njs_value_t *value) { u_char *start; size_t size; @@ -1215,12 +1233,23 @@ njs_json_stringify_iterator(njs_vm_t *vm njs_int_t i; njs_int_t ret; njs_str_t str; - njs_value_t *key, *value; + njs_value_t *key, *retval, wrapper; + njs_object_t *object; njs_function_t *to_json; njs_json_state_t *state; njs_object_prop_t *prop, scratch; njs_lvlhsh_query_t lhq; + object = njs_json_wrap_value(vm, &wrapper, value); + if (njs_slow_path(object == NULL)) { + goto memory_error; + } + + state = njs_json_push_stringify_state(vm, stringify, &wrapper); + if (njs_slow_path(state == NULL)) { + goto memory_error; + } + start: state = stringify->state; @@ -1229,14 +1258,14 @@ start: switch (state->type) { case NJS_JSON_OBJECT_START: njs_json_stringify_append("{", 1); - njs_json_stringify_indent(stringify->stack.items); + njs_json_stringify_indent(stringify->depth); state->type = NJS_JSON_OBJECT_CONTINUE; /* Fall through. */ case NJS_JSON_OBJECT_CONTINUE: if (state->index >= state->keys->length) { - njs_json_stringify_indent(stringify->stack.items - 1); + njs_json_stringify_indent(stringify->depth - 1); njs_json_stringify_append("}", 1); state = njs_json_pop_stringify_state(stringify); @@ -1352,9 +1381,9 @@ start: njs_json_stringify_append_key(&stringify->key); - value = &stringify->retval; - if (njs_is_object(value)) { - state = njs_json_push_stringify_state(vm, stringify, value); + retval = &stringify->retval; + if (njs_is_object(retval)) { + state = njs_json_push_stringify_state(vm, stringify, retval); if (state == NULL) { return NJS_ERROR; } @@ -1362,20 +1391,20 @@ start: break; } - njs_json_stringify_append_value(value); + njs_json_stringify_append_value(retval); break; case NJS_JSON_ARRAY_START: njs_json_stringify_append("[", 1); - njs_json_stringify_indent(stringify->stack.items); + njs_json_stringify_indent(stringify->depth); state->type = NJS_JSON_ARRAY_CONTINUE; /* Fall through. */ case NJS_JSON_ARRAY_CONTINUE: if (state->index >= njs_array_len(&state->value)) { - njs_json_stringify_indent(stringify->stack.items - 1); + njs_json_stringify_indent(stringify->depth - 1); njs_json_stringify_append("]", 1); state = njs_json_pop_stringify_state(stringify); @@ -1388,16 +1417,16 @@ start: if (state->written) { njs_json_stringify_append(",", 1); - njs_json_stringify_indent(stringify->stack.items); + njs_json_stringify_indent(stringify->depth); } - value = &njs_array_start(&state->value)[state->index++]; - - if (njs_is_object(value)) { - to_json = njs_object_to_json_function(vm, value); + retval = &njs_array_start(&state->value)[state->index++]; + + if (njs_is_object(retval)) { + to_json = njs_object_to_json_function(vm, retval); if (to_json != NULL) { ret = njs_json_stringify_to_json(vm, stringify, to_json, - NULL, value); + NULL, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -1407,7 +1436,7 @@ start: } if (njs_is_function(&stringify->replacer)) { - ret = njs_json_stringify_replacer(vm, stringify, NULL, value); + ret = njs_json_stringify_replacer(vm, stringify, NULL, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -1415,8 +1444,8 @@ start: goto start; } - if (njs_json_is_object(value)) { - state = njs_json_push_stringify_state(vm, stringify, value); + if (njs_json_is_object(retval)) { + state = njs_json_push_stringify_state(vm, stringify, retval); if (state == NULL) { return NJS_ERROR; } @@ -1424,7 +1453,7 @@ start: break; } - njs_json_stringify_append_value(value); + njs_json_stringify_append_value(retval); break; @@ -1699,73 +1728,6 @@ njs_json_stringify_array(njs_vm_t *vm, n } -static njs_json_state_t * -njs_json_push_stringify_state(njs_vm_t *vm, njs_json_stringify_t *stringify, - const njs_value_t *value) -{ - njs_json_state_t *state; - - if (stringify->stack.items >= 32) { - njs_type_error(stringify->vm, - "Nested too deep or a cyclic structure"); - return NULL; - } - - state = njs_arr_add(&stringify->stack); - if (njs_slow_path(state == NULL)) { - njs_memory_error(vm); - return NULL; - } - - state = njs_arr_last(&stringify->stack); - state->value = *value; - state->index = 0; - state->written = 0; - - if (njs_is_array(value)) { - state->type = NJS_JSON_ARRAY_START; - - } else { - state->type = NJS_JSON_OBJECT_START; - state->prop_value = NULL; - - if (njs_is_array(&stringify->replacer)) { - state->keys = njs_array(&stringify->replacer); - - } else { - if (njs_is_external(value)) { - state->keys = njs_extern_keys_array(vm, value->external.proto); - - } else { - state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, - 0); - } - - if (state->keys == NULL) { - return NULL; - } - } - } - - stringify->state = state; - return state; -} - - -static njs_json_state_t * -njs_json_pop_stringify_state(njs_json_stringify_t *stringify) -{ - if (stringify->stack.items > 1) { - stringify->stack.items--; - stringify->state = njs_arr_last(&stringify->stack); - stringify->state->written = 1; - return stringify->state; - } - - return NULL; -} - - static njs_int_t njs_json_append_value(njs_json_stringify_t *stringify, const njs_value_t *value) { @@ -1945,19 +1907,14 @@ njs_json_append_number(njs_json_stringif /* * Wraps a value as '{"": }'. */ -static njs_value_t * -njs_json_wrap_value(njs_vm_t *vm, const njs_value_t *value) +static njs_object_t * +njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper, + const njs_value_t *value) { njs_int_t ret; - njs_value_t *wrapper; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; - wrapper = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); - if (njs_slow_path(wrapper == NULL)) { - return NULL; - } - wrapper->data.u.object = njs_object_alloc(vm); if (njs_slow_path(njs_object(wrapper) == NULL)) { return NULL; @@ -1984,7 +1941,7 @@ njs_json_wrap_value(njs_vm_t *vm, const return NULL; } - return wrapper; + return wrapper->data.u.object; } @@ -2174,7 +2131,7 @@ njs_dump_value(njs_json_stringify_t *str case NJS_STRING: njs_string_get(value, &str); - if (!console || stringify->stack.items != 0) { + if (!console || stringify->depth != 0) { return njs_json_append_string(stringify, value, '\''); } @@ -2384,7 +2341,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ njs_json_state_t *state; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; - njs_json_stringify_t *stringify; + njs_json_stringify_t *stringify, dump_stringify; const njs_value_t string_get = njs_string("[Getter]"); const njs_value_t string_set = njs_string("[Setter]"); @@ -2394,17 +2351,13 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ goto exception; } - stringify = njs_mp_alloc(vm->mem_pool, sizeof(njs_json_stringify_t)); - - if (njs_slow_path(stringify == NULL)) { - goto memory_error; - } + stringify = &dump_stringify; stringify->vm = vm; stringify->pool = vm->mem_pool; + stringify->depth = 0; stringify->nodes = NULL; stringify->last = NULL; - stringify->stack.items = 0; njs_set_undefined(&stringify->replacer); if (!njs_dump_is_object(value)) { @@ -2416,39 +2369,26 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ goto done; } + indent = njs_min(indent, 10); stringify->space.length = indent; - stringify->space.start = njs_mp_alloc(vm->mem_pool, indent); - if (njs_slow_path(stringify->space.start == NULL)) { - goto memory_error; - } + stringify->space.start = stringify->space_buf; njs_memset(stringify->space.start, ' ', indent); - if (njs_arr_init(vm->mem_pool, &stringify->stack, NULL, 4, - sizeof(njs_json_state_t)) - == NULL) - { - goto memory_error; - } - - if (njs_json_push_stringify_state(vm, stringify, value) == NULL) { - goto memory_error; - } - - state = stringify->state; + state = njs_json_push_stringify_state(vm, stringify, value); for ( ;; ) { switch (state->type) { case NJS_JSON_OBJECT_START: njs_json_stringify_append("{", 1); - njs_json_stringify_indent(stringify->stack.items + 1); + njs_json_stringify_indent(stringify->depth + 1); state->type = NJS_JSON_OBJECT_CONTINUE; /* Fall through. */ case NJS_JSON_OBJECT_CONTINUE: if (state->index >= state->keys->length) { - njs_json_stringify_indent(stringify->stack.items); + njs_json_stringify_indent(stringify->depth); njs_json_stringify_append("}", 1); state = njs_json_pop_stringify_state(stringify); @@ -2512,7 +2452,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ if (state->written) { njs_json_stringify_append(",", 1); - njs_json_stringify_indent(stringify->stack.items + 1); + njs_json_stringify_indent(stringify->depth + 1); } state->written = 1; @@ -2537,14 +2477,14 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ case NJS_JSON_ARRAY_START: njs_json_stringify_append("[", 1); - njs_json_stringify_indent(stringify->stack.items + 1); + njs_json_stringify_indent(stringify->depth + 1); state->type = NJS_JSON_ARRAY_CONTINUE; /* Fall through. */ case NJS_JSON_ARRAY_CONTINUE: if (state->index >= njs_array_len(&state->value)) { - njs_json_stringify_indent(stringify->stack.items); + njs_json_stringify_indent(stringify->depth); njs_json_stringify_append("]", 1); state = njs_json_pop_stringify_state(stringify); @@ -2557,7 +2497,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ if (state->written) { njs_json_stringify_append(",", 1); - njs_json_stringify_indent(stringify->stack.items + 1); + njs_json_stringify_indent(stringify->depth + 1); } val = &njs_array_start(&state->value)[state->index++]; From xeioex at nginx.com Wed Nov 13 12:20:08 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 13 Nov 2019 12:20:08 +0000 Subject: [njs] Refactored JSON methods. Message-ID: details: https://hg.nginx.org/njs/rev/a6aef1897687 branches: changeset: 1234:a6aef1897687 user: Dmitry Volyntsev date: Wed Nov 13 15:19:49 2019 +0300 description: Refactored JSON methods. Before 540f03725df2 the parse/stringify methods had to store their local context in dynamic memory because each JS function invocation interrupted them. Since JS function calls are synchronous now the code can be greatly simplified. diffstat: src/njs_json.c | 585 ++++++++++++++-------------------------------- src/test/njs_unit_test.c | 10 + 2 files changed, 196 insertions(+), 399 deletions(-) diffs (961 lines): diff -r 6d1d0369b041 -r a6aef1897687 src/njs_json.c --- a/src/njs_json.c Tue Nov 12 15:09:48 2019 +0300 +++ b/src/njs_json.c Wed Nov 13 15:19:49 2019 +0300 @@ -23,19 +23,13 @@ typedef struct { uint8_t written; /* 1 bit */ enum { - NJS_JSON_OBJECT_START, - NJS_JSON_OBJECT_CONTINUE, - NJS_JSON_OBJECT_TO_JSON_REPLACED, - NJS_JSON_OBJECT_REPLACED, - NJS_JSON_ARRAY_START, - NJS_JSON_ARRAY_CONTINUE, - NJS_JSON_ARRAY_TO_JSON_REPLACED, - NJS_JSON_ARRAY_REPLACED + NJS_JSON_OBJECT, + NJS_JSON_ARRAY, } type:8; uint32_t index; njs_array_t *keys; - njs_value_t *prop_value; + njs_object_prop_t *prop; } njs_json_state_t; @@ -43,7 +37,6 @@ typedef struct { njs_value_t retval; njs_uint_t depth; - njs_json_state_t *state; #define NJS_JSON_MAX_DEPTH 32 njs_json_state_t states[NJS_JSON_MAX_DEPTH]; @@ -63,7 +56,6 @@ struct njs_chb_node_s { typedef struct { njs_value_t retval; - njs_value_t key; njs_vm_t *vm; njs_mp_t *pool; @@ -71,7 +63,6 @@ typedef struct { njs_chb_node_t *last; njs_uint_t depth; - njs_json_state_t *state; njs_json_state_t states[NJS_JSON_MAX_DEPTH]; njs_value_t replacer; @@ -97,7 +88,7 @@ static const u_char *njs_json_skip_space static njs_int_t njs_json_parse_iterator(njs_vm_t *vm, njs_json_parse_t *parse, njs_value_t *value); static njs_int_t njs_json_parse_iterator_call(njs_vm_t *vm, - njs_json_parse_t *parse); + njs_json_parse_t *parse, njs_json_state_t *state); static void njs_json_parse_exception(njs_json_parse_ctx_t *ctx, const char *msg, const u_char *pos); @@ -105,11 +96,10 @@ static njs_int_t njs_json_stringify_iter njs_json_stringify_t *stringify, njs_value_t *value); static njs_function_t *njs_object_to_json_function(njs_vm_t *vm, njs_value_t *value); -static njs_int_t njs_json_stringify_to_json(njs_vm_t *vm, - njs_json_stringify_t* stringify, njs_function_t *function, - njs_value_t *key, njs_value_t *value); -static njs_int_t njs_json_stringify_replacer(njs_vm_t *vm, - njs_json_stringify_t* stringify, njs_value_t *key, njs_value_t *value); +static njs_int_t njs_json_stringify_to_json(njs_json_stringify_t* stringify, + njs_json_state_t *state, njs_value_t *key, njs_value_t *value); +static njs_int_t njs_json_stringify_replacer(njs_json_stringify_t* stringify, + njs_json_state_t *state, njs_value_t *key, njs_value_t *value); static njs_int_t njs_json_stringify_array(njs_vm_t *vm, njs_json_stringify_t *stringify); @@ -872,19 +862,17 @@ njs_json_push_parse_state(njs_vm_t *vm, state->index = 0; if (njs_is_array(value)) { - state->type = NJS_JSON_ARRAY_START; + state->type = NJS_JSON_ARRAY; } else { - state->type = NJS_JSON_OBJECT_START; - state->prop_value = NULL; + state->type = NJS_JSON_OBJECT; + state->prop = NULL; state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0); if (state->keys == NULL) { return NULL; } } - parse->state = state; - return state; } @@ -894,8 +882,7 @@ njs_json_pop_parse_state(njs_json_parse_ { if (parse->depth > 1) { parse->depth--; - parse->state = &parse->states[parse->depth - 1]; - return parse->state; + return &parse->states[parse->depth - 1]; } return NULL; @@ -903,24 +890,23 @@ njs_json_pop_parse_state(njs_json_parse_ #define njs_json_is_non_empty(_value) \ - (((_value)->type == NJS_OBJECT) \ - && !njs_lvlhsh_is_empty(njs_object_hash(_value))) \ - || (((_value)->type == NJS_ARRAY) && njs_array_len(_value) != 0) + ((njs_is_object(_value) && !njs_object_hash_is_empty(_value)) \ + || (njs_is_array(_value) && njs_array_len(_value) != 0)) static njs_int_t njs_json_parse_iterator(njs_vm_t *vm, njs_json_parse_t *parse, - njs_value_t *value) + njs_value_t *object) { njs_int_t ret; - njs_value_t *key, *retval, wrapper; - njs_object_t *object; + njs_value_t *key, *value, wrapper; + njs_object_t *obj; njs_json_state_t *state; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; - object = njs_json_wrap_value(vm, &wrapper, value); - if (njs_slow_path(object == NULL)) { + obj = njs_json_wrap_value(vm, &wrapper, object); + if (njs_slow_path(obj == NULL)) { goto memory_error; } @@ -929,18 +915,16 @@ njs_json_parse_iterator(njs_vm_t *vm, nj goto memory_error; } -start: - - state = parse->state; + lhq.proto = &njs_object_hash_proto; for ( ;; ) { + switch (state->type) { - case NJS_JSON_OBJECT_START: + case NJS_JSON_OBJECT: if (state->index < state->keys->length) { key = &state->keys->start[state->index]; njs_string_get(key, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); - lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_find(njs_object_hash(&state->value), &lhq); if (njs_slow_path(ret == NJS_DECLINED)) { @@ -955,7 +939,7 @@ start: break; } - state->prop_value = &prop->value; + state->prop = prop; if (njs_json_is_non_empty(&prop->value)) { state = njs_json_push_parse_state(vm, parse, &prop->value); @@ -974,50 +958,19 @@ start: } } - ret = njs_json_parse_iterator_call(vm, parse); + ret = njs_json_parse_iterator_call(vm, parse, state); if (njs_slow_path(ret != NJS_OK)) { return ret; } - goto start; - - case NJS_JSON_OBJECT_REPLACED: - key = &state->keys->start[state->index]; - njs_string_get(key, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); - lhq.replace = 1; - lhq.proto = &njs_object_hash_proto; - lhq.pool = vm->mem_pool; - - if (njs_is_undefined(&parse->retval)) { - ret = njs_lvlhsh_delete(njs_object_hash(&state->value), &lhq); - - } else { - prop = njs_object_prop_alloc(vm, key, &parse->retval, 1); - if (njs_slow_path(prop == NULL)) { - goto memory_error; - } - - lhq.value = prop; - ret = njs_lvlhsh_insert(njs_object_hash(&state->value), &lhq); - } - - if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(vm, "lvlhsh insert/replace failed"); - return NJS_ERROR; - } - - state->index++; - state->type = NJS_JSON_OBJECT_START; - break; - case NJS_JSON_ARRAY_START: + case NJS_JSON_ARRAY: if (state->index < njs_array_len(&state->value)) { - retval = &njs_array_start(&state->value)[state->index]; - - if (njs_json_is_non_empty(retval)) { - state = njs_json_push_parse_state(vm, parse, retval); + value = &njs_array_start(&state->value)[state->index]; + + if (njs_json_is_non_empty(value)) { + state = njs_json_push_parse_state(vm, parse, value); if (state == NULL) { goto memory_error; } @@ -1026,29 +979,15 @@ start: } } else { - (void) njs_json_pop_parse_state(parse); + state = njs_json_pop_parse_state(parse); } - ret = njs_json_parse_iterator_call(vm, parse); + ret = njs_json_parse_iterator_call(vm, parse, state); if (njs_slow_path(ret != NJS_OK)) { return ret; } - goto start; - - case NJS_JSON_ARRAY_REPLACED: - retval = &njs_array_start(&state->value)[state->index]; - *retval = parse->retval; - - state->index++; - state->type = NJS_JSON_ARRAY_START; - break; - - default: - njs_internal_error(vm, "Unexpected state %d in JSON.parse()", - state->type); - return NJS_ERROR; } } @@ -1061,40 +1000,51 @@ memory_error: static njs_int_t -njs_json_parse_iterator_call(njs_vm_t *vm, njs_json_parse_t *parse) +njs_json_parse_iterator_call(njs_vm_t *vm, njs_json_parse_t *parse, + njs_json_state_t *state) { - njs_value_t arguments[3]; - njs_json_state_t *state; - - state = parse->state; + njs_int_t ret; + njs_value_t arguments[3], *value; arguments[0] = state->value; switch (state->type) { - case NJS_JSON_OBJECT_START: - arguments[1] = state->keys->start[state->index]; - arguments[2] = *state->prop_value; - - state->type = NJS_JSON_OBJECT_REPLACED; + case NJS_JSON_OBJECT: + arguments[1] = state->keys->start[state->index++]; + arguments[2] = state->prop->value; + + ret = njs_function_apply(vm, parse->function, arguments, 3, + &parse->retval); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_undefined(&parse->retval)) { + state->prop->type = NJS_WHITEOUT; + + } else { + state->prop->value = parse->retval; + } + break; - case NJS_JSON_ARRAY_START: + case NJS_JSON_ARRAY: njs_uint32_to_string(&arguments[1], state->index); - arguments[2] = njs_array_start(&state->value)[state->index]; - - state->type = NJS_JSON_ARRAY_REPLACED; + value = &njs_array_start(&state->value)[state->index++]; + arguments[2] = *value; + + ret = njs_function_apply(vm, parse->function, arguments, 3, + &parse->retval); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + *value = parse->retval; + break; - - default: - njs_internal_error(vm, "Unexpected state %d in JSON.parse() apply", - state->type); - return NJS_ERROR; } - njs_set_invalid(&parse->retval); - - return njs_function_apply(vm, parse->function, arguments, 3, - &parse->retval); + return NJS_OK; } @@ -1130,11 +1080,10 @@ njs_json_push_stringify_state(njs_vm_t * state->written = 0; if (njs_is_array(value)) { - state->type = NJS_JSON_ARRAY_START; + state->type = NJS_JSON_ARRAY; } else { - state->type = NJS_JSON_OBJECT_START; - state->prop_value = NULL; + state->type = NJS_JSON_OBJECT; if (njs_is_array(&stringify->replacer)) { state->keys = njs_array(&stringify->replacer); @@ -1154,8 +1103,6 @@ njs_json_push_stringify_state(njs_vm_t * } } - stringify->state = state; - return state; } @@ -1163,11 +1110,13 @@ njs_json_push_stringify_state(njs_vm_t * njs_inline njs_json_state_t * njs_json_pop_stringify_state(njs_json_stringify_t *stringify) { + njs_json_state_t *state; + if (stringify->depth > 1) { stringify->depth--; - stringify->state = &stringify->states[stringify->depth - 1]; - stringify->state->written = 1; - return stringify->state; + state = &stringify->states[stringify->depth - 1]; + state->written = 1; + return state; } return NULL; @@ -1196,23 +1145,7 @@ njs_json_pop_stringify_state(njs_json_st } \ } - -#define njs_json_stringify_append_key(key) \ - if (state->written) { \ - njs_json_stringify_append(",", 1); \ - njs_json_stringify_indent(stringify->depth); \ - } \ - \ - state->written = 1; \ - njs_json_append_string(stringify, key, '\"'); \ - njs_json_stringify_append(":", 1); \ - if (stringify->space.length != 0) { \ - njs_json_stringify_append(" ", 1); \ - } - - #define njs_json_stringify_append_value(value) \ - state->written = 1; \ ret = njs_json_append_value(stringify, value); \ if (njs_slow_path(ret != NJS_OK)) { \ if (ret == NJS_DECLINED) { \ @@ -1225,23 +1158,20 @@ njs_json_pop_stringify_state(njs_json_st static njs_int_t njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify, - njs_value_t *value) + njs_value_t *object) { - u_char *start; - size_t size; - ssize_t length; - njs_int_t i; - njs_int_t ret; - njs_str_t str; - njs_value_t *key, *retval, wrapper; - njs_object_t *object; - njs_function_t *to_json; - njs_json_state_t *state; - njs_object_prop_t *prop, scratch; - njs_lvlhsh_query_t lhq; - - object = njs_json_wrap_value(vm, &wrapper, value); - if (njs_slow_path(object == NULL)) { + u_char *start; + size_t size; + ssize_t length; + njs_int_t i; + njs_int_t ret; + njs_str_t str; + njs_value_t *key, *value, wrapper; + njs_object_t *obj; + njs_json_state_t *state; + + obj = njs_json_wrap_value(vm, &wrapper, object); + if (njs_slow_path(obj == NULL)) { goto memory_error; } @@ -1250,20 +1180,14 @@ njs_json_stringify_iterator(njs_vm_t *vm goto memory_error; } -start: - - state = stringify->state; - for ( ;; ) { switch (state->type) { - case NJS_JSON_OBJECT_START: - njs_json_stringify_append("{", 1); - njs_json_stringify_indent(stringify->depth); - state->type = NJS_JSON_OBJECT_CONTINUE; - - /* Fall through. */ - - case NJS_JSON_OBJECT_CONTINUE: + case NJS_JSON_OBJECT: + if (state->index == 0) { + njs_json_stringify_append("{", 1); + njs_json_stringify_indent(stringify->depth); + } + if (state->index >= state->keys->length) { njs_json_stringify_indent(stringify->depth - 1); njs_json_stringify_append("}", 1); @@ -1276,133 +1200,65 @@ start: break; } + value = &stringify->retval; key = &state->keys->start[state->index++]; - njs_string_get(key, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); - lhq.proto = &njs_object_hash_proto; - - ret = njs_lvlhsh_find(njs_object_hash(&state->value), &lhq); - if (njs_slow_path(ret == NJS_DECLINED)) { - break; - } - - prop = lhq.value; - - if (!prop->enumerable) { - break; + ret = njs_value_property(vm, &state->value, key, value); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; } - if (njs_is_accessor_descriptor(prop) - && njs_is_function(&prop->getter)) - { - scratch = *prop; - prop = &scratch; - - ret = njs_function_apply(vm, njs_function(&prop->getter), - &state->value, 1, &prop->value); - - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - - if (njs_is_undefined(&prop->value) - || njs_is_function(&prop->value) - || !njs_is_valid(&prop->value)) + if (njs_is_undefined(value) + || njs_is_function(value) + || !njs_is_valid(value)) { break; } - if (njs_is_object(&prop->value)) { - to_json = njs_object_to_json_function(vm, &prop->value); - if (to_json != NULL) { - ret = njs_json_stringify_to_json(vm, stringify, to_json, - &prop->name, - &prop->value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - goto start; - } + ret = njs_json_stringify_to_json(stringify, state, key, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_json_stringify_replacer(stringify, state, key, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_undefined(value)) { + break; } - if (njs_is_function(&stringify->replacer)) { - ret = njs_json_stringify_replacer(vm, stringify, &prop->name, - &prop->value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - goto start; + if (state->written) { + njs_json_stringify_append(",", 1); + njs_json_stringify_indent(stringify->depth); } - njs_json_stringify_append_key(&prop->name); - - if (njs_json_is_object(&prop->value)) { - state = njs_json_push_stringify_state(vm, stringify, - &prop->value); - if (state == NULL) { + state->written = 1; + njs_json_append_string(stringify, key, '\"'); + njs_json_stringify_append(":", 1); + if (stringify->space.length != 0) { + njs_json_stringify_append(" ", 1); + } + + if (njs_json_is_object(value)) { + state = njs_json_push_stringify_state(vm, stringify, value); + if (njs_slow_path(state == NULL)) { return NJS_ERROR; } break; } - njs_json_stringify_append_value(&prop->value); + njs_json_stringify_append_value(value); break; - case NJS_JSON_OBJECT_TO_JSON_REPLACED: - if (njs_is_undefined(&stringify->retval)) { - state->type = NJS_JSON_OBJECT_CONTINUE; - break; - } - - if (njs_is_function(&stringify->replacer)) { - ret = njs_json_stringify_replacer(vm, stringify, - &stringify->key, - &stringify->retval); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - goto start; - } - - /* Fall through. */ - - case NJS_JSON_OBJECT_REPLACED: - state->type = NJS_JSON_OBJECT_CONTINUE; - - if (njs_is_undefined(&stringify->retval)) { - break; + case NJS_JSON_ARRAY: + if (state->index == 0) { + njs_json_stringify_append("[", 1); + njs_json_stringify_indent(stringify->depth); } - njs_json_stringify_append_key(&stringify->key); - - retval = &stringify->retval; - if (njs_is_object(retval)) { - state = njs_json_push_stringify_state(vm, stringify, retval); - if (state == NULL) { - return NJS_ERROR; - } - - break; - } - - njs_json_stringify_append_value(retval); - - break; - - case NJS_JSON_ARRAY_START: - njs_json_stringify_append("[", 1); - njs_json_stringify_indent(stringify->depth); - state->type = NJS_JSON_ARRAY_CONTINUE; - - /* Fall through. */ - - case NJS_JSON_ARRAY_CONTINUE: if (state->index >= njs_array_len(&state->value)) { njs_json_stringify_indent(stringify->depth - 1); njs_json_stringify_append("]", 1); @@ -1420,32 +1276,21 @@ start: njs_json_stringify_indent(stringify->depth); } - retval = &njs_array_start(&state->value)[state->index++]; - - if (njs_is_object(retval)) { - to_json = njs_object_to_json_function(vm, retval); - if (to_json != NULL) { - ret = njs_json_stringify_to_json(vm, stringify, to_json, - NULL, retval); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - goto start; - } + stringify->retval = njs_array_start(&state->value)[state->index++]; + value = &stringify->retval; + + ret = njs_json_stringify_to_json(stringify, state, NULL, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } - if (njs_is_function(&stringify->replacer)) { - ret = njs_json_stringify_replacer(vm, stringify, NULL, retval); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - goto start; + ret = njs_json_stringify_replacer(stringify, state, NULL, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } - if (njs_json_is_object(retval)) { - state = njs_json_push_stringify_state(vm, stringify, retval); + if (njs_json_is_object(value)) { + state = njs_json_push_stringify_state(vm, stringify, value); if (state == NULL) { return NJS_ERROR; } @@ -1453,39 +1298,8 @@ start: break; } - njs_json_stringify_append_value(retval); - - break; - - case NJS_JSON_ARRAY_TO_JSON_REPLACED: - if (njs_is_defined(&stringify->retval) - && njs_is_function(&stringify->replacer)) - { - ret = njs_json_stringify_replacer(vm, stringify, NULL, - &stringify->retval); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - goto start; - } - - /* Fall through. */ - - case NJS_JSON_ARRAY_REPLACED: - state->type = NJS_JSON_ARRAY_CONTINUE; - - if (njs_json_is_object(&stringify->retval)) { - state = njs_json_push_stringify_state(vm, stringify, - &stringify->retval); - if (state == NULL) { - return NJS_ERROR; - } - - break; - } - - njs_json_stringify_append_value(&stringify->retval); + state->written = 1; + njs_json_stringify_append_value(value); break; } @@ -1509,11 +1323,8 @@ done: /* Stripping the wrapper's data. */ - start = str.start; - size = str.length; - - start += njs_length("{\"\":"); - size -= njs_length("{\"\":}"); + start = str.start + njs_length("{\"\":"); + size = str.length - njs_length("{\"\":}"); if (stringify->space.length != 0) { start += njs_length("\n "); @@ -1564,90 +1375,73 @@ njs_object_to_json_function(njs_vm_t *vm static njs_int_t -njs_json_stringify_to_json(njs_vm_t *vm, njs_json_stringify_t* stringify, - njs_function_t *function, njs_value_t *key, njs_value_t *value) +njs_json_stringify_to_json(njs_json_stringify_t* stringify, + njs_json_state_t *state, njs_value_t *key, njs_value_t *value) { - njs_value_t arguments[2]; - njs_json_state_t *state; - - njs_set_invalid(&stringify->retval); - - state = stringify->state; + njs_value_t arguments[2]; + njs_function_t *to_json; + + if (!njs_is_object(value)) { + return NJS_OK; + } + + to_json = njs_object_to_json_function(stringify->vm, value); + + if (to_json == NULL) { + return NJS_OK; + } arguments[0] = *value; switch (state->type) { - case NJS_JSON_OBJECT_START: - case NJS_JSON_OBJECT_CONTINUE: + case NJS_JSON_OBJECT: if (key != NULL) { arguments[1] = *key; - stringify->key = *key; } else { njs_string_short_set(&arguments[1], 0, 0); - njs_string_short_set(&stringify->key, 0, 0); } - state->type = NJS_JSON_OBJECT_TO_JSON_REPLACED; break; - case NJS_JSON_ARRAY_START: - case NJS_JSON_ARRAY_CONTINUE: + case NJS_JSON_ARRAY: njs_uint32_to_string(&arguments[1], state->index - 1); - state->type = NJS_JSON_ARRAY_TO_JSON_REPLACED; break; - - default: - njs_internal_error(vm, "Unexpected state %d in JSON.stringify() apply", - state->type); - return NJS_ERROR; } - return njs_function_apply(vm, function, arguments, 2, &stringify->retval); + return njs_function_apply(stringify->vm, to_json, arguments, 2, + &stringify->retval); } static njs_int_t -njs_json_stringify_replacer(njs_vm_t *vm, njs_json_stringify_t* stringify, - njs_value_t *key, njs_value_t *value) +njs_json_stringify_replacer(njs_json_stringify_t* stringify, + njs_json_state_t *state, njs_value_t *key, njs_value_t *value) { - njs_value_t arguments[3]; - njs_json_state_t *state; - - state = stringify->state; + njs_value_t arguments[3]; + + if (!njs_is_function(&stringify->replacer)) { + return NJS_OK; + } arguments[0] = state->value; switch (state->type) { - case NJS_JSON_OBJECT_START: - case NJS_JSON_OBJECT_CONTINUE: - case NJS_JSON_OBJECT_TO_JSON_REPLACED: + case NJS_JSON_OBJECT: arguments[1] = *key; - stringify->key = *key; arguments[2] = *value; - state->type = NJS_JSON_OBJECT_REPLACED; break; - case NJS_JSON_ARRAY_START: - case NJS_JSON_ARRAY_CONTINUE: - case NJS_JSON_ARRAY_TO_JSON_REPLACED: + case NJS_JSON_ARRAY: njs_uint32_to_string(&arguments[1], state->index - 1); arguments[2] = *value; - state->type = NJS_JSON_ARRAY_REPLACED; break; - - default: - njs_internal_error(vm, "Unexpected state %d in " - "JSON.stringify() replacer", state->type); - return NJS_ERROR; } - njs_set_invalid(&stringify->retval); - - return njs_function_apply(vm, njs_function(&stringify->replacer), + return njs_function_apply(stringify->vm, njs_function(&stringify->replacer), arguments, 3, &stringify->retval); } @@ -2318,7 +2112,6 @@ memory_error: #define njs_dump_append_value(value) \ - state->written = 1; \ ret = njs_dump_value(stringify, value, console); \ if (njs_slow_path(ret != NJS_OK)) { \ if (ret == NJS_DECLINED) { \ @@ -2379,14 +2172,12 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ for ( ;; ) { switch (state->type) { - case NJS_JSON_OBJECT_START: - njs_json_stringify_append("{", 1); - njs_json_stringify_indent(stringify->depth + 1); - state->type = NJS_JSON_OBJECT_CONTINUE; - - /* Fall through. */ - - case NJS_JSON_OBJECT_CONTINUE: + case NJS_JSON_OBJECT: + if (state->index == 0) { + njs_json_stringify_append("{", 1); + njs_json_stringify_indent(stringify->depth + 1); + } + if (state->index >= state->keys->length) { njs_json_stringify_indent(stringify->depth); njs_json_stringify_append("}", 1); @@ -2464,7 +2255,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ if (njs_dump_is_object(val)) { state = njs_json_push_stringify_state(vm, stringify, val); - if (state == NULL) { + if (njs_slow_path(state == NULL)) { goto exception; } @@ -2475,14 +2266,12 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ break; - case NJS_JSON_ARRAY_START: - njs_json_stringify_append("[", 1); - njs_json_stringify_indent(stringify->depth + 1); - state->type = NJS_JSON_ARRAY_CONTINUE; - - /* Fall through. */ - - case NJS_JSON_ARRAY_CONTINUE: + case NJS_JSON_ARRAY: + if (state->index == 0) { + njs_json_stringify_append("[", 1); + njs_json_stringify_indent(stringify->depth + 1); + } + if (state->index >= njs_array_len(&state->value)) { njs_json_stringify_indent(stringify->depth); njs_json_stringify_append("]", 1); @@ -2504,19 +2293,17 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ if (njs_dump_is_object(val)) { state = njs_json_push_stringify_state(vm, stringify, val); - if (state == NULL) { + if (njs_slow_path(state == NULL)) { goto exception; } break; } + state->written = 1; njs_dump_append_value(val); break; - - default: - njs_unreachable(); } } diff -r 6d1d0369b041 -r a6aef1897687 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue Nov 12 15:09:48 2019 +0300 +++ b/src/test/njs_unit_test.c Wed Nov 13 15:19:49 2019 +0300 @@ -13863,6 +13863,12 @@ static njs_unit_test_t njs_test[] = { njs_str("JSON.stringify([$r])"), njs_str("[null]") }, + { njs_str("JSON.stringify({get key() {throw new Error('Oops')}})"), + njs_str("Error: Oops") }, + + { njs_str("JSON.stringify({toJSON() {throw new Error('Oops')}})"), + njs_str("Error: Oops") }, + /* Ignoring named properties of an array. */ { njs_str("var a = [1,2]; a.a = 1;" @@ -13982,6 +13988,10 @@ static njs_unit_test_t njs_test[] = "JSON.stringify(o)"), njs_str("{\"a\":{\"b\":1,\"c\":2}}") }, + { njs_str("var o = Object.defineProperty({}, 'a', { get: () => ({})});" + "JSON.stringify(o)"), + njs_str("{}") }, + { njs_str("JSON.stringify({toJSON:function(k){}})"), njs_str("undefined") }, From xeioex at nginx.com Wed Nov 13 12:49:26 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 13 Nov 2019 12:49:26 +0000 Subject: [njs] Fixed potential NULL-pointer dereference (CID 1455631). Message-ID: details: https://hg.nginx.org/njs/rev/96d99066e8e5 branches: changeset: 1235:96d99066e8e5 user: Dmitry Volyntsev date: Wed Nov 13 15:31:41 2019 +0300 description: Fixed potential NULL-pointer dereference (CID 1455631). diffstat: src/njs_json.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diffs (13 lines): diff -r a6aef1897687 -r 96d99066e8e5 src/njs_json.c --- a/src/njs_json.c Wed Nov 13 15:19:49 2019 +0300 +++ b/src/njs_json.c Wed Nov 13 15:31:41 2019 +0300 @@ -2169,6 +2169,9 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ njs_memset(stringify->space.start, ' ', indent); state = njs_json_push_stringify_state(vm, stringify, value); + if (njs_slow_path(state == NULL)) { + goto memory_error; + } for ( ;; ) { switch (state->type) { From xeioex at nginx.com Wed Nov 13 16:32:59 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 13 Nov 2019 16:32:59 +0000 Subject: [njs] Added extra configure options for debug builds. Message-ID: details: https://hg.nginx.org/njs/rev/077e125e31c1 branches: changeset: 1236:077e125e31c1 user: Dmitry Volyntsev date: Wed Nov 13 18:27:06 2019 +0300 description: Added extra configure options for debug builds. diffstat: auto/cc | 20 ++++++++++++++++++++ auto/help | 14 ++++++++++---- auto/options | 9 ++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diffs (102 lines): diff -r 96d99066e8e5 -r 077e125e31c1 auto/cc --- a/auto/cc Wed Nov 13 15:31:41 2019 +0300 +++ b/auto/cc Wed Nov 13 18:27:06 2019 +0300 @@ -99,6 +99,11 @@ case $NJS_CC_NAME in # Debug. NJS_CFLAGS="$NJS_CFLAGS -g" + + if [ "$NJS_ADDRESS_SANITIZER" = "YES" ]; then + NJS_CFLAGS="$NJS_CFLAGS -fsanitize=address" + NJS_CFLAGS="$NJS_CFLAGS -fno-omit-frame-pointer" + fi ;; clang) @@ -135,6 +140,12 @@ case $NJS_CC_NAME in # error on PowerPC Clang. NJS_CFLAGS="$NJS_CFLAGS -g" fi + + if [ "$NJS_ADDRESS_SANITIZER" = "YES" ]; then + NJS_CFLAGS="$NJS_CFLAGS -fsanitize=address" + NJS_CFLAGS="$NJS_CFLAGS -fno-omit-frame-pointer" + NJS_CFLAGS="$NJS_CFLAGS -fsanitize-address-use-after-scope" + fi ;; SunC) @@ -147,6 +158,10 @@ case $NJS_CC_NAME in NJS_CFLAGS="$NJS_CFLAGS -errwarn=%all" # Debug. NJS_CFLAGS="$NJS_CFLAGS -g" + + if [ "$NJS_ADDRESS_SANITIZER" = "YES" ]; then + echo " - Address sanitizer is not supported by $NJS_CC_NAME" + fi ;; *) @@ -154,5 +169,10 @@ case $NJS_CC_NAME in esac +if [ "$NJS_DEBUG" = "YES" ]; then + njs_define=NJS_DEBUG . auto/define + njs_define=NJS_DEBUG_MEMORY . auto/define +fi + # Stop on error exit status again. set -e diff -r 96d99066e8e5 -r 077e125e31c1 auto/help --- a/auto/help Wed Nov 13 15:31:41 2019 +0300 +++ b/auto/help Wed Nov 13 18:27:06 2019 +0300 @@ -7,9 +7,15 @@ cat << END ./configure options: - --cc=FILE set C compiler filename, default: "$CC" - --cc-opt=OPTIONS set additional C compiler options - --ld-opt=OPTIONS set additional linker options - --ar=FILE set static linking program, default: "$AR" + --cc=FILE set C compiler filename, default: "$CC" + --cc-opt=OPTIONS set additional C compiler options, \ +default: "$NJS_CC_OPT" + --ld-opt=OPTIONS set additional linker options, \ +default: "$NJS_LD_OPT" + --ar=FILE set static linking program, default: "$AR" + --debug=YES enables additional runtime checks, \ +default: "$NJS_DEBUG" + --address-sanitizer=YES enables build with address sanitizer, \ +default: "$NJS_ADDRESS_SANITIZER" END diff -r 96d99066e8e5 -r 077e125e31c1 auto/options --- a/auto/options Wed Nov 13 15:31:41 2019 +0300 +++ b/auto/options Wed Nov 13 18:27:06 2019 +0300 @@ -3,10 +3,14 @@ # Copyright (C) NGINX, Inc. -NJS_CONFIGURE_OPTIONS= NJS_CC_OPT=${NJS_CC_OPT:--O} NJS_LD_OPT=${NJS_CC_OPT:--O} +NJS_DEBUG=NO +NJS_ADDRESS_SANITIZER=NO + +NJS_CONFIGURE_OPTIONS= + for njs_option do case "$njs_option" in @@ -20,6 +24,9 @@ do --ld-opt=*) NJS_LD_OPT="$value" ;; --ar=*) AR="$value" ;; + --debug=*) NJS_DEBUG="$value" ;; + --address-sanitizer=*) NJS_ADDRESS_SANITIZER="$value" ;; + --help) . auto/help exit 0 From xeioex at nginx.com Wed Nov 13 16:33:00 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 13 Nov 2019 16:33:00 +0000 Subject: [njs] Added support for labels in console.time(). Message-ID: details: https://hg.nginx.org/njs/rev/3e9e1de11557 branches: changeset: 1237:3e9e1de11557 user: Artem S. Povalyukhin date: Tue Nov 12 16:07:42 2019 +0300 description: Added support for labels in console.time(). This closes #248 issue on Github. diffstat: src/njs_shell.c | 159 ++++++++++++++++++++++++++++++++++++++++------ test/njs_expect_test.exp | 26 +++++- 2 files changed, 158 insertions(+), 27 deletions(-) diffs (282 lines): diff -r 077e125e31c1 -r 3e9e1de11557 src/njs_shell.c --- a/src/njs_shell.c Wed Nov 13 18:27:06 2019 +0300 +++ b/src/njs_shell.c Tue Nov 12 16:07:42 2019 +0300 @@ -57,12 +57,18 @@ typedef struct { typedef struct { + njs_value_t name; + uint64_t time; +} njs_timelabel_t; + + +typedef struct { njs_vm_t *vm; njs_lvlhsh_t events; /* njs_ev_t * */ njs_queue_t posted_events; - uint64_t time; + njs_lvlhsh_t labels; /* njs_timelabel_t */ njs_completion_t completion; } njs_console_t; @@ -95,6 +101,8 @@ static njs_host_event_t njs_console_set_ static void njs_console_clear_timer(njs_external_ptr_t external, njs_host_event_t event); +static njs_int_t njs_timelabel_hash_test(njs_lvlhsh_query_t *lhq, void *data); + static njs_int_t lvlhsh_key_test(njs_lvlhsh_query_t *lhq, void *data); static void *lvlhsh_pool_alloc(void *pool, size_t size); static void lvlhsh_pool_free(void *pool, void *p, size_t size); @@ -170,6 +178,14 @@ static const njs_lvlhsh_proto_t lvlhsh_ }; +static const njs_lvlhsh_proto_t njs_timelabel_hash_proto njs_aligned(64) = { + NJS_LVLHSH_DEFAULT, + njs_timelabel_hash_test, + lvlhsh_pool_alloc, + lvlhsh_pool_free, +}; + + static njs_vm_ops_t njs_console_ops = { njs_console_set_timer, njs_console_clear_timer @@ -391,7 +407,7 @@ njs_console_init(njs_vm_t *vm, njs_conso njs_lvlhsh_init(&console->events); njs_queue_init(&console->posted_events); - console->time = UINT64_MAX; + njs_lvlhsh_init(&console->labels); console->completion.completions = njs_vm_completions(vm, NULL); if (console->completion.completions == NULL) { @@ -1010,23 +1026,75 @@ njs_ext_console_dump(njs_vm_t *vm, njs_v } +static const njs_value_t njs_default_label = njs_string("default"); + + static njs_int_t njs_ext_console_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_console_t *console; - - if (!njs_value_is_undefined(njs_arg(args, nargs, 1))) { - njs_vm_error(vm, "labels not implemented"); - return NJS_ERROR; - } + njs_int_t ret; + njs_console_t *console; + njs_value_t *value; + njs_timelabel_t *label; + njs_str_t name; + njs_lvlhsh_query_t lhq; console = njs_vm_external(vm, njs_arg(args, nargs, 0)); if (njs_slow_path(console == NULL)) { return NJS_ERROR; } - console->time = njs_time(); + value = njs_arg(args, nargs, 1); + + if (njs_slow_path(!njs_is_string(value))) { + if (njs_is_undefined(value)) { + value = njs_value_arg(&njs_default_label); + + } else { + ret = njs_value_to_string(vm, value, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + } + + njs_string_get(value, &name); + + label = njs_mp_alloc(vm->mem_pool, sizeof(njs_timelabel_t)); + if (njs_slow_path(label == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } + + lhq.replace = 0; + lhq.key = name; + lhq.key_hash = njs_djb_hash(name.start, name.length); + lhq.value = label; + lhq.pool = vm->mem_pool; + lhq.proto = &njs_timelabel_hash_proto; + + ret = njs_lvlhsh_insert(&console->labels, &lhq); + + if (njs_fast_path(ret == NJS_OK)) { + /* GC: retain. */ + label->name = *value; + + } else { + njs_mp_free(vm->mem_pool, label); + + if (njs_slow_path(ret == NJS_ERROR)) { + njs_internal_error(vm, "lvlhsh insert failed"); + + return NJS_ERROR; + } + + njs_printf("Timer \"%V\" already exists.\n", &name); + + label = lhq.value; + } + + label->time = njs_time(); njs_set_undefined(&vm->retval); @@ -1038,34 +1106,66 @@ static njs_int_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - uint64_t ns, ms; - njs_console_t *console; + uint64_t ns, ms; + njs_int_t ret; + njs_console_t *console; + njs_value_t *value; + njs_timelabel_t *label; + njs_str_t name; + njs_lvlhsh_query_t lhq; ns = njs_time(); - if (!njs_value_is_undefined(njs_arg(args, nargs, 1))) { - njs_vm_error(vm, "labels not implemented"); - return NJS_ERROR; - } - console = njs_vm_external(vm, njs_arg(args, nargs, 0)); if (njs_slow_path(console == NULL)) { return NJS_ERROR; } - if (njs_fast_path(console->time != UINT64_MAX)) { + value = njs_arg(args, nargs, 1); + + if (njs_slow_path(!njs_is_string(value))) { + if (njs_is_undefined(value)) { + value = njs_value_arg(&njs_default_label); + + } else { + ret = njs_value_to_string(vm, value, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + } - ns = ns - console->time; + njs_string_get(value, &name); + + lhq.key = name; + lhq.key_hash = njs_djb_hash(name.start, name.length); + lhq.pool = vm->mem_pool; + lhq.proto = &njs_timelabel_hash_proto; + + ret = njs_lvlhsh_delete(&console->labels, &lhq); + + if (njs_fast_path(ret == NJS_OK)) { + + label = lhq.value; + + ns = ns - label->time; ms = ns / 1000000; ns = ns % 1000000; - njs_printf("default: %uL.%06uLms\n", ms, ns); + njs_printf("%V: %uL.%06uLms\n", &name, ms, ns); - console->time = UINT64_MAX; + /* GC: release. */ + njs_mp_free(vm->mem_pool, label); } else { - njs_printf("Timer \"default\" doesn?t exist.\n"); + if (ret == NJS_ERROR) { + njs_internal_error(vm, "lvlhsh delete failed"); + + return NJS_ERROR; + } + + njs_printf("Timer \"%V\" doesn?t exist.\n", &name); } njs_set_undefined(&vm->retval); @@ -1153,6 +1253,23 @@ njs_console_clear_timer(njs_external_ptr static njs_int_t +njs_timelabel_hash_test(njs_lvlhsh_query_t *lhq, void *data) +{ + njs_timelabel_t *label; + njs_str_t str; + + label = data; + njs_string_get(&label->name, &str); + + if (njs_strstr_eq(&lhq->key, &str)) { + return NJS_OK; + } + + return NJS_DECLINED; +} + + +static njs_int_t lvlhsh_key_test(njs_lvlhsh_query_t *lhq, void *data) { njs_ev_t *ev; diff -r 077e125e31c1 -r 3e9e1de11557 test/njs_expect_test.exp --- a/test/njs_expect_test.exp Wed Nov 13 18:27:06 2019 +0300 +++ b/test/njs_expect_test.exp Tue Nov 12 16:07:42 2019 +0300 @@ -225,16 +225,30 @@ njs_test { njs_test { {"console.time()\r\n" "console.time()\r\nundefined\r\n>> "} - {"console.time(undefined)\r\n" - "console.time(undefined)\r\nundefined\r\n>> "} {"console.timeEnd()\r\n" "console.timeEnd()\r\ndefault: *.*ms\r\nundefined\r\n>> "} - {"console.time('a')\r\n" - "console.time('a')\r\nError: labels not implemented"} - {"console.timeEnd('a')\r\n" - "console.timeEnd('a')\r\nError: labels not implemented"} + {"console.time(undefined)\r\n" + "console.time(undefined)\r\nundefined\r\n>> "} + {"console.timeEnd(undefined)\r\n" + "console.timeEnd(undefined)\r\ndefault: *.*ms\r\nundefined\r\n>> "} + {"console.time('abc')\r\n" + "console.time('abc')\r\nundefined\r\n>> "} + {"console.time('abc')\r\n" + "console.time('abc')\r\nTimer \"abc\" already exists.\r\nundefined\r\n>> "} + {"console.timeEnd('abc')\r\n" + "console.timeEnd('abc')\r\nabc: *.*ms\r\nundefined\r\n>> "} + {"console.time(true)\r\n" + "console.time(true)\r\nundefined\r\n>> "} + {"console.timeEnd(true)\r\n" + "console.timeEnd(true)\r\ntrue: *.*ms\r\nundefined\r\n>> "} + {"console.time(42)\r\n" + "console.time(42)\r\nundefined\r\n>> "} + {"console.timeEnd(42)\r\n" + "console.timeEnd(42)\r\n42: *.*ms\r\nundefined\r\n>> "} {"console.timeEnd()\r\n" "console.timeEnd()\r\nTimer \"default\" doesn?t exist."} + {"console.timeEnd('abc')\r\n" + "console.timeEnd('abc')\r\nTimer \"abc\" doesn?t exist."} } njs_test { From jwang60606 at gmail.com Wed Nov 13 19:03:45 2019 From: jwang60606 at gmail.com (Jason Wang) Date: Wed, 13 Nov 2019 11:03:45 -0800 Subject: Option to fail TLS handshake on bad client cert Message-ID: Hi, I'm using nginx to proxy gRPC requests that have the client authenticate with a client certificate. When connecting directly to Go's gRPC server with an untrusted client certificate or with no client certificate when one is required, the server will fail the TLS handshake. I believe it would be useful if nginx supported enabling this behavior. This behavior is useful because it allows clients to know that they are not authenticated when they dial as opposed to on making a gRPC request. Additionally, failing the TLS handshake removes the need for the error pages served to the client indicating a untrusted certificate to have the Content-Type, gprc-status, and grpc-message headers set. Would the project be open to implementing this or accepting patches based on this rationale? Best, Jason -------------- next part -------------- An HTML attachment was scrubbed... URL: From guillaume-nginx at outters.eu Thu Nov 14 15:53:55 2019 From: guillaume-nginx at outters.eu (Guillaume Outters) Date: Thu, 14 Nov 2019 16:53:55 +0100 Subject: [nginx] Document modules as a way to patch nginx? In-Reply-To: <3f5df681b7518aa8c8e08646af8ad09f@outters.eu> References: <3f5df681b7518aa8c8e08646af8ad09f@outters.eu> Message-ID: <2b79a5e1387be7fb81ba484c12177c04@outters.eu> Last night, while trying to progress on my {{.}} syntax in config files by recompiling and recompiling my nginx, I looked at nginx_upstream_check_module source and incidentally discovered that nginx modules where both powerful and easy to create. So easy and powerful that the [PATCH] I submitted in september and october could be quickly set into a module, integrating nicely in nginx build. So I wondered: couldn't this way of playing with nginx internals be advertized as a notice in http://nginx.org/en/docs/contributing_changes.html? Something like: ---------------- Many changes to nginx core source, including those requiring to patch nginx, can be implemented as modules, which are easy to create, easy to publish, and easy to integrate to nginx. Before trying to contribute to nginx source, please contemplate packaging your changes as a module. Thus: - you will be able to test it in a clean way with your installation, before submitting it to the core - your specific needs can be implemented there without conflicting with the community's ones - you will be able to distribute it long before it is integrated to nginx - you will be able to run it on the very release of nginx your production uses ---------------- -- Guillaume From xeioex at nginx.com Thu Nov 14 18:08:31 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 14 Nov 2019 18:08:31 +0000 Subject: [njs] Setting optimization level to "-O0" when debug option is enabled. Message-ID: details: https://hg.nginx.org/njs/rev/4d414fccd53d branches: changeset: 1238:4d414fccd53d user: Dmitry Volyntsev date: Thu Nov 14 21:01:28 2019 +0300 description: Setting optimization level to "-O0" when debug option is enabled. diffstat: auto/cc | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (11 lines): diff -r 3e9e1de11557 -r 4d414fccd53d auto/cc --- a/auto/cc Tue Nov 12 16:07:42 2019 +0300 +++ b/auto/cc Thu Nov 14 21:01:28 2019 +0300 @@ -170,6 +170,7 @@ case $NJS_CC_NAME in esac if [ "$NJS_DEBUG" = "YES" ]; then + NJS_CC_OPT="$NJS_CC_OPT -O0" njs_define=NJS_DEBUG . auto/define njs_define=NJS_DEBUG_MEMORY . auto/define fi From xeioex at nginx.com Thu Nov 14 18:08:31 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 14 Nov 2019 18:08:31 +0000 Subject: [njs] Reimplemented "bound" functions according to specification. Message-ID: details: https://hg.nginx.org/njs/rev/2488363b220b branches: changeset: 1239:2488363b220b user: Dmitry Volyntsev date: Thu Nov 14 21:06:48 2019 +0300 description: Reimplemented "bound" functions according to specification. This closes #202 and closes #204 issues on Github. diffstat: src/njs_builtin.c | 4 +- src/njs_function.c | 151 +++++++++++++++++++++++++++++++++++++++++----- src/njs_function.h | 2 + src/njs_object_prop.c | 29 +------- src/njs_value.h | 1 + src/njs_vm.c | 7 +- src/njs_vm.h | 2 +- src/njs_vmcode.c | 25 ++++++- src/test/njs_unit_test.c | 60 ++++++++++++++++++ test/njs_expect_test.exp | 2 + 10 files changed, 233 insertions(+), 50 deletions(-) diffs (495 lines): diff -r 4d414fccd53d -r 2488363b220b src/njs_builtin.c --- a/src/njs_builtin.c Thu Nov 14 21:01:28 2019 +0300 +++ b/src/njs_builtin.c Thu Nov 14 21:06:48 2019 +0300 @@ -761,7 +761,7 @@ njs_object_completions(njs_vm_t *vm, njs njs_int_t -njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function, +njs_builtin_match_native_function(njs_vm_t *vm, njs_function_native_t func, njs_str_t *name) { njs_int_t ret; @@ -772,7 +772,7 @@ njs_builtin_match_native_function(njs_vm njs_builtin_traverse_t ctx; ctx.type = NJS_BUILTIN_TRAVERSE_MATCH; - ctx.native = function->u.native; + ctx.native = func; /* Global object. */ diff -r 4d414fccd53d -r 2488363b220b src/njs_function.c --- a/src/njs_function.c Thu Nov 14 21:01:28 2019 +0300 +++ b/src/njs_function.c Thu Nov 14 21:06:48 2019 +0300 @@ -111,6 +111,53 @@ njs_function_closures(njs_vm_t *vm, njs_ } +njs_int_t +njs_function_name_set(njs_vm_t *vm, njs_function_t *function, + njs_value_t *name, njs_bool_t bound) +{ + u_char *start; + njs_int_t ret; + njs_string_prop_t string; + njs_object_prop_t *prop; + njs_lvlhsh_query_t lhq; + + prop = njs_object_prop_alloc(vm, &njs_string_name, name, 0); + if (njs_slow_path(name == NULL)) { + return NJS_ERROR; + } + + if (bound) { + (void) njs_string_prop(&string, name); + + start = njs_string_alloc(vm, &prop->value, string.size + 6, + string.length + 6); + if (njs_slow_path(start == NULL)) { + return NJS_ERROR; + } + + start = njs_cpymem(start, "bound ", 6); + memcpy(start, string.start, string.size); + } + + prop->configurable = 1; + + lhq.key_hash = NJS_NAME_HASH; + lhq.key = njs_str_value("name"); + lhq.replace = 0; + lhq.value = prop; + lhq.proto = &njs_object_hash_proto; + lhq.pool = vm->mem_pool; + + ret = njs_lvlhsh_insert(&function->object.hash, &lhq); + if (njs_slow_path(ret != NJS_OK)) { + njs_internal_error(vm, "lvlhsh insert failed"); + return NJS_ERROR; + } + + return NJS_OK; +} + + static njs_function_t * njs_function_copy(njs_vm_t *vm, njs_function_t *function) { @@ -347,10 +394,32 @@ njs_function_lambda_frame(njs_vm_t *vm, njs_uint_t n, max_args, closures; njs_value_t *value, *bound; njs_frame_t *frame; + njs_function_t *target; njs_native_frame_t *native_frame; njs_function_lambda_t *lambda; - lambda = function->u.lambda; + bound = function->bound; + + if (njs_fast_path(bound == NULL)) { + lambda = function->u.lambda; + target = function; + + } else { + target = function->u.bound_target; + + if (njs_slow_path(target->bound != NULL)) { + + /* + * FIXME: bound functions should call target function with + * bound "this" and bound args. + */ + + njs_internal_error(vm, "chain of bound function are not supported"); + return NJS_ERROR; + } + + lambda = target->u.lambda; + } max_args = njs_max(nargs, lambda->nargs); @@ -365,7 +434,7 @@ njs_function_lambda_frame(njs_vm_t *vm, return NJS_ERROR; } - native_frame->function = function; + native_frame->function = target; native_frame->nargs = nargs; native_frame->ctor = ctor; @@ -375,8 +444,6 @@ njs_function_lambda_frame(njs_vm_t *vm, njs_frame_size(closures)); native_frame->arguments = value; - bound = function->bound; - if (bound == NULL) { *value++ = *this; @@ -384,10 +451,16 @@ njs_function_lambda_frame(njs_vm_t *vm, n = function->args_offset; native_frame->nargs += n - 1; - do { + if (ctor) { + *value++ = *this; + bound++; + n--; + } + + while (n != 0) { *value++ = *bound++; n--; - } while (n != 0); + }; } vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value; @@ -580,18 +653,32 @@ njs_function_lambda_call(njs_vm_t *vm) njs_int_t njs_function_native_call(njs_vm_t *vm) { - njs_int_t ret; - njs_value_t *value; - njs_frame_t *frame; - njs_function_t *function; - njs_native_frame_t *native, *previous; + njs_int_t ret; + njs_value_t *value; + njs_frame_t *frame; + njs_function_t *function, *target; + njs_native_frame_t *native, *previous; + njs_function_native_t call; native = vm->top_frame; frame = (njs_frame_t *) native; function = native->function; - ret = function->u.native(vm, native->arguments, native->nargs, - function->magic); + if (njs_fast_path(function->bound == NULL)) { + call = function->u.native; + + } else { + target = function->u.bound_target; + + if (njs_slow_path(target->bound != NULL)) { + njs_internal_error(vm, "chain of bound function are not supported"); + return NJS_ERROR; + } + + call = target->u.native; + } + + ret = call(vm, native->arguments, native->nargs, function->magic); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } @@ -1044,21 +1131,51 @@ static njs_int_t njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - size_t size; - njs_value_t *values; - njs_function_t *function; + size_t size; + njs_int_t ret; + njs_value_t *values, name; + njs_function_t *function; + njs_lvlhsh_query_t lhq; if (!njs_is_function(&args[0])) { njs_type_error(vm, "\"this\" argument is not a function"); return NJS_ERROR; } - function = njs_function_copy(vm, njs_function(&args[0])); + function = njs_mp_alloc(vm->mem_pool, sizeof(njs_function_t)); if (njs_slow_path(function == NULL)) { njs_memory_error(vm); return NJS_ERROR; } + *function = *njs_function(&args[0]); + + njs_lvlhsh_init(&function->object.hash); + + /* Bound functions have no "prototype" property. */ + function->object.shared_hash = vm->shared->arrow_instance_hash; + + function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object; + function->object.shared = 0; + + function->u.bound_target = njs_function(&args[0]); + + njs_object_property_init(&lhq, "name", NJS_NAME_HASH); + + ret = njs_object_property(vm, &args[0], &lhq, &name); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (!njs_is_string(&name)) { + name = njs_string_empty; + } + + ret = njs_function_name_set(vm, function, &name, 1); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + if (nargs == 1) { args = njs_value_arg(&njs_value_undefined); diff -r 4d414fccd53d -r 2488363b220b src/njs_function.h --- a/src/njs_function.h Thu Nov 14 21:01:28 2019 +0300 +++ b/src/njs_function.h Thu Nov 14 21:06:48 2019 +0300 @@ -101,6 +101,8 @@ struct njs_frame_s { njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda, njs_closure_t *closures[], njs_bool_t shared); njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value); +njs_int_t njs_function_name_set(njs_vm_t *vm, njs_function_t *function, + njs_value_t *name, njs_bool_t bound); njs_int_t njs_function_arguments_object_init(njs_vm_t *vm, njs_native_frame_t *frame); njs_int_t njs_function_rest_parameters_init(njs_vm_t *vm, diff -r 4d414fccd53d -r 2488363b220b src/njs_object_prop.c --- a/src/njs_object_prop.c Thu Nov 14 21:01:28 2019 +0300 +++ b/src/njs_object_prop.c Thu Nov 14 21:06:48 2019 +0300 @@ -376,10 +376,9 @@ exception: njs_int_t njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq) { - njs_int_t ret; - njs_function_t *function; - njs_object_prop_t *prop, *shared, *name; - njs_lvlhsh_query_t lhq; + njs_int_t ret; + njs_function_t *function; + njs_object_prop_t *prop, *shared; prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), sizeof(njs_object_prop_t)); @@ -436,27 +435,7 @@ njs_prop_private_copy(njs_vm_t *vm, njs_ return NJS_ERROR; } - name = njs_object_prop_alloc(vm, &njs_string_name, &prop->name, 0); - if (njs_slow_path(name == NULL)) { - return NJS_ERROR; - } - - name->configurable = 1; - - lhq.key_hash = NJS_NAME_HASH; - lhq.key = njs_str_value("name"); - lhq.replace = 0; - lhq.value = name; - lhq.proto = &njs_object_hash_proto; - lhq.pool = vm->mem_pool; - - ret = njs_lvlhsh_insert(&function->object.hash, &lhq); - if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(vm, "lvlhsh insert failed"); - return NJS_ERROR; - } - - return NJS_OK; + return njs_function_name_set(vm, function, &prop->name, 0); } diff -r 4d414fccd53d -r 2488363b220b src/njs_value.h --- a/src/njs_value.h Thu Nov 14 21:01:28 2019 +0300 +++ b/src/njs_value.h Thu Nov 14 21:06:48 2019 +0300 @@ -245,6 +245,7 @@ struct njs_function_s { union { njs_function_lambda_t *lambda; njs_function_native_t native; + njs_function_t *bound_target; } u; njs_value_t *bound; diff -r 4d414fccd53d -r 2488363b220b src/njs_vm.c --- a/src/njs_vm.c Thu Nov 14 21:01:28 2019 +0300 +++ b/src/njs_vm.c Thu Nov 14 21:06:48 2019 +0300 @@ -1056,7 +1056,12 @@ njs_vm_add_backtrace_entry(njs_vm_t *vm, } if (function->native) { - ret = njs_builtin_match_native_function(vm, function, &be->name); + while (function->bound != NULL) { + function = function->u.bound_target; + } + + ret = njs_builtin_match_native_function(vm, function->u.native, + &be->name); if (ret == NJS_OK) { return NJS_OK; } diff -r 4d414fccd53d -r 2488363b220b src/njs_vm.h --- a/src/njs_vm.h Thu Nov 14 21:01:28 2019 +0300 +++ b/src/njs_vm.h Thu Nov 14 21:06:48 2019 +0300 @@ -275,7 +275,7 @@ njs_int_t njs_vm_add_backtrace_entry(njs njs_int_t njs_builtin_objects_create(njs_vm_t *vm); njs_int_t njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global); njs_int_t njs_builtin_match_native_function(njs_vm_t *vm, - njs_function_t *function, njs_str_t *name); + njs_function_native_t func, njs_str_t *name); njs_arr_t *njs_vm_backtrace(njs_vm_t *vm); njs_arr_t *njs_vm_completions(njs_vm_t *vm, njs_str_t *expression); diff -r 4d414fccd53d -r 2488363b220b src/njs_vmcode.c --- a/src/njs_vmcode.c Thu Nov 14 21:01:28 2019 +0300 +++ b/src/njs_vmcode.c Thu Nov 14 21:06:48 2019 +0300 @@ -1328,22 +1328,30 @@ static njs_jump_off_t njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object, njs_value_t *constructor) { - njs_value_t value; + njs_value_t value, bound; njs_object_t *prototype, *proto; + njs_function_t *function; njs_jump_off_t ret; const njs_value_t *retval; - const njs_value_t prototype_string = njs_string("prototype"); + static const njs_value_t prototype_string = njs_string("prototype"); if (!njs_is_function(constructor)) { njs_type_error(vm, "right argument is not callable"); return NJS_ERROR; } + function = njs_function(constructor); + + if (function->bound != NULL) { + function = function->u.bound_target; + njs_set_function(&bound, function); + constructor = &bound; + } + retval = &njs_value_false; if (njs_is_object(object)) { - njs_set_undefined(&value); ret = njs_value_property(vm, constructor, njs_value_arg(&prototype_string), &value); @@ -1607,8 +1615,9 @@ njs_function_frame_create(njs_vm_t *vm, static njs_object_t * njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor) { - njs_value_t proto; + njs_value_t proto, bound; njs_object_t *object; + njs_function_t *function; njs_jump_off_t ret; const njs_value_t prototype_string = njs_string("prototype"); @@ -1618,6 +1627,14 @@ njs_function_new_object(njs_vm_t *vm, nj return NULL; } + function = njs_function(constructor); + + if (function->bound != NULL) { + function = function->u.bound_target; + njs_set_function(&bound, function); + constructor = &bound; + } + ret = njs_value_property(vm, constructor, njs_value_arg(&prototype_string), &proto); diff -r 4d414fccd53d -r 2488363b220b src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Nov 14 21:01:28 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Nov 14 21:06:48 2019 +0300 @@ -5528,6 +5528,62 @@ static njs_unit_test_t njs_test[] = "var o = { toString: f }; o"), njs_str("0,1,2") }, + { njs_str("var f = Object.defineProperty(function() {}, 'name', {value: 'F'});" + "[f.name, f.bind().name, f.bind().bind().name]"), + njs_str("F,bound F,bound bound F") }, + + { njs_str("var f = Object.defineProperty(function() {}, 'name', {value: undefined});" + "[f.name, f.bind().name, f.bind().bind().name]"), + njs_str(",bound ,bound bound ") }, + + { njs_str("var f = Object.defineProperty(function() {}, 'name', {get:()=>'F'});" + "[f.name, f.bind().name]"), + njs_str("F,bound F") }, + + { njs_str("var f = Object.defineProperty(function() {}, 'name', {get:()=>{throw Error('Oops')}});" + "f.bind().name"), + njs_str("Error: Oops") }, + + { njs_str("var f = function() {}; f.a = 'a'; [f.bind().a, f.a]"), + njs_str(",a") }, + + { njs_str("var f = function() {}; var bf = f.bind(); bf.b = 'b'; " + "[f.b, bf.b]"), + njs_str(",b") }, + + { njs_str("function f(x,y) {return {args:arguments,length:arguments.length}};" + "var nf = Function.prototype.bind.call(f, {}, 'a', 'b');" + "var o = new nf();[o.length, o.args[0]]"), + njs_str("2,a") }, + + { njs_str("function f(x,y) {return {args:arguments,length:arguments.length}};" + "var nf = Function.prototype.bind.call(f, {});" + "var o = new nf('a', 'b');[o.length, o.args[0]]"), + njs_str("2,a") }, + + { njs_str("var f = function(a,b) { this.a = a; this.b = b; };" + "f.prototype.X = 'X';" + "var bf = f.bind(null, 1,2);" + "var o = new bf(); " + "[Object.keys(o), o.X,(o instanceof f) && (o instanceof bf),bf.prototype]"), + njs_str("a,b,X,true,") }, + + { njs_str("var bArray = Array.bind(null, 10,16); bArray()"), + njs_str("10,16") }, + + { njs_str("var bArray = Array.bind(null, 10,16); new bArray()"), + njs_str("10,16") }, + + { njs_str("var bArray = Array.bind(null, 10); new bArray(16)"), + njs_str("10,16") }, + +#if 0 /* FIXME: refactor Bound calls (9.4.1.1[[Call]]). */ + { njs_str("function f(x,y) {return {args:arguments,length:arguments.length}};" + "var bf = f.bind({}, 'a'); var bbf = bf.bind({},'b'); var o = bbf('c');"), + "[o.args[0], o.args[2], o.length]" + njs_str("a,c,3") }, +#endif + { njs_str("var s = { toString: function() { return '123' } };" "var a = 'abc'; a.concat('???', s)"), njs_str("abc???123") }, @@ -7907,6 +7963,10 @@ static njs_unit_test_t njs_test[] = { njs_str("(function(){ var a = 1; return (function() { return a; })})().bind()()"), njs_str("1") }, + { njs_str("var r = (function(){ var a = 1; return (function() { return {a,args:arguments}; })})().bind()('b');" + "njs.dump(r)"), + njs_str("{a:1,args:{0:'b'}}") }, + { njs_str("function f() { var a = 1; function baz() { return a; } return baz; } f().bind()()"), njs_str("1") }, diff -r 4d414fccd53d -r 2488363b220b test/njs_expect_test.exp --- a/test/njs_expect_test.exp Thu Nov 14 21:01:28 2019 +0300 +++ b/test/njs_expect_test.exp Thu Nov 14 21:06:48 2019 +0300 @@ -266,6 +266,8 @@ njs_test { "1 a \\\[1,2]\r\nundefined\r\n>> "} {"var print = console.dump.bind(console); print(1, 'a', [1, 2])\r\n" "1 a \\\[\r\n 1,\r\n 2\r\n]\r\nundefined\r\n>> "} + {"var print = console.log.bind(console); print(console.a.a)\r\n" + "TypeError: cannot get property \"a\" of undefined*at console.log"} } # Backtraces for external objects From wi2p at hotmail.com Fri Nov 15 18:09:46 2019 From: wi2p at hotmail.com (kev jr) Date: Fri, 15 Nov 2019 18:09:46 +0000 Subject: Nginx TCP mode and TLS encryption Message-ID: Dear all, I have the following configuration : - 2 modems (M1 and M2) - 1 Nginx proxy (N1) - 2 remote servers (S1 and S2 accessible by internet) My both modems can communicate by TCP/IP but not able to works with TLS. I would like to perform the following configuration : M1 - > TCP communication-> N1->TCP encrypted by TLS - > S1. Which configuration I have to implement on Nginx in order to permit by modem to communicate with my remote server. And I want at least encrypt the communication between the Nginx proxy and the remote server (S1 and S2) Thank you for help, Regards, -------------- next part -------------- An HTML attachment was scrubbed... URL: From ea at exante.eu Sun Nov 17 14:37:03 2019 From: ea at exante.eu (Evgeniy Alekseev) Date: Sun, 17 Nov 2019 17:37:03 +0300 Subject: [PATCH] handle too many requests (429) response from auth subrequest Message-ID: <696df5e051a4d8ea757e.1574001423@arcanis-t580.arcanis.me> # HG changeset patch # User Evgeniy Alekseev # Date 1573998557 -10800 # Sun Nov 17 16:49:17 2019 +0300 # Node ID 696df5e051a4d8ea757ee5aadfaca8183192c8c7 # Parent 776d1bebdca217e9ff20a6a5b2a4c67b11e5dbc5 handle too many requests (429) response from auth subrequest The service which provides client authorization can also handle requests limits, which is usefull in particular if the main service does not handle any auth metadata. Suggested implementation passes 429 response from subrequest and also copies Retry-After header if it is set. diff -r 776d1bebdca2 -r 696df5e051a4 src/http/modules/ngx_http_auth_request_module.c --- a/src/http/modules/ngx_http_auth_request_module.c Wed Nov 06 19:03:18 2019 +0300 +++ b/src/http/modules/ngx_http_auth_request_module.c Sun Nov 17 16:49:17 2019 +0300 @@ -106,6 +106,9 @@ ngx_http_post_subrequest_t *ps; ngx_http_auth_request_ctx_t *ctx; ngx_http_auth_request_conf_t *arcf; + ngx_list_part_t *part; + ngx_uint_t i; + u_char *retry_after_header = (u_char *) "retry-after"; arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module); @@ -161,6 +164,44 @@ return ctx->status; } + if (ctx->status == NGX_HTTP_TOO_MANY_REQUESTS) { + sr = ctx->subrequest; + + part = &sr->headers_out.headers.part; + h = part->elts; + + for (i = 0 ; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (h[i].hash == 0) { + continue; + } + + if (ngx_strncasecmp(h[i].key.data, retry_after_header, h[i].key.len) == 0) { + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = h[i]; + + break; + } + } + + return ctx->status; + } + + if (ctx->status >= NGX_HTTP_OK && ctx->status < NGX_HTTP_SPECIAL_RESPONSE) { From arut at nginx.com Mon Nov 18 13:19:47 2019 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 18 Nov 2019 16:19:47 +0300 Subject: Nginx TCP mode and TLS encryption In-Reply-To: References: Message-ID: <20191118131947.osro44hxj7ze6fwe@Romans-MacBook-Pro.local> Hi, On Fri, Nov 15, 2019 at 06:09:46PM +0000, kev jr wrote: > Dear all, > > I have the following configuration : > - 2 modems (M1 and M2) > - 1 Nginx proxy (N1) > - 2 remote servers (S1 and S2 accessible by internet) > > My both modems can communicate by TCP/IP but not able to works with TLS. > > I would like to perform the following configuration : > M1 - > TCP communication-> N1->TCP encrypted by TLS - > S1. > > Which configuration I have to implement on Nginx in order to permit by modem to communicate with my remote server. And I want at least encrypt the communication between the Nginx proxy and the remote server (S1 and S2) You need to use the proxy_ssl directive to encrypt the connection between nginx and the proxied server. http://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_ssl Here's a configuration snippet: stream { server { listen 1234; proxy_pass remote_server.example.com:5678; proxy_ssl on; } } > > Thank you for help, > > Regards, > > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From dave.brennan at ultra-cpg.com Mon Nov 18 14:03:15 2019 From: dave.brennan at ultra-cpg.com (Dave Brennan) Date: Mon, 18 Nov 2019 14:03:15 +0000 Subject: Seg fault in http read event handler caused by rouge event call without context Message-ID: For the last few years we have been using the "nginx_upload" module to streamline result posting within our environment. With the introduction of nginx 1.17.5 we saw a large number of segment faults, causing us to revert to 1.17.4 on our development system. While isolating the fault we added an increase in debug messages to monitor the request and context variables being passed to event handlers. So a good response in 1.17.4 looks like this:- 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload handle pre alloc Request address = 0000563E9FE451F0 Context = 0000000000000000 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload Handler post alloc Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload_eval_path Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload eval state path Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload client read Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 do read upload client Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 process request body Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 upload md5 variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload File size variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload Body Handler Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 In 1.17.5 the event stream looks like this:- 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload handle pre alloc Request address = 0000558ADDD4F780 Context = 0000000000000000 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload Handler post alloc Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload_eval_path Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload eval state path Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload client read Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 do read upload client Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 process request body Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 process request body Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 upload md5 variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload File size variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload Body Handler Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 2019/11/13 14:21:52 [debug] 28086#28086: *3703 read upload clent request body Request address = 0000558ADDD4F780 Context = 0000000000000000 2019/11/13 14:21:52 [debug] 28086#28086: *3703 do read upload client Request address = 0000558ADDD4F780 Context = 0000000000000000 There appears to be an extra call to the request "read event" and although the request address has not changed the context address returned by:- ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); Returns NULL, which causes any reference to the context table to cause a segment fault. While it is possible to work round this by checking for a NULL context, the read event appears to be rouge when compared to the previous version of nginx, and I can only assume has been generated by code changes in 1.17.5. Dave Brennan Cyber Protection Senior Technologist CORVID Protect Holdings Limited, trading as CORVID Protect, is registered in Guernsey, company number FC034204, whose registered office is at Royal Bank Place, 1 Glategny Esplanade, St Peter Port, Guernsey GY1 4ND. CORVID Protect Holdings Limited is a subsidiary company of Ultra Electronics Holdings plc registered in England and Wales, company number 02830397, whose registered office is at 35 Portman Square, London W1H 6LR. Ultra Electronics is committed to safeguarding the privacy of all personal data: data privacy notice. Email communications may be monitored by us, as permitted by applicable law and regulations. This email is confidential and may also be privileged. If you have received this message in error you should notify the sender immediately by reply e-mail and delete the message from your system. -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Mon Nov 18 16:03:11 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 18 Nov 2019 19:03:11 +0300 Subject: Seg fault in http read event handler caused by rouge event call without context In-Reply-To: References: Message-ID: <20191118160311.GN12894@mdounin.ru> Hello! On Mon, Nov 18, 2019 at 02:03:15PM +0000, Dave Brennan wrote: > For the last few years we have been using the "nginx_upload" > module to streamline result posting within our environment. > > With the introduction of nginx 1.17.5 we saw a large number of > segment faults, causing us to revert to 1.17.4 on our > development system. > > While isolating the fault we added an increase in debug messages > to monitor the request and context variables being passed to > event handlers. > > So a good response in 1.17.4 looks like this:- > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload handle pre alloc Request address = 0000563E9FE451F0 Context = 0000000000000000 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload Handler post alloc Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload_eval_path Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload eval state path Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload client read Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 do read upload client Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 process request body Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 upload md5 variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload File size variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload Body Handler Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > In 1.17.5 the event stream looks like this:- > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload handle pre alloc Request address = 0000558ADDD4F780 Context = 0000000000000000 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload Handler post alloc Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload_eval_path Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload eval state path Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload client read Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 do read upload client Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 process request body Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 process request body Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 upload md5 variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload File size variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload Body Handler Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 read upload clent request body Request address = 0000558ADDD4F780 Context = 0000000000000000 > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 do read upload client Request address = 0000558ADDD4F780 Context = 0000000000000000 > > > There appears to be an extra call to the request "read event" > and although the request address has not changed the context > address returned by:- > > ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); > > Returns NULL, which causes any reference to the context table to cause a segment fault. > > While it is possible to work round this by checking for a NULL > context, the read event appears to be rouge when compared to the > previous version of nginx, and I can only assume has been > generated by code changes in 1.17.5. In 1.17.5, a mechanism to limit amount of data being read in a single event loop iteration was introduced. See the following commits: http://hg.nginx.org/nginx/rev/efd71d49bde0 http://hg.nginx.org/nginx/rev/9d2ad2fb4423 This is not expected to affect any properly written code, but may change various timings and sizes, revealing existing bugs in the code. Most importantly, this can affect SSL connections. Further, looking into the upload module code suggests that it is based on an old version of the nginx body reading code, and seems to miss this this fix from 2011 (http://hg.nginx.org/nginx/rev/cf334deeea66): changeset: 4072:cf334deeea66 user: Maxim Dounin date: Mon Sep 05 12:43:31 2011 +0000 summary: Bugfix: read event was not blocked after reading body. That is, the upload module fails to disable read event handler once request body reading is done, and any additional client activity might trigger a problem. In this particular case an additional event probably happens due to the limits introduced, and the code cannot handle it. I would expect the same problem to also happen with nginx 1.17.4 on real additional client activity. Either way, it looks like a bug in the upload module. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Mon Nov 18 16:39:35 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 18 Nov 2019 19:39:35 +0300 Subject: [nginx] Document modules as a way to patch nginx? In-Reply-To: <2b79a5e1387be7fb81ba484c12177c04@outters.eu> References: <3f5df681b7518aa8c8e08646af8ad09f@outters.eu> <2b79a5e1387be7fb81ba484c12177c04@outters.eu> Message-ID: <20191118163935.GO12894@mdounin.ru> Hello! On Thu, Nov 14, 2019 at 04:53:55PM +0100, Guillaume Outters wrote: > Last night, while trying to progress on my {{.}} syntax in config files > by recompiling and recompiling my nginx, I looked at > nginx_upstream_check_module source and incidentally discovered that > nginx modules where both powerful and easy to create. > > So easy and powerful that the [PATCH] I submitted in september and > october could be quickly set into a module, integrating nicely in nginx > build. > > So I wondered: couldn't this way of playing with nginx internals be > advertized as a notice in > http://nginx.org/en/docs/contributing_changes.html? > Something like: > > ---------------- > Many changes to nginx core source, including those requiring to patch > nginx, can be implemented as modules, which are easy to create, easy to > publish, and easy to integrate to nginx. > Before trying to contribute to nginx source, please contemplate > packaging your changes as a module. Thus: > - you will be able to test it in a clean way with your installation, > before submitting it to the core > - your specific needs can be implemented there without conflicting with > the community's ones > - you will be able to distribute it long before it is integrated to > nginx > - you will be able to run it on the very release of nginx your > production uses > ---------------- It currently says "make it clear why the suggested change is needed", and this implies the question "wouldn't it better to be a module". Making something a module might be or might not be beneficial, depending on various factors. Note well that many things can be implemented as modules only by introducing various hacks, such as re-implementing nginx internal functions. While this approach may appear to work, such a module will experience various unexpected problems on changes in nginx internals, and hence this is not recommended. -- Maxim Dounin http://mdounin.ru/ From serg.brester at sebres.de Mon Nov 18 16:48:46 2019 From: serg.brester at sebres.de (Sergey Brester) Date: Mon, 18 Nov 2019 17:48:46 +0100 Subject: Seg fault in http read event handler caused by rouge event call without context In-Reply-To: References: Message-ID: <97b92649f6ec24465052fe83cd273d24@sebres.de> Looks like [efd71d49bde0 [2]] could be indeed responsible for that: I see at least one state where rev->ready could remain 1 (after rev->available gets 0) e. g. deviation between blocks [efd71d49bde0#l10.8 [3]] and [efd71d49bde0#l11.8 [4]] where first did not reset rev->ready and for example if ngx_socket_nread in [efd71d49bde0#l10.38 [5]] would write 0 into rev->available, so rev->ready remains 1 yet. Maybe it should be changed to this one: if (rev->available == 0 && !rev->pending_eof) { if (rev->available <= 0 && !rev->pending_eof) { Also rev->available could remain negative if n != size and ngx_readv_chain or ngx_unix_recv wouldn't enter this blocks or if ngx_socket_nread failed (returns -1). And there are some code pices where nginx would expect positive ev->available. So I guess either one of this blocks are not fully correct, or perhaps the block [efd71d49bde0#l10.28 [6]] could be moved to end of the #if (NGX_HAVE_FIONREAD) block (before #endif at least in case !rev->pending_eof). Regards, Sergey. 18.11.2019 15:03, Dave Brennan wrote: > For the last few years we have been using the "nginx_upload" module to streamline result posting within our environment. > > With the introduction of nginx 1.17.5 we saw a large number of segment faults, causing us to revert to 1.17.4 on our development system. > > While isolating the fault we added an increase in debug messages to monitor the request and context variables being passed to event handlers. > > So a good response in 1.17.4 looks like this:- > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload handle pre alloc Request address = 0000563E9FE451F0 Context = 0000000000000000 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload Handler post alloc Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload_eval_path Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload eval state path Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload client read Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 do read upload client Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 process request body Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 upload md5 variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload File size variable Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > 2019/11/14 10:24:21 [debug] 12398#12398: *9770 Upload Body Handler Request address = 0000563E9FE451F0 Context = 0000563E9FE81CD8 > > In 1.17.5 the event stream looks like this:- > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload handle pre alloc Request address = 0000558ADDD4F780 Context = 0000000000000000 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload Handler post alloc Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload_eval_path Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload eval state path Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload client read Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 do read upload client Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 process request body Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 process request body Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 upload md5 variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload File size variable Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [debug] 28086#28086: *3703 Upload Body Handler Request address = 0000558ADDD4F780 Context = 0000558ADDD49FF8 > > 2019/11/13 14:21:52 [DEBUG] 28086#28086: *3703 READ UPLOAD CLENT REQUEST BODY REQUEST ADDRESS = 0000558ADDD4F780 CONTEXT = 0000000000000000 > > 2019/11/13 14:21:52 [DEBUG] 28086#28086: *3703 DO READ UPLOAD CLIENT REQUEST ADDRESS = 0000558ADDD4F780 CONTEXT = 0000000000000000 > > There appears to be an extra call to the request "read event" and although the request address has not changed the context address returned by:- > > ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); > > Returns NULL, which causes any reference to the context table to cause a segment fault. > > While it is possible to work round this by checking for a NULL context, the read event appears to be rouge when compared to the previous version of nginx, and I can only assume has been generated by code changes in 1.17.5. > > Dave Brennan > Cyber Protection Senior Technologist > > CORVID Protect Holdings Limited, trading as CORVID Protect, is registered in Guernsey, company number FC034204, whose registered office is at Royal Bank Place, 1 Glategny Esplanade, St Peter Port, Guernsey GY1 4ND. CORVID Protect Holdings Limited is a subsidiary company of Ultra Electronics Holdings plc registered in England and Wales, company number 02830397, whose registered office is at 35 Portman Square, London W1H 6LR. > > Ultra Electronics is committed to safeguarding the privacy of all personal data: data privacy notice. Email communications may be monitored by us, as permitted by applicable law and regulations. This email is confidential and may also be privileged. If you have received this message in error you should notify the sender immediately by reply e-mail and delete the message from your system. > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel [1] Links: ------ [1] http://mailman.nginx.org/mailman/listinfo/nginx-devel [2] https://hg.nginx.org/nginx/rev/efd71d49bde0 [3] https://hg.nginx.org/nginx/rev/efd71d49bde0#l10.8 [4] https://hg.nginx.org/nginx/rev/efd71d49bde0#l11.8 [5] https://hg.nginx.org/nginx/rev/efd71d49bde0#l10.38 [6] https://hg.nginx.org/nginx/rev/efd71d49bde0#l10.28 -------------- next part -------------- An HTML attachment was scrubbed... URL: From valery+nginxen at grid.net.ru Mon Nov 18 17:02:52 2019 From: valery+nginxen at grid.net.ru (Valery Kholodkov) Date: Mon, 18 Nov 2019 18:02:52 +0100 Subject: [nginx] Document modules as a way to patch nginx? In-Reply-To: <20191118163935.GO12894@mdounin.ru> References: <3f5df681b7518aa8c8e08646af8ad09f@outters.eu> <2b79a5e1387be7fb81ba484c12177c04@outters.eu> <20191118163935.GO12894@mdounin.ru> Message-ID: <344b2c6b-5bc7-b30b-1b64-aac874751c5b@grid.net.ru> Considering the definition of the term "hack" as well as the factual side that you just have stated, this renders Nginx modularity essentially dysfunctional from professional programming point of view. On 18-11-19 17:39, Maxim Dounin wrote: > Note well that many things can be implemented as modules only by > introducing various hacks, such as re-implementing nginx internal > functions. While this approach may appear to work, such a module > will experience various unexpected problems on changes in nginx > internals, and hence this is not recommended. -- Val From mdounin at mdounin.ru Mon Nov 18 17:11:43 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 18 Nov 2019 20:11:43 +0300 Subject: Seg fault in http read event handler caused by rouge event call without context In-Reply-To: <97b92649f6ec24465052fe83cd273d24@sebres.de> References: <97b92649f6ec24465052fe83cd273d24@sebres.de> Message-ID: <20191118171143.GP12894@mdounin.ru> Hello! On Mon, Nov 18, 2019 at 05:48:46PM +0100, Sergey Brester wrote: > Looks like [efd71d49bde0 [2]] could be indeed responsible for that: > > I see at least one state where rev->ready could remain 1 (after > rev->available gets 0) e. g. deviation between blocks > [efd71d49bde0#l10.8 [3]] and [efd71d49bde0#l11.8 [4]] where first did > not reset rev->ready and for example if ngx_socket_nread in > [efd71d49bde0#l10.38 [5]] would write 0 into rev->available, so > rev->ready remains 1 yet. There is no deviation here. The rev->available field holds the number of bytes available for reading (or -1 if it's not known), while rev->ready indicates if reading is possible. The reading can be possible even when rev->available is zero - notably, when there is a potential pending EOF. > Maybe it should be changed to this one: > > if (rev->available == 0 && !rev->pending_eof) { > > if (rev->available <= 0 && !rev->pending_eof) { No. Negative rev->available means we don't know how many bytes are available, but some are. It is wrong to stop reading in rev->available is negative. > Also rev->available could remain negative if n != size and > ngx_readv_chain or ngx_unix_recv wouldn't enter this blocks or if > ngx_socket_nread failed (returns -1). Sure, this is expected. > And there are some code pices where nginx would expect positive > ev->available. If you think you know places which expect only positive ev->available, please point them out. > So I guess either one of this blocks are not fully correct, or perhaps > the block [efd71d49bde0#l10.28 [6]] could be moved to end of the #if > (NGX_HAVE_FIONREAD) block (before #endif at least in case > !rev->pending_eof). The code in the NGX_HAVE_FIONREAD block does not require any particular event method and/or kernel version, so it cannot rely on rev->pending_eof being correctly set. -- Maxim Dounin http://mdounin.ru/ From serg.brester at sebres.de Mon Nov 18 19:05:39 2019 From: serg.brester at sebres.de (Sergey Brester) Date: Mon, 18 Nov 2019 20:05:39 +0100 Subject: Seg fault in http read event handler caused by rouge event call without context In-Reply-To: <20191118171143.GP12894@mdounin.ru> References: <97b92649f6ec24465052fe83cd273d24@sebres.de> <20191118171143.GP12894@mdounin.ru> Message-ID: <7c73f8e3667dedd0952c01420b3e92d9@sebres.de> 18.11.2019 18:11, Maxim Dounin wrote: > Hello! > > On Mon, Nov 18, 2019 at 05:48:46PM +0100, Sergey Brester wrote: > >> Looks like [efd71d49bde0 [2]] could be indeed responsible for that: I see at least one state where rev->ready could remain 1 (after rev->available gets 0) e. g. deviation between blocks [efd71d49bde0#l10.8 [3]] and [efd71d49bde0#l11.8 [4]] where first did not reset rev->ready and for example if ngx_socket_nread in [efd71d49bde0#l10.38 [5]] would write 0 into rev->available, so rev->ready remains 1 yet. > > There is no deviation here. The rev->available field holds the > number of bytes available for reading (or -1 if it's not known), > while rev->ready indicates if reading is possible. The reading > can be possible even WHEN REV->AVAILABLE IS ZERO - notably, when > there is a potential pending EOF. Sure, but I told about the case where rev->ready indicates the READING IS POSSIBLE, but REV->AVAILABLE IS LESS AS ZERO. If you mean this is to noglect, then it is ok, but somehow it looks backwards incompatible to me. At least other async-IO procedures (for example KQUEUE [1]) handle this differently. Anyway I saw never REV->AVAILABLE LESS AS ZERO, if rev->ready got 1 until now. Regards, Sergey. Links: ------ [1] https://github.com/nginx/nginx/commit/fac4c7bdf53ee7d8fec6568f1e9fecefcde6feba#diff-0b707fd972a54f561081982c62457febR155-R158 -------------- next part -------------- An HTML attachment was scrubbed... URL: From arut at nginx.com Tue Nov 19 11:18:56 2019 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 19 Nov 2019 11:18:56 +0000 Subject: [nginx] Updated comment after 776d1bebdca2. Message-ID: details: https://hg.nginx.org/nginx/rev/e84fb4991d74 branches: changeset: 7593:e84fb4991d74 user: Roman Arutyunyan date: Mon Nov 18 17:46:52 2019 +0300 description: Updated comment after 776d1bebdca2. diffstat: src/http/ngx_http_request.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 776d1bebdca2 -r e84fb4991d74 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Wed Nov 06 19:03:18 2019 +0300 +++ b/src/http/ngx_http_request.h Mon Nov 18 17:46:52 2019 +0300 @@ -510,7 +510,7 @@ struct ngx_http_request_s { /* * instead of using the request context data in * ngx_http_limit_conn_module and ngx_http_limit_req_module - * we use the single bits in the request structure + * we use the bit fields in the request structure */ unsigned limit_conn_set:1; unsigned limit_req_status:3; From arut at nginx.com Tue Nov 19 11:18:58 2019 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 19 Nov 2019 11:18:58 +0000 Subject: [nginx] Limit conn: limit_conn_dry_run directive. Message-ID: details: https://hg.nginx.org/nginx/rev/359b0ea2b067 branches: changeset: 7594:359b0ea2b067 user: Roman Arutyunyan date: Tue Nov 19 11:30:41 2019 +0300 description: Limit conn: limit_conn_dry_run directive. A new directive limit_conn_dry_run allows enabling the dry run mode. In this mode connections are not rejected, but reject status is logged as usual. diffstat: src/http/modules/ngx_http_limit_conn_module.c | 24 +++++++++++++++++++++++- src/stream/ngx_stream_limit_conn_module.c | 24 +++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diffs (144 lines): diff -r e84fb4991d74 -r 359b0ea2b067 src/http/modules/ngx_http_limit_conn_module.c --- a/src/http/modules/ngx_http_limit_conn_module.c Mon Nov 18 17:46:52 2019 +0300 +++ b/src/http/modules/ngx_http_limit_conn_module.c Tue Nov 19 11:30:41 2019 +0300 @@ -40,6 +40,7 @@ typedef struct { ngx_array_t limits; ngx_uint_t log_level; ngx_uint_t status_code; + ngx_flag_t dry_run; } ngx_http_limit_conn_conf_t; @@ -102,6 +103,13 @@ static ngx_command_t ngx_http_limit_con offsetof(ngx_http_limit_conn_conf_t, status_code), &ngx_http_limit_conn_status_bounds }, + { ngx_string("limit_conn_dry_run"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_limit_conn_conf_t, dry_run), + NULL }, + ngx_null_command }; @@ -200,6 +208,11 @@ ngx_http_limit_conn_handler(ngx_http_req if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_http_limit_conn_cleanup_all(r->pool); + + if (lccf->dry_run) { + return NGX_DECLINED; + } + return lccf->status_code; } @@ -221,10 +234,16 @@ ngx_http_limit_conn_handler(ngx_http_req ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(lccf->log_level, r->connection->log, 0, - "limiting connections by zone \"%V\"", + "limiting connections%s by zone \"%V\"", + lccf->dry_run ? ", dry run," : "", &limits[i].shm_zone->shm.name); ngx_http_limit_conn_cleanup_all(r->pool); + + if (lccf->dry_run) { + return NGX_DECLINED; + } + return lccf->status_code; } @@ -466,6 +485,7 @@ ngx_http_limit_conn_create_conf(ngx_conf conf->log_level = NGX_CONF_UNSET_UINT; conf->status_code = NGX_CONF_UNSET_UINT; + conf->dry_run = NGX_CONF_UNSET; return conf; } @@ -485,6 +505,8 @@ ngx_http_limit_conn_merge_conf(ngx_conf_ ngx_conf_merge_uint_value(conf->status_code, prev->status_code, NGX_HTTP_SERVICE_UNAVAILABLE); + ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0); + return NGX_CONF_OK; } diff -r e84fb4991d74 -r 359b0ea2b067 src/stream/ngx_stream_limit_conn_module.c --- a/src/stream/ngx_stream_limit_conn_module.c Mon Nov 18 17:46:52 2019 +0300 +++ b/src/stream/ngx_stream_limit_conn_module.c Tue Nov 19 11:30:41 2019 +0300 @@ -39,6 +39,7 @@ typedef struct { typedef struct { ngx_array_t limits; ngx_uint_t log_level; + ngx_flag_t dry_run; } ngx_stream_limit_conn_conf_t; @@ -89,6 +90,13 @@ static ngx_command_t ngx_stream_limit_c offsetof(ngx_stream_limit_conn_conf_t, log_level), &ngx_stream_limit_conn_log_levels }, + { ngx_string("limit_conn_dry_run"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_limit_conn_conf_t, dry_run), + NULL }, + ngx_null_command }; @@ -178,6 +186,11 @@ ngx_stream_limit_conn_handler(ngx_stream if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_stream_limit_conn_cleanup_all(s->connection->pool); + + if (lccf->dry_run) { + return NGX_DECLINED; + } + return NGX_STREAM_SERVICE_UNAVAILABLE; } @@ -199,10 +212,16 @@ ngx_stream_limit_conn_handler(ngx_stream ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(lccf->log_level, s->connection->log, 0, - "limiting connections by zone \"%V\"", + "limiting connections%s by zone \"%V\"", + lccf->dry_run ? ", dry run," : "", &limits[i].shm_zone->shm.name); ngx_stream_limit_conn_cleanup_all(s->connection->pool); + + if (lccf->dry_run) { + return NGX_DECLINED; + } + return NGX_STREAM_SERVICE_UNAVAILABLE; } @@ -444,6 +463,7 @@ ngx_stream_limit_conn_create_conf(ngx_co */ conf->log_level = NGX_CONF_UNSET_UINT; + conf->dry_run = NGX_CONF_UNSET; return conf; } @@ -461,6 +481,8 @@ ngx_stream_limit_conn_merge_conf(ngx_con ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR); + ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0); + return NGX_CONF_OK; } From arut at nginx.com Tue Nov 19 11:18:59 2019 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 19 Nov 2019 11:18:59 +0000 Subject: [nginx] Limit conn: $limit_conn_status variable. Message-ID: details: https://hg.nginx.org/nginx/rev/9606d93aa586 branches: changeset: 7595:9606d93aa586 user: Roman Arutyunyan date: Mon Nov 18 17:48:32 2019 +0300 description: Limit conn: $limit_conn_status variable. The variable takes one of the values: PASSED, REJECTED or REJECTED_DRY_RUN. diffstat: src/http/modules/ngx_http_limit_conn_module.c | 76 +++++++++++++++++++++++++- src/http/ngx_http_request.h | 2 +- src/stream/ngx_stream.h | 2 + src/stream/ngx_stream_limit_conn_module.c | 74 +++++++++++++++++++++++++- 4 files changed, 149 insertions(+), 5 deletions(-) diffs (339 lines): diff -r 359b0ea2b067 -r 9606d93aa586 src/http/modules/ngx_http_limit_conn_module.c --- a/src/http/modules/ngx_http_limit_conn_module.c Tue Nov 19 11:30:41 2019 +0300 +++ b/src/http/modules/ngx_http_limit_conn_module.c Mon Nov 18 17:48:32 2019 +0300 @@ -10,6 +10,11 @@ #include +#define NGX_HTTP_LIMIT_CONN_PASSED 1 +#define NGX_HTTP_LIMIT_CONN_REJECTED 2 +#define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN 3 + + typedef struct { u_char color; u_char len; @@ -49,6 +54,8 @@ static ngx_rbtree_node_t *ngx_http_limit static void ngx_http_limit_conn_cleanup(void *data); static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool); +static ngx_int_t ngx_http_limit_conn_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf); static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -56,6 +63,7 @@ static char *ngx_http_limit_conn_zone(ng void *conf); static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_limit_conn_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf); @@ -115,7 +123,7 @@ static ngx_command_t ngx_http_limit_con static ngx_http_module_t ngx_http_limit_conn_module_ctx = { - NULL, /* preconfiguration */ + ngx_http_limit_conn_add_variables, /* preconfiguration */ ngx_http_limit_conn_init, /* postconfiguration */ NULL, /* create main configuration */ @@ -145,6 +153,22 @@ ngx_module_t ngx_http_limit_conn_module }; +static ngx_http_variable_t ngx_http_limit_conn_vars[] = { + + { ngx_string("limit_conn_status"), NULL, + ngx_http_limit_conn_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + ngx_http_null_variable +}; + + +static ngx_str_t ngx_http_limit_conn_status[] = { + ngx_string("PASSED"), + ngx_string("REJECTED"), + ngx_string("REJECTED_DRY_RUN") +}; + + static ngx_int_t ngx_http_limit_conn_handler(ngx_http_request_t *r) { @@ -161,7 +185,7 @@ ngx_http_limit_conn_handler(ngx_http_req ngx_http_limit_conn_limit_t *limits; ngx_http_limit_conn_cleanup_t *lccln; - if (r->main->limit_conn_set) { + if (r->main->limit_conn_status) { return NGX_DECLINED; } @@ -187,7 +211,7 @@ ngx_http_limit_conn_handler(ngx_http_req continue; } - r->main->limit_conn_set = 1; + r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED; hash = ngx_crc32_short(key.data, key.len); @@ -210,9 +234,13 @@ ngx_http_limit_conn_handler(ngx_http_req ngx_http_limit_conn_cleanup_all(r->pool); if (lccf->dry_run) { + r->main->limit_conn_status = + NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN; return NGX_DECLINED; } + r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED; + return lccf->status_code; } @@ -241,9 +269,13 @@ ngx_http_limit_conn_handler(ngx_http_req ngx_http_limit_conn_cleanup_all(r->pool); if (lccf->dry_run) { + r->main->limit_conn_status = + NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN; return NGX_DECLINED; } + r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED; + return lccf->status_code; } @@ -467,6 +499,25 @@ ngx_http_limit_conn_init_zone(ngx_shm_zo } +static ngx_int_t +ngx_http_limit_conn_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->main->limit_conn_status == 0) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].len; + v->data = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].data; + + return NGX_OK; +} + + static void * ngx_http_limit_conn_create_conf(ngx_conf_t *cf) { @@ -674,6 +725,25 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_ static ngx_int_t +ngx_http_limit_conn_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_limit_conn_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; diff -r 359b0ea2b067 -r 9606d93aa586 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Tue Nov 19 11:30:41 2019 +0300 +++ b/src/http/ngx_http_request.h Mon Nov 18 17:48:32 2019 +0300 @@ -512,7 +512,7 @@ struct ngx_http_request_s { * ngx_http_limit_conn_module and ngx_http_limit_req_module * we use the bit fields in the request structure */ - unsigned limit_conn_set:1; + unsigned limit_conn_status:2; unsigned limit_req_status:3; unsigned limit_rate_set:1; diff -r 359b0ea2b067 -r 9606d93aa586 src/stream/ngx_stream.h --- a/src/stream/ngx_stream.h Tue Nov 19 11:30:41 2019 +0300 +++ b/src/stream/ngx_stream.h Mon Nov 18 17:48:32 2019 +0300 @@ -226,6 +226,8 @@ struct ngx_stream_session_s { unsigned stat_processing:1; unsigned health_check:1; + + unsigned limit_conn_status:2; }; diff -r 359b0ea2b067 -r 9606d93aa586 src/stream/ngx_stream_limit_conn_module.c --- a/src/stream/ngx_stream_limit_conn_module.c Tue Nov 19 11:30:41 2019 +0300 +++ b/src/stream/ngx_stream_limit_conn_module.c Mon Nov 18 17:48:32 2019 +0300 @@ -10,6 +10,11 @@ #include +#define NGX_STREAM_LIMIT_CONN_PASSED 1 +#define NGX_STREAM_LIMIT_CONN_REJECTED 2 +#define NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN 3 + + typedef struct { u_char color; u_char len; @@ -48,6 +53,8 @@ static ngx_rbtree_node_t *ngx_stream_lim static void ngx_stream_limit_conn_cleanup(void *data); static ngx_inline void ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool); +static ngx_int_t ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); static void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf); static char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -55,6 +62,7 @@ static char *ngx_stream_limit_conn_zone( void *conf); static char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_stream_limit_conn_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf); @@ -102,7 +110,7 @@ static ngx_command_t ngx_stream_limit_c static ngx_stream_module_t ngx_stream_limit_conn_module_ctx = { - NULL, /* preconfiguration */ + ngx_stream_limit_conn_add_variables, /* preconfiguration */ ngx_stream_limit_conn_init, /* postconfiguration */ NULL, /* create main configuration */ @@ -129,6 +137,22 @@ ngx_module_t ngx_stream_limit_conn_modu }; +static ngx_stream_variable_t ngx_stream_limit_conn_vars[] = { + + { ngx_string("limit_conn_status"), NULL, + ngx_stream_limit_conn_status_variable, 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + ngx_stream_null_variable +}; + + +static ngx_str_t ngx_stream_limit_conn_status[] = { + ngx_string("PASSED"), + ngx_string("REJECTED"), + ngx_string("REJECTED_DRY_RUN") +}; + + static ngx_int_t ngx_stream_limit_conn_handler(ngx_stream_session_t *s) { @@ -167,6 +191,8 @@ ngx_stream_limit_conn_handler(ngx_stream continue; } + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_PASSED; + hash = ngx_crc32_short(key.data, key.len); shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; @@ -188,9 +214,13 @@ ngx_stream_limit_conn_handler(ngx_stream ngx_stream_limit_conn_cleanup_all(s->connection->pool); if (lccf->dry_run) { + s->limit_conn_status = + NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN; return NGX_DECLINED; } + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED; + return NGX_STREAM_SERVICE_UNAVAILABLE; } @@ -219,9 +249,13 @@ ngx_stream_limit_conn_handler(ngx_stream ngx_stream_limit_conn_cleanup_all(s->connection->pool); if (lccf->dry_run) { + s->limit_conn_status = + NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN; return NGX_DECLINED; } + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED; + return NGX_STREAM_SERVICE_UNAVAILABLE; } @@ -446,6 +480,25 @@ ngx_stream_limit_conn_init_zone(ngx_shm_ } +static ngx_int_t +ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + if (s->limit_conn_status == 0) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ngx_stream_limit_conn_status[s->limit_conn_status - 1].len; + v->data = ngx_stream_limit_conn_status[s->limit_conn_status - 1].data; + + return NGX_OK; +} + + static void * ngx_stream_limit_conn_create_conf(ngx_conf_t *cf) { @@ -650,6 +703,25 @@ ngx_stream_limit_conn(ngx_conf_t *cf, ng static ngx_int_t +ngx_stream_limit_conn_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_limit_conn_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf) { ngx_stream_handler_pt *h; From arut at nginx.com Tue Nov 19 11:19:01 2019 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 19 Nov 2019 11:19:01 +0000 Subject: [nginx] Limit conn: added shared context. Message-ID: details: https://hg.nginx.org/nginx/rev/b45f052483b8 branches: changeset: 7596:b45f052483b8 user: Roman Arutyunyan date: Mon Nov 18 19:50:59 2019 +0300 description: Limit conn: added shared context. Previously only an rbtree was associated with a limit_conn. To make it possible to associate more data with a limit_conn, shared context is introduced similar to limit_req. Also, shared pool pointer is kept in a way similar to limit_req. diffstat: src/http/modules/ngx_http_limit_conn_module.c | 90 ++++++++++++-------------- src/stream/ngx_stream_limit_conn_module.c | 89 ++++++++++++------------- 2 files changed, 86 insertions(+), 93 deletions(-) diffs (439 lines): diff -r 9606d93aa586 -r b45f052483b8 src/http/modules/ngx_http_limit_conn_module.c --- a/src/http/modules/ngx_http_limit_conn_module.c Mon Nov 18 17:48:32 2019 +0300 +++ b/src/http/modules/ngx_http_limit_conn_module.c Mon Nov 18 19:50:59 2019 +0300 @@ -16,36 +16,43 @@ typedef struct { - u_char color; - u_char len; - u_short conn; - u_char data[1]; + u_char color; + u_char len; + u_short conn; + u_char data[1]; } ngx_http_limit_conn_node_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_rbtree_node_t *node; + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *node; } ngx_http_limit_conn_cleanup_t; typedef struct { - ngx_rbtree_t *rbtree; - ngx_http_complex_value_t key; + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; +} ngx_http_limit_conn_shctx_t; + + +typedef struct { + ngx_http_limit_conn_shctx_t *sh; + ngx_slab_pool_t *shpool; + ngx_http_complex_value_t key; } ngx_http_limit_conn_ctx_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_uint_t conn; + ngx_shm_zone_t *shm_zone; + ngx_uint_t conn; } ngx_http_limit_conn_limit_t; typedef struct { - ngx_array_t limits; - ngx_uint_t log_level; - ngx_uint_t status_code; - ngx_flag_t dry_run; + ngx_array_t limits; + ngx_uint_t log_level; + ngx_uint_t status_code; + ngx_flag_t dry_run; } ngx_http_limit_conn_conf_t; @@ -176,7 +183,6 @@ ngx_http_limit_conn_handler(ngx_http_req uint32_t hash; ngx_str_t key; ngx_uint_t i; - ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_pool_cleanup_t *cln; ngx_http_limit_conn_ctx_t *ctx; @@ -215,11 +221,9 @@ ngx_http_limit_conn_handler(ngx_http_req hash = ngx_crc32_short(key.data, key.len); - shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; + ngx_shmtx_lock(&ctx->shpool->mutex); - ngx_shmtx_lock(&shpool->mutex); - - node = ngx_http_limit_conn_lookup(ctx->rbtree, &key, hash); + node = ngx_http_limit_conn_lookup(&ctx->sh->rbtree, &key, hash); if (node == NULL) { @@ -227,10 +231,10 @@ ngx_http_limit_conn_handler(ngx_http_req + offsetof(ngx_http_limit_conn_node_t, data) + key.len; - node = ngx_slab_alloc_locked(shpool, n); + node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_http_limit_conn_cleanup_all(r->pool); if (lccf->dry_run) { @@ -251,7 +255,7 @@ ngx_http_limit_conn_handler(ngx_http_req lc->conn = 1; ngx_memcpy(lc->data, key.data, key.len); - ngx_rbtree_insert(ctx->rbtree, node); + ngx_rbtree_insert(&ctx->sh->rbtree, node); } else { @@ -259,7 +263,7 @@ ngx_http_limit_conn_handler(ngx_http_req if ((ngx_uint_t) lc->conn >= limits[i].conn) { - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(lccf->log_level, r->connection->log, 0, "limiting connections%s by zone \"%V\"", @@ -285,7 +289,7 @@ ngx_http_limit_conn_handler(ngx_http_req ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit conn: %08Xi %d", node->key, lc->conn); - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_conn_cleanup_t)); @@ -389,17 +393,15 @@ ngx_http_limit_conn_cleanup(void *data) { ngx_http_limit_conn_cleanup_t *lccln = data; - ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_http_limit_conn_ctx_t *ctx; ngx_http_limit_conn_node_t *lc; ctx = lccln->shm_zone->data; - shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr; node = lccln->node; lc = (ngx_http_limit_conn_node_t *) &node->color; - ngx_shmtx_lock(&shpool->mutex); + ngx_shmtx_lock(&ctx->shpool->mutex); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0, "limit conn cleanup: %08Xi %d", node->key, lc->conn); @@ -407,11 +409,11 @@ ngx_http_limit_conn_cleanup(void *data) lc->conn--; if (lc->conn == 0) { - ngx_rbtree_delete(ctx->rbtree, node); - ngx_slab_free_locked(shpool, node); + ngx_rbtree_delete(&ctx->sh->rbtree, node); + ngx_slab_free_locked(ctx->shpool, node); } - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); } @@ -437,8 +439,6 @@ ngx_http_limit_conn_init_zone(ngx_shm_zo ngx_http_limit_conn_ctx_t *octx = data; size_t len; - ngx_slab_pool_t *shpool; - ngx_rbtree_node_t *sentinel; ngx_http_limit_conn_ctx_t *ctx; ctx = shm_zone->data; @@ -457,42 +457,38 @@ ngx_http_limit_conn_init_zone(ngx_shm_zo return NGX_ERROR; } - ctx->rbtree = octx->rbtree; + ctx->sh = octx->sh; + ctx->shpool = octx->shpool; return NGX_OK; } - shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; if (shm_zone->shm.exists) { - ctx->rbtree = shpool->data; + ctx->sh = ctx->shpool->data; return NGX_OK; } - ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); - if (ctx->rbtree == NULL) { + ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_conn_shctx_t)); + if (ctx->sh == NULL) { return NGX_ERROR; } - shpool->data = ctx->rbtree; + ctx->shpool->data = ctx->sh; - sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); - if (sentinel == NULL) { - return NGX_ERROR; - } - - ngx_rbtree_init(ctx->rbtree, sentinel, + ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, ngx_http_limit_conn_rbtree_insert_value); len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; - shpool->log_ctx = ngx_slab_alloc(shpool, len); - if (shpool->log_ctx == NULL) { + ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); + if (ctx->shpool->log_ctx == NULL) { return NGX_ERROR; } - ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", + ngx_sprintf(ctx->shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", &shm_zone->shm.name); return NGX_OK; diff -r 9606d93aa586 -r b45f052483b8 src/stream/ngx_stream_limit_conn_module.c --- a/src/stream/ngx_stream_limit_conn_module.c Mon Nov 18 17:48:32 2019 +0300 +++ b/src/stream/ngx_stream_limit_conn_module.c Mon Nov 18 19:50:59 2019 +0300 @@ -16,35 +16,42 @@ typedef struct { - u_char color; - u_char len; - u_short conn; - u_char data[1]; + u_char color; + u_char len; + u_short conn; + u_char data[1]; } ngx_stream_limit_conn_node_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_rbtree_node_t *node; + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *node; } ngx_stream_limit_conn_cleanup_t; typedef struct { - ngx_rbtree_t *rbtree; - ngx_stream_complex_value_t key; + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; +} ngx_stream_limit_conn_shctx_t; + + +typedef struct { + ngx_stream_limit_conn_shctx_t *sh; + ngx_slab_pool_t *shpool; + ngx_stream_complex_value_t key; } ngx_stream_limit_conn_ctx_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_uint_t conn; + ngx_shm_zone_t *shm_zone; + ngx_uint_t conn; } ngx_stream_limit_conn_limit_t; typedef struct { - ngx_array_t limits; - ngx_uint_t log_level; - ngx_flag_t dry_run; + ngx_array_t limits; + ngx_uint_t log_level; + ngx_flag_t dry_run; } ngx_stream_limit_conn_conf_t; @@ -160,7 +167,6 @@ ngx_stream_limit_conn_handler(ngx_stream uint32_t hash; ngx_str_t key; ngx_uint_t i; - ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_pool_cleanup_t *cln; ngx_stream_limit_conn_ctx_t *ctx; @@ -195,11 +201,9 @@ ngx_stream_limit_conn_handler(ngx_stream hash = ngx_crc32_short(key.data, key.len); - shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; + ngx_shmtx_lock(&ctx->shpool->mutex); - ngx_shmtx_lock(&shpool->mutex); - - node = ngx_stream_limit_conn_lookup(ctx->rbtree, &key, hash); + node = ngx_stream_limit_conn_lookup(&ctx->sh->rbtree, &key, hash); if (node == NULL) { @@ -207,10 +211,10 @@ ngx_stream_limit_conn_handler(ngx_stream + offsetof(ngx_stream_limit_conn_node_t, data) + key.len; - node = ngx_slab_alloc_locked(shpool, n); + node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_stream_limit_conn_cleanup_all(s->connection->pool); if (lccf->dry_run) { @@ -231,7 +235,7 @@ ngx_stream_limit_conn_handler(ngx_stream lc->conn = 1; ngx_memcpy(lc->data, key.data, key.len); - ngx_rbtree_insert(ctx->rbtree, node); + ngx_rbtree_insert(&ctx->sh->rbtree, node); } else { @@ -239,7 +243,7 @@ ngx_stream_limit_conn_handler(ngx_stream if ((ngx_uint_t) lc->conn >= limits[i].conn) { - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(lccf->log_level, s->connection->log, 0, "limiting connections%s by zone \"%V\"", @@ -265,7 +269,7 @@ ngx_stream_limit_conn_handler(ngx_stream ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "limit conn: %08Xi %d", node->key, lc->conn); - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); cln = ngx_pool_cleanup_add(s->connection->pool, sizeof(ngx_stream_limit_conn_cleanup_t)); @@ -370,17 +374,15 @@ ngx_stream_limit_conn_cleanup(void *data { ngx_stream_limit_conn_cleanup_t *lccln = data; - ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_stream_limit_conn_ctx_t *ctx; ngx_stream_limit_conn_node_t *lc; ctx = lccln->shm_zone->data; - shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr; node = lccln->node; lc = (ngx_stream_limit_conn_node_t *) &node->color; - ngx_shmtx_lock(&shpool->mutex); + ngx_shmtx_lock(&ctx->shpool->mutex); ngx_log_debug2(NGX_LOG_DEBUG_STREAM, lccln->shm_zone->shm.log, 0, "limit conn cleanup: %08Xi %d", node->key, lc->conn); @@ -388,11 +390,11 @@ ngx_stream_limit_conn_cleanup(void *data lc->conn--; if (lc->conn == 0) { - ngx_rbtree_delete(ctx->rbtree, node); - ngx_slab_free_locked(shpool, node); + ngx_rbtree_delete(&ctx->sh->rbtree, node); + ngx_slab_free_locked(ctx->shpool, node); } - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); } @@ -418,8 +420,6 @@ ngx_stream_limit_conn_init_zone(ngx_shm_ ngx_stream_limit_conn_ctx_t *octx = data; size_t len; - ngx_slab_pool_t *shpool; - ngx_rbtree_node_t *sentinel; ngx_stream_limit_conn_ctx_t *ctx; ctx = shm_zone->data; @@ -438,42 +438,39 @@ ngx_stream_limit_conn_init_zone(ngx_shm_ return NGX_ERROR; } - ctx->rbtree = octx->rbtree; + ctx->sh = octx->sh; + ctx->shpool = octx->shpool; return NGX_OK; } - shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; if (shm_zone->shm.exists) { - ctx->rbtree = shpool->data; + ctx->sh = ctx->shpool->data; return NGX_OK; } - ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); - if (ctx->rbtree == NULL) { + ctx->sh = ngx_slab_alloc(ctx->shpool, + sizeof(ngx_stream_limit_conn_shctx_t)); + if (ctx->sh == NULL) { return NGX_ERROR; } - shpool->data = ctx->rbtree; + ctx->shpool->data = ctx->sh; - sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); - if (sentinel == NULL) { - return NGX_ERROR; - } - - ngx_rbtree_init(ctx->rbtree, sentinel, + ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, ngx_stream_limit_conn_rbtree_insert_value); len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; - shpool->log_ctx = ngx_slab_alloc(shpool, len); - if (shpool->log_ctx == NULL) { + ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); + if (ctx->shpool->log_ctx == NULL) { return NGX_ERROR; } - ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", + ngx_sprintf(ctx->shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", &shm_zone->shm.name); return NGX_OK; From mdounin at mdounin.ru Tue Nov 19 12:32:51 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 19 Nov 2019 15:32:51 +0300 Subject: Seg fault in http read event handler caused by rouge event call without context In-Reply-To: <7c73f8e3667dedd0952c01420b3e92d9@sebres.de> References: <97b92649f6ec24465052fe83cd273d24@sebres.de> <20191118171143.GP12894@mdounin.ru> <7c73f8e3667dedd0952c01420b3e92d9@sebres.de> Message-ID: <20191119123251.GS12894@mdounin.ru> Hello! On Mon, Nov 18, 2019 at 08:05:39PM +0100, Sergey Brester wrote: > 18.11.2019 18:11, Maxim Dounin wrote: > > > Hello! > > > > On Mon, Nov 18, 2019 at 05:48:46PM +0100, Sergey Brester wrote: > > > >> Looks like [efd71d49bde0 [2]] could be indeed responsible for that: I see at least one state where rev->ready could remain 1 (after rev->available gets 0) e. g. deviation between blocks [efd71d49bde0#l10.8 [3]] and [efd71d49bde0#l11.8 [4]] where first did not reset rev->ready and for example if ngx_socket_nread in [efd71d49bde0#l10.38 [5]] would write 0 into rev->available, so rev->ready remains 1 yet. > > > > There is no deviation here. The rev->available field holds the > > number of bytes available for reading (or -1 if it's not known), > > while rev->ready indicates if reading is possible. The reading > > can be possible even WHEN REV->AVAILABLE IS ZERO - notably, when > > there is a potential pending EOF. > > Sure, > but I told about the case where rev->ready indicates the READING IS > POSSIBLE, > but REV->AVAILABLE IS LESS AS ZERO. > > If you mean this is to noglect, then it is ok, but somehow it looks > backwards incompatible to me. > At least other async-IO procedures (for example KQUEUE [1]) handle this > differently. The code you are referring to defines how nginx behaves when rev->available becomes zero or negative after it is decremented by the amount of bytes read. In case of kqueue, rev->available is set to 0 to indicate there are no data to read, and rev->ready is also set to 0 unless pending EOF is indicated by kqueue. In case of unspecified event methods, we cannot rely on the pending EOF indication (as this indication is only available with kqueue and with epoll with recent enough kernels). As such, we can only reset rev->ready when rev->available becomes negative (and then set to 0), but not when rev->available becomes exactly zero. > Anyway I saw never REV->AVAILABLE LESS AS ZERO, if rev->ready got 1 > until now. This is explained in the commit log of the revant commit. Quoting it here: : With other event methods rev->available is now set to -1 when the socket : is ready for reading. Later in ngx_recv() and ngx_recv_chain(), if : full buffer is received, real number of bytes in the socket buffer is : retrieved using ioctl(FIONREAD). Reading more than this number of bytes : ensures that even with edge-triggered event methods the event will be : triggered again, so it is safe to stop processing of the socket and : switch to other connections. Further, it is now explicitly documented in the relevant comment in src/event/ngx_event.h, see here: http://hg.nginx.org/nginx/rev/efd71d49bde0#l9.1 The rev->available set to -1 now indicates that there are unspecified amount of bytes to read. Previously rev->available was never negative (and cannot be negative on most platforms, given that it was a single bit field), and certainly this is a change. And in theory it can cause problems if some code relies on the previous semantics of rev->available being 0 or 1 with epoll, and checks for "rev->available == 1" for some reason (tests for "rev->available", "!rev->available" and "rev->available == 0" will work correctly without modifications). But in practice I wouldn't expect this change to cause any problems with properly written code. As already outlined in my reply to the original message, the reason for the segmentation faults observed seems to be failure of the upload module to block events after reading the request body. -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Tue Nov 19 12:36:45 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 19 Nov 2019 12:36:45 +0000 Subject: [njs] Version 0.3.7. Message-ID: details: https://hg.nginx.org/njs/rev/9e5ef927c7ea branches: changeset: 1240:9e5ef927c7ea user: Dmitry Volyntsev date: Tue Nov 19 15:35:24 2019 +0300 description: Version 0.3.7. diffstat: CHANGES | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 55 insertions(+), 0 deletions(-) diffs (62 lines): diff -r 2488363b220b -r 9e5ef927c7ea CHANGES --- a/CHANGES Thu Nov 14 21:06:48 2019 +0300 +++ b/CHANGES Tue Nov 19 15:35:24 2019 +0300 @@ -1,3 +1,58 @@ + +Changes with njs 0.3.7 19 Nov 2019 + + nginx modules: + + *) Improvement: refactored iteration over external objects. + + Core: + + *) Feature: added Object.assign(). + + *) Feature: added Array.prototype.copyWithin(). + + *) Feature: added support for labels in console.time(). + + *) Change: removed console.help() from CLI. + + *) Improvement: moved constructors and top-level objects to + global object. + + *) Improvement: arguments validation for configure script. + + *) Improvement: refactored JSON methods. + + *) Bugfix: fixed heap-buffer-overflow in njs_array_reverse_iterator() + function. The following functions were affected: + Array.prototype.lastIndexOf(), Array.prototype.reduceRight(). + + *) Bugfix: fixed [[Prototype]] slot of NativeErrors. + + *) Bugfix: fixed NativeError.prototype.message properties. + + *) Bugfix: added conversion of "this" value to object in + Array.prototype functions. + + *) Bugfix: fixed iterator for Array.prototype.find() and + Array.prototype.findIndex() functions. + + *) Bugfix: fixed Array.prototype.includes() and + Array.prototype.join() with "undefined" argument. + + *) Bugfix: fixed "constructor" property of "Hash" and "Hmac" + objects. + + *) Bugfix: fixed "__proto__" property of getters and setters. + + *) Bugfix: fixed "Date" object string formatting. + + *) Bugfix: fixed handling of NaN and -0 arguments in Math.min() + and Math.max(). + + *) Bugfix: fixed Math.round() according to the specification. + + *) Bugfix: reimplemented "bound" functions according to + the specification. Changes with njs 0.3.6 22 Oct 2019 From xeioex at nginx.com Tue Nov 19 12:36:46 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 19 Nov 2019 12:36:46 +0000 Subject: [njs] Added tag 0.3.7 for changeset 9e5ef927c7ea Message-ID: details: https://hg.nginx.org/njs/rev/5ad25ec92f48 branches: changeset: 1241:5ad25ec92f48 user: Dmitry Volyntsev date: Tue Nov 19 15:35:55 2019 +0300 description: Added tag 0.3.7 for changeset 9e5ef927c7ea diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff -r 9e5ef927c7ea -r 5ad25ec92f48 .hgtags --- a/.hgtags Tue Nov 19 15:35:24 2019 +0300 +++ b/.hgtags Tue Nov 19 15:35:55 2019 +0300 @@ -30,3 +30,4 @@ c65a4be9867d434ca449a18d868305d5dcd5b91b 8eadbb3a7c7b7c3426f73adabfa251cd9d296752 0.3.4 b7fa83f27f1b64443b88c990e1851f59583b2182 0.3.5 7b302775917b72537e1abdbd5dc9d04e55a7d582 0.3.6 +9e5ef927c7eaed003de3e5e4a16fa3eab08de7f7 0.3.7 From dave.brennan at ultra-cpg.com Tue Nov 19 14:11:56 2019 From: dave.brennan at ultra-cpg.com (Dave Brennan) Date: Tue, 19 Nov 2019 14:11:56 +0000 Subject: Seg fault in http read event handler caused by rouge event call without context In-Reply-To: <7c73f8e3667dedd0952c01420b3e92d9@sebres.de> References: <97b92649f6ec24465052fe83cd273d24@sebres.de> <20191118171143.GP12894@mdounin.ru> <7c73f8e3667dedd0952c01420b3e92d9@sebres.de> Message-ID: <976cfe352fcc49c28a66f60ef9635993@ultra-cpg.com> Shutting down the event handler after the body is received fixes the problem. Regards Dave -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Tue Nov 19 14:24:32 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 19 Nov 2019 14:24:32 +0000 Subject: [nginx] nginx-1.17.6-RELEASE Message-ID: details: https://hg.nginx.org/nginx/rev/de68d0d94320 branches: changeset: 7597:de68d0d94320 user: Maxim Dounin date: Tue Nov 19 17:18:58 2019 +0300 description: nginx-1.17.6-RELEASE diffstat: docs/xml/nginx/changes.xml | 32 ++++++++++++++++++++++++++++++++ 1 files changed, 32 insertions(+), 0 deletions(-) diffs (42 lines): diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,38 @@ + + + + +?????????? $proxy_protocol_server_addr ? $proxy_protocol_server_port. + + +the $proxy_protocol_server_addr and $proxy_protocol_server_port variables. + + + + + +????????? limit_conn_dry_run. + + +the "limit_conn_dry_run" directive. + + + + + +?????????? $limit_req_status ? $limit_conn_status. + + +the $limit_req_status and $limit_conn_status variables. + + + + + + From mdounin at mdounin.ru Tue Nov 19 14:24:34 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 19 Nov 2019 14:24:34 +0000 Subject: [nginx] release-1.17.6 tag Message-ID: details: https://hg.nginx.org/nginx/rev/d13eddd9e252 branches: changeset: 7598:d13eddd9e252 user: Maxim Dounin date: Tue Nov 19 17:18:58 2019 +0300 description: release-1.17.6 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -444,3 +444,4 @@ 2fc9f853a6b7cd29dc84e0af2ed3cf78e0da6ca8 ed4303aa1b31a9aad5440640c0840d9d0af45fed release-1.17.3 ce2ced3856909f36f8130c99eaa4dbdbae636ddc release-1.17.4 9af0dddbddb2c368bfedd2801bc100ffad01e19b release-1.17.5 +de68d0d94320cbf033599c6f3ca37e5335c67fd7 release-1.17.6 From xeioex at nginx.com Tue Nov 19 16:31:58 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 19 Nov 2019 16:31:58 +0000 Subject: [njs] Version bump. Message-ID: details: https://hg.nginx.org/njs/rev/11850951c75c branches: changeset: 1242:11850951c75c user: Dmitry Volyntsev date: Tue Nov 19 19:17:06 2019 +0300 description: Version bump. diffstat: src/njs.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 5ad25ec92f48 -r 11850951c75c src/njs.h --- a/src/njs.h Tue Nov 19 15:35:55 2019 +0300 +++ b/src/njs.h Tue Nov 19 19:17:06 2019 +0300 @@ -11,7 +11,7 @@ #include -#define NJS_VERSION "0.3.7" +#define NJS_VERSION "0.3.8" #include /* STDOUT_FILENO, STDERR_FILENO */ From xeioex at nginx.com Tue Nov 19 16:31:59 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 19 Nov 2019 16:31:59 +0000 Subject: [njs] Detecting standard data types in configure script. Message-ID: details: https://hg.nginx.org/njs/rev/e4383fdfdc82 branches: changeset: 1243:e4383fdfdc82 user: Dmitry Volyntsev date: Tue Nov 19 19:17:09 2019 +0300 description: Detecting standard data types in configure script. diffstat: auto/time | 15 ------- auto/types | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure | 1 + 3 files changed, 121 insertions(+), 15 deletions(-) diffs (160 lines): diff -r 11850951c75c -r e4383fdfdc82 auto/time --- a/auto/time Tue Nov 19 19:17:06 2019 +0300 +++ b/auto/time Tue Nov 19 19:17:09 2019 +0300 @@ -34,21 +34,6 @@ if [ $njs_found = no ]; then fi -njs_feature="sizeof(time_t)" -njs_feature_name=NJS_TIME_T_SIZE -njs_feature_run=value -njs_feature_incs= -njs_feature_libs= -njs_feature_test="#include - #include - - int main(void) { - printf(\"%zu\", sizeof(time_t)); - return 0; - }" -. auto/feature - - # Linux, FreeBSD, MacOSX. njs_feature="struct tm.tm_gmtoff" diff -r 11850951c75c -r e4383fdfdc82 auto/types --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/auto/types Tue Nov 19 19:17:09 2019 +0300 @@ -0,0 +1,120 @@ + +# Copyright (C) Igor Sysoev +# Copyright (C) NGINX, Inc. + + +# Sizes of C types. + +# "-Wall -Werror" or similar constraints in default CFLAGS may require +# to use "%zu" format to printf() result of sizeof(). But "%zu" may +# be unavailable, so the "(int)" cast is a simple and portable solution: +# printf("%d", (int) sizeof(TYPE)); + + +njs_feature="sizeof(int)" +njs_feature_name=NJS_INT_SIZE +njs_feature_run=value +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#include + + int main() { + printf(\"%d\", (int) sizeof(int)); + return 0; + }" +. auto/feature + + +njs_feature="sizeof(u_int)" +njs_feature_name=NJS_UINT_SIZE +njs_feature_run=value +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#include + #include + + int main() { + printf(\"%d\", (int) sizeof(u_int)); + return 0; + }" +. auto/feature + + +njs_feature="sizeof(void *)" +njs_feature_name=NJS_PTR_SIZE +njs_feature_run=value +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#include + + int main() { + printf(\"%d\", (int) sizeof(void *)); + return 0; + }" +. auto/feature + + +njs_feature="sizeof(uintptr_t)" +njs_feature_name=NJS_UINTPTR_T_SIZE +njs_feature_run=value +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#include + #include + + int main() { + printf(\"%d\", (int) sizeof(uintptr_t)); + return 0; + }" +. auto/feature + + +case "$njs_feature_value" in + 8) NJS_64BIT=1 ;; + *) NJS_64BIT=0 ;; +esac + + +njs_feature="sizeof(size_t)" +njs_feature_name=NJS_SIZE_T_SIZE +njs_feature_run=value +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#include + + int main() { + printf(\"%d\", (int) sizeof(size_t)); + return 0; + }" +. auto/feature + + +njs_feature="sizeof(off_t)" +njs_feature_name=NJS_OFF_T_SIZE +njs_feature_run=value +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#define _FILE_OFFSET_BITS 64 + #include + #include + + int main() { + printf(\"%d\", (int) sizeof(off_t)); + return 0; + }" +. auto/feature + + +njs_feature="sizeof(time_t)" +njs_feature_name=NJS_TIME_T_SIZE +njs_feature_run=value +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#include + #include + + int main(void) { + printf(\"%d\", (int) sizeof(time_t)); + return 0; + }" +. auto/feature diff -r 11850951c75c -r e4383fdfdc82 configure --- a/configure Tue Nov 19 19:17:06 2019 +0300 +++ b/configure Tue Nov 19 19:17:09 2019 +0300 @@ -47,6 +47,7 @@ NJS_LIBRT= . auto/os . auto/options . auto/cc +. auto/types . auto/clang . auto/time . auto/memalign From xeioex at nginx.com Tue Nov 19 16:31:59 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 19 Nov 2019 16:31:59 +0000 Subject: [njs] Fixed Object.getPrototypeOf() according to the specification. Message-ID: details: https://hg.nginx.org/njs/rev/d4fdf0fc449d branches: changeset: 1244:d4fdf0fc449d user: Artem S. Povalyukhin date: Tue Nov 19 17:22:53 2019 +0300 description: Fixed Object.getPrototypeOf() according to the specification. This closes #252 issue on Github. diffstat: src/njs_object.c | 10 ++++++++++ src/test/njs_unit_test.c | 8 +++----- 2 files changed, 13 insertions(+), 5 deletions(-) diffs (45 lines): diff -r e4383fdfdc82 -r d4fdf0fc449d src/njs_object.c --- a/src/njs_object.c Tue Nov 19 19:17:09 2019 +0300 +++ b/src/njs_object.c Tue Nov 19 17:22:53 2019 +0300 @@ -1387,6 +1387,7 @@ static njs_int_t njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { + uint32_t index; njs_value_t *value; value = njs_arg(args, nargs, 1); @@ -1396,6 +1397,15 @@ njs_object_get_prototype_of(njs_vm_t *vm return NJS_OK; } + if (!njs_is_null_or_undefined(value)) { + index = njs_primitive_prototype_index(value->type); + + njs_set_type_object(&vm->retval, &vm->prototypes[index].object, + njs_object_value_type(value->type)); + + return NJS_OK; + } + njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); diff -r e4383fdfdc82 -r d4fdf0fc449d src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue Nov 19 19:17:09 2019 +0300 +++ b/src/test/njs_unit_test.c Tue Nov 19 17:22:53 2019 +0300 @@ -11064,11 +11064,9 @@ static njs_unit_test_t njs_test[] = "Object.getPrototypeOf(o) === Object.prototype"), njs_str("true") }, - { njs_str("Object.getPrototypeOf(1)"), - njs_str("TypeError: cannot convert number argument to object") }, - - { njs_str("Object.getPrototypeOf('a')"), - njs_str("TypeError: cannot convert string argument to object") }, + { njs_str("[true, 42, '' /*, Symbol()*/]" + ".every((x) => Object.getPrototypeOf(x) == Object.getPrototypeOf(Object(x)))"), + njs_str("true") }, { njs_str("var p = {}; var o = Object.create(p);" "p.isPrototypeOf(o)"), From xeioex at nginx.com Tue Nov 19 16:31:59 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 19 Nov 2019 16:31:59 +0000 Subject: [njs] Fixed handling of "init" flag in njs_vm_create(). Message-ID: details: https://hg.nginx.org/njs/rev/452ce96df2e3 branches: changeset: 1245:452ce96df2e3 user: Dmitry Volyntsev date: Tue Nov 19 19:19:53 2019 +0300 description: Fixed handling of "init" flag in njs_vm_create(). diffstat: src/njs_shell.c | 2 +- src/njs_vm.c | 2 +- src/test/njs_interactive_test.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diffs (35 lines): diff -r d4fdf0fc449d -r 452ce96df2e3 src/njs_shell.c --- a/src/njs_shell.c Tue Nov 19 17:22:53 2019 +0300 +++ b/src/njs_shell.c Tue Nov 19 19:19:53 2019 +0300 @@ -243,7 +243,7 @@ main(int argc, char **argv) vm_options.file.start = (u_char *) opts.file; vm_options.file.length = njs_strlen(opts.file); - vm_options.init = !opts.interactive; + vm_options.init = 1; vm_options.accumulative = opts.interactive; vm_options.disassemble = opts.disassemble; vm_options.backtrace = 1; diff -r d4fdf0fc449d -r 452ce96df2e3 src/njs_vm.c --- a/src/njs_vm.c Tue Nov 19 17:22:53 2019 +0300 +++ b/src/njs_vm.c Tue Nov 19 19:19:53 2019 +0300 @@ -85,7 +85,7 @@ njs_vm_create(njs_vm_opt_t *options) vm->debug = debug; } - if (options->accumulative) { + if (options->init) { ret = njs_vm_init(vm); if (njs_slow_path(ret != NJS_OK)) { return NULL; diff -r d4fdf0fc449d -r 452ce96df2e3 src/test/njs_interactive_test.c --- a/src/test/njs_interactive_test.c Tue Nov 19 17:22:53 2019 +0300 +++ b/src/test/njs_interactive_test.c Tue Nov 19 19:19:53 2019 +0300 @@ -289,6 +289,7 @@ njs_interactive_test(njs_bool_t verbose) njs_memzero(&options, sizeof(njs_vm_opt_t)); + options.init = 1; options.accumulative = 1; options.backtrace = 1; From git at abrody.com Tue Nov 19 17:08:18 2019 From: git at abrody.com (Andy Brody) Date: Tue, 19 Nov 2019 12:08:18 -0500 Subject: [PATCH] SSL: add new variable $ssl_client_fingerprint_sha256 Message-ID: <5e7ba820a2da690eeb11.1574183298@abrody.com> The existing $ssl_client_fingerprint uses SHA-1 to compute the certificate digest. This is no longer considered secure. The CA/Browser forum voted to sunset use of SHA1 in 2014, and all major CAs ceased issuing SHA1 certificates at the end of 2015. The first publicly known collision in the full SHA-1 was discovered in 2017. NGINX users should be able to switch to SHA-256 for TLS client certificate fingerprints. Ideally $ssl_client_fingerprint should be deprecated. Add a new variable, $ssl_client_fingerprint_sha256, which uses SHA-256 instead. Refactor ngx_ssl_get_fingerprint() into a new function ngx_ssl_get_fingerprint_generic(), which accepts any OpenSSL digest function, and add ngx_ssl_get_fingerprint_sha256(). src/event/ngx_event_openssl.c | 17 ++++++++++++++++- src/event/ngx_event_openssl.h | 4 ++++ src/http/modules/ngx_http_ssl_module.c | 3 +++ src/stream/ngx_stream_ssl_module.c | 3 +++ 4 files changed, 26 insertions(+), 1 deletions(-) -------------- next part -------------- A non-text attachment was scrubbed... Name: nginx.patch Type: text/x-patch Size: 4179 bytes Desc: not available URL: From git at abrody.com Tue Nov 19 17:22:29 2019 From: git at abrody.com (Andy Brody) Date: Tue, 19 Nov 2019 12:22:29 -0500 Subject: [PATCH] Tests: add ssl_client_fingerprint.t to test fingerprint variables Message-ID: This patch adds tests for the corresponding patch to nginx. ssl_client_fingerprint.t | 139 +++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 139 insertions(+), 0 deletions(-) -------------- next part -------------- A non-text attachment was scrubbed... Name: nginx-tests.patch Type: text/x-patch Size: 4135 bytes Desc: not available URL: From xeioex at nginx.com Tue Nov 19 20:05:00 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 19 Nov 2019 20:05:00 +0000 Subject: [njs] Introduced njs_fuzzer target. Message-ID: details: https://hg.nginx.org/njs/rev/884d3a86beab branches: changeset: 1246:884d3a86beab user: Dmitry Volyntsev date: Tue Nov 19 19:32:26 2019 +0300 description: Introduced njs_fuzzer target. diffstat: auto/make | 27 ++++ src/njs_shell.c | 324 +++++++++++++++++++++++++++++++++---------------------- 2 files changed, 218 insertions(+), 133 deletions(-) diffs (494 lines): diff -r 452ce96df2e3 -r 884d3a86beab auto/make --- a/auto/make Tue Nov 19 19:19:53 2019 +0300 +++ b/auto/make Tue Nov 19 19:32:26 2019 +0300 @@ -121,6 +121,31 @@ END fi + +# njs fuzzer. + +cat << END >> $NJS_MAKEFILE + +$NJS_BUILD_DIR/njs_process_script_fuzzer.o: \\ + src/njs_shell.c + \$(NJS_CC) -c \$(CFLAGS) $NJS_LIB_AUX_CFLAGS \\ + \$(NJS_LIB_INCS) -Injs \\ + -DNJS_FUZZER_TARGET \\ + -o $NJS_BUILD_DIR/njs_process_script_fuzzer.o \\ + src/njs_shell.c + +$NJS_BUILD_DIR/njs_process_script_fuzzer: \\ + $NJS_BUILD_DIR/libnjs.a \\ + $NJS_BUILD_DIR/njs_process_script_fuzzer.o + \$(CXX) \$(CXXFLAGS) -o $NJS_BUILD_DIR/njs_process_script_fuzzer \\ + $NJS_LIB_AUX_CFLAGS \$(NJS_LIB_INCS) -Injs \\ + \$(LIB_FUZZING_ENGINE) \\ + $NJS_BUILD_DIR/njs_process_script_fuzzer.o \\ + $NJS_BUILD_DIR/libnjs.a \\ + -lm $NJS_LIBS $NJS_LIB_AUX_LIBS + +END + # lib tests. for njs_src in $NJS_LIB_TEST_SRCS @@ -186,6 +211,8 @@ all: $NJS_BUILD_DIR/njs_auto_config.h \\ $NJS_DEFAULT_TARGET test lib_test benchmark njs: $NJS_BUILD_DIR/njs_auto_config.h $NJS_BUILD_DIR/njs +njs_fuzzer: $NJS_BUILD_DIR/njs_auto_config.h \\ + $NJS_BUILD_DIR/njs_process_script_fuzzer lib_test: $NJS_BUILD_DIR/njs_auto_config.h \\ $NJS_BUILD_DIR/random_unit_test \\ diff -r 452ce96df2e3 -r 884d3a86beab src/njs_shell.c --- a/src/njs_shell.c Tue Nov 19 19:19:53 2019 +0300 +++ b/src/njs_shell.c Tue Nov 19 19:32:26 2019 +0300 @@ -6,6 +6,9 @@ #include + +#ifndef NJS_FUZZER_TARGET + #include #if (NJS_HAVE_EDITLINE) #include @@ -18,12 +21,15 @@ #endif #endif +#endif + typedef struct { uint8_t disassemble; uint8_t interactive; uint8_t module; uint8_t quiet; + uint8_t silent; uint8_t sandbox; uint8_t safe; uint8_t version; @@ -74,19 +80,23 @@ typedef struct { } njs_console_t; -static njs_int_t njs_get_options(njs_opts_t *opts, int argc, char **argv); static njs_int_t njs_console_init(njs_vm_t *vm, njs_console_t *console); static njs_int_t njs_externals_init(njs_vm_t *vm, njs_console_t *console); +static njs_vm_t *njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options); +static njs_int_t njs_process_script(njs_opts_t *opts, + njs_console_t *console, const njs_str_t *script); + +#ifndef NJS_FUZZER_TARGET + +static njs_int_t njs_get_options(njs_opts_t *opts, int argc, char **argv); +static njs_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options); static njs_int_t njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options); -static njs_vm_t *njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options); -static njs_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options); -static njs_int_t njs_process_script(njs_console_t *console, - const njs_str_t *script); static njs_int_t njs_editline_init(void); -static char **njs_completion_handler(const char *text, int start, int end); static char *njs_completion_generator(const char *text, int state); +#endif + static njs_int_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); static njs_int_t njs_ext_console_dump(njs_vm_t *vm, njs_value_t *args, @@ -195,6 +205,8 @@ static njs_vm_ops_t njs_console_ops = { static njs_console_t njs_console; +#ifndef NJS_FUZZER_TARGET + int main(int argc, char **argv) { @@ -265,7 +277,7 @@ main(int argc, char **argv) if (vm != NULL) { command.start = (u_char *) opts.command; command.length = njs_strlen(opts.command); - ret = njs_process_script(vm_options.external, &command); + ret = njs_process_script(&opts, vm_options.external, &command); } } else { @@ -400,109 +412,6 @@ njs_get_options(njs_opts_t *opts, int ar static njs_int_t -njs_console_init(njs_vm_t *vm, njs_console_t *console) -{ - console->vm = vm; - - njs_lvlhsh_init(&console->events); - njs_queue_init(&console->posted_events); - - njs_lvlhsh_init(&console->labels); - - console->completion.completions = njs_vm_completions(vm, NULL); - if (console->completion.completions == NULL) { - return NJS_ERROR; - } - - return NJS_OK; -} - - -static njs_int_t -njs_externals_init(njs_vm_t *vm, njs_console_t *console) -{ - njs_uint_t ret; - njs_value_t *value; - const njs_extern_t *proto; - - static const njs_str_t name = njs_str("console"); - - proto = njs_vm_external_prototype(vm, &njs_externals[0]); - if (proto == NULL) { - njs_stderror("failed to add console proto\n"); - return NJS_ERROR; - } - - value = njs_mp_zalloc(vm->mem_pool, sizeof(njs_opaque_value_t)); - if (value == NULL) { - return NJS_ERROR; - } - - ret = njs_vm_external_create(vm, value, proto, console); - if (ret != NJS_OK) { - return NJS_ERROR; - } - - ret = njs_vm_external_bind(vm, &name, value); - if (ret != NJS_OK) { - return NJS_ERROR; - } - - ret = njs_console_init(vm, console); - if (ret != NJS_OK) { - return NJS_ERROR; - } - - return NJS_OK; -} - - -static njs_int_t -njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options) -{ - njs_vm_t *vm; - njs_str_t line; - - if (njs_editline_init() != NJS_OK) { - njs_stderror("failed to init completions\n"); - return NJS_ERROR; - } - - vm = njs_create_vm(opts, vm_options); - if (vm == NULL) { - return NJS_ERROR; - } - - if (!opts->quiet) { - njs_printf("interactive njs %s\n\n", NJS_VERSION); - - njs_printf("v. -> the properties and prototype methods of v.\n\n"); - } - - for ( ;; ) { - line.start = (u_char *) readline(">> "); - if (line.start == NULL) { - break; - } - - line.length = njs_strlen(line.start); - if (line.length == 0) { - continue; - } - - add_history((char *) line.start); - - njs_process_script(vm_options->external, &line); - - /* editline allocs a new buffer every time. */ - free(line.start); - } - - return NJS_OK; -} - - -static njs_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options) { int fd; @@ -612,7 +521,7 @@ njs_process_file(njs_opts_t *opts, njs_v } } - ret = njs_process_script(vm_options->external, &script); + ret = njs_process_script(opts, vm_options->external, &script); if (ret != NJS_OK) { ret = NJS_ERROR; goto done; @@ -635,6 +544,103 @@ close_fd: return ret; } +#else + +int +LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + njs_vm_t *vm; + njs_opts_t opts; + njs_str_t script; + njs_vm_opt_t vm_options; + + if (size == 0) { + return 0; + } + + njs_memzero(&opts, sizeof(njs_opts_t)); + + opts.silent = 1; + + njs_memzero(&vm_options, sizeof(njs_vm_opt_t)); + + vm_options.init = 1; + vm_options.backtrace = 0; + vm_options.ops = &njs_console_ops; + vm_options.external = &njs_console; + + vm = njs_create_vm(&opts, &vm_options); + + if (njs_fast_path(vm != NULL)) { + script.length = size; + script.start = (u_char *) data; + + (void) njs_process_script(&opts, vm_options.external, &script); + njs_vm_destroy(vm); + } + + return 0; +} + +#endif + +static njs_int_t +njs_console_init(njs_vm_t *vm, njs_console_t *console) +{ + console->vm = vm; + + njs_lvlhsh_init(&console->events); + njs_queue_init(&console->posted_events); + + njs_lvlhsh_init(&console->labels); + + console->completion.completions = njs_vm_completions(vm, NULL); + if (console->completion.completions == NULL) { + return NJS_ERROR; + } + + return NJS_OK; +} + + +static njs_int_t +njs_externals_init(njs_vm_t *vm, njs_console_t *console) +{ + njs_uint_t ret; + njs_value_t *value; + const njs_extern_t *proto; + + static const njs_str_t name = njs_str("console"); + + proto = njs_vm_external_prototype(vm, &njs_externals[0]); + if (proto == NULL) { + njs_stderror("failed to add console proto\n"); + return NJS_ERROR; + } + + value = njs_mp_zalloc(vm->mem_pool, sizeof(njs_opaque_value_t)); + if (value == NULL) { + return NJS_ERROR; + } + + ret = njs_vm_external_create(vm, value, proto, console); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + ret = njs_vm_external_bind(vm, &name, value); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + ret = njs_console_init(vm, console); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + return NJS_OK; +} + static njs_vm_t * njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options) @@ -696,21 +702,23 @@ njs_create_vm(njs_opts_t *opts, njs_vm_o static void -njs_output(njs_vm_t *vm, njs_int_t ret) +njs_output(njs_opts_t *opts, njs_vm_t *vm, njs_int_t ret) { njs_str_t out; - if (njs_vm_retval_dump(vm, &out, 1) != NJS_OK) { - out = njs_str_value("failed to get retval from VM"); - ret = NJS_ERROR; - } + if (!opts->silent) { + if (njs_vm_retval_dump(vm, &out, 1) != NJS_OK) { + out = njs_str_value("failed to get retval from VM"); + ret = NJS_ERROR; + } - if (ret != NJS_OK) { - njs_stderror("%V\n", &out); + if (ret != NJS_OK) { + njs_stderror("%V\n", &out); - } else if (vm->options.accumulative) { - njs_print(out.start, out.length); - njs_printf("\n"); + } else if (vm->options.accumulative) { + njs_print(out.start, out.length); + njs_printf("\n"); + } } } @@ -745,7 +753,8 @@ njs_process_events(njs_console_t *consol static njs_int_t -njs_process_script(njs_console_t *console, const njs_str_t *script) +njs_process_script(njs_opts_t *opts, njs_console_t *console, + const njs_str_t *script) { u_char *start; njs_vm_t *vm; @@ -760,7 +769,7 @@ njs_process_script(njs_console_t *consol ret = njs_vm_start(vm); } - njs_output(vm, ret); + njs_output(opts, vm, ret); for ( ;; ) { if (!njs_vm_pending(vm)) { @@ -785,7 +794,7 @@ njs_process_script(njs_console_t *consol ret = njs_vm_run(vm); if (ret == NJS_ERROR) { - njs_output(vm, ret); + njs_output(opts, vm, ret); } } @@ -793,6 +802,62 @@ njs_process_script(njs_console_t *consol } +#ifndef NJS_FUZZER_TARGET + +static njs_int_t +njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options) +{ + njs_vm_t *vm; + njs_str_t line; + + if (njs_editline_init() != NJS_OK) { + njs_stderror("failed to init completions\n"); + return NJS_ERROR; + } + + vm = njs_create_vm(opts, vm_options); + if (vm == NULL) { + return NJS_ERROR; + } + + if (!opts->quiet) { + njs_printf("interactive njs %s\n\n", NJS_VERSION); + + njs_printf("v. -> the properties and prototype methods of v.\n\n"); + } + + for ( ;; ) { + line.start = (u_char *) readline(">> "); + if (line.start == NULL) { + break; + } + + line.length = njs_strlen(line.start); + if (line.length == 0) { + continue; + } + + add_history((char *) line.start); + + njs_process_script(opts, vm_options->external, &line); + + /* editline allocs a new buffer every time. */ + free(line.start); + } + + return NJS_OK; +} + + +static char ** +njs_completion_handler(const char *text, int start, int end) +{ + rl_attempted_completion_over = 1; + + return rl_completion_matches(text, njs_completion_generator); +} + + static njs_int_t njs_editline_init(void) { @@ -806,15 +871,6 @@ njs_editline_init(void) } -static char ** -njs_completion_handler(const char *text, int start, int end) -{ - rl_attempted_completion_over = 1; - - return rl_completion_matches(text, njs_completion_generator); -} - - /* editline frees the buffer every time. */ #define njs_editline(s) strndup((char *) (s)->start, (s)->length) @@ -961,6 +1017,8 @@ next: return NULL; } +#endif + static njs_int_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, From joe.konno at linux.intel.com Tue Nov 19 21:01:03 2019 From: joe.konno at linux.intel.com (Joe Konno) Date: Tue, 19 Nov 2019 13:01:03 -0800 Subject: [RFC] The nginx way of handling optimizations for hw architectures Message-ID: <1bee90e8-7422-bb2e-13eb-4be4a39ddf49@linux.intel.com> Hey all, I'm requesting comment/input on how nginx would like to do architecture detection at build time as well as at run time. This is for the purposes of optimizing nginx for specific hardware (see my e-mail address for what specific hardware I hope to optimize for). ^_^ For build time, I see nginx has '--with-cpu-opt'. Does the community use that configure flag much (if at all)? I could see adding some host detection facilities. But, for OSes who build and/or package for multiple architectures, the limitations of such detection become immediately apparent. Bear with me as I get architecture-specific, but for run time, I do see a core function using (the first leaf of) CPUID output to determine a cache line size. Other leaves of the CPUID instruction could be used, at run time /or/ configure/build time, to determine which optimized function(s) to use given the host CPU's capabilities. There are a few ways to handle optimizations at the instruction set level, so I am looking for guidance on the "nginx way" of doing such things. Is there a line between what optimizations should be determined at build time? At run time? The Development Guide doesn't say much about this-- or I missed it-- so any hints/clues are appreciated. ^_^ -- Cheers From osa at freebsd.org.ru Tue Nov 19 23:15:12 2019 From: osa at freebsd.org.ru (Sergey A. Osokin) Date: Wed, 20 Nov 2019 02:15:12 +0300 Subject: [RFC] The nginx way of handling optimizations for hw architectures In-Reply-To: <1bee90e8-7422-bb2e-13eb-4be4a39ddf49@linux.intel.com> References: <1bee90e8-7422-bb2e-13eb-4be4a39ddf49@linux.intel.com> Message-ID: <20191119231512.GB11131@FreeBSD.org.ru> Hi Joe, hope you're doing well. I'm supporting nginx for FreeBSD ports tree for years and we usually don't use '--with-cpu-opt' flag for the port/package build. Also, this is the general recommendation described on FreeBSD Porters Handbook to avoid to use opti- misation flags from the third-party Makefiles, please see https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/dads-cflags.html for details. Let me know if you have any questions. Thanks. -- Sergey Osokin On Tue, Nov 19, 2019 at 01:01:03PM -0800, Joe Konno wrote: > Hey all, > > I'm requesting comment/input on how nginx would like to do architecture > detection at build time as well as at run time. This is for the purposes > of optimizing nginx for specific hardware (see my e-mail address for > what specific hardware I hope to optimize for). ^_^ > > For build time, I see nginx has '--with-cpu-opt'. Does the community use > that configure flag much (if at all)? I could see adding some host > detection facilities. But, for OSes who build and/or package for > multiple architectures, the limitations of such detection become > immediately apparent. > > Bear with me as I get architecture-specific, but for run time, I do see > a core function using (the first leaf of) CPUID output to determine a > cache line size. Other leaves of the CPUID instruction could be used, at > run time /or/ configure/build time, to determine which optimized > function(s) to use given the host CPU's capabilities. > > There are a few ways to handle optimizations at the instruction set > level, so I am looking for guidance on the "nginx way" of doing such > things. Is there a line between what optimizations should be determined > at build time? At run time? The Development Guide doesn't say much about > this-- or I missed it-- so any hints/clues are appreciated. > > ^_^ > > -- > Cheers > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From joe.konno at linux.intel.com Tue Nov 19 23:33:32 2019 From: joe.konno at linux.intel.com (Joe Konno) Date: Tue, 19 Nov 2019 15:33:32 -0800 Subject: [RFC] The nginx way of handling optimizations for hw architectures In-Reply-To: <20191119231512.GB11131@FreeBSD.org.ru> References: <1bee90e8-7422-bb2e-13eb-4be4a39ddf49@linux.intel.com> <20191119231512.GB11131@FreeBSD.org.ru> Message-ID: <4e4fa8eb-725c-49dc-18a1-4d5c2c236f46@linux.intel.com> On 11/19/19 3:15 PM, Sergey A. Osokin wrote: > Hi Joe, > > hope you're doing well. > > I'm supporting nginx for FreeBSD ports tree for years and we usually don't > use '--with-cpu-opt' flag for the port/package build. Also, this is the general > recommendation described on FreeBSD Porters Handbook to avoid to use opti- > misation flags from the third-party Makefiles, please see > https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/dads-cflags.html > for details. Sensible. I like it! ^_^ > > Let me know if you have any questions. > Thanks. Appreciated-- thanks for reaching out! From pluknet at nginx.com Wed Nov 20 10:24:36 2019 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 20 Nov 2019 13:24:36 +0300 Subject: [RFC] The nginx way of handling optimizations for hw architectures In-Reply-To: <1bee90e8-7422-bb2e-13eb-4be4a39ddf49@linux.intel.com> References: <1bee90e8-7422-bb2e-13eb-4be4a39ddf49@linux.intel.com> Message-ID: <198FDC3C-73B5-4485-AA34-F066E0E8BE2E@nginx.com> > On 20 Nov 2019, at 00:01, Joe Konno wrote: > > Hey all, > > I'm requesting comment/input on how nginx would like to do architecture > detection at build time as well as at run time. This is for the purposes > of optimizing nginx for specific hardware (see my e-mail address for > what specific hardware I hope to optimize for). ^_^ > > For build time, I see nginx has '--with-cpu-opt'. Does the community use > that configure flag much (if at all)? A specific use case is building 64-bit binary on Solaris x86-64 with SunC, where one would specify --with-cpu-opt=amd64 to override the 80386 target, which essentially provides the -m64 flag to set 64-bit addressing model and a suitable assembler code for atomic instructions. -- Sergey Kandaurov From wi2p at hotmail.com Wed Nov 20 10:53:42 2019 From: wi2p at hotmail.com (kev jr) Date: Wed, 20 Nov 2019 10:53:42 +0000 Subject: Error : NGINX - MYSQL TLS encryption and Message-ID: Hi all, Question 1 Is it possible to have NGINX reverse proxy to multiple MySQL servers listening on the same port using different names like you can with http? We don't want to perform any load balancing operation on them, we just want to be able to redirect to MySQL instances based on a logical name, same as on http. Question 2 When I try to implement TLS encryption between NGINX and MYSQL Database server, I have the following error on my MySQL Client : ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error I have the following configuration : Ubuntu server with the MySQL Client // NGINX (with the configuration below) // MYSQL Database (with SSL activated) stream { ? upstream mysql1 {? server 172.31.39.168:3306;? ? }? ? server {? listen 3306;? proxy_pass mysql1;? proxy_ssl on;? ? proxy_ssl_certificate /etc/ssl/client-cert.pem;? proxy_ssl_certificate_key /etc/ssl/client-key.pem;? #proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;? #proxy_ssl_ciphers HIGH:!aNULL:!MD5;? proxy_ssl_trusted_certificate /etc/ssl/ca-cert.pem;? ? proxy_ssl_verify on;? proxy_ssl_verify_depth 2;? proxy_ssl_session_reuse on;? }? }? If I comment proxy_ssl* parameters on NGINX, the connection works between "Ubuntu server (with the MySQL Client)" and "MYSQL Database (with SSL activated)" throught "NGINX". Thanks all -------------- next part -------------- An HTML attachment was scrubbed... URL: From arut at nginx.com Wed Nov 20 11:39:54 2019 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 20 Nov 2019 14:39:54 +0300 Subject: Error : NGINX - MYSQL TLS encryption and In-Reply-To: References: Message-ID: <20191120113954.3fddj3vehqvubz5j@Romans-MacBook-Pro.local> Hi, On Wed, Nov 20, 2019 at 10:53:42AM +0000, kev jr wrote: > Hi all, > > Question 1 > Is it possible to have NGINX reverse proxy to multiple MySQL servers listening on the same port using different names like you can with http? We don't want to perform any load balancing operation on them, we just want to be able to redirect to MySQL instances based on a logical name, same as on http. If by logical name you mean TLS SNI then it is possible. You need to use the ssl preread module: http://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html#ssl_preread > Question 2 > When I try to implement TLS encryption between NGINX and MYSQL Database server, I have the following error on my MySQL Client : ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error > > I have the following configuration : Ubuntu server with the MySQL Client // NGINX (with the configuration below) // MYSQL Database (with SSL activated) > stream { > ? > upstream mysql1 {? > server 172.31.39.168:3306;? > ? }? > ? > server {? > listen 3306;? > proxy_pass mysql1;? > proxy_ssl on;? > ? > proxy_ssl_certificate /etc/ssl/client-cert.pem;? > proxy_ssl_certificate_key /etc/ssl/client-key.pem;? > #proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;? > #proxy_ssl_ciphers HIGH:!aNULL:!MD5;? > proxy_ssl_trusted_certificate /etc/ssl/ca-cert.pem;? > ? > proxy_ssl_verify on;? > proxy_ssl_verify_depth 2;? > proxy_ssl_session_reuse on;? > }? > }? > > If I comment proxy_ssl* parameters on NGINX, the connection works between "Ubuntu server (with the MySQL Client)" and "MYSQL Database (with SSL activated)" throught "NGINX". I can only assume that the MySQL port you connect to does not expect TLS connections. You should probably connect to a different port when proxy_ssl is enabled. > > Thanks all > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From mdounin at mdounin.ru Wed Nov 20 11:48:04 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 20 Nov 2019 14:48:04 +0300 Subject: Error : NGINX - MYSQL TLS encryption and In-Reply-To: References: Message-ID: <20191120114804.GB12894@mdounin.ru> Hello! On Wed, Nov 20, 2019 at 10:53:42AM +0000, kev jr wrote: > Question 1 > Is it possible to have NGINX reverse proxy to multiple MySQL > servers listening on the same port using different names like > you can with http? We don't want to perform any load balancing > operation on them, we just want to be able to redirect to MySQL > instances based on a logical name, same as on http. > > Question 2 > When I try to implement TLS encryption between NGINX and MYSQL > Database server, I have the following error on my MySQL Client : > ERROR 2013 (HY000): Lost connection to MySQL server at 'reading > initial communication packet', system error This is a mailing list for nginx developers. For questions on how to configure nginx, please use the nginx@ mailing list instead. Thank you. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Wed Nov 20 14:29:02 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 20 Nov 2019 17:29:02 +0300 Subject: [PATCH] Optimal performance when use http non-persistent connection In-Reply-To: <07890E72BF5A6C44976E3EB9981B18780435D11E@dggeml527-mbx.china.huawei.com> References: <07890E72BF5A6C44976E3EB9981B18780435D11E@dggeml527-mbx.china.huawei.com> Message-ID: <20191120142902.GE12894@mdounin.ru> Hello! On Mon, Nov 11, 2019 at 03:07:02AM +0000, Zhangshaokun wrote: > # HG changeset patch > # User Rui Sun > > # Date 1572848389 -28800 > # Mon Nov 04 14:19:49 2019 +0800 > # Branch local > # Node ID a5ae6e9e99f747fcb45082bac8795622938184f1 > # Parent 89adf49fe76ada86d84e2af8f5cee9ca8c3dca19 > Optimal performance when use http non-persistent connection > > diff -r 89adf49fe76a -r a5ae6e9e99f7 src/core/ngx_cycle.c > --- a/src/core/ngx_cycle.c Mon Oct 21 20:22:30 2019 +0300 > +++ b/src/core/ngx_cycle.c Mon Nov 04 14:19:49 2019 +0800 > @@ -35,6 +35,40 @@ > /* STUB */ > > > +void > +ngx_change_pid_core(ngx_cycle_t *cycle) > +{ > + ngx_pid_t setpid; > + ngx_cpuset_t *setaffinity=NULL; > + setpid = ngx_getpid(); > + { > +#if (NGX_HAVE_CPU_AFFINITY) > + ngx_core_conf_t *ccf; > + > + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); > + > + if (ccf->cpu_affinity == NULL) { > + setaffinity = NULL; > + } > + > + if (ccf->cpu_affinity_auto) { > + setaffinity = NULL; > + } > + > + setaffinity = &ccf->cpu_affinity[0]; > + > +#else > + > + setaffinity = NULL; > + > +#endif > + } > + > + if (setaffinity) > + // set new mask > + sched_setaffinity(setpid, sizeof(ngx_cpuset_t), setaffinity); > +} > + > ngx_cycle_t * > ngx_init_cycle(ngx_cycle_t *old_cycle) > { > @@ -278,6 +312,8 @@ > return NULL; > } > > + ngx_change_pid_core(cycle); > + > if (ngx_test_config && !ngx_quiet_mode) { > ngx_log_stderr(0, "the configuration file %s syntax is ok", > cycle->conf_file.data); > Sorry, but it is not clear what you are trying to achieve with this patch. You may want to provide more details. Additionally, please make sure to check the "Contributing Changes" article, as available here: http://nginx.org/en/docs/contributing_changes.html It contains some hints on how to submit patches. Thanks. -- Maxim Dounin http://mdounin.ru/ From zhangshaokun at hisilicon.com Thu Nov 21 11:22:16 2019 From: zhangshaokun at hisilicon.com (Shaokun Zhang) Date: Thu, 21 Nov 2019 19:22:16 +0800 Subject: [PATCH] Optimal performance when use http non-persistent connection In-Reply-To: <20191120142902.GE12894@mdounin.ru> References: <07890E72BF5A6C44976E3EB9981B18780435D11E@dggeml527-mbx.china.huawei.com> <20191120142902.GE12894@mdounin.ru> Message-ID: <6434936b-a321-9905-a3bc-c906cfc41641@hisilicon.com> Hi Maixm, On 2019/11/20 22:29, Maxim Dounin wrote: > Hello! > > On Mon, Nov 11, 2019 at 03:07:02AM +0000, Zhangshaokun wrote: > >> # HG changeset patch >> # User Rui Sun > >> # Date 1572848389 -28800 >> # Mon Nov 04 14:19:49 2019 +0800 >> # Branch local >> # Node ID a5ae6e9e99f747fcb45082bac8795622938184f1 >> # Parent 89adf49fe76ada86d84e2af8f5cee9ca8c3dca19 >> Optimal performance when use http non-persistent connection >> >> diff -r 89adf49fe76a -r a5ae6e9e99f7 src/core/ngx_cycle.c >> --- a/src/core/ngx_cycle.c Mon Oct 21 20:22:30 2019 +0300 >> +++ b/src/core/ngx_cycle.c Mon Nov 04 14:19:49 2019 +0800 >> @@ -35,6 +35,40 @@ >> /* STUB */ >> >> >> +void >> +ngx_change_pid_core(ngx_cycle_t *cycle) >> +{ >> + ngx_pid_t setpid; >> + ngx_cpuset_t *setaffinity=NULL; >> + setpid = ngx_getpid(); >> + { >> +#if (NGX_HAVE_CPU_AFFINITY) >> + ngx_core_conf_t *ccf; >> + >> + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); >> + >> + if (ccf->cpu_affinity == NULL) { >> + setaffinity = NULL; >> + } >> + >> + if (ccf->cpu_affinity_auto) { >> + setaffinity = NULL; >> + } >> + >> + setaffinity = &ccf->cpu_affinity[0]; >> + >> +#else >> + >> + setaffinity = NULL; >> + >> +#endif >> + } >> + >> + if (setaffinity) >> + // set new mask >> + sched_setaffinity(setpid, sizeof(ngx_cpuset_t), setaffinity); >> +} >> + >> ngx_cycle_t * >> ngx_init_cycle(ngx_cycle_t *old_cycle) >> { >> @@ -278,6 +312,8 @@ >> return NULL; >> } >> >> + ngx_change_pid_core(cycle); >> + >> if (ngx_test_config && !ngx_quiet_mode) { >> ngx_log_stderr(0, "the configuration file %s syntax is ok", >> cycle->conf_file.data); >> > > Sorry, but it is not clear what you are trying to achieve with > this patch. You may want to provide more details. > when we test nginx in kunpeng920 which has 2chip and each chip has 2 NUMA. We user 32cores in 2 different NUMA to test nginx, when nginx start the master worker runs on which core is undefined, when the master's core and the worker's core in the same chip, the performance of non-persistent connection is 17W, but when master's core and the worker's core in the different chip, the performance of non-persistent connection only has 12W. Now, when nginx start, we migrate master process depend on the first worker process's cpu affinity, the performance is showed as follow: | default| optimize master and worker process on same chip when nginx start | 171699 | 176020 master and worker process on diff chip when nginx start | 129639 | 180637 Thanks, Shaokun > Additionally, please make sure to check the "Contributing Changes" > article, as available here: > > http://nginx.org/en/docs/contributing_changes.html > > It contains some hints on how to submit patches. Thanks. > From mdounin at mdounin.ru Thu Nov 21 15:21:47 2019 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 21 Nov 2019 18:21:47 +0300 Subject: [PATCH] Optimal performance when use http non-persistent connection In-Reply-To: <6434936b-a321-9905-a3bc-c906cfc41641@hisilicon.com> References: <07890E72BF5A6C44976E3EB9981B18780435D11E@dggeml527-mbx.china.huawei.com> <20191120142902.GE12894@mdounin.ru> <6434936b-a321-9905-a3bc-c906cfc41641@hisilicon.com> Message-ID: <20191121152147.GG12894@mdounin.ru> Hello! On Thu, Nov 21, 2019 at 07:22:16PM +0800, Shaokun Zhang wrote: > Hi Maixm, > > On 2019/11/20 22:29, Maxim Dounin wrote: > > Hello! > > > > On Mon, Nov 11, 2019 at 03:07:02AM +0000, Zhangshaokun wrote: > > > >> # HG changeset patch > >> # User Rui Sun > > >> # Date 1572848389 -28800 > >> # Mon Nov 04 14:19:49 2019 +0800 > >> # Branch local > >> # Node ID a5ae6e9e99f747fcb45082bac8795622938184f1 > >> # Parent 89adf49fe76ada86d84e2af8f5cee9ca8c3dca19 > >> Optimal performance when use http non-persistent connection > >> > >> diff -r 89adf49fe76a -r a5ae6e9e99f7 src/core/ngx_cycle.c > >> --- a/src/core/ngx_cycle.c Mon Oct 21 20:22:30 2019 +0300 > >> +++ b/src/core/ngx_cycle.c Mon Nov 04 14:19:49 2019 +0800 > >> @@ -35,6 +35,40 @@ > >> /* STUB */ > >> > >> > >> +void > >> +ngx_change_pid_core(ngx_cycle_t *cycle) > >> +{ > >> + ngx_pid_t setpid; > >> + ngx_cpuset_t *setaffinity=NULL; > >> + setpid = ngx_getpid(); > >> + { > >> +#if (NGX_HAVE_CPU_AFFINITY) > >> + ngx_core_conf_t *ccf; > >> + > >> + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); > >> + > >> + if (ccf->cpu_affinity == NULL) { > >> + setaffinity = NULL; > >> + } > >> + > >> + if (ccf->cpu_affinity_auto) { > >> + setaffinity = NULL; > >> + } > >> + > >> + setaffinity = &ccf->cpu_affinity[0]; > >> + > >> +#else > >> + > >> + setaffinity = NULL; > >> + > >> +#endif > >> + } > >> + > >> + if (setaffinity) > >> + // set new mask > >> + sched_setaffinity(setpid, sizeof(ngx_cpuset_t), setaffinity); > >> +} > >> + > >> ngx_cycle_t * > >> ngx_init_cycle(ngx_cycle_t *old_cycle) > >> { > >> @@ -278,6 +312,8 @@ > >> return NULL; > >> } > >> > >> + ngx_change_pid_core(cycle); > >> + > >> if (ngx_test_config && !ngx_quiet_mode) { > >> ngx_log_stderr(0, "the configuration file %s syntax is ok", > >> cycle->conf_file.data); > >> > > > > Sorry, but it is not clear what you are trying to achieve with > > this patch. You may want to provide more details. > > > > when we test nginx in kunpeng920 which has 2chip and each chip has 2 NUMA. > We user 32cores in 2 different NUMA to test nginx, when nginx start the master > worker runs on which core is undefined, when the master's core and the > worker's core in the same chip, the performance of non-persistent connection > is 17W, but when master's core and the worker's core in the different chip, > the performance of non-persistent connection only has 12W. Now, when nginx > start, we migrate master process depend on the first worker process's cpu > affinity, the performance is showed as follow: > | default| optimize > master and worker process on same chip when nginx start | 171699 | 176020 > master and worker process on diff chip when nginx start | 129639 | 180637 Ok, so you are trying to bind the master process to the same core the first worker process runs on. Presumably, this can be beneficial from performance point of view in configurations with small number of worker processes, as various structures allocated by the master process after parsing configuration will be allocated from the same NUMA region the worker process runs on. Correct? So the following questions are: 0. What units of measurement the numbers use? Connections per second? What are error margins? 1. How did you tested it? Given that many configuration structures are allocated by the master process during configuration parsing, the numbers look strange. I would expect performance with master of worker process on different chips to be smaller than that on the same chip, even with the patch applied. Well, with error margins we'll probably see there is no difference between 176020 and 180637, but this brings another question: where the difference between 129639 and 180637 comes from? Listening sockets created by the kernel on the same chip? So this probably means we shouldn't bind worker process in general, but rather create listenings sockets on the same chip instead? Note this is not the same, especially with reuseport, not to mention this cannot be done at all when we inherit listening sockets form previous configurations. 2. What happens when there are multiple worker processes? Will this change still be beneficial, or negative, or neutral? Don't you think the case you are trying to optimize is too narrow to care about? 3. In nginx, there are platform-independent functions ngx_get_cpu_affinity() and ngx_setaffinity() to work with CPU affinity. Why you are not using them in your patch? Additionally, why you are not trying to bind master process to a particular CPU with "worker_cpu_affinity auto;"? -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Thu Nov 21 17:57:34 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 21 Nov 2019 17:57:34 +0000 Subject: [njs] Added initial version of Symbol primitive type. Message-ID: details: https://hg.nginx.org/njs/rev/263c75a40999 branches: changeset: 1247:263c75a40999 user: Artem S. Povalyukhin date: Tue Nov 19 20:45:22 2019 +0300 description: Added initial version of Symbol primitive type. This closes #249 issue on Github. diffstat: auto/sources | 1 + src/njs_builtin.c | 10 + src/njs_json.c | 27 ++ src/njs_main.h | 1 + src/njs_object.c | 14 +- src/njs_object_hash.h | 10 + src/njs_string.c | 8 + src/njs_symbol.c | 441 +++++++++++++++++++++++++++++++++++++++++++++++ src/njs_symbol.h | 36 +++ src/njs_value.c | 17 + src/njs_value.h | 76 ++++++- src/njs_vm.c | 2 + src/njs_vm.h | 3 + src/njs_vmcode.c | 33 ++- src/test/njs_unit_test.c | 303 ++++++++++++++++++++++++++++++++- 15 files changed, 955 insertions(+), 27 deletions(-) diffs (truncated from 1392 to 1000 lines): diff -r 884d3a86beab -r 263c75a40999 auto/sources --- a/auto/sources Tue Nov 19 19:32:26 2019 +0300 +++ b/auto/sources Tue Nov 19 20:45:22 2019 +0300 @@ -25,6 +25,7 @@ NJS_LIB_SRCS=" \ src/njs_vmcode.c \ src/njs_boolean.c \ src/njs_number.c \ + src/njs_symbol.c \ src/njs_string.c \ src/njs_object.c \ src/njs_object_prop.c \ diff -r 884d3a86beab -r 263c75a40999 src/njs_builtin.c --- a/src/njs_builtin.c Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_builtin.c Tue Nov 19 20:45:22 2019 +0300 @@ -60,6 +60,7 @@ static const njs_object_type_init_t *con &njs_array_type_init, &njs_boolean_type_init, &njs_number_type_init, + &njs_symbol_type_init, &njs_string_type_init, &njs_function_type_init, &njs_regexp_type_init, @@ -1169,6 +1170,15 @@ static const njs_object_prop_t njs_glob { .type = NJS_PROPERTY_HANDLER, + .name = njs_string("Symbol"), + .value = njs_prop_handler2(njs_top_level_constructor, + NJS_OBJ_TYPE_SYMBOL, NJS_SYMBOL_HASH), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, .name = njs_string("String"), .value = njs_prop_handler2(njs_top_level_constructor, NJS_OBJ_TYPE_STRING, NJS_STRING_HASH), diff -r 884d3a86beab -r 263c75a40999 src/njs_json.c --- a/src/njs_json.c Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_json.c Tue Nov 19 20:45:22 2019 +0300 @@ -1933,6 +1933,33 @@ njs_dump_value(njs_json_stringify_t *str break; + case NJS_OBJECT_SYMBOL: + value = njs_object_value(value); + + ret = njs_symbol_to_string(stringify->vm, &str_val, value); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + njs_string_get(&str_val, &str); + + njs_dump("[Symbol: "); + njs_json_buf_append(stringify, (char *) str.start, str.length); + njs_dump("]"); + + break; + + case NJS_SYMBOL: + ret = njs_symbol_to_string(stringify->vm, &str_val, value); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + njs_string_get(&str_val, &str); + njs_json_buf_append(stringify, (char *) str.start, str.length); + + break; + case NJS_OBJECT_NUMBER: value = njs_object_value(value); diff -r 884d3a86beab -r 263c75a40999 src/njs_main.h --- a/src/njs_main.h Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_main.h Tue Nov 19 20:45:22 2019 +0300 @@ -52,6 +52,7 @@ #include #include +#include #include #include #include diff -r 884d3a86beab -r 263c75a40999 src/njs_object.c --- a/src/njs_object.c Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_object.c Tue Nov 19 20:45:22 2019 +0300 @@ -1387,7 +1387,7 @@ static njs_int_t njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - uint32_t index; + uint32_t index, type; njs_value_t *value; value = njs_arg(args, nargs, 1); @@ -1399,9 +1399,10 @@ njs_object_get_prototype_of(njs_vm_t *vm if (!njs_is_null_or_undefined(value)) { index = njs_primitive_prototype_index(value->type); - - njs_set_type_object(&vm->retval, &vm->prototypes[index].object, - njs_object_value_type(value->type)); + type = njs_is_symbol(value) ? NJS_OBJECT + : njs_object_value_type(value->type); + + njs_set_type_object(&vm->retval, &vm->prototypes[index].object, type); return NJS_OK; } @@ -2189,6 +2190,8 @@ static const njs_value_t njs_object_boo njs_long_string("[object Boolean]"); static const njs_value_t njs_object_number_string = njs_long_string("[object Number]"); +static const njs_value_t njs_object_symbol_string = + njs_long_string("[object Symbol]"); static const njs_value_t njs_object_string_string = njs_long_string("[object String]"); static const njs_value_t njs_object_data_string = @@ -2220,6 +2223,7 @@ njs_object_prototype_to_string(njs_vm_t &njs_object_undefined_string, &njs_object_boolean_string, &njs_object_number_string, + &njs_object_symbol_string, &njs_object_string_string, &njs_object_data_string, @@ -2232,13 +2236,13 @@ njs_object_prototype_to_string(njs_vm_t NULL, NULL, NULL, - NULL, /* Objects. */ &njs_object_object_string, &njs_object_array_string, &njs_object_boolean_string, &njs_object_number_string, + &njs_object_symbol_string, &njs_object_string_string, &njs_object_function_string, &njs_object_regexp_string, diff -r 884d3a86beab -r 263c75a40999 src/njs_object_hash.h --- a/src/njs_object_hash.h Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_object_hash.h Tue Nov 19 20:45:22 2019 +0300 @@ -421,6 +421,16 @@ 'S'), 't'), 'r'), 'i'), 'n'), 'g') +#define NJS_SYMBOL_HASH \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add(NJS_DJB_HASH_INIT, \ + 'S'), 'y'), 'm'), 'b'), 'o'), 'l') + + #define NJS_SYNTAX_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ diff -r 884d3a86beab -r 263c75a40999 src/njs_string.c --- a/src/njs_string.c Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_string.c Tue Nov 19 20:45:22 2019 +0300 @@ -552,6 +552,10 @@ njs_string_constructor(njs_vm_t *vm, njs value = &args[1]; if (njs_slow_path(!njs_is_string(value))) { + if (!vm->top_frame->ctor && njs_is_symbol(value)) { + return njs_symbol_to_string(vm, &vm->retval, value); + } + ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; @@ -4273,6 +4277,10 @@ njs_primitive_value_to_string(njs_vm_t * case NJS_NUMBER: return njs_number_to_string(vm, dst, src); + case NJS_SYMBOL: + njs_symbol_conversion_failed(vm, 1); + return NJS_ERROR; + case NJS_STRING: /* GC: njs_retain(src); */ value = src; diff -r 884d3a86beab -r 263c75a40999 src/njs_symbol.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/njs_symbol.c Tue Nov 19 20:45:22 2019 +0300 @@ -0,0 +1,441 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + + +#include + + +static const njs_value_t njs_symbol_async_iterator_name = + njs_long_string("Symbol.asyncIterator"); +static const njs_value_t njs_symbol_has_instance_name = + njs_long_string("Symbol.hasInstance"); +static const njs_value_t njs_symbol_is_concat_spreadable_name = + njs_long_string("Symbol.isConcatSpreadable"); +static const njs_value_t njs_symbol_iterator_name = + njs_long_string("Symbol.iterator"); +static const njs_value_t njs_symbol_match_name = + njs_string("Symbol.match"); +static const njs_value_t njs_symbol_match_all_name = + njs_long_string("Symbol.matchAll"); +static const njs_value_t njs_symbol_replace_name = + njs_string("Symbol.replace"); +static const njs_value_t njs_symbol_search_name = + njs_string("Symbol.search"); +static const njs_value_t njs_symbol_species_name = + njs_string("Symbol.species"); +static const njs_value_t njs_symbol_split_name = + njs_string("Symbol.split"); +static const njs_value_t njs_symbol_to_primitive_name = + njs_long_string("Symbol.toPrimitive"); +static const njs_value_t njs_symbol_to_string_tag_name = + njs_long_string("Symbol.toStringTag"); +static const njs_value_t njs_symbol_unscopables_name = + njs_long_string("Symbol.unscopables"); + + +static const njs_value_t *njs_symbol_names[NJS_SYMBOL_KNOWN_MAX] = { + &njs_string_invalid, + &njs_symbol_async_iterator_name, + &njs_symbol_has_instance_name, + &njs_symbol_is_concat_spreadable_name, + &njs_symbol_iterator_name, + &njs_symbol_match_name, + &njs_symbol_match_all_name, + &njs_symbol_replace_name, + &njs_symbol_search_name, + &njs_symbol_species_name, + &njs_symbol_split_name, + &njs_symbol_to_primitive_name, + &njs_symbol_to_string_tag_name, + &njs_symbol_unscopables_name, +}; + + +njs_int_t +njs_symbol_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *value) +{ + u_char *start; + const njs_value_t *name; + njs_string_prop_t string; + + static const njs_value_t string_symbol = njs_string("Symbol()"); + + name = value->data.u.value; + + if (name == NULL) { + if (njs_fast_path(njs_symbol_key(value) < NJS_SYMBOL_KNOWN_MAX)) { + + name = njs_symbol_names[njs_symbol_key(value)]; + + } else { + *dst = string_symbol; + + return NJS_OK; + } + } + + (void) njs_string_prop(&string, name); + string.length += njs_length("Symbol()"); + + start = njs_string_alloc(vm, dst, string.size + 8, string.length); + if (njs_slow_path(start == NULL)) { + return NJS_ERROR; + } + + start = njs_cpymem(start, "Symbol(", 7); + start = njs_cpymem(start, string.start, string.size); + *start = ')'; + + return NJS_OK; +} + + +static njs_int_t +njs_symbol_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_int_t ret; + njs_value_t *value, *name; + uint64_t key; + + if (njs_slow_path(vm->top_frame->ctor)) { + njs_type_error(vm, "Symbol is not a constructor"); + return NJS_ERROR; + } + + value = njs_arg(args, nargs, 1); + + if (njs_is_undefined(value)) { + name = NULL; + + } else { + if (njs_slow_path(!njs_is_string(value))) { + ret = njs_value_to_string(vm, value, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + name = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); + if (njs_slow_path(name == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } + + /* GC: retain */ + *name = *value; + } + + key = ++vm->symbol_generator; + + if (njs_slow_path(key >= UINT32_MAX)) { + njs_internal_error(vm, "Symbol generator overflow"); + return NJS_ERROR; + } + + vm->retval.type = NJS_SYMBOL; + vm->retval.data.truth = 1; + vm->retval.data.magic32 = key; + vm->retval.data.u.value = name; + + return NJS_OK; +} + + +static njs_int_t +njs_symbol_for(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ + njs_internal_error(vm, "not implemented"); + + return NJS_ERROR; +} + + +static njs_int_t +njs_symbol_key_for(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ + njs_internal_error(vm, "not implemented"); + + return NJS_ERROR; +} + + +static const njs_object_prop_t njs_symbol_constructor_properties[] = +{ + /* Symbol.name == "Symbol". */ + { + .type = NJS_PROPERTY, + .name = njs_string("name"), + .value = njs_string("Symbol"), + .configurable = 1, + }, + + /* Symbol.length == 0. */ + { + .type = NJS_PROPERTY, + .name = njs_string("length"), + .value = njs_value(NJS_NUMBER, 0, 0.0), + .configurable = 1, + }, + + /* Symbol.prototype. */ + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("prototype"), + .value = njs_prop_handler(njs_object_prototype_create), + }, + + /* Symbol.for(). */ + { + .type = NJS_PROPERTY, + .name = njs_string("for"), + .value = njs_native_function(njs_symbol_for, 1), + .writable = 1, + .configurable = 1, + }, + + /* Symbol.keyFor(). */ + { + .type = NJS_PROPERTY, + .name = njs_string("keyFor"), + .value = njs_native_function(njs_symbol_key_for, 1), + .writable = 1, + .configurable = 1, + }, + + /* Symbol.asyncIterator. */ + { + .type = NJS_PROPERTY, + .name = njs_string("asyncIterator"), + .value = njs_wellknown_symbol(NJS_SYMBOL_ASYNC_ITERATOR), + }, + + /* Symbol.hasInstance. */ + { + .type = NJS_PROPERTY, + .name = njs_string("hasInstance"), + .value = njs_wellknown_symbol(NJS_SYMBOL_HAS_INSTANCE), + }, + + /* Symbol.isConcatSpreadable. */ + { + .type = NJS_PROPERTY, + .name = njs_long_string("isConcatSpreadable"), + .value = njs_wellknown_symbol(NJS_SYMBOL_IS_CONCAT_SPREADABLE), + }, + + /* Symbol.iterator. */ + { + .type = NJS_PROPERTY, + .name = njs_string("iterator"), + .value = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR), + }, + + /* Symbol.match. */ + { + .type = NJS_PROPERTY, + .name = njs_string("match"), + .value = njs_wellknown_symbol(NJS_SYMBOL_MATCH), + }, + + /* Symbol.matchAll. */ + { + .type = NJS_PROPERTY, + .name = njs_string("matchAll"), + .value = njs_wellknown_symbol(NJS_SYMBOL_MATCH_ALL), + }, + + /* Symbol.replace. */ + { + .type = NJS_PROPERTY, + .name = njs_string("replace"), + .value = njs_wellknown_symbol(NJS_SYMBOL_REPLACE), + }, + + /* Symbol.search. */ + { + .type = NJS_PROPERTY, + .name = njs_string("search"), + .value = njs_wellknown_symbol(NJS_SYMBOL_SEARCH), + }, + + /* Symbol.species. */ + { + .type = NJS_PROPERTY, + .name = njs_string("species"), + .value = njs_wellknown_symbol(NJS_SYMBOL_SPECIES), + }, + + /* Symbol.split. */ + { + .type = NJS_PROPERTY, + .name = njs_string("split"), + .value = njs_wellknown_symbol(NJS_SYMBOL_SPLIT), + }, + + /* Symbol.toPrimitive. */ + { + .type = NJS_PROPERTY, + .name = njs_string("toPrimitive"), + .value = njs_wellknown_symbol(NJS_SYMBOL_TO_PRIMITIVE), + }, + + /* Symbol.toStringTag. */ + { + .type = NJS_PROPERTY, + .name = njs_string("toStringTag"), + .value = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + }, + + /* Symbol.unscopables. */ + { + .type = NJS_PROPERTY, + .name = njs_string("unscopables"), + .value = njs_wellknown_symbol(NJS_SYMBOL_UNSCOPABLES), + }, + +}; + + +const njs_object_init_t njs_symbol_constructor_init = { + njs_symbol_constructor_properties, + njs_nitems(njs_symbol_constructor_properties), +}; + + +static njs_int_t +njs_symbol_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_value_t *value; + + value = &args[0]; + + if (value->type != NJS_SYMBOL) { + + if (value->type == NJS_OBJECT_SYMBOL) { + value = njs_object_value(value); + + } else { + njs_type_error(vm, "unexpected value type:%s", + njs_type_string(value->type)); + + return NJS_ERROR; + } + } + + vm->retval = *value; + + return NJS_OK; +} + + +static njs_int_t +njs_symbol_prototype_to_string(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ + njs_int_t ret; + + ret = njs_symbol_prototype_value_of(vm, args, nargs, unused); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + return njs_symbol_to_string(vm, &vm->retval, &vm->retval); +} + + +static njs_int_t +njs_symbol_prototype_description(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ + njs_int_t ret; + const njs_value_t *value, *name; + + ret = njs_symbol_prototype_value_of(vm, args, nargs, unused); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + value = &vm->retval; + + name = value->data.u.value; + + if (name == NULL) { + if (njs_fast_path(njs_symbol_key(value) < NJS_SYMBOL_KNOWN_MAX)) { + name = njs_symbol_names[njs_symbol_key(value)]; + + } else { + name = &njs_value_undefined; + } + } + + vm->retval = *name; + + return NJS_OK; +} + + +static const njs_object_prop_t njs_symbol_prototype_properties[] = +{ + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("__proto__"), + .value = njs_prop_handler(njs_primitive_prototype_get_proto), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("constructor"), + .value = njs_prop_handler(njs_object_prototype_create_constructor), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("valueOf"), + .value = njs_native_function(njs_symbol_prototype_value_of, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("toString"), + .value = njs_native_function(njs_symbol_prototype_to_string, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("description"), + .value = njs_value(NJS_INVALID, 1, NAN), + .getter = njs_native_function(njs_symbol_prototype_description, 0), + .setter = njs_value(NJS_UNDEFINED, 0, NAN), + .writable = NJS_ATTRIBUTE_UNSET, + .configurable = 1, + }, +}; + + +const njs_object_init_t njs_symbol_prototype_init = { + njs_symbol_prototype_properties, + njs_nitems(njs_symbol_prototype_properties), +}; + + +const njs_object_type_init_t njs_symbol_type_init = { + .constructor = njs_symbol_constructor, + .prototype_props = &njs_symbol_prototype_init, + .constructor_props = &njs_symbol_constructor_init, + .value = { .object = { .type = NJS_OBJECT } }, +}; diff -r 884d3a86beab -r 263c75a40999 src/njs_symbol.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/njs_symbol.h Tue Nov 19 20:45:22 2019 +0300 @@ -0,0 +1,36 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NJS_SYMBOL_H_INCLUDED_ +#define _NJS_SYMBOL_H_INCLUDED_ + +typedef enum { + NJS_SYMBOL_INVALID = 0, + NJS_SYMBOL_ASYNC_ITERATOR = 1, + NJS_SYMBOL_HAS_INSTANCE = 2, + NJS_SYMBOL_IS_CONCAT_SPREADABLE = 3, + NJS_SYMBOL_ITERATOR = 4, + NJS_SYMBOL_MATCH = 5, + NJS_SYMBOL_MATCH_ALL = 6, + NJS_SYMBOL_REPLACE = 7, + NJS_SYMBOL_SEARCH = 8, + NJS_SYMBOL_SPECIES = 9, + NJS_SYMBOL_SPLIT = 10, + NJS_SYMBOL_TO_PRIMITIVE = 11, + NJS_SYMBOL_TO_STRING_TAG = 12, + NJS_SYMBOL_UNSCOPABLES = 13, +#define NJS_SYMBOL_KNOWN_MAX (NJS_SYMBOL_UNSCOPABLES + 1) +} njs_wellknown_symbol_t; + + +njs_int_t njs_symbol_to_string(njs_vm_t *vm, njs_value_t *dst, + const njs_value_t *value); + + +extern const njs_object_type_init_t njs_symbol_type_init; + + +#endif /* _NJS_SYMBOL_H_INCLUDED_ */ diff -r 884d3a86beab -r 263c75a40999 src/njs_value.c --- a/src/njs_value.c Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_value.c Tue Nov 19 20:45:22 2019 +0300 @@ -47,6 +47,7 @@ const njs_value_t njs_string_minus_infi const njs_value_t njs_string_plus_infinity = njs_string("Infinity"); const njs_value_t njs_string_nan = njs_string("NaN"); +const njs_value_t njs_string_symbol = njs_string("symbol"); const njs_value_t njs_string_string = njs_string("string"); const njs_value_t njs_string_name = njs_string("name"); const njs_value_t njs_string_data = njs_string("data"); @@ -312,6 +313,9 @@ njs_type_string(njs_value_type_t type) case NJS_NUMBER: return "number"; + case NJS_SYMBOL: + return "symbol"; + case NJS_STRING: return "string"; @@ -333,6 +337,9 @@ njs_type_string(njs_value_type_t type) case NJS_OBJECT_NUMBER: return "object number"; + case NJS_OBJECT_SYMBOL: + return "object symbol"; + case NJS_OBJECT_STRING: return "object string"; @@ -514,6 +521,7 @@ njs_property_query(njs_vm_t *vm, njs_pro case NJS_BOOLEAN: case NJS_NUMBER: + case NJS_SYMBOL: index = njs_primitive_prototype_index(value->type); obj = &vm->prototypes[index].object; break; @@ -534,6 +542,7 @@ njs_property_query(njs_vm_t *vm, njs_pro case NJS_ARRAY: case NJS_OBJECT_BOOLEAN: case NJS_OBJECT_NUMBER: + case NJS_OBJECT_SYMBOL: case NJS_OBJECT_STRING: case NJS_REGEXP: case NJS_DATE: @@ -1206,3 +1215,11 @@ njs_value_to_object(njs_vm_t *vm, njs_va return NJS_ERROR; } + +void +njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string) +{ + njs_type_error(vm, to_string + ? "Cannot convert a Symbol value to a string" + : "Cannot convert a Symbol value to a number"); +} diff -r 884d3a86beab -r 263c75a40999 src/njs_value.h --- a/src/njs_value.h Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_value.h Tue Nov 19 20:45:22 2019 +0300 @@ -32,14 +32,16 @@ typedef enum { * a numeric value of the null and false values is zero, * a numeric value of the void value is NaN. */ - NJS_STRING = 0x04, + NJS_SYMBOL = 0x04, + + NJS_STRING = 0x05, /* The order of the above type is used in njs_is_primitive(). */ - NJS_DATA = 0x05, + NJS_DATA = 0x06, /* The type is external code. */ - NJS_EXTERNAL = 0x06, + NJS_EXTERNAL = 0x07, /* * The invalid value type is used: @@ -47,7 +49,7 @@ typedef enum { * to detect non-declared explicitly or implicitly variables, * for native property getters. */ - NJS_INVALID = 0x07, + NJS_INVALID = 0x08, /* * The object types are >= NJS_OBJECT, this is used in njs_is_object(). @@ -60,12 +62,13 @@ typedef enum { NJS_ARRAY = 0x11, NJS_OBJECT_BOOLEAN = 0x12, NJS_OBJECT_NUMBER = 0x13, - NJS_OBJECT_STRING = 0x14, - NJS_FUNCTION = 0x15, - NJS_REGEXP = 0x16, - NJS_DATE = 0x17, - NJS_OBJECT_VALUE = 0x18, -#define NJS_VALUE_TYPE_MAX (NJS_OBJECT_VALUE + 1) + NJS_OBJECT_SYMBOL = 0x14, + NJS_OBJECT_STRING = 0x15, + NJS_FUNCTION = 0x16, + NJS_REGEXP = 0x17, + NJS_DATE = 0x18, + NJS_OBJECT_VALUE = 0x19, + NJS_VALUE_TYPE_MAX } njs_value_type_t; @@ -367,6 +370,16 @@ typedef struct { } +#define njs_wellknown_symbol(key) { \ + .data = { \ + .type = NJS_SYMBOL, \ + .truth = 1, \ + .magic32 = key, \ + .u = { .value = NULL } \ + } \ +} + + #define njs_string(s) { \ .short_string = { \ .type = NJS_STRING, \ @@ -480,6 +493,10 @@ typedef struct { ((value)->type <= NJS_NUMBER) +#define njs_is_symbol(value) \ + ((value)->type == NJS_SYMBOL) + + #define njs_is_string(value) \ ((value)->type == NJS_STRING) @@ -661,6 +678,14 @@ typedef struct { *(value) = njs_value_false +#define njs_symbol_key(value) \ + ((value)->data.magic32) + + +#define njs_symbol_eq(value1, value2) \ + (njs_symbol_key(value1) == njs_symbol_key(value2)) + + extern const njs_value_t njs_value_null; extern const njs_value_t njs_value_undefined; extern const njs_value_t njs_value_false; @@ -681,6 +706,7 @@ extern const njs_value_t njs_string_min extern const njs_value_t njs_string_minus_infinity; extern const njs_value_t njs_string_plus_infinity; extern const njs_value_t njs_string_nan; +extern const njs_value_t njs_string_symbol; extern const njs_value_t njs_string_string; extern const njs_value_t njs_string_data; extern const njs_value_t njs_string_name; @@ -874,6 +900,10 @@ njs_int_t njs_value_to_object(njs_vm_t * #include "njs_number.h" +void +njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string); + + njs_inline njs_int_t njs_value_to_number(njs_vm_t *vm, njs_value_t *value, double *dst) { @@ -890,6 +920,12 @@ njs_value_to_number(njs_vm_t *vm, njs_va } if (njs_slow_path(!njs_is_numeric(value))) { + + if (njs_slow_path(njs_is_symbol(value))) { + njs_symbol_conversion_failed(vm, 0); + return NJS_ERROR; + } + *dst = NAN; if (njs_is_string(value)) { @@ -1014,12 +1050,18 @@ njs_value_to_string(njs_vm_t *vm, njs_va njs_value_t primitive; if (njs_slow_path(!njs_is_primitive(value))) { - ret = njs_value_to_primitive(vm, &primitive, value, 1); - if (njs_slow_path(ret != NJS_OK)) { - return ret; + if (njs_slow_path(value->type == NJS_OBJECT_SYMBOL)) { + /* should fail */ + value = njs_object_value(value); + + } else { + ret = njs_value_to_primitive(vm, &primitive, value, 1); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + value = &primitive; } - - value = &primitive; } return njs_primitive_value_to_string(vm, dst, value); @@ -1047,6 +1089,10 @@ njs_values_strict_equal(const njs_value_ return njs_string_eq(val1, val2); } + if (njs_is_symbol(val1)) { + return njs_symbol_eq(val1, val2); + } + return (njs_object(val1) == njs_object(val2)); } diff -r 884d3a86beab -r 263c75a40999 src/njs_vm.c --- a/src/njs_vm.c Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_vm.c Tue Nov 19 20:45:22 2019 +0300 @@ -92,6 +92,8 @@ njs_vm_create(njs_vm_opt_t *options) } } + vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX; + return vm; } diff -r 884d3a86beab -r 263c75a40999 src/njs_vm.h --- a/src/njs_vm.h Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_vm.h Tue Nov 19 20:45:22 2019 +0300 @@ -84,6 +84,7 @@ typedef enum { NJS_OBJ_TYPE_ARRAY, NJS_OBJ_TYPE_BOOLEAN, NJS_OBJ_TYPE_NUMBER, + NJS_OBJ_TYPE_SYMBOL, NJS_OBJ_TYPE_STRING, NJS_OBJ_TYPE_FUNCTION, NJS_OBJ_TYPE_REGEXP, @@ -232,6 +233,8 @@ struct njs_vm_s { * and NJS_PROPERTY_QUERY_DELETE modes. */ uintptr_t stash; /* njs_property_query_t * */ + + uint64_t symbol_generator; }; diff -r 884d3a86beab -r 263c75a40999 src/njs_vmcode.c --- a/src/njs_vmcode.c Tue Nov 19 19:32:26 2019 +0300 +++ b/src/njs_vmcode.c Tue Nov 19 20:45:22 2019 +0300 @@ -261,6 +261,16 @@ next: value2 = &primitive2; } + if (njs_slow_path(njs_is_symbol(value1) + || njs_is_symbol(value2))) + { + njs_symbol_conversion_failed(vm, + (op == NJS_VMCODE_ADDITION) && + (njs_is_string(value1) || njs_is_string(value2))); + + goto error; + } + retval = njs_vmcode_operand(vm, vmcode->operand1); if (op == NJS_VMCODE_ADDITION) { @@ -1397,6 +1407,7 @@ njs_vmcode_typeof(njs_vm_t *vm, njs_valu &njs_string_undefined, &njs_string_boolean, &njs_string_number, + &njs_string_symbol, &njs_string_string, &njs_string_data, &njs_string_external, @@ -1408,13 +1419,13 @@ njs_vmcode_typeof(njs_vm_t *vm, njs_valu &njs_string_undefined, &njs_string_undefined, &njs_string_undefined, - &njs_string_undefined, &njs_string_object, &njs_string_object, &njs_string_object, &njs_string_object, &njs_string_object, + &njs_string_object, &njs_string_function, &njs_string_object, &njs_string_object, @@ -1494,10 +1505,14 @@ again: return njs_string_eq(val1, val2); } + if (njs_is_symbol(val1)) { + return njs_symbol_eq(val1, val2); + } + return (njs_object(val1) == njs_object(val2)); } - /* Sort values as: numeric < string < objects. */ + /* Sort values as: numeric < symbol < string < objects. */ if (val1->type > val2->type) { hv = val1; @@ -1513,12 +1528,18 @@ again: return 0; } - /* If "hv" is a string then "lv" can only be a numeric. */ - if (njs_is_string(hv)) { - return (njs_number(lv) == njs_string_to_number(hv, 0)); + /* If "hv" is a symbol then "lv" can only be a numeric. */ + if (njs_is_symbol(hv)) { + return 0; } - /* "hv" is an object and "lv" is either a string or a numeric. */ + /* If "hv" is a string then "lv" can be a numeric or symbol. */ From xeioex at nginx.com Thu Nov 21 17:57:34 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 21 Nov 2019 17:57:34 +0000 Subject: [njs] Added Symbol support for builtin operations. Message-ID: details: https://hg.nginx.org/njs/rev/10a19a2e1d4f branches: changeset: 1248:10a19a2e1d4f user: Dmitry Volyntsev date: Thu Nov 21 20:56:06 2019 +0300 description: Added Symbol support for builtin operations. 1) object property get/set, object literals. 2) Added Object.getOwnPropertySymbols(). 3) Extended to support Symbol: Object.getOwnPropertyNames(), Object.keys(), Object.defineProperty(), Object.defineProperties(), Object.getOwnPropertyDescriptor(), Object.getOwnPropertyDescriptors(). diffstat: src/njs_array.c | 7 +- src/njs_date.c | 4 +- src/njs_error.c | 2 +- src/njs_function.c | 2 +- src/njs_json.c | 16 +- src/njs_object.c | 339 ++++++++++++++++++++++++++++------------------ src/njs_object.h | 170 ++++++++++++++++++++--- src/njs_object_prop.c | 19 ++- src/njs_value.c | 38 +++- src/njs_value.h | 14 +- src/njs_vmcode.c | 14 +- src/test/njs_unit_test.c | 49 ++++++ 12 files changed, 482 insertions(+), 192 deletions(-) diffs (truncated from 1329 to 1000 lines): diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_array.c --- a/src/njs_array.c Tue Nov 19 20:45:22 2019 +0300 +++ b/src/njs_array.c Thu Nov 21 20:56:06 2019 +0300 @@ -1106,8 +1106,10 @@ njs_array_prototype_to_string(njs_vm_t * njs_value_t value; njs_lvlhsh_query_t lhq; + static const njs_value_t join_string = njs_string("join"); + if (njs_is_object(&args[0])) { - njs_object_property_init(&lhq, "join", NJS_JOIN_HASH); + njs_object_property_init(&lhq, &join_string, NJS_JOIN_HASH); ret = njs_object_property(vm, &args[0], &lhq, &value); @@ -1322,7 +1324,8 @@ njs_object_indexes(njs_vm_t *vm, njs_val uint32_t i; njs_array_t *keys; - keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, 0); + keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, NJS_ENUM_STRING, + 0); if (njs_slow_path(keys == NULL)) { return NULL; } diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_date.c --- a/src/njs_date.c Tue Nov 19 20:45:22 2019 +0300 +++ b/src/njs_date.c Thu Nov 21 20:56:06 2019 +0300 @@ -1403,8 +1403,10 @@ njs_date_prototype_to_json(njs_vm_t *vm, njs_value_t value; njs_lvlhsh_query_t lhq; + static const njs_value_t to_iso_string = njs_string("toISOString"); + if (njs_is_object(&args[0])) { - njs_object_property_init(&lhq, "toISOString", NJS_TO_ISO_STRING_HASH); + njs_object_property_init(&lhq, &to_iso_string, NJS_TO_ISO_STRING_HASH); ret = njs_object_property(vm, &args[0], &lhq, &value); diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_error.c --- a/src/njs_error.c Tue Nov 19 20:45:22 2019 +0300 +++ b/src/njs_error.c Thu Nov 21 20:56:06 2019 +0300 @@ -630,7 +630,7 @@ njs_error_to_string(njs_vm_t *vm, njs_va static const njs_value_t default_name = njs_string("Error"); - njs_object_property_init(&lhq, "name", NJS_NAME_HASH); + njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH); ret = njs_object_property(vm, error, &lhq, &value1); diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_function.c --- a/src/njs_function.c Tue Nov 19 20:45:22 2019 +0300 +++ b/src/njs_function.c Thu Nov 21 20:56:06 2019 +0300 @@ -1160,7 +1160,7 @@ njs_function_prototype_bind(njs_vm_t *vm function->u.bound_target = njs_function(&args[0]); - njs_object_property_init(&lhq, "name", NJS_NAME_HASH); + njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH); ret = njs_object_property(vm, &args[0], &lhq, &name); if (njs_slow_path(ret == NJS_ERROR)) { diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_json.c --- a/src/njs_json.c Tue Nov 19 20:45:22 2019 +0300 +++ b/src/njs_json.c Thu Nov 21 20:56:06 2019 +0300 @@ -68,6 +68,7 @@ typedef struct { njs_value_t replacer; njs_str_t space; u_char space_buf[16]; + njs_object_enum_type_t keys_type; } njs_json_stringify_t; @@ -227,6 +228,7 @@ njs_json_stringify(njs_vm_t *vm, njs_val stringify->depth = 0; stringify->nodes = NULL; stringify->last = NULL; + stringify->keys_type = NJS_ENUM_STRING; replacer = njs_arg(args, nargs, 2); @@ -867,7 +869,8 @@ njs_json_push_parse_state(njs_vm_t *vm, } else { state->type = NJS_JSON_OBJECT; state->prop = NULL; - state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0); + state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + NJS_ENUM_STRING, 0); if (state->keys == NULL) { return NULL; } @@ -1094,7 +1097,7 @@ njs_json_push_stringify_state(njs_vm_t * } else { state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, - 0); + stringify->keys_type, 0); } if (njs_slow_path(state->keys == NULL)) { @@ -1362,7 +1365,9 @@ njs_object_to_json_function(njs_vm_t *vm njs_value_t retval; njs_lvlhsh_query_t lhq; - njs_object_property_init(&lhq, "toJSON", NJS_TO_JSON_HASH); + static const njs_value_t to_json_string = njs_string("toJSON"); + + njs_object_property_init(&lhq, &to_json_string, NJS_TO_JSON_HASH); ret = njs_object_property(vm, value, &lhq, &retval); @@ -2179,6 +2184,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ stringify->nodes = NULL; stringify->last = NULL; njs_set_undefined(&stringify->replacer); + stringify->keys_type = NJS_ENUM_STRING | NJS_ENUM_SYMBOL; if (!njs_dump_is_object(value)) { ret = njs_dump_value(stringify, value, console); @@ -2221,8 +2227,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ } key = &state->keys->start[state->index++]; - njs_string_get(key, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + njs_object_property_key_set(&lhq, key, 0); if (njs_is_external(&state->value)) { lhq.proto = &njs_extern_hash_proto; @@ -2277,6 +2282,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ } state->written = 1; + njs_key_string_get(vm, key, &lhq.key); njs_json_stringify_append((char *) lhq.key.start, lhq.key.length); njs_json_stringify_append(":", 1); if (stringify->space.length != 0) { diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_object.c --- a/src/njs_object.c Tue Nov 19 20:45:22 2019 +0300 +++ b/src/njs_object.c Thu Nov 21 20:56:06 2019 +0300 @@ -14,19 +14,20 @@ static njs_object_prop_t *njs_object_exi static uint32_t njs_object_enumerate_array_length(const njs_object_t *object); static uint32_t njs_object_enumerate_string_length(const njs_object_t *object); static uint32_t njs_object_enumerate_object_length(const njs_object_t *object, - njs_bool_t all); + njs_object_enum_type_t type, njs_bool_t all); static uint32_t njs_object_own_enumerate_object_length( - const njs_object_t *object, const njs_object_t *parent, njs_bool_t all); + const njs_object_t *object, const njs_object_t *parent, + njs_object_enum_type_t type, njs_bool_t all); static njs_int_t njs_object_enumerate_array(njs_vm_t *vm, const njs_array_t *array, njs_array_t *items, njs_object_enum_t kind); static njs_int_t njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value, njs_array_t *items, njs_object_enum_t kind); static njs_int_t njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object, njs_array_t *items, njs_object_enum_t kind, - njs_bool_t all); + njs_object_enum_type_t type, njs_bool_t all); static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, - njs_object_enum_t kind, njs_bool_t all); + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); njs_object_t * @@ -128,8 +129,9 @@ njs_object_hash_create(njs_vm_t *vm, njs lhq.pool = vm->mem_pool; while (n != 0) { - njs_string_get(&prop->name, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + + njs_object_property_key_set(&lhq, &prop->name, 0); + lhq.value = (void *) prop; ret = njs_lvlhsh_insert(hash, &lhq); @@ -161,25 +163,34 @@ njs_object_hash_test(njs_lvlhsh_query_t { size_t size; u_char *start; + njs_value_t *name; njs_object_prop_t *prop; prop = data; - - size = prop->name.short_string.size; + name = &prop->name; + + if (njs_slow_path(njs_is_symbol(name))) { + return ((njs_symbol_key(name) == lhq->key_hash) + && lhq->key.length == 0) ? NJS_OK : NJS_DECLINED; + } + + /* string. */ + + size = name->short_string.size; if (size != NJS_STRING_LONG) { if (lhq->key.length != size) { return NJS_DECLINED; } - start = prop->name.short_string.start; + start = name->short_string.start; } else { - if (lhq->key.length != prop->name.long_string.size) { + if (lhq->key.length != name->long_string.size) { return NJS_DECLINED; } - start = prop->name.long_string.data->start; + start = name->long_string.data->start; } if (memcmp(start, lhq->key.start, lhq->key.length) == 0) { @@ -293,7 +304,8 @@ njs_object_keys(njs_vm_t *vm, njs_value_ return NJS_ERROR; } - keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0); + keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + NJS_ENUM_STRING, 0); if (keys == NULL) { return NJS_ERROR; } @@ -320,7 +332,8 @@ njs_object_values(njs_vm_t *vm, njs_valu return NJS_ERROR; } - array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES, 0); + array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES, + NJS_ENUM_STRING, 0); if (array == NULL) { return NJS_ERROR; } @@ -347,7 +360,8 @@ njs_object_entries(njs_vm_t *vm, njs_val return NJS_ERROR; } - array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH, 0); + array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH, + NJS_ENUM_STRING, 0); if (array == NULL) { return NJS_ERROR; } @@ -396,23 +410,26 @@ next: njs_inline uint32_t -njs_object_enumerate_length(const njs_object_t *object, njs_bool_t all) +njs_object_enumerate_length(const njs_object_t *object, + njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; - length = njs_object_enumerate_object_length(object, all); - - switch (object->type) { - case NJS_ARRAY: - length += njs_object_enumerate_array_length(object); - break; - - case NJS_OBJECT_STRING: - length += njs_object_enumerate_string_length(object); - break; - - default: - break; + length = njs_object_enumerate_object_length(object, type, all); + + if (type & NJS_STRING) { + switch (object->type) { + case NJS_ARRAY: + length += njs_object_enumerate_array_length(object); + break; + + case NJS_OBJECT_STRING: + length += njs_object_enumerate_string_length(object); + break; + + default: + break; + } } return length; @@ -421,23 +438,25 @@ njs_object_enumerate_length(const njs_ob njs_inline uint32_t njs_object_own_enumerate_length(const njs_object_t *object, - const njs_object_t *parent, njs_bool_t all) + const njs_object_t *parent, njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; - length = njs_object_own_enumerate_object_length(object, parent, all); - - switch (object->type) { - case NJS_ARRAY: - length += njs_object_enumerate_array_length(object); - break; - - case NJS_OBJECT_STRING: - length += njs_object_enumerate_string_length(object); - break; - - default: - break; + length = njs_object_own_enumerate_object_length(object, parent, type, all); + + if (type & NJS_ENUM_STRING) { + switch (object->type) { + case NJS_ARRAY: + length += njs_object_enumerate_array_length(object); + break; + + case NJS_OBJECT_STRING: + length += njs_object_enumerate_string_length(object); + break; + + default: + break; + } } return length; @@ -446,34 +465,37 @@ njs_object_own_enumerate_length(const nj njs_inline njs_int_t njs_object_enumerate_value(njs_vm_t *vm, const njs_object_t *object, - njs_array_t *items, njs_object_enum_t kind, njs_bool_t all) + njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type, + njs_bool_t all) { njs_int_t ret; njs_object_value_t *obj_val; - switch (object->type) { - case NJS_ARRAY: - ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, - kind); - break; - - case NJS_OBJECT_STRING: - obj_val = (njs_object_value_t *) object; - - ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind); - break; - - default: - goto object; - } - - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; + if (type & NJS_ENUM_STRING) { + switch (object->type) { + case NJS_ARRAY: + ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, + kind); + break; + + case NJS_OBJECT_STRING: + obj_val = (njs_object_value_t *) object; + + ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind); + break; + + default: + goto object; + } + + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } } object: - ret = njs_object_enumerate_object(vm, object, items, kind, all); + ret = njs_object_enumerate_object(vm, object, items, kind, type, all); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -485,34 +507,37 @@ object: njs_inline njs_int_t njs_object_own_enumerate_value(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind, - njs_bool_t all) + njs_object_enum_type_t type, njs_bool_t all) { njs_int_t ret; njs_object_value_t *obj_val; - switch (object->type) { - case NJS_ARRAY: - ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, - kind); - break; - - case NJS_OBJECT_STRING: - obj_val = (njs_object_value_t *) object; - - ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind); - break; - - default: - goto object; - } - - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; + if (type & NJS_ENUM_STRING) { + switch (object->type) { + case NJS_ARRAY: + ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, + kind); + break; + + case NJS_OBJECT_STRING: + obj_val = (njs_object_value_t *) object; + + ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind); + break; + + default: + goto object; + } + + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } } object: - ret = njs_object_own_enumerate_object(vm, object, parent, items, kind, all); + ret = njs_object_own_enumerate_object(vm, object, parent, items, kind, + type, all); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -523,20 +548,20 @@ object: njs_array_t * njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object, - njs_object_enum_t kind, njs_bool_t all) + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; njs_int_t ret; njs_array_t *items; - length = njs_object_enumerate_length(object, all); + length = njs_object_enumerate_length(object, type, all); items = njs_array_alloc(vm, length, NJS_ARRAY_SPARE); if (njs_slow_path(items == NULL)) { return NULL; } - ret = njs_object_enumerate_value(vm, object, items, kind, all); + ret = njs_object_enumerate_value(vm, object, items, kind, type, all); if (njs_slow_path(ret != NJS_OK)) { return NULL; } @@ -549,20 +574,21 @@ njs_object_enumerate(njs_vm_t *vm, const njs_array_t * njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object, - njs_object_enum_t kind, njs_bool_t all) + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; njs_int_t ret; njs_array_t *items; - length = njs_object_own_enumerate_length(object, object, all); + length = njs_object_own_enumerate_length(object, object, type, all); items = njs_array_alloc(vm, length, NJS_ARRAY_SPARE); if (njs_slow_path(items == NULL)) { return NULL; } - ret = njs_object_own_enumerate_value(vm, object, object, items, kind, all); + ret = njs_object_own_enumerate_value(vm, object, object, items, kind, type, + all); if (njs_slow_path(ret != NJS_OK)) { return NULL; } @@ -604,17 +630,18 @@ njs_object_enumerate_string_length(const static uint32_t -njs_object_enumerate_object_length(const njs_object_t *object, njs_bool_t all) +njs_object_enumerate_object_length(const njs_object_t *object, + njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; const njs_object_t *proto; - length = njs_object_own_enumerate_object_length(object, object, all); + length = njs_object_own_enumerate_object_length(object, object, type, all); proto = object->__proto__; while (proto != NULL) { - length += njs_object_own_enumerate_length(proto, object, all); + length += njs_object_own_enumerate_length(proto, object, type, all); proto = proto->__proto__; } @@ -622,9 +649,17 @@ njs_object_enumerate_object_length(const } +njs_inline njs_bool_t +njs_is_enumerable(const njs_value_t *value, njs_object_enum_type_t type) +{ + return (njs_is_string(value) && (type & NJS_ENUM_STRING)) + || (njs_is_symbol(value) && (type & NJS_ENUM_SYMBOL)); +} + + static uint32_t njs_object_own_enumerate_object_length(const njs_object_t *object, - const njs_object_t *parent, njs_bool_t all) + const njs_object_t *parent, njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; njs_int_t ret; @@ -645,8 +680,11 @@ njs_object_own_enumerate_object_length(c break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); @@ -667,10 +705,12 @@ njs_object_own_enumerate_object_length(c break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); - - lhq.proto = &njs_object_hash_proto; + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); + ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK) { @@ -866,12 +906,14 @@ njs_object_enumerate_string(njs_vm_t *vm static njs_int_t njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object, - njs_array_t *items, njs_object_enum_t kind, njs_bool_t all) + njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type, + njs_bool_t all) { njs_int_t ret; const njs_object_t *proto; - ret = njs_object_own_enumerate_object(vm, object, object, items, kind, all); + ret = njs_object_own_enumerate_object(vm, object, object, items, kind, + type, all); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -880,7 +922,7 @@ njs_object_enumerate_object(njs_vm_t *vm while (proto != NULL) { ret = njs_object_own_enumerate_value(vm, proto, object, items, kind, - all); + type, all); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -895,7 +937,7 @@ njs_object_enumerate_object(njs_vm_t *vm static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind, - njs_bool_t all) + njs_object_enum_type_t type, njs_bool_t all) { njs_int_t ret; njs_value_t *item; @@ -910,6 +952,8 @@ njs_object_own_enumerate_object(njs_vm_t item = items->start; hash = &object->hash; + lhq.proto = &njs_object_hash_proto; + switch (kind) { case NJS_ENUM_KEYS: for ( ;; ) { @@ -919,8 +963,11 @@ njs_object_own_enumerate_object(njs_vm_t break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); @@ -941,10 +988,12 @@ njs_object_own_enumerate_object(njs_vm_t break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); - - lhq.proto = &njs_object_hash_proto; + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); + ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK) { @@ -966,8 +1015,11 @@ njs_object_own_enumerate_object(njs_vm_t break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); @@ -989,10 +1041,12 @@ njs_object_own_enumerate_object(njs_vm_t break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); - - lhq.proto = &njs_object_hash_proto; + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); + ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK) { @@ -1014,8 +1068,11 @@ njs_object_own_enumerate_object(njs_vm_t break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); @@ -1048,10 +1105,12 @@ njs_object_own_enumerate_object(njs_vm_t break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); - - lhq.proto = &njs_object_hash_proto; + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); + ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK && (prop->enumerable || all)) { @@ -1194,13 +1253,6 @@ njs_object_define_property(njs_vm_t *vm, name = njs_lvalue_arg(&lvalue, args, nargs, 2); - if (njs_slow_path(!njs_is_string(name))) { - ret = njs_value_to_string(vm, name, name); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - ret = njs_object_prop_define(vm, value, name, desc, NJS_OBJECT_PROP_DESCRIPTOR); if (njs_slow_path(ret != NJS_OK)) { @@ -1274,7 +1326,7 @@ static njs_int_t njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_value_t *value, *property; + njs_value_t lvalue, *value, *property; value = njs_arg(args, nargs, 1); @@ -1284,7 +1336,7 @@ njs_object_get_own_property_descriptor(n return NJS_ERROR; } - property = njs_arg(args, nargs, 2); + property = njs_lvalue_arg(&lvalue, args, nargs, 2); return njs_object_prop_descriptor(vm, &vm->retval, value, property); } @@ -1311,7 +1363,8 @@ njs_object_get_own_property_descriptors( return NJS_ERROR; } - names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 1); + names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1); if (njs_slow_path(names == NULL)) { return NJS_ERROR; } @@ -1339,8 +1392,7 @@ njs_object_get_own_property_descriptors( return NJS_ERROR; } - njs_string_get(key, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + njs_object_property_key_set(&lhq, key, 0); lhq.value = pr; ret = njs_lvlhsh_insert(&descriptors->hash, &lhq); @@ -1357,8 +1409,8 @@ njs_object_get_own_property_descriptors( static njs_int_t -njs_object_get_own_property_names(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) +njs_object_get_own_property(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t type) { njs_array_t *names; njs_value_t *value; @@ -1372,7 +1424,8 @@ njs_object_get_own_property_names(njs_vm return NJS_ERROR; } - names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 1); + names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + type, 1); if (names == NULL) { return NJS_ERROR; } @@ -1669,7 +1722,8 @@ njs_object_assign(njs_vm_t *vm, njs_valu for (i = 2; i < nargs; i++) { source = &args[i]; - names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS, 1); + names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS, + NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1); if (njs_slow_path(names == NULL)) { return NJS_ERROR; } @@ -1913,7 +1967,18 @@ static const njs_object_prop_t njs_obje { .type = NJS_PROPERTY, .name = njs_long_string("getOwnPropertyNames"), - .value = njs_native_function(njs_object_get_own_property_names, 1), + .value = njs_native_function2(njs_object_get_own_property, 1, + NJS_ENUM_STRING), + .writable = 1, + .configurable = 1, + }, + + /* Object.getOwnPropertySymbols(). */ + { + .type = NJS_PROPERTY, + .name = njs_long_string("getOwnPropertySymbols"), + .value = njs_native_function2(njs_object_get_own_property, 1, + NJS_ENUM_SYMBOL), .writable = 1, .configurable = 1, }, diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_object.h --- a/src/njs_object.h Tue Nov 19 20:45:22 2019 +0300 +++ b/src/njs_object.h Thu Nov 21 20:56:06 2019 +0300 @@ -8,27 +8,6 @@ #define _NJS_OBJECT_H_INCLUDED_ -#define njs_is_data_descriptor(prop) \ - ((prop)->writable != NJS_ATTRIBUTE_UNSET || njs_is_valid(&(prop)->value)) - - -#define njs_is_accessor_descriptor(prop) \ - (njs_is_function_or_undefined(&(prop)->getter) \ - || njs_is_function_or_undefined(&(prop)->setter)) - - -#define njs_is_generic_descriptor(prop) \ - (!njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop)) - - -#define njs_object_property_init(lhq, _key, hash) \ - do { \ - (lhq)->proto = &njs_object_hash_proto; \ - (lhq)->key_hash = hash; \ - (lhq)->key = njs_str_value(_key); \ - } while (0) - - typedef enum { NJS_OBJECT_PROP_DESCRIPTOR, NJS_OBJECT_PROP_GETTER, @@ -65,9 +44,9 @@ njs_object_t *njs_object_value_copy(njs_ njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, njs_uint_t type); njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object, - njs_object_enum_t kind, njs_bool_t all); + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); njs_array_t *njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object, - njs_object_enum_t kind, njs_bool_t all); + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); njs_int_t njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx, njs_object_traverse_cb_t cb); njs_int_t njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash, @@ -101,7 +80,147 @@ njs_int_t njs_object_prop_descriptor(njs njs_value_t *value, njs_value_t *setval); const char *njs_prop_type_string(njs_object_prop_type_t type); -extern const njs_object_type_init_t njs_obj_type_init; + +njs_inline njs_bool_t +njs_is_data_descriptor(njs_object_prop_t *prop) +{ + return prop->writable != NJS_ATTRIBUTE_UNSET || njs_is_valid(&prop->value); +} + + +njs_inline njs_bool_t +njs_is_accessor_descriptor(njs_object_prop_t *prop) +{ + return njs_is_function_or_undefined(&prop->getter) + || njs_is_function_or_undefined(&prop->setter); +} + + +njs_inline njs_bool_t +njs_is_generic_descriptor(njs_object_prop_t *prop) +{ + return !njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop); +} + + +njs_inline void +njs_object_property_key_set(njs_lvlhsh_query_t *lhq, const njs_value_t *key, + uint32_t hash) +{ + if (njs_is_symbol(key)) { + + lhq->key.length = 0; + lhq->key_hash = njs_symbol_key(key); + + } else { + + /* string. */ + + njs_string_get(key, &lhq->key); + + if (hash == 0) { + lhq->key_hash = njs_djb_hash(lhq->key.start, lhq->key.length); + + } else { + lhq->key_hash = hash; + } + } +} + + +njs_inline void +njs_object_property_init(njs_lvlhsh_query_t *lhq, const njs_value_t *key, + uint32_t hash) +{ + lhq->proto = &njs_object_hash_proto; + + njs_object_property_key_set(lhq, key, hash); +} + + +njs_inline njs_int_t +njs_primitive_value_to_key(njs_vm_t *vm, njs_value_t *dst, + const njs_value_t *src) +{ + const njs_value_t *value; + + switch (src->type) { + + case NJS_NULL: + value = &njs_string_null; + break; + + case NJS_UNDEFINED: + value = &njs_string_undefined; + break; + + case NJS_BOOLEAN: + value = njs_is_true(src) ? &njs_string_true : &njs_string_false; + break; + + case NJS_NUMBER: + return njs_number_to_string(vm, dst, src); + + case NJS_SYMBOL: + case NJS_STRING: + /* GC: njs_retain(src); */ + value = src; + break; + + default: + return NJS_ERROR; + } + + *dst = *value; + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_value_to_key(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) +{ + njs_int_t ret; + njs_value_t primitive; + + if (njs_slow_path(!njs_is_primitive(value))) { + if (njs_slow_path(value->type == NJS_OBJECT_SYMBOL)) { + /* should fail */ + value = njs_object_value(value); + + } else { + ret = njs_value_to_primitive(vm, &primitive, value, 1); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + value = &primitive; + } + } + + return njs_primitive_value_to_key(vm, dst, value); +} + + +njs_inline njs_int_t +njs_key_string_get(njs_vm_t *vm, const njs_value_t *key, njs_str_t *str) +{ + njs_int_t ret; + njs_value_t dst; + + if (njs_slow_path(njs_is_symbol(key))) { + ret = njs_symbol_to_string(vm, &dst, key); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + key = &dst; + } + + njs_string_get(key, str); + + return NJS_OK; +} njs_inline njs_int_t @@ -118,4 +237,7 @@ njs_object_length_set(njs_vm_t *vm, njs_ From xeioex at nginx.com Thu Nov 21 20:04:07 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 21 Nov 2019 20:04:07 +0000 Subject: [njs] Fixed hash collision for empty string key. Message-ID: details: https://hg.nginx.org/njs/rev/37f1f20de909 branches: changeset: 1249:37f1f20de909 user: Artem S. Povalyukhin date: Thu Nov 21 22:31:14 2019 +0300 description: Fixed hash collision for empty string key. The issue was introduced in 10a19a2e1d4f. diffstat: src/njs_object.c | 2 +- src/njs_object.h | 1 + src/njs_value.h | 1 + src/test/njs_unit_test.c | 4 ++++ 4 files changed, 7 insertions(+), 1 deletions(-) diffs (48 lines): diff -r 10a19a2e1d4f -r 37f1f20de909 src/njs_object.c --- a/src/njs_object.c Thu Nov 21 20:56:06 2019 +0300 +++ b/src/njs_object.c Thu Nov 21 22:31:14 2019 +0300 @@ -171,7 +171,7 @@ njs_object_hash_test(njs_lvlhsh_query_t if (njs_slow_path(njs_is_symbol(name))) { return ((njs_symbol_key(name) == lhq->key_hash) - && lhq->key.length == 0) ? NJS_OK : NJS_DECLINED; + && lhq->key.start == NULL) ? NJS_OK : NJS_DECLINED; } /* string. */ diff -r 10a19a2e1d4f -r 37f1f20de909 src/njs_object.h --- a/src/njs_object.h Thu Nov 21 20:56:06 2019 +0300 +++ b/src/njs_object.h Thu Nov 21 22:31:14 2019 +0300 @@ -110,6 +110,7 @@ njs_object_property_key_set(njs_lvlhsh_q if (njs_is_symbol(key)) { lhq->key.length = 0; + lhq->key.start = NULL; lhq->key_hash = njs_symbol_key(key); } else { diff -r 10a19a2e1d4f -r 37f1f20de909 src/njs_value.h --- a/src/njs_value.h Thu Nov 21 20:56:06 2019 +0300 +++ b/src/njs_value.h Thu Nov 21 22:31:14 2019 +0300 @@ -871,6 +871,7 @@ njs_set_object_value(njs_value_t *value, #define njs_property_query_init(pq, _query, _own) \ do { \ (pq)->lhq.key.length = 0; \ + (pq)->lhq.key.start = NULL; \ (pq)->lhq.value = NULL; \ (pq)->own_whiteout = NULL; \ (pq)->query = _query; \ diff -r 10a19a2e1d4f -r 37f1f20de909 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Nov 21 20:56:06 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Nov 21 22:31:14 2019 +0300 @@ -10302,6 +10302,10 @@ static njs_unit_test_t njs_test[] = "Object.getOwnPropertyDescriptor(o, Symbol.isConcatSpreadable).value"), njs_str("true") }, + { njs_str("var o = {}, n = 5381 /* NJS_DJB_HASH_INIT */;" + "while(n--) o[Symbol()] = 'test'; o[''];"), + njs_str("undefined") }, + /* String */ { njs_str("String()"), From xeioex at nginx.com Fri Nov 22 16:34:14 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 22 Nov 2019 16:34:14 +0000 Subject: [njs] Fixed typo in njs_string_prototype_pad(). Message-ID: details: https://hg.nginx.org/njs/rev/f98304d4019b branches: changeset: 1250:f98304d4019b user: Artem S. Povalyukhin date: Fri Nov 22 11:02:55 2019 +0300 description: Fixed typo in njs_string_prototype_pad(). The typo was introduced in 47cdd4680fc2. This closes #253 issue on Github. diffstat: src/njs_string.c | 2 +- src/test/njs_unit_test.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletions(-) diffs (35 lines): diff -r 37f1f20de909 -r f98304d4019b src/njs_string.c --- a/src/njs_string.c Thu Nov 21 22:31:14 2019 +0300 +++ b/src/njs_string.c Fri Nov 22 11:02:55 2019 +0300 @@ -2939,7 +2939,7 @@ njs_string_prototype_pad(njs_vm_t *vm, n } else { ret = njs_value_to_string(vm, pad, pad); if (njs_slow_path(ret != NJS_OK)) { - return NJS_OK; + return NJS_ERROR; } } } diff -r 37f1f20de909 -r f98304d4019b src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Nov 21 22:31:14 2019 +0300 +++ b/src/test/njs_unit_test.c Fri Nov 22 11:02:55 2019 +0300 @@ -6964,6 +6964,9 @@ static njs_unit_test_t njs_test[] = { njs_str("'???'.padStart(10, '??')"), njs_str("??????????") }, + { njs_str("'abc'.padStart(10, Symbol())"), + njs_str("TypeError: Cannot convert a Symbol value to a string") }, + { njs_str("'1234'.padEnd(4)"), njs_str("1234") }, @@ -7012,6 +7015,9 @@ static njs_unit_test_t njs_test[] = { njs_str("'????'.padEnd(10, '????')"), njs_str("??????????") }, + { njs_str("'abc'.padEnd(10, Symbol())"), + njs_str("TypeError: Cannot convert a Symbol value to a string") }, + { njs_str("String.bytesFrom({})"), njs_str("TypeError: value must be a string or array") }, From xeioex at nginx.com Fri Nov 22 16:34:15 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 22 Nov 2019 16:34:15 +0000 Subject: [njs] Introduced Symbol.toStringTag support for builtin objects. Message-ID: details: https://hg.nginx.org/njs/rev/133c31ef36e5 branches: changeset: 1251:133c31ef36e5 user: Dmitry Volyntsev date: Fri Nov 22 19:03:23 2019 +0300 description: Introduced Symbol.toStringTag support for builtin objects. This closes #255 issue on Github. diffstat: src/njs_builtin.c | 21 ++++++++++++++ src/njs_crypto.c | 34 ++-------------------- src/njs_json.c | 33 ++++++++++++++++----- src/njs_lvlhsh.h | 4 ++ src/njs_math.c | 7 ++++ src/njs_object.c | 61 ++++++++++++++++++++++++++++++++++------ src/njs_object.h | 21 ++++++++++++++ src/njs_symbol.c | 7 ++++ src/test/njs_unit_test.c | 71 +++++++++++++++++++++++++++++++++++------------ 9 files changed, 193 insertions(+), 66 deletions(-) diffs (561 lines): diff -r f98304d4019b -r 133c31ef36e5 src/njs_builtin.c --- a/src/njs_builtin.c Fri Nov 22 11:02:55 2019 +0300 +++ b/src/njs_builtin.c Fri Nov 22 19:03:23 2019 +0300 @@ -956,6 +956,13 @@ njs_top_level_constructor(njs_vm_t *vm, static const njs_object_prop_t njs_global_this_object_properties[] = { + { + .type = NJS_PROPERTY, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("global"), + .configurable = 1, + }, + /* Global constants. */ { @@ -1313,6 +1320,13 @@ static const njs_object_prop_t njs_njs_ { { .type = NJS_PROPERTY, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("njs"), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, .name = njs_string("version"), .value = njs_string(NJS_VERSION), .configurable = 1, @@ -1510,6 +1524,13 @@ njs_process_object_ppid(njs_vm_t *vm, nj static const njs_object_prop_t njs_process_object_properties[] = { { + .type = NJS_PROPERTY, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("process"), + .configurable = 1, + }, + + { .type = NJS_PROPERTY_HANDLER, .name = njs_string("argv"), .value = njs_prop_handler(njs_process_object_argv), diff -r f98304d4019b -r 133c31ef36e5 src/njs_crypto.c --- a/src/njs_crypto.c Fri Nov 22 11:02:55 2019 +0300 +++ b/src/njs_crypto.c Fri Nov 22 19:03:23 2019 +0300 @@ -299,18 +299,6 @@ njs_hash_prototype_digest(njs_vm_t *vm, } -static njs_int_t -njs_hash_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - static const njs_value_t string = njs_string("[object Hash]"); - - vm->retval = string; - - return NJS_OK; -} - - static const njs_object_prop_t njs_hash_prototype_properties[] = { { @@ -322,9 +310,8 @@ static const njs_object_prop_t njs_hash { .type = NJS_PROPERTY, - .name = njs_string("toString"), - .value = njs_native_function(njs_hash_prototype_to_string, 0), - .writable = 1, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("Hash"), .configurable = 1, }, @@ -598,18 +585,6 @@ njs_hmac_prototype_digest(njs_vm_t *vm, } -static njs_int_t -njs_hmac_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - static const njs_value_t string = njs_string("[object Hmac]"); - - vm->retval = string; - - return NJS_OK; -} - - static const njs_object_prop_t njs_hmac_prototype_properties[] = { { @@ -621,9 +596,8 @@ static const njs_object_prop_t njs_hmac { .type = NJS_PROPERTY, - .name = njs_string("toString"), - .value = njs_native_function(njs_hmac_prototype_to_string, 0), - .writable = 1, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("Hmac"), .configurable = 1, }, diff -r f98304d4019b -r 133c31ef36e5 src/njs_json.c --- a/src/njs_json.c Fri Nov 22 11:02:55 2019 +0300 +++ b/src/njs_json.c Fri Nov 22 19:03:23 2019 +0300 @@ -205,7 +205,7 @@ njs_vm_json_parse(njs_vm_t *vm, njs_valu { njs_function_t *parse; - parse = njs_function(&njs_json_object_properties[0].value); + parse = njs_function(&njs_json_object_properties[1].value); return njs_vm_call(vm, parse, args, nargs); } @@ -285,7 +285,7 @@ njs_vm_json_stringify(njs_vm_t *vm, njs_ { njs_function_t *stringify; - stringify = njs_function(&njs_json_object_properties[1].value); + stringify = njs_function(&njs_json_object_properties[2].value); return njs_vm_call(vm, stringify, args, nargs); } @@ -1133,7 +1133,7 @@ njs_json_pop_stringify_state(njs_json_st #define njs_json_stringify_append(str, len) \ - ret = njs_json_buf_append(stringify, str, len); \ + ret = njs_json_buf_append(stringify, (char *) str, len); \ if (ret != NJS_OK) { \ goto memory_error; \ } @@ -1143,7 +1143,7 @@ njs_json_pop_stringify_state(njs_json_st if (stringify->space.length != 0) { \ njs_json_stringify_append("\n", 1); \ for (i = 0; i < (njs_int_t) (times) - 1; i++) { \ - njs_json_stringify_append((char *) stringify->space.start, \ + njs_json_stringify_append(stringify->space.start, \ stringify->space.length); \ } \ } @@ -1857,7 +1857,13 @@ njs_json_buf_pullup(njs_json_stringify_t static const njs_object_prop_t njs_json_object_properties[] = { - /* JSON.parse(). */ + { + .type = NJS_PROPERTY, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("JSON"), + .configurable = 1, + }, + { .type = NJS_PROPERTY, .name = njs_string("parse"), @@ -1866,7 +1872,6 @@ static const njs_object_prop_t njs_json .configurable = 1, }, - /* JSON.stringify(). */ { .type = NJS_PROPERTY, .name = njs_string("stringify"), @@ -2161,9 +2166,10 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ njs_int_t i; njs_int_t ret; njs_str_t str; - njs_value_t *key, *val, ext_val; + njs_value_t *key, *val, tag, ext_val; njs_object_t *object; njs_json_state_t *state; + njs_string_prop_t string; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; njs_json_stringify_t *stringify, dump_stringify; @@ -2210,6 +2216,17 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ switch (state->type) { case NJS_JSON_OBJECT: if (state->index == 0) { + ret = njs_object_string_tag(vm, &state->value, &tag); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (ret == NJS_OK) { + (void) njs_string_prop(&string, &tag); + njs_json_stringify_append(string.start, string.size) + njs_json_stringify_append(" ", 1); + } + njs_json_stringify_append("{", 1); njs_json_stringify_indent(stringify->depth + 1); } @@ -2283,7 +2300,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ state->written = 1; njs_key_string_get(vm, key, &lhq.key); - njs_json_stringify_append((char *) lhq.key.start, lhq.key.length); + njs_json_stringify_append(lhq.key.start, lhq.key.length); njs_json_stringify_append(":", 1); if (stringify->space.length != 0) { njs_json_stringify_append(" ", 1); diff -r f98304d4019b -r 133c31ef36e5 src/njs_lvlhsh.h --- a/src/njs_lvlhsh.h Fri Nov 22 11:02:55 2019 +0300 +++ b/src/njs_lvlhsh.h Fri Nov 22 19:03:23 2019 +0300 @@ -105,6 +105,10 @@ struct njs_lvlhsh_query_s { #define njs_lvlhsh_init(lh) \ (lh)->slot = NULL + +#define njs_lvlhsh_eq(lhl, lhr) \ + ((lhl)->slot == (lhr)->slot) + /* * njs_lvlhsh_find() finds a hash element. If the element has been * found then it is stored in the lhq->value and njs_lvlhsh_find() diff -r f98304d4019b -r 133c31ef36e5 src/njs_math.c --- a/src/njs_math.c Fri Nov 22 11:02:55 2019 +0300 +++ b/src/njs_math.c Fri Nov 22 19:03:23 2019 +0300 @@ -986,6 +986,13 @@ static const njs_object_prop_t njs_math { { .type = NJS_PROPERTY, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("Math"), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, .name = njs_string("E"), .value = njs_value(NJS_NUMBER, 1, M_E), }, diff -r f98304d4019b -r 133c31ef36e5 src/njs_object.c --- a/src/njs_object.c Fri Nov 22 11:02:55 2019 +0300 +++ b/src/njs_object.c Fri Nov 22 19:03:23 2019 +0300 @@ -2274,12 +2274,18 @@ static const njs_value_t njs_object_reg static const njs_value_t njs_object_date_string = njs_string("[object Date]"); static const njs_value_t njs_object_error_string = njs_string("[object Error]"); +static const njs_value_t njs_object_arguments_string = + njs_long_string("[object Arguments]"); njs_int_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { + u_char *p; + njs_int_t ret; + njs_value_t tag, *value; + njs_string_prop_t string; const njs_value_t *name; static const njs_value_t *class_name[NJS_VALUE_TYPE_MAX] = { @@ -2315,21 +2321,56 @@ njs_object_prototype_to_string(njs_vm_t &njs_object_object_string, }; - name = class_name[args[0].type]; - - if (njs_is_error(&args[0])) { - name = &njs_object_error_string; - } - - if (njs_fast_path(name != NULL)) { + value = njs_argument(args, 0); + name = class_name[value->type]; + + if (njs_is_null_or_undefined(value)) { vm->retval = *name; return NJS_OK; } - njs_internal_error(vm, "Unknown value type"); - - return NJS_ERROR; + if (njs_is_error(value)) { + name = &njs_object_error_string; + } + + if (njs_is_object(value) + && njs_lvlhsh_eq(&njs_object(value)->shared_hash, + &vm->shared->arguments_object_instance_hash)) + { + name = &njs_object_arguments_string; + } + + ret = njs_object_string_tag(vm, value, &tag); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (ret == NJS_DECLINED) { + if (njs_slow_path(name == NULL)) { + njs_internal_error(vm, "Unknown value type"); + + return NJS_ERROR; + } + + vm->retval = *name; + + return NJS_OK; + } + + (void) njs_string_prop(&string, &tag); + + p = njs_string_alloc(vm, &vm->retval, string.size + njs_length("[object ]"), + string.length + njs_length("[object ]")); + if (njs_slow_path(p == NULL)) { + return NJS_ERROR; + } + + p = njs_cpymem(p, "[object ", 8); + p = njs_cpymem(p, string.start, string.size); + *p = ']'; + + return NJS_OK; } diff -r f98304d4019b -r 133c31ef36e5 src/njs_object.h --- a/src/njs_object.h Fri Nov 22 11:02:55 2019 +0300 +++ b/src/njs_object.h Fri Nov 22 19:03:23 2019 +0300 @@ -238,6 +238,27 @@ njs_object_length_set(njs_vm_t *vm, njs_ } +njs_inline njs_int_t +njs_object_string_tag(njs_vm_t *vm, njs_value_t *value, njs_value_t *tag) +{ + njs_int_t ret; + + static const njs_value_t to_string_tag = + njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG); + + ret = njs_value_property(vm, value, njs_value_arg(&to_string_tag), tag); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (!njs_is_string(tag)) { + return NJS_DECLINED; + } + + return NJS_OK; +} + + extern const njs_object_type_init_t njs_obj_type_init; diff -r f98304d4019b -r 133c31ef36e5 src/njs_symbol.c --- a/src/njs_symbol.c Fri Nov 22 11:02:55 2019 +0300 +++ b/src/njs_symbol.c Fri Nov 22 19:03:23 2019 +0300 @@ -384,6 +384,13 @@ njs_symbol_prototype_description(njs_vm_ static const njs_object_prop_t njs_symbol_prototype_properties[] = { { + .type = NJS_PROPERTY, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("Symbol"), + .configurable = 1, + }, + + { .type = NJS_PROPERTY_HANDLER, .name = njs_string("__proto__"), .value = njs_prop_handler(njs_primitive_prototype_get_proto), diff -r f98304d4019b -r 133c31ef36e5 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Nov 22 11:02:55 2019 +0300 +++ b/src/test/njs_unit_test.c Fri Nov 22 19:03:23 2019 +0300 @@ -8152,6 +8152,9 @@ static njs_unit_test_t njs_test[] = { njs_str("(function () {arguments = [];})"), njs_str("SyntaxError: Identifier \"arguments\" is forbidden as left-hand in assignment in 1") }, + { njs_str("(function(){return arguments;})()"), + njs_str("[object Arguments]") }, + { njs_str("(function(){return arguments[0];})(1,2,3)"), njs_str("1") }, @@ -8368,16 +8371,16 @@ static njs_unit_test_t njs_test[] = /* arrow functions + global this. */ { njs_str("(() => this)()"), - njs_str("[object Object]") }, + njs_str("[object global]") }, { njs_str("(() => this).call('abc')"), - njs_str("[object Object]") }, + njs_str("[object global]") }, { njs_str("(() => this).apply('abc')"), - njs_str("[object Object]") }, + njs_str("[object global]") }, { njs_str("(() => this).bind('abc')()"), - njs_str("[object Object]") }, + njs_str("[object global]") }, { njs_str("(function() { return (() => this); })()()"), njs_str("undefined") }, @@ -9338,7 +9341,7 @@ static njs_unit_test_t njs_test[] = /* global this. */ { njs_str("this"), - njs_str("[object Object]") }, + njs_str("[object global]") }, { njs_str("Object.getOwnPropertyDescriptor(this, 'NaN').value"), njs_str("NaN") }, @@ -9415,7 +9418,7 @@ static njs_unit_test_t njs_test[] = njs_str("TypeError: right argument is not callable") }, { njs_str("njs"), - njs_str("[object Object]") }, + njs_str("[object njs]") }, { njs_str("var o = Object(); o"), njs_str("[object Object]") }, @@ -10041,7 +10044,7 @@ static njs_unit_test_t njs_test[] = njs_str("true") }, { njs_str("Object.prototype.toString.call(Symbol.prototype)"), - njs_str("[object Object]") }, + njs_str("[object Symbol]") }, { njs_str("Symbol.prototype.toString()"), njs_str("TypeError: unexpected value type:object") }, @@ -10312,6 +10315,20 @@ static njs_unit_test_t njs_test[] = "while(n--) o[Symbol()] = 'test'; o[''];"), njs_str("undefined") }, + { njs_str("[" + " Object.prototype," + " Symbol.prototype," + " Math," + " JSON," + " process," + " njs," + " this," + "]" + ".map(v=>Object.getOwnPropertyDescriptor(v, Symbol.toStringTag))" + ".map(d=>{if (d && !d.writable && !d.enumerable && d.configurable) return d.value})" + ".map(v=>njs.dump(v))"), + njs_str("undefined,Symbol,Math,JSON,process,njs,global") }, + /* String */ { njs_str("String()"), @@ -10649,6 +10666,10 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.prototype.toString.call(true)"), njs_str("[object Boolean]") }, + { njs_str("Boolean.prototype[Symbol.toStringTag] = 'XXX';" + "Object.prototype.toString.call(true)"), + njs_str("[object XXX]") }, + { njs_str("Object.prototype.toString.call(1)"), njs_str("[object Number]") }, @@ -10661,6 +10682,10 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.prototype.toString.call([])"), njs_str("[object Array]") }, + { njs_str("var a = []; a[Symbol.toStringTag] = 'XXX';" + "Object.prototype.toString.call(a)"), + njs_str("[object XXX]") }, + { njs_str("Object.prototype.toString.call(new Object(true))"), njs_str("[object Boolean]") }, @@ -10682,9 +10707,19 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.prototype.toString.call(function(){})"), njs_str("[object Function]") }, + { njs_str("var f = ()=>1; f[Symbol.toStringTag] = '?'.repeat(32);" + "var toStr = Object.prototype.toString.call(f); [toStr, toStr.length]"), + njs_str("[object ????????????????????????????????],41") }, + { njs_str("Object.prototype.toString.call(/./)"), njs_str("[object RegExp]") }, + { njs_str("Object.prototype.toString.call(Math)"), + njs_str("[object Math]") }, + + { njs_str("Object.prototype.toString.call(JSON)"), + njs_str("[object JSON]") }, + { njs_str("var p = { a:5 }; var o = Object.create(p); o.a"), njs_str("5") }, @@ -13571,10 +13606,8 @@ static njs_unit_test_t njs_test[] = { njs_str("Math.trunc()"), njs_str("NaN") }, - /* ES5FIX: "[object Math]". */ - { njs_str("Math"), - njs_str("[object Object]") }, + njs_str("[object Math]") }, { njs_str("Math.x = function (x) {return 2*x;}; Math.x(3)"), njs_str("6") }, @@ -13810,6 +13843,9 @@ static njs_unit_test_t njs_test[] = { njs_str("this.Math = 1; Math"), njs_str("1") }, + { njs_str("JSON"), + njs_str("[object JSON]") }, + { njs_str("JSON === JSON"), njs_str("true") }, @@ -14535,7 +14571,7 @@ static njs_unit_test_t njs_test[] = { njs_str("njs.dump($r.header)"), njs_str("{type:\"object\",props:[\"getter\",\"keys\"]}") }, - { njs_str("njs.dump(njs) == `{version:'${njs.version}'}`"), + { njs_str("njs.dump(njs) == `njs {version:'${njs.version}'}`"), njs_str("true") }, { njs_str("njs.dump(-0)"), @@ -14760,11 +14796,9 @@ static njs_unit_test_t njs_test[] = /* require('crypto').createHash() */ - { njs_str("require('crypto').createHash('sha1')"), - njs_str("[object Hash]") }, - - { njs_str("Object.prototype.toString.call(require('crypto').createHash('sha1'))"), - njs_str("[object Object]") }, + { njs_str("var h = require('crypto').createHash('sha1');" + "[Object.prototype.toString.call(h), njs.dump(h),h]"), + njs_str("[object Hash],Hash {},[object Hash]") }, { njs_str("var h = require('crypto').createHash('sha1');" "var Hash = h.constructor; " @@ -14853,8 +14887,9 @@ static njs_unit_test_t njs_test[] = /* require('crypto').createHmac() */ - { njs_str("require('crypto').createHmac('sha1', '')"), - njs_str("[object Hmac]") }, + { njs_str("var h = require('crypto').createHmac('sha1', '');" + "[Object.prototype.toString.call(h), njs.dump(h),h]"), + njs_str("[object Hmac],Hmac {},[object Hmac]") }, { njs_str("var h = require('crypto').createHmac('md5', '');" "h.digest('hex')"), From yp at fuzzit.dev Fri Nov 22 16:42:04 2019 From: yp at fuzzit.dev (Yevgeny Pats) Date: Fri, 22 Nov 2019 18:42:04 +0200 Subject: Continuous Fuzzing Message-ID: Hey Team, I'm Yevgeny Pats, Founder of Fuzzit . I'm not sure about the current state of fuzzing in Nginx but I thought it was worth asking/discussing. If adding new fuzz targets to nginx and running those continuously as part of the CI is interesting I'll be happy to help both write some of the fuzz target as well as help integrate the fuzz target to Fuzzit (we have a free plan for OSS projects). Would love to hear your thoughts as well as answer any questions about Fuzzit service that you might have. Cheers, Yevgeny -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Sat Nov 23 11:29:18 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Sat, 23 Nov 2019 11:29:18 +0000 Subject: [njs] Fixed Object.prototype.valueOf() according to the specification. Message-ID: details: https://hg.nginx.org/njs/rev/180b28e542b6 branches: changeset: 1252:180b28e542b6 user: Artem S. Povalyukhin date: Sat Nov 23 00:09:26 2019 +0300 description: Fixed Object.prototype.valueOf() according to the specification. This closes #256 issue on Github. diffstat: src/njs_object.c | 6 +++++- src/test/njs_unit_test.c | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletions(-) diffs (37 lines): diff -r 133c31ef36e5 -r 180b28e542b6 src/njs_object.c --- a/src/njs_object.c Fri Nov 22 19:03:23 2019 +0300 +++ b/src/njs_object.c Sat Nov 23 00:09:26 2019 +0300 @@ -2242,7 +2242,11 @@ static njs_int_t njs_object_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - vm->retval = args[0]; + vm->retval = *njs_argument(args, 0); + + if (!njs_is_object(&vm->retval)) { + return njs_value_to_object(vm, &vm->retval); + } return NJS_OK; } diff -r 133c31ef36e5 -r 180b28e542b6 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Nov 22 19:03:23 2019 +0300 +++ b/src/test/njs_unit_test.c Sat Nov 23 00:09:26 2019 +0300 @@ -9531,6 +9531,17 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.prototype.valueOf.prototype"), njs_str("undefined") }, + { njs_str("Object.prototype.valueOf.call()"), + njs_str("TypeError: cannot convert null or undefined to object") }, + + { njs_str("Object.prototype.valueOf.call(null)"), + njs_str("TypeError: cannot convert null or undefined to object") }, + + { njs_str("[false, NaN, Symbol(), '']" + ".map((x) => Object.prototype.valueOf.call(x))" + ".map((x) => Object.prototype.toString.call(x))"), + njs_str("[object Boolean],[object Number],[object Symbol],[object String]") }, + { njs_str("Object.constructor === Function"), njs_str("true") }, From xeioex at nginx.com Sat Nov 23 11:29:18 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Sat, 23 Nov 2019 11:29:18 +0000 Subject: [njs] Fixed handling of Symbol values in JSON.stringify(). Message-ID: details: https://hg.nginx.org/njs/rev/4367a0615234 branches: changeset: 1253:4367a0615234 user: Artem S. Povalyukhin date: Sat Nov 23 01:02:04 2019 +0300 description: Fixed handling of Symbol values in JSON.stringify(). diffstat: src/njs_json.c | 3 +++ src/test/njs_unit_test.c | 12 ++++++++++++ 2 files changed, 15 insertions(+), 0 deletions(-) diffs (63 lines): diff -r 180b28e542b6 -r 4367a0615234 src/njs_json.c --- a/src/njs_json.c Sat Nov 23 00:09:26 2019 +0300 +++ b/src/njs_json.c Sat Nov 23 01:02:04 2019 +0300 @@ -1128,6 +1128,7 @@ njs_json_pop_stringify_state(njs_json_st #define njs_json_is_object(value) \ (((value)->type == NJS_OBJECT) \ + || ((value)->type == NJS_OBJECT_SYMBOL) \ || ((value)->type == NJS_ARRAY) \ || ((value)->type >= NJS_REGEXP)) @@ -1211,6 +1212,7 @@ njs_json_stringify_iterator(njs_vm_t *vm } if (njs_is_undefined(value) + || njs_is_symbol(value) || njs_is_function(value) || !njs_is_valid(value)) { @@ -1559,6 +1561,7 @@ njs_json_append_value(njs_json_stringify case NJS_UNDEFINED: case NJS_NULL: + case NJS_SYMBOL: case NJS_INVALID: case NJS_FUNCTION: default: diff -r 180b28e542b6 -r 4367a0615234 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Sat Nov 23 00:09:26 2019 +0300 +++ b/src/test/njs_unit_test.c Sat Nov 23 01:02:04 2019 +0300 @@ -14264,6 +14264,9 @@ static njs_unit_test_t njs_test[] = { njs_str("JSON.stringify(undefined)"), njs_str("undefined") }, + { njs_str("JSON.stringify(Symbol())"), + njs_str("undefined") }, + { njs_str("JSON.stringify({})"), njs_str("{}") }, @@ -14279,6 +14282,9 @@ static njs_unit_test_t njs_test[] = { njs_str("JSON.stringify({a:1, b:undefined})"), njs_str("{\"a\":1}") }, + { njs_str("JSON.stringify({a:1, b:Symbol()})"), + njs_str("{\"a\":1}") }, + { njs_str("var o = {a:1, c:2};" "Object.defineProperty(o, 'b', {enumerable:false, value:3});" "JSON.stringify(o)"), @@ -14290,6 +14296,12 @@ static njs_unit_test_t njs_test[] = { njs_str("JSON.stringify(RegExp())"), njs_str("{}") }, + { njs_str("JSON.stringify(Object(Symbol()))"), + njs_str("{}") }, + + { njs_str("var s = Object(Symbol()); s.test = 'test'; JSON.stringify(s)"), + njs_str("{\"test\":\"test\"}") }, + { njs_str("JSON.stringify(SyntaxError('e'))"), njs_str("{}") }, From xeioex at nginx.com Sat Nov 23 11:29:18 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Sat, 23 Nov 2019 11:29:18 +0000 Subject: [njs] Pass unprintable values to JSON.stringify() replacer function. Message-ID: details: https://hg.nginx.org/njs/rev/cb81e4469b2c branches: changeset: 1254:cb81e4469b2c user: Artem S. Povalyukhin date: Sat Nov 23 12:52:06 2019 +0300 description: Pass unprintable values to JSON.stringify() replacer function. This closes #257 issue on Github. diffstat: src/njs_json.c | 14 +++++--------- src/test/njs_unit_test.c | 4 ++++ 2 files changed, 9 insertions(+), 9 deletions(-) diffs (45 lines): diff -r 4367a0615234 -r cb81e4469b2c src/njs_json.c --- a/src/njs_json.c Sat Nov 23 01:02:04 2019 +0300 +++ b/src/njs_json.c Sat Nov 23 12:52:06 2019 +0300 @@ -1211,14 +1211,6 @@ njs_json_stringify_iterator(njs_vm_t *vm return ret; } - if (njs_is_undefined(value) - || njs_is_symbol(value) - || njs_is_function(value) - || !njs_is_valid(value)) - { - break; - } - ret = njs_json_stringify_to_json(stringify, state, key, value); if (njs_slow_path(ret != NJS_OK)) { return ret; @@ -1229,7 +1221,11 @@ njs_json_stringify_iterator(njs_vm_t *vm return ret; } - if (njs_is_undefined(value)) { + if (njs_is_undefined(value) + || njs_is_symbol(value) + || njs_is_function(value) + || !njs_is_valid(value)) + { break; } diff -r 4367a0615234 -r cb81e4469b2c src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Sat Nov 23 01:02:04 2019 +0300 +++ b/src/test/njs_unit_test.c Sat Nov 23 12:52:06 2019 +0300 @@ -14562,6 +14562,10 @@ static njs_unit_test_t njs_test[] = "JSON.stringify(objs)"), njs_str("[{\"\":{\"a\":1}},{\"a\":1}]") }, + { njs_str("JSON.stringify({a: () => 1, b: Symbol(), c: undefined}," + "(k, v) => k.length ? String(v) : v)"), + njs_str("{\"a\":\"[object Function]\",\"b\":\"Symbol()\",\"c\":\"undefined\"}") }, + { njs_str("var a = []; a[0] = a; JSON.stringify(a)"), njs_str("TypeError: Nested too deep or a cyclic structure") }, From zelenkov at nginx.com Mon Nov 25 14:07:13 2019 From: zelenkov at nginx.com (Andrei Zeliankou) Date: Mon, 25 Nov 2019 17:07:13 +0300 Subject: Continuous Fuzzing In-Reply-To: References: Message-ID: <5B929F57-4332-4074-931B-0276C6348F0C@nginx.com> > On 22 Nov 2019, at 19:42, Yevgeny Pats wrote: > > Hey Team, > > I'm Yevgeny Pats, Founder of Fuzzit. > > I'm not sure about the current state of fuzzing in Nginx but I thought it was worth asking/discussing. > > If adding new fuzz targets to nginx and running those continuously as part of the CI is interesting I'll be happy to help both write some of the fuzz target as well as help integrate the fuzz target to Fuzzit (we have a free plan for OSS projects). > > Would love to hear your thoughts as well as answer any questions about Fuzzit service that you might have. > > Cheers, > Yevgeny > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel Hi Yevgeny, Currently, nginx has no library so it's not possible to use fuzz targets. Possible way to fuzz nginx is in binary mode (e.g. routing fuzz input to the listen socket). Is it possible to run continuously fuzzing in Fuzzit without fuzz targets? -- Andrei Zeliankou From xeioex at nginx.com Mon Nov 25 14:54:27 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 25 Nov 2019 14:54:27 +0000 Subject: [njs] Added externals support in JSON.stringify(). Message-ID: details: https://hg.nginx.org/njs/rev/3eb134dfa896 branches: changeset: 1255:3eb134dfa896 user: Dmitry Volyntsev date: Mon Nov 25 17:53:19 2019 +0300 description: Added externals support in JSON.stringify(). diffstat: src/njs_json.c | 10 +++------- src/test/njs_unit_test.c | 5 +++-- 2 files changed, 6 insertions(+), 9 deletions(-) diffs (42 lines): diff -r cb81e4469b2c -r 3eb134dfa896 src/njs_json.c --- a/src/njs_json.c Sat Nov 23 12:52:06 2019 +0300 +++ b/src/njs_json.c Mon Nov 25 17:53:19 2019 +0300 @@ -1092,13 +1092,8 @@ njs_json_push_stringify_state(njs_vm_t * state->keys = njs_array(&stringify->replacer); } else { - if (njs_is_external(value)) { - state->keys = njs_extern_keys_array(vm, value->external.proto); - - } else { - state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, - stringify->keys_type, 0); - } + state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + stringify->keys_type, 0); if (njs_slow_path(state->keys == NULL)) { return NULL; @@ -1129,6 +1124,7 @@ njs_json_pop_stringify_state(njs_json_st #define njs_json_is_object(value) \ (((value)->type == NJS_OBJECT) \ || ((value)->type == NJS_OBJECT_SYMBOL) \ + || ((value)->type == NJS_EXTERNAL) \ || ((value)->type == NJS_ARRAY) \ || ((value)->type >= NJS_REGEXP)) diff -r cb81e4469b2c -r 3eb134dfa896 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Sat Nov 23 12:52:06 2019 +0300 +++ b/src/test/njs_unit_test.c Mon Nov 25 17:53:19 2019 +0300 @@ -14317,8 +14317,9 @@ static njs_unit_test_t njs_test[] = { njs_str("var e = URIError('e'); e.foo = 'E'; JSON.stringify(e)"), njs_str("{\"foo\":\"E\"}") }, - { njs_str("JSON.stringify([$r])"), - njs_str("[null]") }, + { njs_str("var r = JSON.parse(JSON.stringify($r));" + "[r.uri, r.host, r.props.a, njs.dump(r.vars), njs.dump(r.consts), r.header['02']]"), + njs_str("???,???????????,1,{},{},02|???") }, { njs_str("JSON.stringify({get key() {throw new Error('Oops')}})"), njs_str("Error: Oops") }, From xeioex at nginx.com Mon Nov 25 14:54:27 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 25 Nov 2019 14:54:27 +0000 Subject: [njs] Fixed njs.dump() for objects with NJS_PROPERTY_HANDLER props. Message-ID: details: https://hg.nginx.org/njs/rev/790caa744e0e branches: changeset: 1256:790caa744e0e user: Dmitry Volyntsev date: Mon Nov 25 17:53:32 2019 +0300 description: Fixed njs.dump() for objects with NJS_PROPERTY_HANDLER props. diffstat: src/njs_json.c | 177 ++++++++++++++++++---------------------------- src/njs_value.c | 20 ++++- src/test/njs_unit_test.c | 7 +- 3 files changed, 91 insertions(+), 113 deletions(-) diffs (307 lines): diff -r 3eb134dfa896 -r 790caa744e0e src/njs_json.c --- a/src/njs_json.c Mon Nov 25 17:53:19 2019 +0300 +++ b/src/njs_json.c Mon Nov 25 17:53:32 2019 +0300 @@ -1906,15 +1906,12 @@ static njs_int_t njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value, njs_uint_t console) { - njs_int_t ret; - njs_str_t str; - njs_uint_t written; - njs_value_t str_val; - const njs_extern_t *ext_proto; - u_char buf[32], *p; - - njs_int_t (*to_string)(njs_vm_t *, njs_value_t *, - const njs_value_t *); + njs_int_t ret; + njs_str_t str; + njs_value_t str_val; + u_char buf[32], *p; + + njs_int_t (*to_string)(njs_vm_t *, njs_value_t *, const njs_value_t *); switch (value->type) { case NJS_OBJECT_STRING: @@ -2032,52 +2029,6 @@ njs_dump_value(njs_json_stringify_t *str break; - case NJS_EXTERNAL: - ext_proto = value->external.proto; - - written = 0; - njs_dump_item("{type:"); - - switch (ext_proto->type) { - case NJS_EXTERN_PROPERTY: - njs_dump("\"property\""); - break; - case NJS_EXTERN_METHOD: - njs_dump("\"method\""); - break; - case NJS_EXTERN_OBJECT: - njs_dump("\"object\""); - break; - case NJS_EXTERN_CASELESS_OBJECT: - njs_dump("\"caseless_object\""); - break; - } - - njs_dump_item("props:["); - written = 0; - - if (ext_proto->get != NULL) { - njs_dump_item("\"getter\""); - } - - if (ext_proto->set != NULL) { - njs_dump_item("\"setter\""); - } - - if (ext_proto->function != NULL) { - njs_dump_item("\"method\""); - } - - if (ext_proto->find != NULL) { - njs_dump_item("\"find\""); - } - - if (ext_proto->keys != NULL) { - njs_dump_item("\"keys\""); - } - - return njs_json_buf_append(stringify, "]}", 2); - case NJS_NUMBER: if (njs_slow_path(njs_number(value) == 0.0 && signbit(njs_number(value)))) @@ -2135,12 +2086,25 @@ memory_error: } -#define njs_dump_is_object(value) \ - (((value)->type == NJS_OBJECT && !njs_object(value)->error_data) \ - || ((value)->type == NJS_ARRAY) \ - || ((value)->type == NJS_OBJECT_VALUE) \ - || ((value)->type == NJS_EXTERNAL \ - && !njs_lvlhsh_is_empty(&(value)->external.proto->hash))) +njs_inline njs_bool_t +njs_dump_is_external_object(const njs_value_t *value) +{ + if (!njs_is_external(value)) { + return 0; + } + + return value->external.proto->type == NJS_EXTERN_OBJECT; +} + + +njs_inline njs_bool_t +njs_dump_is_object(const njs_value_t *value) +{ + return (value->type == NJS_OBJECT && !njs_object(value)->error_data) + || (value->type == NJS_ARRAY) + || (value->type == NJS_OBJECT_VALUE) + || njs_dump_is_external_object(value); +} #define njs_dump_append_value(value) \ @@ -2154,6 +2118,11 @@ memory_error: } +static const njs_value_t string_get = njs_string("[Getter]"); +static const njs_value_t string_set = njs_string("[Setter]"); +static const njs_value_t string_get_set = njs_long_string("[Getter/Setter]"); + + njs_int_t njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value, njs_uint_t console, njs_uint_t indent) @@ -2161,18 +2130,13 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ njs_int_t i; njs_int_t ret; njs_str_t str; - njs_value_t *key, *val, tag, ext_val; - njs_object_t *object; + njs_value_t *key, *val, tag; njs_json_state_t *state; njs_string_prop_t string; njs_object_prop_t *prop; - njs_lvlhsh_query_t lhq; + njs_property_query_t pq; njs_json_stringify_t *stringify, dump_stringify; - const njs_value_t string_get = njs_string("[Getter]"); - const njs_value_t string_set = njs_string("[Setter]"); - const njs_value_t string_get_set = njs_long_string("[Getter/Setter]"); - if (njs_vm_backtrace(vm) != NULL) { goto exception; } @@ -2238,53 +2202,50 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ break; } + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); + key = &state->keys->start[state->index++]; - njs_object_property_key_set(&lhq, key, 0); - - if (njs_is_external(&state->value)) { - lhq.proto = &njs_extern_hash_proto; - - ret = njs_lvlhsh_find(&state->value.external.proto->hash, &lhq); - if (njs_slow_path(ret == NJS_DECLINED)) { + + ret = njs_property_query(vm, &pq, &state->value, key); + if (njs_slow_path(ret != NJS_OK)) { + if (ret == NJS_DECLINED) { break; } - ext_val.type = NJS_EXTERNAL; - ext_val.data.truth = 1; - ext_val.external.proto = lhq.value; - - val = &ext_val; - - } else { - object = njs_object(&state->value); - lhq.proto = &njs_object_hash_proto; - - ret = njs_lvlhsh_find(&object->hash, &lhq); - if (ret == NJS_DECLINED) { - ret = njs_lvlhsh_find(&object->shared_hash, &lhq); - if (njs_slow_path(ret == NJS_DECLINED)) { - break; - } + goto exception; + } + + prop = pq.lhq.value; + + if (prop->type == NJS_WHITEOUT || !prop->enumerable) { + break; + } + + val = &prop->value; + + if (prop->type == NJS_PROPERTY_HANDLER) { + pq.scratch = *prop; + prop = &pq.scratch; + ret = prop->value.data.u.prop_handler(vm, prop, &state->value, + NULL, &prop->value); + + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; } - prop = lhq.value; val = &prop->value; - - if (prop->type == NJS_WHITEOUT || !prop->enumerable) { - break; - } - - if (njs_is_accessor_descriptor(prop)) { - if (njs_is_defined(&prop->getter)) { - if (njs_is_defined(&prop->setter)) { - val = njs_value_arg(&string_get_set); - } else { - val = njs_value_arg(&string_get); - } - + } + + if (njs_is_accessor_descriptor(prop)) { + if (njs_is_defined(&prop->getter)) { + if (njs_is_defined(&prop->setter)) { + val = njs_value_arg(&string_get_set); } else { - val = njs_value_arg(&string_set); + val = njs_value_arg(&string_get); } + + } else { + val = njs_value_arg(&string_set); } } @@ -2294,8 +2255,8 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ } state->written = 1; - njs_key_string_get(vm, key, &lhq.key); - njs_json_stringify_append(lhq.key.start, lhq.key.length); + njs_key_string_get(vm, key, &pq.lhq.key); + njs_json_stringify_append(pq.lhq.key.start, pq.lhq.key.length); njs_json_stringify_append(":", 1); if (stringify->space.length != 0) { njs_json_stringify_append(" ", 1); diff -r 3eb134dfa896 -r 790caa744e0e src/njs_value.c --- a/src/njs_value.c Mon Nov 25 17:53:19 2019 +0300 +++ b/src/njs_value.c Mon Nov 25 17:53:32 2019 +0300 @@ -815,10 +815,18 @@ njs_external_property_query(njs_vm_t *vm prop = &pq->scratch; - prop->type = NJS_PROPERTY; - prop->writable = 0; + njs_memzero(prop, sizeof(njs_object_prop_t)); + + /* + * njs_memzero() does also: + * prop->type = NJS_PROPERTY; + * prop->writable = 0; + * prop->configurable = 0; + * njs_set_null(&prop->getter); + * njs_set_null(&prop->setter); + */ + prop->enumerable = 1; - prop->configurable = 0; ext_proto = object->external.proto; @@ -840,6 +848,12 @@ njs_external_property_query(njs_vm_t *vm data = ext_proto->data; } else { + + if (pq->lhq.key.start == NULL) { + /* Symbol.toStringTag is not supported yet. */ + goto done; + } + data = (uintptr_t) &pq->lhq.key; } diff -r 3eb134dfa896 -r 790caa744e0e src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Mon Nov 25 17:53:19 2019 +0300 +++ b/src/test/njs_unit_test.c Mon Nov 25 17:53:32 2019 +0300 @@ -13842,6 +13842,9 @@ static njs_unit_test_t njs_test[] = { njs_str("this.njs = 1; njs"), njs_str("1") }, + { njs_str("njs.dump(this) === `global {njs:njs {version:'${njs.version}'},process:process {}}`"), + njs_str("true") }, + { njs_str("process === process"), njs_str("true") }, @@ -14594,10 +14597,10 @@ static njs_unit_test_t njs_test[] = njs_str("{a:'[Setter]'}") }, { njs_str("njs.dump($r.props)"), - njs_str("{a:{type:\"property\",props:[\"getter\"]},b:{type:\"property\",props:[\"getter\"]}}") }, + njs_str("{a:'1',b:42}") }, { njs_str("njs.dump($r.header)"), - njs_str("{type:\"object\",props:[\"getter\",\"keys\"]}") }, + njs_str("{01:'01|???',02:'02|???',03:'03|???'}") }, { njs_str("njs.dump(njs) == `njs {version:'${njs.version}'}`"), njs_str("true") }, From yp at fuzzit.dev Mon Nov 25 16:35:13 2019 From: yp at fuzzit.dev (Yevgeny Pats) Date: Mon, 25 Nov 2019 18:35:13 +0200 Subject: Continuous Fuzzing In-Reply-To: <5B929F57-4332-4074-931B-0276C6348F0C@nginx.com> References: <5B929F57-4332-4074-931B-0276C6348F0C@nginx.com> Message-ID: Hey Andrei, Thanks for your response. Both libFuzzer and AFL needs to collect coverage somehow to operate efficiently and find bugs. I'm not very familiar yet with nginx code base but I did integrate fuzz targets for envoy proxy so maybe we can do something similar. Is it possible for example to compile only parts of nginx to a standalone library? (some of the parsing code that has no other dependencies). Best, Yevgeny On Mon, Nov 25, 2019 at 4:07 PM Andrei Zeliankou wrote: > > > > On 22 Nov 2019, at 19:42, Yevgeny Pats wrote: > > > > Hey Team, > > > > I'm Yevgeny Pats, Founder of Fuzzit. > > > > I'm not sure about the current state of fuzzing in Nginx but I thought > it was worth asking/discussing. > > > > If adding new fuzz targets to nginx and running those continuously as > part of the CI is interesting I'll be happy to help both write some of the > fuzz target as well as help integrate the fuzz target to Fuzzit (we have a > free plan for OSS projects). > > > > Would love to hear your thoughts as well as answer any questions about > Fuzzit service that you might have. > > > > Cheers, > > Yevgeny > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > Hi Yevgeny, > > Currently, nginx has no library so it's not possible to use fuzz targets. > Possible way to fuzz nginx is in binary mode (e.g. routing fuzz input to > the > listen socket). Is it possible to run continuously fuzzing in Fuzzit > without fuzz targets? > > -- > Andrei Zeliankou > > > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Tue Nov 26 11:15:49 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 26 Nov 2019 11:15:49 +0000 Subject: [njs] HTTP: improved r.parent property handler. Message-ID: details: https://hg.nginx.org/njs/rev/851550b92d9d branches: changeset: 1257:851550b92d9d user: Dmitry Volyntsev date: Mon Nov 25 17:54:58 2019 +0300 description: HTTP: improved r.parent property handler. Previously, r.parent handler threw an exception if it was called not for a subrequest. This is too restrictive, as it prevents any iterations over all r properties (for example: JSON.stringify()). Instead, "undefined" value is returned as it is already done for r.requestBody when request body is unavailable. diffstat: nginx/ngx_http_js_module.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff -r 790caa744e0e -r 851550b92d9d nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Mon Nov 25 17:53:32 2019 +0300 +++ b/nginx/ngx_http_js_module.c Mon Nov 25 17:54:58 2019 +0300 @@ -2091,8 +2091,8 @@ ngx_http_js_ext_get_parent(njs_vm_t *vm, : NULL; if (ctx == NULL || ctx->vm != vm) { - njs_vm_error(vm, "parent can only be returned for a subrequest"); - return NJS_ERROR; + njs_value_undefined_set(value); + return NJS_OK; } njs_value_assign(value, njs_value_arg(&ctx->request)); From xeioex at nginx.com Tue Nov 26 12:42:34 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 26 Nov 2019 12:42:34 +0000 Subject: [njs] Fixed "accumulative" mode of VM. Message-ID: details: https://hg.nginx.org/njs/rev/8a41cab86cc2 branches: changeset: 1258:8a41cab86cc2 user: Dmitry Volyntsev date: Tue Nov 26 15:01:02 2019 +0300 description: Fixed "accumulative" mode of VM. The issue was introduced in 452ce96df2e3. This closes #258 issue on Github. diffstat: src/njs_vm.c | 2 +- src/test/njs_interactive_test.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletions(-) diffs (32 lines): diff -r 851550b92d9d -r 8a41cab86cc2 src/njs_vm.c --- a/src/njs_vm.c Mon Nov 25 17:54:58 2019 +0300 +++ b/src/njs_vm.c Tue Nov 26 15:01:02 2019 +0300 @@ -195,7 +195,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **st vm->variables_hash = scope->variables; - if (vm->options.init) { + if (vm->options.init && !vm->options.accumulative) { ret = njs_vm_init(vm); if (njs_slow_path(ret != NJS_OK)) { return ret; diff -r 851550b92d9d -r 8a41cab86cc2 src/test/njs_interactive_test.c --- a/src/test/njs_interactive_test.c Mon Nov 25 17:54:58 2019 +0300 +++ b/src/test/njs_interactive_test.c Tue Nov 26 15:01:02 2019 +0300 @@ -75,6 +75,16 @@ static njs_interactive_test_t njs_test[ { njs_str("/abc/i.test('ABC')" ENTER), njs_str("true") }, + /* Accumulative mode. */ + + { njs_str("var a = 1" ENTER + "a" ENTER), + njs_str("1") }, + + { njs_str("Number.prototype.test = 'test'" ENTER + "Number.prototype.test" ENTER), + njs_str("test") }, + /* Error handling */ { njs_str("var a = ;" ENTER From xeioex at nginx.com Tue Nov 26 12:42:35 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 26 Nov 2019 12:42:35 +0000 Subject: [njs] Introduced njs_vm_bind(). Message-ID: details: https://hg.nginx.org/njs/rev/68c9971d7366 branches: changeset: 1259:68c9971d7366 user: Dmitry Volyntsev date: Tue Nov 26 15:09:45 2019 +0300 description: Introduced njs_vm_bind(). This is a generic replacement for njs_vm_external_bind(). diffstat: src/njs.h | 5 +- src/njs_builtin.c | 84 ++++++---------------------------------------- src/njs_extern.c | 83 ---------------------------------------------- src/njs_extern.h | 2 - src/njs_parser_terminal.c | 10 ----- src/njs_shell.c | 12 +++--- src/njs_variable.c | 6 --- src/njs_vm.c | 41 ++++++++++++++++++++++- src/njs_vm.h | 1 - src/test/njs_unit_test.c | 77 ++++++++++++++++++++++++++++++++++-------- test/njs_expect_test.exp | 11 +++-- 11 files changed, 129 insertions(+), 203 deletions(-) diffs (609 lines): diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs.h --- a/src/njs.h Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs.h Tue Nov 26 15:09:45 2019 +0300 @@ -235,16 +235,17 @@ NJS_EXPORT const njs_extern_t *njs_vm_ex njs_external_t *external); NJS_EXPORT njs_int_t njs_vm_external_create(njs_vm_t *vm, njs_value_t *value, const njs_extern_t *proto, njs_external_ptr_t object); -NJS_EXPORT njs_int_t njs_vm_external_bind(njs_vm_t *vm, - const njs_str_t *var_name, const njs_value_t *value); NJS_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm, const njs_value_t *value); NJS_EXPORT void njs_disassembler(njs_vm_t *vm); NJS_EXPORT void njs_disassemble(u_char *start, u_char *end); +NJS_EXPORT njs_int_t njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name, + const njs_value_t *value, njs_bool_t shared); NJS_EXPORT const njs_value_t *njs_vm_value(njs_vm_t *vm, const njs_str_t *name); NJS_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, const njs_str_t *name); + NJS_EXPORT njs_value_t *njs_vm_retval(njs_vm_t *vm); NJS_EXPORT void njs_vm_retval_set(njs_vm_t *vm, const njs_value_t *value); diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_builtin.c --- a/src/njs_builtin.c Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs_builtin.c Tue Nov 26 15:09:45 2019 +0300 @@ -176,6 +176,7 @@ njs_builtin_objects_create(njs_vm_t *vm) return NJS_ERROR; } + object->type = NJS_OBJECT; object->shared = 1; object->extensible = 1; @@ -260,13 +261,6 @@ njs_builtin_objects_create(njs_vm_t *vm) shared->prototypes[NJS_OBJ_TYPE_REGEXP].regexp.pattern = shared->empty_regexp_pattern; - string_object = &shared->string_object; - njs_lvlhsh_init(&string_object->hash); - string_object->shared_hash = shared->string_instance_hash; - string_object->type = NJS_OBJECT_STRING; - string_object->shared = 1; - string_object->extensible = 0; - constructor = shared->constructors; for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) { @@ -286,6 +280,16 @@ njs_builtin_objects_create(njs_vm_t *vm) } } + vm->global_object = shared->objects[0]; + vm->global_object.shared = 0; + + string_object = &shared->string_object; + njs_lvlhsh_init(&string_object->hash); + string_object->shared_hash = shared->string_instance_hash; + string_object->type = NJS_OBJECT_STRING; + string_object->shared = 1; + string_object->extensible = 0; + vm->shared = shared; return NJS_OK; @@ -333,10 +337,7 @@ njs_builtin_objects_clone(njs_vm_t *vm, vm->constructors[i].object.__proto__ = error_constructor; } - vm->global_object = vm->shared->objects[0]; vm->global_object.__proto__ = object_prototype; - vm->global_object.shared = 0; - njs_set_object(global, &vm->global_object); vm->string_object = vm->shared->string_object; @@ -452,15 +453,11 @@ njs_builtin_traverse(njs_vm_t *vm, njs_t static njs_arr_t * njs_builtin_completions(njs_vm_t *vm) { - u_char *compl; - size_t len; njs_arr_t *array; njs_str_t *completion; njs_int_t ret; njs_keyword_t *keyword; - njs_lvlhsh_each_t lhe, lhe_prop; - njs_extern_value_t *ev; - const njs_extern_t *ext_proto, *ext_prop; + njs_lvlhsh_each_t lhe; njs_builtin_traverse_t ctx; const njs_object_prop_t *prop; @@ -516,63 +513,6 @@ njs_builtin_completions(njs_vm_t *vm) njs_string_get(&prop->name, completion); } - /* Externals completions. */ - - njs_lvlhsh_each_init(&lhe, &njs_extern_value_hash_proto); - - for ( ;; ) { - ev = njs_lvlhsh_each(&vm->externals_hash, &lhe); - - if (ev == NULL) { - break; - } - - ext_proto = ev->value.external.proto; - - njs_lvlhsh_each_init(&lhe_prop, &njs_extern_hash_proto); - - len = ev->name.length + 1; - compl = njs_mp_zalloc(vm->mem_pool, len); - if (compl == NULL) { - return NULL; - } - - njs_sprintf(compl, compl + len, "%V%Z", &ev->name); - - completion = njs_arr_add(array); - if (njs_slow_path(completion == NULL)) { - return NULL; - } - - completion->length = len; - completion->start = (u_char *) compl; - - for ( ;; ) { - ext_prop = njs_lvlhsh_each(&ext_proto->hash, &lhe_prop); - - if (ext_prop == NULL) { - break; - } - - len = ev->name.length + ev->name.length + 2; - compl = njs_mp_zalloc(vm->mem_pool, len); - if (compl == NULL) { - return NULL; - } - - njs_sprintf(compl, compl + len, "%V.%V%Z", &ev->name, - &ext_prop->name); - - completion = njs_arr_add(array); - if (njs_slow_path(completion == NULL)) { - return NULL; - } - - completion->length = len; - completion->start = (u_char *) compl; - } - } - return array; } diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_extern.c --- a/src/njs_extern.c Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs_extern.c Tue Nov 26 15:09:45 2019 +0300 @@ -31,21 +31,6 @@ njs_extern_hash_test(njs_lvlhsh_query_t } -static njs_int_t -njs_extern_value_hash_test(njs_lvlhsh_query_t *lhq, void *data) -{ - njs_extern_value_t *ev; - - ev = (njs_extern_value_t *) data; - - if (njs_strstr_eq(&lhq->key, &ev->name)) { - return NJS_OK; - } - - return NJS_DECLINED; -} - - const njs_lvlhsh_proto_t njs_extern_hash_proto njs_aligned(64) = { @@ -56,16 +41,6 @@ const njs_lvlhsh_proto_t njs_extern_has }; -const njs_lvlhsh_proto_t njs_extern_value_hash_proto - njs_aligned(64) = -{ - NJS_LVLHSH_DEFAULT, - njs_extern_value_hash_test, - njs_lvlhsh_alloc, - njs_lvlhsh_free, -}; - - static njs_extern_t * njs_vm_external_add(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_external_t *external, njs_uint_t n) @@ -215,45 +190,6 @@ njs_vm_external_create(njs_vm_t *vm, njs } -njs_int_t -njs_vm_external_bind(njs_vm_t *vm, const njs_str_t *var_name, - const njs_value_t *value) -{ - njs_int_t ret; - njs_extern_value_t *ev; - njs_lvlhsh_query_t lhq; - - if (njs_slow_path(!njs_is_external(value))) { - return NJS_ERROR; - } - - ev = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), - sizeof(njs_extern_value_t)); - if (njs_slow_path(ev == NULL)) { - njs_memory_error(vm); - return NJS_ERROR; - } - - ev->value = *value; - ev->name = *var_name; - - lhq.key = *var_name; - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); - lhq.proto = &njs_extern_value_hash_proto; - lhq.value = ev; - lhq.replace = 0; - lhq.pool = vm->mem_pool; - - ret = njs_lvlhsh_insert(&vm->externals_hash, &lhq); - if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(vm, "lvlhsh insert failed"); - return ret; - } - - return NJS_OK; -} - - njs_external_ptr_t njs_vm_external(njs_vm_t *vm, const njs_value_t *value) { @@ -321,25 +257,6 @@ njs_extern_keys_array(njs_vm_t *vm, cons } -njs_value_t * -njs_external_lookup(njs_vm_t *vm, njs_str_t *name, uint32_t hash) -{ - njs_lvlhsh_query_t lhq; - njs_extern_value_t *ev; - - lhq.key_hash = hash; - lhq.key = *name; - lhq.proto = &njs_extern_value_hash_proto; - - if (njs_lvlhsh_find(&vm->externals_hash, &lhq) == NJS_OK) { - ev = (njs_extern_value_t *) lhq.value; - return &ev->value; - } - - return NULL; -} - - static njs_int_t njs_external_match(njs_vm_t *vm, njs_function_native_t func, njs_extern_t *ext, njs_str_t *name, njs_extern_part_t *head, njs_extern_part_t *ppart) diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_extern.h --- a/src/njs_extern.h Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs_extern.h Tue Nov 26 15:09:45 2019 +0300 @@ -41,13 +41,11 @@ typedef struct { njs_array_t *njs_extern_keys_array(njs_vm_t *vm, const njs_extern_t *external); -njs_value_t *njs_external_lookup(njs_vm_t *vm, njs_str_t *name, uint32_t hash); njs_int_t njs_external_match_native_function(njs_vm_t *vm, njs_function_native_t func, njs_str_t *name); extern const njs_lvlhsh_proto_t njs_extern_hash_proto; -extern const njs_lvlhsh_proto_t njs_extern_value_hash_proto; #endif /* _NJS_EXTERN_H_INCLUDED_ */ diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_parser_terminal.c --- a/src/njs_parser_terminal.c Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs_parser_terminal.c Tue Nov 26 15:09:45 2019 +0300 @@ -203,7 +203,6 @@ njs_parser_reference(njs_vm_t *vm, njs_p njs_str_t *name, uint32_t hash, uint32_t token_line) { njs_int_t ret; - njs_value_t *ext; njs_variable_t *var; njs_parser_node_t *node; njs_parser_scope_t *scope; @@ -291,15 +290,6 @@ njs_parser_reference(njs_vm_t *vm, njs_p node->token_line = token_line; - ext = njs_external_lookup(vm, name, hash); - - if (ext != NULL) { - node->token = NJS_TOKEN_EXTERNAL; - node->u.value = *ext; - node->index = (njs_index_t) ext; - break; - } - ret = njs_variable_reference(vm, parser->scope, node, name, hash, NJS_REFERENCE); if (njs_slow_path(ret != NJS_OK)) { diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_shell.c --- a/src/njs_shell.c Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs_shell.c Tue Nov 26 15:09:45 2019 +0300 @@ -613,28 +613,28 @@ njs_externals_init(njs_vm_t *vm, njs_con static const njs_str_t name = njs_str("console"); proto = njs_vm_external_prototype(vm, &njs_externals[0]); - if (proto == NULL) { + if (njs_slow_path(proto == NULL)) { njs_stderror("failed to add console proto\n"); return NJS_ERROR; } value = njs_mp_zalloc(vm->mem_pool, sizeof(njs_opaque_value_t)); - if (value == NULL) { + if (njs_slow_path(value == NULL)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, value, proto, console); - if (ret != NJS_OK) { + if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } - ret = njs_vm_external_bind(vm, &name, value); - if (ret != NJS_OK) { + ret = njs_vm_bind(vm, &name, value, 1); + if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_console_init(vm, console); - if (ret != NJS_OK) { + if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_variable.c --- a/src/njs_variable.c Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs_variable.c Tue Nov 26 15:09:45 2019 +0300 @@ -683,12 +683,6 @@ njs_vm_value(njs_vm_t *vm, const njs_str return njs_vmcode_operand(vm, ((njs_variable_t *) lhq.value)->index); } - lhq.proto = &njs_extern_value_hash_proto; - - if (njs_lvlhsh_find(&vm->externals_hash, &lhq) == NJS_OK) { - return &((njs_extern_value_t *) lhq.value)->value; - } - return &njs_value_undefined; } diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_vm.c --- a/src/njs_vm.c Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs_vm.c Tue Nov 26 15:09:45 2019 +0300 @@ -65,7 +65,6 @@ njs_vm_create(njs_vm_opt_t *options) return NULL; } - njs_lvlhsh_init(&vm->externals_hash); njs_lvlhsh_init(&vm->external_prototypes_hash); vm->trace.level = NJS_LEVEL_TRACE; @@ -570,6 +569,46 @@ njs_vm_retval_set(njs_vm_t *vm, const nj njs_int_t +njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name, const njs_value_t *value, + njs_bool_t shared) +{ + njs_int_t ret; + njs_object_t *global; + njs_lvlhsh_t *hash; + njs_object_prop_t *prop; + njs_lvlhsh_query_t lhq; + + prop = njs_object_prop_alloc(vm, &njs_value_undefined, value, 1); + if (njs_slow_path(prop == NULL)) { + return NJS_ERROR; + } + + ret = njs_string_new(vm, &prop->name, var_name->start, var_name->length, 0); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + lhq.value = prop; + lhq.key = *var_name; + lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + lhq.replace = 1; + lhq.pool = vm->mem_pool; + lhq.proto = &njs_object_hash_proto; + + global = &vm->global_object; + hash = shared ? &global->shared_hash : &global->hash; + + ret = njs_lvlhsh_insert(hash, &lhq); + if (njs_slow_path(ret != NJS_OK)) { + njs_internal_error(vm, "lvlhsh insert failed"); + return ret; + } + + return NJS_OK; +} + + +njs_int_t njs_vm_value_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size) { diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_vm.h --- a/src/njs_vm.h Tue Nov 26 15:01:02 2019 +0300 +++ b/src/njs_vm.h Tue Nov 26 15:09:45 2019 +0300 @@ -175,7 +175,6 @@ struct njs_vm_s { njs_arr_t *external_objects; /* of njs_external_ptr_t */ - njs_lvlhsh_t externals_hash; njs_lvlhsh_t external_prototypes_hash; njs_lvlhsh_t variables_hash; diff -r 8a41cab86cc2 -r 68c9971d7366 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue Nov 26 15:01:02 2019 +0300 +++ b/src/test/njs_unit_test.c Tue Nov 26 15:09:45 2019 +0300 @@ -9352,8 +9352,17 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.getOwnPropertyNames(this).includes('NaN')"), njs_str("true") }, - { njs_str("Object.keys(this)"), - njs_str("njs,process") }, + { njs_str("Object.keys(this).sort()"), + njs_str("$r,$r2,$r3,njs,process") }, + + { njs_str("var r = njs.dump(this); " + "['$r', 'global', njs.version].every(v=>r.includes(v))"), + njs_str("true") }, + + { njs_str("var r = JSON.stringify(this); " + "['$r', njs.version].every(v=>r.includes(v))"), + njs_str("true") }, + { njs_str("this.a = 1; this.a"), njs_str("1") }, @@ -13842,9 +13851,6 @@ static njs_unit_test_t njs_test[] = { njs_str("this.njs = 1; njs"), njs_str("1") }, - { njs_str("njs.dump(this) === `global {njs:njs {version:'${njs.version}'},process:process {}}`"), - njs_str("true") }, - { njs_str("process === process"), njs_str("true") }, @@ -15233,6 +15239,15 @@ static njs_unit_test_t njs_shared_test[ { njs_str("var r; for (var i = 0; i < 2**10; i++) {r = $r.create('XXX').uri;}"), njs_str("undefined") }, + + { njs_str("delete $r3.vars.p; $r3.vars.p"), + njs_str("undefined") }, + + { njs_str("var sr = $r.create('XXX'); sr.vars.p = 'a'; sr.vars.p"), + njs_str("a") }, + + { njs_str("$r.bind('XXX', 37); XXX"), + njs_str("37") }, }; @@ -15793,6 +15808,26 @@ memory_error: } +static njs_int_t +njs_unit_test_bind_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_str_t name; + njs_unit_test_req_t *r; + + r = njs_vm_external(vm, njs_arg(args, nargs, 0)); + if (njs_slow_path(r == NULL)) { + return NJS_ERROR; + } + + if (njs_vm_value_to_string(vm, &name, njs_arg(args, nargs, 1)) != NJS_OK) { + return NJS_ERROR; + } + + return njs_vm_bind(vm, &name, njs_arg(args, nargs, 2), 0); +} + + static njs_external_t njs_unit_test_r_props[] = { { njs_str("a"), @@ -15909,6 +15944,17 @@ static njs_external_t njs_unit_test_r_e njs_unit_test_create_external, 0 }, + { njs_str("bind"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + njs_unit_test_bind_external, + 0 }, + }; @@ -15982,15 +16028,14 @@ njs_externals_init(njs_vm_t *vm) njs_unit_test_prop_t *prop; proto = njs_vm_external_prototype(vm, &njs_test_external[0]); - if (proto == NULL) { + if (njs_slow_path(proto == NULL)) { njs_printf("njs_vm_external_prototype() failed\n"); return NJS_ERROR; } - requests = njs_mp_zalloc(vm->mem_pool, - njs_nitems(njs_test_requests) + requests = njs_mp_zalloc(vm->mem_pool, njs_nitems(njs_test_requests) * sizeof(njs_unit_test_req_t)); - if (requests == NULL) { + if (njs_slow_path(requests == NULL)) { return NJS_ERROR; } @@ -16002,15 +16047,15 @@ njs_externals_init(njs_vm_t *vm) ret = njs_vm_external_create(vm, njs_value_arg(&requests[i].value), proto, &requests[i]); - if (ret != NJS_OK) { + if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_vm_external_create() failed\n"); return NJS_ERROR; } - ret = njs_vm_external_bind(vm, &njs_test_requests[i].name, - njs_value_arg(&requests[i].value)); - if (ret != NJS_OK) { - njs_printf("njs_vm_external_bind() failed\n"); + ret = njs_vm_bind(vm, &njs_test_requests[i].name, + njs_value_arg(&requests[i].value), 1); + if (njs_slow_path(ret != NJS_OK)) { + njs_printf("njs_vm_bind() failed\n"); return NJS_ERROR; } @@ -16019,13 +16064,13 @@ njs_externals_init(njs_vm_t *vm) &njs_test_requests[i].props[j].name, &njs_test_requests[i].props[j].value); - if (prop == NULL) { + if (njs_slow_path(prop == NULL)) { njs_printf("lvlhsh_unit_test_alloc() failed\n"); return NJS_ERROR; } ret = lvlhsh_unit_test_add(&requests[i], prop); - if (ret != NJS_OK) { + if (njs_slow_path(ret != NJS_OK)) { njs_printf("lvlhsh_unit_test_add() failed\n"); return NJS_ERROR; } diff -r 8a41cab86cc2 -r 68c9971d7366 test/njs_expect_test.exp --- a/test/njs_expect_test.exp Tue Nov 26 15:01:02 2019 +0300 +++ b/test/njs_expect_test.exp Tue Nov 26 15:09:45 2019 +0300 @@ -87,10 +87,13 @@ njs_test { "Ma\a*th"} } -njs_test { - {"conso\t" - "conso\a*le"} -} +# FIXME: completions for external objects +# are not supported + +# njs_test { +# {"conso\t" +# "conso\a*le"} +# } # Global completions, multiple partial match njs_test { From xeioex at nginx.com Tue Nov 26 12:42:35 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 26 Nov 2019 12:42:35 +0000 Subject: [njs] Interactive shell: introduced global print() alias to console.log(). Message-ID: details: https://hg.nginx.org/njs/rev/e5917fbde8d2 branches: changeset: 1260:e5917fbde8d2 user: Dmitry Volyntsev date: Tue Nov 26 15:11:40 2019 +0300 description: Interactive shell: introduced global print() alias to console.log(). diffstat: src/njs_extern.c | 13 +++++++++---- src/njs_shell.c | 32 ++++++++++++++++++++++++-------- test/njs_expect_test.exp | 8 ++++++-- 3 files changed, 39 insertions(+), 14 deletions(-) diffs (111 lines): diff -r 68c9971d7366 -r e5917fbde8d2 src/njs_extern.c --- a/src/njs_extern.c Tue Nov 26 15:09:45 2019 +0300 +++ b/src/njs_extern.c Tue Nov 26 15:11:40 2019 +0300 @@ -181,10 +181,15 @@ njs_vm_external_create(njs_vm_t *vm, njs memcpy(obj, &object, sizeof(void *)); - ext_val->type = NJS_EXTERNAL; - ext_val->data.truth = 1; - ext_val->external.proto = proto; - ext_val->external.index = vm->external_objects->items - 1; + if (proto->type != NJS_EXTERN_METHOD) { + ext_val->type = NJS_EXTERNAL; + ext_val->data.truth = 1; + ext_val->external.proto = proto; + ext_val->external.index = vm->external_objects->items - 1; + + } else { + njs_set_function(ext_val, proto->function); + } return NJS_OK; } diff -r 68c9971d7366 -r e5917fbde8d2 src/njs_shell.c --- a/src/njs_shell.c Tue Nov 26 15:09:45 2019 +0300 +++ b/src/njs_shell.c Tue Nov 26 15:11:40 2019 +0300 @@ -604,17 +604,16 @@ njs_console_init(njs_vm_t *vm, njs_conso static njs_int_t -njs_externals_init(njs_vm_t *vm, njs_console_t *console) +njs_externals_add(njs_vm_t *vm, njs_external_t *definition, + const njs_str_t *name, njs_external_ptr_t object) { - njs_uint_t ret; + njs_int_t ret; njs_value_t *value; const njs_extern_t *proto; - static const njs_str_t name = njs_str("console"); - - proto = njs_vm_external_prototype(vm, &njs_externals[0]); + proto = njs_vm_external_prototype(vm, definition); if (njs_slow_path(proto == NULL)) { - njs_stderror("failed to add console proto\n"); + njs_stderror("failed to add \"%V\" proto\n", name); return NJS_ERROR; } @@ -623,12 +622,29 @@ njs_externals_init(njs_vm_t *vm, njs_con return NJS_ERROR; } - ret = njs_vm_external_create(vm, value, proto, console); + ret = njs_vm_external_create(vm, value, proto, object); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } - ret = njs_vm_bind(vm, &name, value, 1); + return njs_vm_bind(vm, name, value, 1); +} + + +static njs_int_t +njs_externals_init(njs_vm_t *vm, njs_console_t *console) +{ + njs_int_t ret; + + static const njs_str_t console_name = njs_str("console"); + static const njs_str_t print_name = njs_str("print"); + + ret = njs_externals_add(vm, &njs_externals[0], &console_name, console); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + ret = njs_externals_add(vm, &njs_ext_console[0], &print_name, console); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } diff -r 68c9971d7366 -r e5917fbde8d2 test/njs_expect_test.exp --- a/test/njs_expect_test.exp Tue Nov 26 15:09:45 2019 +0300 +++ b/test/njs_expect_test.exp Tue Nov 26 15:11:40 2019 +0300 @@ -214,6 +214,8 @@ njs_test { "console.log(1)\r\n1\r\nundefined\r\n>> "} {"console.log(1, 'a')\r\n" "console.log(1, 'a')\r\n1 a\r\nundefined\r\n>> "} + {"print(1, 'a')\r\n" + "print(1, 'a')\r\n1 a\r\nundefined\r\n>> "} {"console.log('\\t???\\n??')\r\n" "console.log('\\\\t???\\\\n??')\r\n\t???\r\n??\r\nundefined\r\n>> "} {"console.dump()\r\n" @@ -270,13 +272,15 @@ njs_test { {"var print = console.dump.bind(console); print(1, 'a', [1, 2])\r\n" "1 a \\\[\r\n 1,\r\n 2\r\n]\r\nundefined\r\n>> "} {"var print = console.log.bind(console); print(console.a.a)\r\n" - "TypeError: cannot get property \"a\" of undefined*at console.log"} + "TypeError: cannot get property \"a\" of undefined*at print"} + {"print(console.a.a)\r\n" + "TypeError: cannot get property \"a\" of undefined*at print"} } # Backtraces for external objects njs_test { {"console.log(console.a.a)\r\n" - "console.log(console.a.a)\r\nTypeError:*at console.log (native)"} + "console.log(console.a.a)\r\nTypeError:*at print (native)"} } # dumper From zelenkov at nginx.com Tue Nov 26 13:17:14 2019 From: zelenkov at nginx.com (Andrei Zeliankou) Date: Tue, 26 Nov 2019 16:17:14 +0300 Subject: Continuous Fuzzing In-Reply-To: References: <5B929F57-4332-4074-931B-0276C6348F0C@nginx.com> Message-ID: Hi Yevgeny, Currently, nginx has no support of compiling parts of source as standalone library. It's quite sophisticated problem and there is no short term plans to implement it. If you succeed in developing a library or fuzz targets - please let us know, we are interested in solving these problems for nginx. Regards, Andrei Zeliankou > On 25 Nov 2019, at 19:35, Yevgeny Pats wrote: > > Hey Andrei, > > Thanks for your response. Both libFuzzer and AFL needs to collect coverage somehow to operate efficiently and find bugs. > > I'm not very familiar yet with nginx code base but I did integrate fuzz targets for envoy proxy so maybe we can do something similar. > > Is it possible for example to compile only parts of nginx to a standalone library? (some of the parsing code that has no other dependencies). > > Best, > Yevgeny > > On Mon, Nov 25, 2019 at 4:07 PM Andrei Zeliankou wrote: > > > > On 22 Nov 2019, at 19:42, Yevgeny Pats wrote: > > > > Hey Team, > > > > I'm Yevgeny Pats, Founder of Fuzzit. > > > > I'm not sure about the current state of fuzzing in Nginx but I thought it was worth asking/discussing. > > > > If adding new fuzz targets to nginx and running those continuously as part of the CI is interesting I'll be happy to help both write some of the fuzz target as well as help integrate the fuzz target to Fuzzit (we have a free plan for OSS projects). > > > > Would love to hear your thoughts as well as answer any questions about Fuzzit service that you might have. > > > > Cheers, > > Yevgeny > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > Hi Yevgeny, > > Currently, nginx has no library so it's not possible to use fuzz targets. > Possible way to fuzz nginx is in binary mode (e.g. routing fuzz input to the > listen socket). Is it possible to run continuously fuzzing in Fuzzit > without fuzz targets? > > -- > Andrei Zeliankou > > > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From xeioex at nginx.com Tue Nov 26 15:38:33 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 26 Nov 2019 15:38:33 +0000 Subject: [njs] Fixed operator "in" according to the specification. Message-ID: details: https://hg.nginx.org/njs/rev/a41681864650 branches: changeset: 1261:a41681864650 user: Artem S. Povalyukhin date: Tue Nov 26 13:22:09 2019 +0300 description: Fixed operator "in" according to the specification. diffstat: src/njs_vmcode.c | 35 +++++++++++++---------------------- src/test/njs_unit_test.c | 25 ++++++++++++++++++++----- 2 files changed, 33 insertions(+), 27 deletions(-) diffs (103 lines): diff -r e5917fbde8d2 -r a41681864650 src/njs_vmcode.c --- a/src/njs_vmcode.c Tue Nov 26 15:11:40 2019 +0300 +++ b/src/njs_vmcode.c Tue Nov 26 13:22:09 2019 +0300 @@ -1268,11 +1268,20 @@ static njs_jump_off_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *value, njs_value_t *key) { njs_int_t ret; - njs_bool_t found; - njs_object_prop_t *prop; njs_property_query_t pq; - found = 0; + if (njs_slow_path(njs_is_primitive(value))) { + njs_type_error(vm, "property \"in\" on primitive %s type", + njs_type_string(value->type)); + return NJS_ERROR; + } + + if (njs_slow_path(!njs_is_key(key))) { + ret = njs_value_to_key(vm, key, key); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); @@ -1281,25 +1290,7 @@ njs_vmcode_property_in(njs_vm_t *vm, njs return ret; } - if (ret == NJS_DECLINED) { - if (!njs_is_object(value) && !njs_is_external(value)) { - njs_type_error(vm, "property in on a primitive value"); - - return NJS_ERROR; - } - - } else { - prop = pq.lhq.value; - - if (/* !njs_is_data_descriptor(prop) */ - prop->writable == NJS_ATTRIBUTE_UNSET - || njs_is_valid(&prop->value)) - { - found = 1; - } - } - - njs_set_boolean(&vm->retval, found); + njs_set_boolean(&vm->retval, ret == NJS_OK); return sizeof(njs_vmcode_3addr_t); } diff -r e5917fbde8d2 -r a41681864650 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue Nov 26 15:11:40 2019 +0300 +++ b/src/test/njs_unit_test.c Tue Nov 26 13:22:09 2019 +0300 @@ -3583,6 +3583,8 @@ static njs_unit_test_t njs_test[] = { njs_str("Math.E = 1"), njs_str("TypeError: Cannot assign to read-only property \"E\" of object") }, + /* "in" operation. */ + { njs_str("var o = { 'a': 1, 'b': 2 }; var i; " "for (i in o) { delete o.a; delete o.b; }; njs.dump(o)"), njs_str("{}") }, @@ -3597,6 +3599,12 @@ static njs_unit_test_t njs_test[] = { njs_str("'a' in {a:1}"), njs_str("true") }, + { njs_str("Symbol.unscopables in { [Symbol.unscopables]: 1 }"), + njs_str("true") }, + + { njs_str("Object(Symbol.toStringTag) in Math"), + njs_str("true") }, + { njs_str("'1' in [0,,2]"), njs_str("false") }, @@ -3609,11 +3617,18 @@ static njs_unit_test_t njs_test[] = { njs_str("'a' in Object.create({a:1})"), njs_str("true") }, - { njs_str("var a = 1; 1 in a"), - njs_str("TypeError: property in on a primitive value") }, - - { njs_str("var a = true; 1 in a"), - njs_str("TypeError: property in on a primitive value") }, + { njs_str("[false, NaN, '', Symbol()]" + ".map((x) => { " + " try { 'toString' in x; } " + " catch (e) { return e instanceof TypeError ? e.message : ''; } " + "})" + ".every((x) => x.startsWith('property \"in\" on primitive'))"), + njs_str("true") }, + + { njs_str("var p = new String('test');" + "p.toString = () => { throw new Error('failed') };" + "p in 1"), + njs_str("TypeError: property \"in\" on primitive number type") }, { njs_str("var n = { toString: function() { return 'a' } };" "var o = { a: 5 }; o[n]"), From xeioex at nginx.com Tue Nov 26 16:09:25 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 26 Nov 2019 16:09:25 +0000 Subject: [njs] Fixed JSON.stringify() and njs.dump() allocation error handling. Message-ID: details: https://hg.nginx.org/njs/rev/2ad4c5976839 branches: changeset: 1262:2ad4c5976839 user: Dmitry Volyntsev date: Tue Nov 26 18:44:11 2019 +0300 description: Fixed JSON.stringify() and njs.dump() allocation error handling. Previously, the return value of njs_json_buf_append() was not verified in some places. Found by Coverity (CID 1456031). diffstat: src/njs_json.c | 51 +++++++++++++++++++++++++++++---------------------- 1 files changed, 29 insertions(+), 22 deletions(-) diffs (103 lines): diff -r a41681864650 -r 2ad4c5976839 src/njs_json.c --- a/src/njs_json.c Tue Nov 26 13:22:09 2019 +0300 +++ b/src/njs_json.c Tue Nov 26 18:44:11 2019 +0300 @@ -1664,9 +1664,7 @@ njs_json_append_string(njs_json_stringif njs_json_buf_written(stringify, dst - stringify->last->pos); - njs_json_buf_append(stringify, "e, 1); - - return NJS_OK; + return njs_json_buf_append(stringify, "e, 1); } @@ -1890,26 +1888,15 @@ const njs_object_init_t njs_json_object } -#define njs_dump_item(str) \ - if (written) { \ - njs_json_buf_append(stringify, ",", 1); \ - } \ - \ - written = 1; \ - ret = njs_json_buf_append(stringify, str, njs_length(str)); \ - if (njs_slow_path(ret != NJS_OK)) { \ - goto memory_error; \ - } - - static njs_int_t njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value, njs_uint_t console) { + u_char *p; njs_int_t ret; njs_str_t str; njs_value_t str_val; - u_char buf[32], *p; + u_char buf[32]; njs_int_t (*to_string)(njs_vm_t *, njs_value_t *, const njs_value_t *); @@ -1920,8 +1907,13 @@ njs_dump_value(njs_json_stringify_t *str njs_string_get(value, &str); njs_dump("[String: "); - njs_json_append_string(stringify, value, '\''); - njs_dump("]") + + ret = njs_json_append_string(stringify, value, '\''); + if (njs_slow_path(ret != NJS_OK)) { + goto memory_error; + } + + njs_dump("]"); break; case NJS_STRING: @@ -1946,7 +1938,12 @@ njs_dump_value(njs_json_stringify_t *str njs_string_get(&str_val, &str); njs_dump("[Symbol: "); - njs_json_buf_append(stringify, (char *) str.start, str.length); + + ret = njs_json_buf_append(stringify, (char *) str.start, str.length); + if (njs_slow_path(ret != NJS_OK)) { + goto memory_error; + } + njs_dump("]"); break; @@ -1958,7 +1955,11 @@ njs_dump_value(njs_json_stringify_t *str } njs_string_get(&str_val, &str); - njs_json_buf_append(stringify, (char *) str.start, str.length); + + ret = njs_json_buf_append(stringify, (char *) str.start, str.length); + if (njs_slow_path(ret != NJS_OK)) { + goto memory_error; + } break; @@ -1981,8 +1982,14 @@ njs_dump_value(njs_json_stringify_t *str njs_string_get(&str_val, &str); njs_dump("[Number: "); - njs_json_buf_append(stringify, (char *) str.start, str.length); - njs_dump("]") + + ret = njs_json_buf_append(stringify, (char *) str.start, str.length); + if (njs_slow_path(ret != NJS_OK)) { + goto memory_error; + } + + njs_dump("]"); + break; case NJS_OBJECT_BOOLEAN: From yp at fuzzit.dev Wed Nov 27 11:32:54 2019 From: yp at fuzzit.dev (Yevgeny Pats) Date: Wed, 27 Nov 2019 13:32:54 +0200 Subject: Continuous Fuzzing In-Reply-To: References: <5B929F57-4332-4074-931B-0276C6348F0C@nginx.com> Message-ID: Hey Andrei, Got it. I believe it is possible but in that case it will require more development indeed. I don't think I'll be able to pull it off in my free time but if there is someway of sponsoring this kind of work it might help accelerating this feature. Best, Yevgeny On Tue, Nov 26, 2019, 3:17 PM Andrei Zeliankou wrote: > Hi Yevgeny, > > Currently, nginx has no support of compiling parts of source as standalone > library. It's quite sophisticated problem and there is no short term > plans to > implement it. If you succeed in developing a library or fuzz targets - > please > let us know, we are interested in solving these problems for nginx. > > Regards, > Andrei Zeliankou > > > > On 25 Nov 2019, at 19:35, Yevgeny Pats wrote: > > > > Hey Andrei, > > > > Thanks for your response. Both libFuzzer and AFL needs to collect > coverage somehow to operate efficiently and find bugs. > > > > I'm not very familiar yet with nginx code base but I did integrate fuzz > targets for envoy proxy so maybe we can do something similar. > > > > Is it possible for example to compile only parts of nginx to a > standalone library? (some of the parsing code that has no other > dependencies). > > > > Best, > > Yevgeny > > > > On Mon, Nov 25, 2019 at 4:07 PM Andrei Zeliankou > wrote: > > > > > > > On 22 Nov 2019, at 19:42, Yevgeny Pats wrote: > > > > > > Hey Team, > > > > > > I'm Yevgeny Pats, Founder of Fuzzit. > > > > > > I'm not sure about the current state of fuzzing in Nginx but I thought > it was worth asking/discussing. > > > > > > If adding new fuzz targets to nginx and running those continuously as > part of the CI is interesting I'll be happy to help both write some of the > fuzz target as well as help integrate the fuzz target to Fuzzit (we have a > free plan for OSS projects). > > > > > > Would love to hear your thoughts as well as answer any questions about > Fuzzit service that you might have. > > > > > > Cheers, > > > Yevgeny > > > _______________________________________________ > > > nginx-devel mailing list > > > nginx-devel at nginx.org > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > Hi Yevgeny, > > > > Currently, nginx has no library so it's not possible to use fuzz targets. > > Possible way to fuzz nginx is in binary mode (e.g. routing fuzz input to > the > > listen socket). Is it possible to run continuously fuzzing in Fuzzit > > without fuzz targets? > > > > -- > > Andrei Zeliankou > > > > > > > > > > > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Wed Nov 27 14:15:29 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 27 Nov 2019 14:15:29 +0000 Subject: [njs] Introduced specification primitives SameValue(), SameValueZero(). Message-ID: details: https://hg.nginx.org/njs/rev/facfa87548b6 branches: changeset: 1263:facfa87548b6 user: Dmitry Volyntsev date: Wed Nov 27 14:36:04 2019 +0300 description: Introduced specification primitives SameValue(), SameValueZero(). diffstat: src/njs_value.h | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 78 insertions(+), 5 deletions(-) diffs (104 lines): diff -r 2ad4c5976839 -r facfa87548b6 src/njs_value.h --- a/src/njs_value.h Tue Nov 26 18:44:11 2019 +0300 +++ b/src/njs_value.h Wed Nov 27 14:36:04 2019 +0300 @@ -1080,6 +1080,21 @@ njs_value_to_string(njs_vm_t *vm, njs_va njs_inline njs_bool_t +njs_values_same_non_numeric(const njs_value_t *val1, const njs_value_t *val2) +{ + if (njs_is_string(val1)) { + return njs_string_eq(val1, val2); + } + + if (njs_is_symbol(val1)) { + return njs_symbol_eq(val1, val2); + } + + return (njs_object(val1) == njs_object(val2)); +} + + +njs_inline njs_bool_t njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2) { if (val1->type != val2->type) { @@ -1096,15 +1111,73 @@ njs_values_strict_equal(const njs_value_ return (njs_number(val1) == njs_number(val2)); } - if (njs_is_string(val1)) { - return njs_string_eq(val1, val2); + return njs_values_same_non_numeric(val1, val2); +} + + +njs_inline njs_bool_t +njs_values_same(const njs_value_t *val1, const njs_value_t *val2) +{ + double num1, num2; + + if (val1->type != val2->type) { + return 0; } - if (njs_is_symbol(val1)) { - return njs_symbol_eq(val1, val2); + if (njs_is_numeric(val1)) { + + if (njs_is_undefined(val1)) { + return 1; + } + + num1 = njs_number(val1); + num2 = njs_number(val2); + + if (njs_slow_path(isnan(num1) && isnan(num2))) { + return 1; + } + + if (njs_slow_path(num1 == 0 && num2 == 0 + && (signbit(num1) ^ signbit(num2)))) + { + return 0; + } + + /* Infinities are handled correctly by comparision. */ + return num1 == num2; } - return (njs_object(val1) == njs_object(val2)); + return njs_values_same_non_numeric(val1, val2); +} + + +njs_inline njs_bool_t +njs_values_same_zero(const njs_value_t *val1, const njs_value_t *val2) +{ + double num1, num2; + + if (val1->type != val2->type) { + return 0; + } + + if (njs_is_numeric(val1)) { + + if (njs_is_undefined(val1)) { + return 1; + } + + num1 = njs_number(val1); + num2 = njs_number(val2); + + if (njs_slow_path(isnan(num1) && isnan(num2))) { + return 1; + } + + /* Infinities are handled correctly by comparision. */ + return num1 == num2; + } + + return njs_values_same_non_numeric(val1, val2); } From xeioex at nginx.com Wed Nov 27 14:15:29 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 27 Nov 2019 14:15:29 +0000 Subject: [njs] Checking extensible flag in ValidateAndApplyPropertyDescriptor(). Message-ID: details: https://hg.nginx.org/njs/rev/4e0d11660ce1 branches: changeset: 1264:4e0d11660ce1 user: Dmitry Volyntsev date: Wed Nov 27 15:40:44 2019 +0300 description: Checking extensible flag in ValidateAndApplyPropertyDescriptor(). diffstat: src/njs_object.c | 19 +++---------------- src/njs_object_prop.c | 6 ++++++ src/njs_value.c | 4 ++++ src/test/njs_unit_test.c | 37 ++++++++++++++++++++++++++----------- 4 files changed, 39 insertions(+), 27 deletions(-) diffs (192 lines): diff -r facfa87548b6 -r 4e0d11660ce1 src/njs_object.c --- a/src/njs_object.c Wed Nov 27 14:36:04 2019 +0300 +++ b/src/njs_object.c Wed Nov 27 15:40:44 2019 +0300 @@ -1232,15 +1232,7 @@ njs_object_define_property(njs_vm_t *vm, njs_value_t *value, *name, *desc, lvalue; if (!njs_is_object(njs_arg(args, nargs, 1))) { - njs_type_error(vm, "cannot convert %s argument to object", - njs_type_string(njs_arg(args, nargs, 1)->type)); - return NJS_ERROR; - } - - value = njs_argument(args, 1); - - if (!njs_object(value)->extensible) { - njs_type_error(vm, "object is not extensible"); + njs_type_error(vm, "Object.defineProperty is called on non-object"); return NJS_ERROR; } @@ -1251,6 +1243,7 @@ njs_object_define_property(njs_vm_t *vm, return NJS_ERROR; } + value = njs_argument(args, 1); name = njs_lvalue_arg(&lvalue, args, nargs, 2); ret = njs_object_prop_define(vm, value, name, desc, @@ -1276,18 +1269,12 @@ njs_object_define_properties(njs_vm_t *v njs_object_prop_t *prop; if (!njs_is_object(njs_arg(args, nargs, 1))) { - njs_type_error(vm, "cannot convert %s argument to object", - njs_type_string(njs_arg(args, nargs, 1)->type)); + njs_type_error(vm, "Object.defineProperties is called on non-object"); return NJS_ERROR; } value = njs_argument(args, 1); - if (!njs_object(value)->extensible) { - njs_type_error(vm, "object is not extensible"); - return NJS_ERROR; - } - desc = njs_arg(args, nargs, 2); if (!njs_is_object(desc)) { diff -r facfa87548b6 -r 4e0d11660ce1 src/njs_object_prop.c --- a/src/njs_object_prop.c Wed Nov 27 14:36:04 2019 +0300 +++ b/src/njs_object_prop.c Wed Nov 27 15:40:44 2019 +0300 @@ -157,6 +157,12 @@ njs_object_prop_define(njs_vm_t *vm, njs if (njs_fast_path(ret == NJS_DECLINED)) { + if (!njs_object(object)->extensible) { + njs_type_error(vm, "Cannot add property \"%V\", " + "object is not extensible", &pq.lhq.key); + return NJS_ERROR; + } + /* 6.2.5.6 CompletePropertyDescriptor */ if (njs_is_accessor_descriptor(prop)) { diff -r facfa87548b6 -r 4e0d11660ce1 src/njs_value.c --- a/src/njs_value.c Wed Nov 27 14:36:04 2019 +0300 +++ b/src/njs_value.c Wed Nov 27 15:40:44 2019 +0300 @@ -719,6 +719,10 @@ njs_array_property_query(njs_vm_t *vm, n return NJS_DECLINED; } + if (!array->object.extensible) { + return NJS_DECLINED; + } + size = index - array->length; ret = njs_array_expand(vm, array, 0, size + 1); diff -r facfa87548b6 -r 4e0d11660ce1 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Wed Nov 27 14:36:04 2019 +0300 +++ b/src/test/njs_unit_test.c Wed Nov 27 15:40:44 2019 +0300 @@ -11074,7 +11074,7 @@ static njs_unit_test_t njs_test[] = njs_str("2") }, { njs_str("var o = {}; Object.defineProperty()"), - njs_str("TypeError: cannot convert undefined argument to object") }, + njs_str("TypeError: Object.defineProperty is called on non-object") }, { njs_str("var o = {}; Object.defineProperty(o)"), njs_str("TypeError: descriptor is not an object") }, @@ -11187,14 +11187,23 @@ static njs_unit_test_t njs_test[] = njs_str("true") }, { njs_str("Object.defineProperties()"), - njs_str("TypeError: cannot convert undefined argument to object") }, + njs_str("TypeError: Object.defineProperties is called on non-object") }, { njs_str("Object.defineProperties(1, {})"), - njs_str("TypeError: cannot convert number argument to object") }, + njs_str("TypeError: Object.defineProperties is called on non-object") }, { njs_str("Object.defineProperties({}, 1)"), njs_str("TypeError: descriptor is not an object") }, + { njs_str("Object.defineProperties(Object.freeze({b:1}), {b:{value:1}}).b"), + njs_str("1") }, + + { njs_str("Object.defineProperties(Object.freeze({b:1}), {b:{value:2}})"), + njs_str("TypeError: Cannot redefine property: \"b\"") }, + + { njs_str("Object.defineProperties(Object.freeze({b:1}), {c:{value:1}})"), + njs_str("TypeError: Cannot add property \"c\", object is not extensible") }, + { njs_str("var o = {a:1}; o.hasOwnProperty('a')"), njs_str("true") }, @@ -11679,10 +11688,10 @@ static njs_unit_test_t njs_test[] = njs_str("name,length") }, { njs_str("Object.defineProperty(Object.freeze({}), 'b', {})"), - njs_str("TypeError: object is not extensible") }, + njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("Object.defineProperties(Object.freeze({}), {b:{}})"), - njs_str("TypeError: object is not extensible") }, + njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("Object.freeze()"), njs_str("undefined") }, @@ -11707,7 +11716,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = Object.freeze([1,2]);" "Object.defineProperty(a, 'a', {value:1}).a"), - njs_str("TypeError: object is not extensible") }, + njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var a = [1,2]; a.a = 1; Object.freeze(a); delete a.a"), njs_str("TypeError: Cannot delete property \"a\" of array") }, @@ -11723,7 +11732,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var f = Object.freeze(function() {});" "Object.defineProperty(f, 'a', {value:1}).a"), - njs_str("TypeError: object is not extensible") }, + njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var f = function() {}; f.a = 1; Object.freeze(f); delete f.a"), njs_str("TypeError: Cannot delete property \"a\" of function") }, @@ -11739,7 +11748,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var d = Object.freeze(new Date(''));" "Object.defineProperty(d, 'a', {value:1}).a"), - njs_str("TypeError: object is not extensible") }, + njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var d = new Date(''); d.a = 1; Object.freeze(d);" "delete d.a"), @@ -11756,7 +11765,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var r = Object.freeze(new RegExp(''));" "Object.defineProperty(r, 'a', {value:1}).a"), - njs_str("TypeError: object is not extensible") }, + njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var r = new RegExp(''); r.a = 1; Object.freeze(r); delete r.a"), njs_str("TypeError: Cannot delete property \"a\" of regexp") }, @@ -11918,11 +11927,11 @@ static njs_unit_test_t njs_test[] = { njs_str("var o = Object.preventExtensions({a:1});" "Object.defineProperty(o, 'b', {value:1})"), - njs_str("TypeError: object is not extensible") }, + njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("var o = Object.preventExtensions({a:1});" "Object.defineProperties(o, {b:{value:1}})"), - njs_str("TypeError: object is not extensible") }, + njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("var o = Object.preventExtensions({a:1}); o.a = 2; o.a"), njs_str("2") }, @@ -11948,6 +11957,12 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.isExtensible([])"), njs_str("true") }, + { njs_str("var arrObj = [];Object.preventExtensions(arrObj); arrObj[1] = 1"), + njs_str("TypeError: Cannot add property \"1\", object is not extensible") }, + + { njs_str("var arrObj = [1,2];Object.preventExtensions(arrObj); arrObj[1] = 1"), + njs_str("1") }, + { njs_str("Object.isExtensible(function() {})"), njs_str("true") }, From xeioex at nginx.com Wed Nov 27 14:15:29 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 27 Nov 2019 14:15:29 +0000 Subject: [njs] Fixed Object.defineProperties() according to the specification. Message-ID: details: https://hg.nginx.org/njs/rev/ba1499515923 branches: changeset: 1265:ba1499515923 user: Dmitry Volyntsev date: Wed Nov 27 15:48:32 2019 +0300 description: Fixed Object.defineProperties() according to the specification. This closes #210 issue on Github. diffstat: src/njs_object.c | 65 ++++++++++++++++++++++++++++------------------- src/njs_object_prop.c | 7 ++++- src/test/njs_unit_test.c | 37 +++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 30 deletions(-) diffs (164 lines): diff -r 4e0d11660ce1 -r ba1499515923 src/njs_object.c --- a/src/njs_object.c Wed Nov 27 15:40:44 2019 +0300 +++ b/src/njs_object.c Wed Nov 27 15:48:32 2019 +0300 @@ -1262,44 +1262,55 @@ static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_int_t ret; - njs_value_t *value, *desc; - njs_lvlhsh_t *hash; - njs_lvlhsh_each_t lhe; - njs_object_prop_t *prop; + uint32_t i, length; + njs_int_t ret; + njs_array_t *keys; + njs_value_t desc, *value, *descs; + njs_object_prop_t *prop; + njs_property_query_t pq; if (!njs_is_object(njs_arg(args, nargs, 1))) { njs_type_error(vm, "Object.defineProperties is called on non-object"); return NJS_ERROR; } - value = njs_argument(args, 1); - - desc = njs_arg(args, nargs, 2); - - if (!njs_is_object(desc)) { - njs_type_error(vm, "descriptor is not an object"); + descs = njs_arg(args, nargs, 2); + ret = njs_value_to_object(vm, descs); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + keys = njs_value_own_enumerate(vm, descs, NJS_ENUM_KEYS, + NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 0); + if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } - njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); - - hash = njs_object_hash(desc); - - for ( ;; ) { - prop = njs_lvlhsh_each(hash, &lhe); - - if (prop == NULL) { - break; + length = keys->length; + value = njs_argument(args, 1); + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); + + for (i = 0; i < length; i++) { + ret = njs_property_query(vm, &pq, descs, &keys->start[i]); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; } - if (prop->enumerable && njs_is_object(&prop->value)) { - ret = njs_object_prop_define(vm, value, &prop->name, &prop->value, - NJS_OBJECT_PROP_DESCRIPTOR); - - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } + prop = pq.lhq.value; + + if (ret == NJS_DECLINED || !prop->enumerable) { + continue; + } + + ret = njs_value_property(vm, descs, &keys->start[i], &desc); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + ret = njs_object_prop_define(vm, value, &keys->start[i], &desc, + NJS_OBJECT_PROP_DESCRIPTOR); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; } } diff -r 4e0d11660ce1 -r ba1499515923 src/njs_object_prop.c --- a/src/njs_object_prop.c Wed Nov 27 15:40:44 2019 +0300 +++ b/src/njs_object_prop.c Wed Nov 27 15:48:32 2019 +0300 @@ -311,7 +311,7 @@ njs_object_prop_define(njs_vm_t *vm, njs if (njs_is_valid(&prop->value) && prev->type != NJS_PROPERTY_HANDLER - && !njs_values_strict_equal(&prop->value, &prev->value)) + && !njs_values_same(&prop->value, &prev->value)) { goto exception; } @@ -464,6 +464,11 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob static const njs_value_t get_string = njs_string("get"); + if (!njs_is_object(desc)) { + njs_type_error(vm, "property descriptor must be an object"); + return NJS_ERROR; + } + data = 0; accessor = 0; diff -r 4e0d11660ce1 -r ba1499515923 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Wed Nov 27 15:40:44 2019 +0300 +++ b/src/test/njs_unit_test.c Wed Nov 27 15:48:32 2019 +0300 @@ -11103,6 +11103,39 @@ static njs_unit_test_t njs_test[] = "Object.keys(o)"), njs_str("b") }, + { njs_str("var o = Object.defineProperties({}, { get x() { return { value: 1 }; } });" + "Object.getOwnPropertyDescriptor(o, 'x').value"), + njs_str("1") }, + + { njs_str("Object.defineProperties({}, { get x() { return 1; } })"), + njs_str("TypeError: property descriptor must be an object") }, + + { njs_str("var obj = {}; var desc = {value:NaN}; Object.defineProperty(obj, 'foo', desc); " + "Object.defineProperties(obj, { foo: desc } ).foo"), + njs_str("NaN") }, + + { njs_str("var obj = {}; var desc = {value:-0}; Object.defineProperty(obj, 'foo', desc); " + "Object.defineProperties(obj, { foo: desc } ).foo"), + njs_str("-0") }, + + { njs_str("var obj = {}; var desc = {value:-0}; Object.defineProperty(obj, 'foo', {value:0}); " + "Object.defineProperties(obj, { foo: desc } ).foo"), + njs_str("TypeError: Cannot redefine property: \"foo\"") }, + + { njs_str("var obj = {}; var desc = {value:0}; Object.defineProperty(obj, 'foo', {value:-0}); " + "Object.defineProperties(obj, { foo: desc } ).foo"), + njs_str("TypeError: Cannot redefine property: \"foo\"") }, + + { njs_str("var descs = {a:{value:1}}; Object.defineProperty(descs, 'b', {value:{value:2}});" + "var o = Object.defineProperties({}, descs);" + "njs.dump([o.a, o.b])"), + njs_str("[1,undefined]") }, + + { njs_str("var descs = {a:{value:1}}; Object.defineProperty(descs, 'b', {value:{value:2}, enumerable:true});" + "var o = Object.defineProperties({}, descs);" + "njs.dump([o.a, o.b])"), + njs_str("[1,2]") }, + { njs_str("var o = {a:1}; delete o.a;" "Object.defineProperty(o, 'a', { value: 1 }); o.a"), njs_str("1") }, @@ -11192,8 +11225,8 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.defineProperties(1, {})"), njs_str("TypeError: Object.defineProperties is called on non-object") }, - { njs_str("Object.defineProperties({}, 1)"), - njs_str("TypeError: descriptor is not an object") }, + { njs_str("njs.dump(Object.defineProperties({}, 1))"), + njs_str("{}") }, { njs_str("Object.defineProperties(Object.freeze({b:1}), {b:{value:1}}).b"), njs_str("1") }, From xeioex at nginx.com Wed Nov 27 14:15:30 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 27 Nov 2019 14:15:30 +0000 Subject: [njs] Improved Array.prototype.includes(). Message-ID: details: https://hg.nginx.org/njs/rev/4b2b845c3bad branches: changeset: 1266:4b2b845c3bad user: Dmitry Volyntsev date: Wed Nov 27 16:51:29 2019 +0300 description: Improved Array.prototype.includes(). diffstat: src/njs_array.c | 30 ++++-------------------------- 1 files changed, 4 insertions(+), 26 deletions(-) diffs (47 lines): diff -r ba1499515923 -r 4b2b845c3bad src/njs_array.c --- a/src/njs_array.c Wed Nov 27 15:48:32 2019 +0300 +++ b/src/njs_array.c Wed Nov 27 16:51:29 2019 +0300 @@ -1886,21 +1886,7 @@ njs_array_handler_includes(njs_vm_t *vm, entry = njs_value_arg(&njs_value_undefined); } - if (njs_values_strict_equal(args->argument, entry)) { - njs_set_true(&vm->retval); - - return 1; - } - - return NJS_OK; -} - - -static njs_int_t -njs_array_handler_includes_nan(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_value_t *entry, uint32_t n) -{ - if (njs_is_numeric(entry) && isnan(njs_number(entry))) { + if (njs_values_same_zero(args->argument, entry)) { njs_set_true(&vm->retval); return 1; @@ -1953,17 +1939,9 @@ njs_array_prototype_includes(njs_vm_t *v iargs.from = (uint32_t) from; iargs.to = length; - if (njs_is_number(iargs.argument) && isnan(njs_number(iargs.argument))) { - ret = njs_array_iterator(vm, &iargs, njs_array_handler_includes_nan); - if (njs_fast_path(ret == NJS_DECLINED)) { - return NJS_OK; - } - - } else { - ret = njs_array_iterator(vm, &iargs, njs_array_handler_includes); - if (njs_fast_path(ret == NJS_DECLINED)) { - return NJS_OK; - } + ret = njs_array_iterator(vm, &iargs, njs_array_handler_includes); + if (njs_fast_path(ret == NJS_DECLINED)) { + return NJS_OK; } not_found: From xeioex at nginx.com Wed Nov 27 16:47:53 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 27 Nov 2019 16:47:53 +0000 Subject: [njs] Fixed TypeError message in ValidateAndApplyPropertyDescriptor(). Message-ID: details: https://hg.nginx.org/njs/rev/24b5c0103726 branches: changeset: 1267:24b5c0103726 user: Dmitry Volyntsev date: Wed Nov 27 18:28:13 2019 +0300 description: Fixed TypeError message in ValidateAndApplyPropertyDescriptor(). For Symbol key when object is not extensible. diffstat: src/njs_object_prop.c | 1 + src/njs_value.c | 1 + src/test/njs_unit_test.c | 7 +++++++ 3 files changed, 9 insertions(+), 0 deletions(-) diffs (46 lines): diff -r 4b2b845c3bad -r 24b5c0103726 src/njs_object_prop.c --- a/src/njs_object_prop.c Wed Nov 27 16:51:29 2019 +0300 +++ b/src/njs_object_prop.c Wed Nov 27 18:28:13 2019 +0300 @@ -158,6 +158,7 @@ njs_object_prop_define(njs_vm_t *vm, njs if (njs_fast_path(ret == NJS_DECLINED)) { if (!njs_object(object)->extensible) { + njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot add property \"%V\", " "object is not extensible", &pq.lhq.key); return NJS_ERROR; diff -r 4b2b845c3bad -r 24b5c0103726 src/njs_value.c --- a/src/njs_value.c Wed Nov 27 16:51:29 2019 +0300 +++ b/src/njs_value.c Wed Nov 27 18:28:13 2019 +0300 @@ -1125,6 +1125,7 @@ njs_value_property_set(njs_vm_t *vm, njs } if (njs_slow_path(!njs_object(value)->extensible)) { + njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot add property \"%V\", " "object is not extensible", &pq.lhq.key); return NJS_ERROR; diff -r 4b2b845c3bad -r 24b5c0103726 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Wed Nov 27 16:51:29 2019 +0300 +++ b/src/test/njs_unit_test.c Wed Nov 27 18:28:13 2019 +0300 @@ -11962,6 +11962,10 @@ static njs_unit_test_t njs_test[] = "Object.defineProperty(o, 'b', {value:1})"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, + { njs_str("var o = Object.preventExtensions({});" + "Object.defineProperty(o, Symbol.unscopables, {})"), + njs_str("TypeError: Cannot add property \"Symbol(Symbol.unscopables)\", object is not extensible") }, + { njs_str("var o = Object.preventExtensions({a:1});" "Object.defineProperties(o, {b:{value:1}})"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, @@ -11975,6 +11979,9 @@ static njs_unit_test_t njs_test[] = { njs_str("var o = Object.preventExtensions({a:1}); o.b = 1; o.b"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, + { njs_str("var o = Object.preventExtensions({a:1}); o[Symbol.unscopables] = 1"), + njs_str("TypeError: Cannot add property \"Symbol(Symbol.unscopables)\", object is not extensible") }, + { njs_str("Object.preventExtensions()"), njs_str("undefined") }, From xeioex at nginx.com Thu Nov 28 12:10:48 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 28 Nov 2019 12:10:48 +0000 Subject: [njs] Added Object.is(). Message-ID: details: https://hg.nginx.org/njs/rev/9dfc67399ef5 branches: changeset: 1268:9dfc67399ef5 user: Artem S. Povalyukhin date: Thu Nov 28 09:59:10 2019 +0300 description: Added Object.is(). This closes #260 issue on Github. diffstat: src/njs_object.c | 20 ++++++++++++++++++++ src/test/njs_unit_test.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 0 deletions(-) diffs (82 lines): diff -r 24b5c0103726 -r 9dfc67399ef5 src/njs_object.c --- a/src/njs_object.c Wed Nov 27 18:28:13 2019 +0300 +++ b/src/njs_object.c Thu Nov 28 09:59:10 2019 +0300 @@ -1761,6 +1761,17 @@ njs_object_assign(njs_vm_t *vm, njs_valu } +static njs_int_t +njs_object_is(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_set_boolean(&vm->retval, njs_values_same(njs_arg(args, nargs, 1), + njs_arg(args, nargs, 2))); + + return NJS_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -2052,6 +2063,15 @@ static const njs_object_prop_t njs_obje .writable = 1, .configurable = 1, }, + + /* Object.is(). */ + { + .type = NJS_PROPERTY, + .name = njs_string("is"), + .value = njs_native_function(njs_object_is, 2), + .writable = 1, + .configurable = 1, + }, }; diff -r 24b5c0103726 -r 9dfc67399ef5 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Wed Nov 27 18:28:13 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Nov 28 09:59:10 2019 +0300 @@ -12036,6 +12036,41 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.isExtensible(undefined)"), njs_str("false") }, + /* Object.is() */ + + { njs_str("typeof Object.is"), + njs_str("function") }, + + { njs_str("Object.is.length == 2"), + njs_str("true") }, + + { njs_str("Object.is()"), + njs_str("true") }, + + { njs_str("[undefined, null, false, NaN, '', Symbol(), {}]" + ".map((x) => Object.is(x, x))" + ".every((x) => x === true)"), + njs_str("true") }, + + { njs_str("[null, false, NaN, '', Symbol(), {}]" + ".map((x) => Object.is(x) || Object.is(void 0, x))" + ".every((x) => x === false)"), + njs_str("true") }, + + { njs_str("[false, NaN, '', Symbol()]" + ".map((x) => Object.is(Object(x), Object(x)))" + ".every((x) => x === false)"), + njs_str("true") }, + + { njs_str("Object.is(0, -0)"), + njs_str("false") }, + + { njs_str("Object.is(0, null)"), + njs_str("false") }, + + { njs_str("Object.is(42, '42')"), + njs_str("false") }, + { njs_str( "var fail;" "function isConfigurableMethods(o) {" From xeioex at nginx.com Thu Nov 28 12:10:48 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 28 Nov 2019 12:10:48 +0000 Subject: [njs] Improved Object.create(). Message-ID: details: https://hg.nginx.org/njs/rev/5bbbc4361799 branches: changeset: 1269:5bbbc4361799 user: Artem S. Povalyukhin date: Thu Nov 28 13:25:00 2019 +0300 description: Improved Object.create(). This closes #261 issue on Github. diffstat: src/njs_object.c | 16 +++++++++++++--- src/test/njs_unit_test.c | 11 +++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diffs (72 lines): diff -r 9dfc67399ef5 -r 5bbbc4361799 src/njs_object.c --- a/src/njs_object.c Thu Nov 28 09:59:10 2019 +0300 +++ b/src/njs_object.c Thu Nov 28 13:25:00 2019 +0300 @@ -28,6 +28,8 @@ static njs_int_t njs_object_enumerate_ob static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); +static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused); njs_object_t * @@ -250,13 +252,11 @@ njs_object_constructor(njs_vm_t *vm, njs } -/* TODO: properties with attributes. */ - static njs_int_t njs_object_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_value_t *value; + njs_value_t *value, *descs, arguments[3]; njs_object_t *object; value = njs_arg(args, nargs, 1); @@ -278,6 +278,16 @@ njs_object_create(njs_vm_t *vm, njs_valu njs_set_object(&vm->retval, object); + descs = njs_arg(args, nargs, 2); + + if (njs_slow_path(!njs_is_undefined(descs))) { + arguments[0] = args[0]; + arguments[1] = vm->retval; + arguments[2] = *descs; + + return njs_object_define_properties(vm, arguments, 3, unused); + } + return NJS_OK; } diff -r 9dfc67399ef5 -r 5bbbc4361799 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Nov 28 09:59:10 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Nov 28 13:25:00 2019 +0300 @@ -10762,6 +10762,8 @@ static njs_unit_test_t njs_test[] = "o.__proto__ === p"), njs_str("true") }, + /* Object.create() */ + { njs_str("var o = Object.create(Object.prototype);" "o.__proto__ === Object.prototype"), njs_str("true") }, @@ -10775,6 +10777,15 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.create(1)"), njs_str("TypeError: prototype may only be an object or null: number") }, + { njs_str("var o = Object.create(null, { a: { value: 1 } }); o.a"), + njs_str("1") }, + + { njs_str("var o = Object.create({ a: 0 }, { a: { value: 1 } }); o.a"), + njs_str("1") }, + + { njs_str("var o = Object.create({ get a() { return this.b; } }, { b: { value: 1 } }); o.a"), + njs_str("1") }, + { njs_str("var o = {a:1, b:2, c:3};" "Object.keys(o)"), njs_str("a,b,c") }, From xeioex at nginx.com Fri Nov 29 14:28:31 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 29 Nov 2019 14:28:31 +0000 Subject: [njs] Added Object.setPrototypeOf(). Message-ID: details: https://hg.nginx.org/njs/rev/01c7375c9b5c branches: changeset: 1271:01c7375c9b5c user: Artem S. Povalyukhin date: Fri Nov 29 12:53:33 2019 +0300 description: Added Object.setPrototypeOf(). This closes #265 issue on Github. diffstat: src/njs_object.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++- src/test/njs_unit_test.c | 35 +++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) diffs (144 lines): diff -r e20023dd991d -r 01c7375c9b5c src/njs_object.c --- a/src/njs_object.c Fri Nov 29 17:11:11 2019 +0300 +++ b/src/njs_object.c Fri Nov 29 12:53:33 2019 +0300 @@ -30,6 +30,8 @@ static njs_int_t njs_object_own_enumerat njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); +static njs_int_t njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object, + const njs_value_t *value); njs_object_t * @@ -1476,6 +1478,52 @@ njs_object_get_prototype_of(njs_vm_t *vm static njs_int_t +njs_object_set_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_int_t ret; + njs_value_t *value, *proto; + + value = njs_arg(args, nargs, 1); + if (njs_slow_path(njs_is_null_or_undefined(value))) { + njs_type_error(vm, "cannot convert %s argument to object", + njs_type_string(value->type)); + return NJS_ERROR; + } + + proto = njs_arg(args, nargs, 2); + if (njs_slow_path(!njs_is_object(proto) && !njs_is_null(proto))) { + njs_type_error(vm, "prototype may only be an object or null: %s", + njs_type_string(proto->type)); + return NJS_ERROR; + } + + if (njs_slow_path(!njs_is_object(value))) { + vm->retval = *value; + + return NJS_OK; + } + + ret = njs_object_set_prototype(vm, njs_object(value), proto); + if (njs_fast_path(ret == NJS_OK)) { + vm->retval = *value; + + return NJS_OK; + } + + if (ret == NJS_DECLINED) { + njs_type_error(vm, "Cannot set property \"prototype\", " + "object is not extensible"); + + } else { + njs_type_error(vm, "Cyclic __proto__ value"); + } + + return NJS_ERROR; +} + + +static njs_int_t njs_object_freeze(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { @@ -2011,6 +2059,15 @@ static const njs_object_prop_t njs_obje .configurable = 1, }, + /* Object.setPrototypeOf(). */ + { + .type = NJS_PROPERTY, + .name = njs_string("setPrototypeOf"), + .value = njs_native_function(njs_object_set_prototype_of, 2), + .writable = 1, + .configurable = 1, + }, + /* Object.freeze(). */ { .type = NJS_PROPERTY, @@ -2095,7 +2152,7 @@ const njs_object_init_t njs_object_cons * ES6, 9.1.2: [[SetPrototypeOf]]. */ static njs_int_t -njs_object_set_prototype_of(njs_vm_t *vm, njs_object_t *object, +njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object, const njs_value_t *value) { const njs_object_t *proto; @@ -2146,7 +2203,7 @@ njs_object_prototype_proto(njs_vm_t *vm, if (setval != NULL) { if (njs_is_object(setval) || njs_is_null(setval)) { - ret = njs_object_set_prototype_of(vm, object, setval); + ret = njs_object_set_prototype(vm, object, setval); if (njs_slow_path(ret == NJS_ERROR)) { njs_type_error(vm, "Cyclic __proto__ value"); return NJS_ERROR; diff -r e20023dd991d -r 01c7375c9b5c src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Nov 29 17:11:11 2019 +0300 +++ b/src/test/njs_unit_test.c Fri Nov 29 12:53:33 2019 +0300 @@ -11527,6 +11527,41 @@ static njs_unit_test_t njs_test[] = ".every((x) => Object.getPrototypeOf(x) == Object.getPrototypeOf(Object(x)))"), njs_str("true") }, + /* Object.setPrototypeOf() */ + + { njs_str("Object.setPrototypeOf()"), + njs_str("TypeError: cannot convert undefined argument to object") }, + + { njs_str("Object.setPrototypeOf(null)"), + njs_str("TypeError: cannot convert null argument to object") }, + + { njs_str("Object.setPrototypeOf({})"), + njs_str("TypeError: prototype may only be an object or null: undefined") }, + + { njs_str("Object.setPrototypeOf(Object.preventExtensions({}))"), + njs_str("TypeError: prototype may only be an object or null: undefined") }, + + { njs_str("[true, 42, '', Symbol()]" + ".every((x) => Object.setPrototypeOf(x, {}) === x)"), + njs_str("true") }, + + { njs_str("Object.setPrototypeOf(Object.preventExtensions({}), {})"), + njs_str("TypeError: Cannot set property \"prototype\", object is not extensible") }, + + { njs_str("var p = {}, o = Object.create(p); Object.setPrototypeOf(p, o)"), + njs_str("TypeError: Cyclic __proto__ value") }, + + { njs_str("var p = {}, o = {}; Object.setPrototypeOf(o, p);" + "p.isPrototypeOf(o)"), + njs_str("true") }, + + { njs_str("var p = {}, o = Object.create(p); Object.setPrototypeOf(o, null);" + "Object.getPrototypeOf(o)"), + njs_str("null") }, + + { njs_str("typeof Object.setPrototypeOf({}, null)"), + njs_str("object") }, + { njs_str("var p = {}; var o = Object.create(p);" "p.isPrototypeOf(o)"), njs_str("true") }, From xeioex at nginx.com Fri Nov 29 14:28:31 2019 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 29 Nov 2019 14:28:31 +0000 Subject: [njs] Making backtrace a property of a thrown exception. Message-ID: details: https://hg.nginx.org/njs/rev/e20023dd991d branches: changeset: 1270:e20023dd991d user: Dmitry Volyntsev date: Fri Nov 29 17:11:11 2019 +0300 description: Making backtrace a property of a thrown exception. diffstat: src/njs.h | 6 +- src/njs_error.c | 172 ++++++++++++++++++++++-- src/njs_error.h | 2 + src/njs_json.c | 4 - src/njs_object_hash.h | 9 + src/njs_shell.c | 22 +- src/njs_vm.c | 276 +++++++++++++++++---------------------- src/njs_vm.h | 7 +- src/njs_vmcode.c | 14 +- src/test/njs_interactive_test.c | 5 +- test/njs_expect_test.exp | 21 ++- 11 files changed, 332 insertions(+), 206 deletions(-) diffs (837 lines): diff -r 5bbbc4361799 -r e20023dd991d src/njs.h --- a/src/njs.h Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs.h Fri Nov 29 17:11:11 2019 +0300 @@ -258,19 +258,19 @@ NJS_EXPORT njs_int_t njs_vm_value_string NJS_EXPORT u_char *njs_vm_value_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size); NJS_EXPORT njs_int_t njs_vm_value_string_copy(njs_vm_t *vm, njs_str_t *retval, - const njs_value_t *value, uintptr_t *next); + njs_value_t *value, uintptr_t *next); /* * Converts a value to string. */ NJS_EXPORT njs_int_t njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst, - const njs_value_t *src); + njs_value_t *src); /* * Calls njs_vm_value_to_string(), if exception was thrown adds backtrace. */ NJS_EXPORT njs_int_t njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, - const njs_value_t *src); + njs_value_t *src); NJS_EXPORT njs_int_t njs_vm_retval_string(njs_vm_t *vm, njs_str_t *dst); NJS_EXPORT njs_int_t njs_vm_value_dump(njs_vm_t *vm, njs_str_t *dst, diff -r 5bbbc4361799 -r e20023dd991d src/njs_error.c --- a/src/njs_error.c Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs_error.c Fri Nov 29 17:11:11 2019 +0300 @@ -10,6 +10,7 @@ static const njs_value_t njs_error_message_string = njs_string("message"); static const njs_value_t njs_error_name_string = njs_string("name"); +static const njs_value_t njs_error_stack_string = njs_string("stack"); void @@ -59,6 +60,127 @@ njs_error_fmt_new(njs_vm_t *vm, njs_valu } +static njs_int_t +njs_error_stack_new(njs_vm_t *vm, njs_object_t *error, njs_value_t *retval) +{ + njs_int_t ret; + njs_str_t string; + njs_arr_t *stack; + njs_value_t value; + njs_native_frame_t *frame; + + njs_set_object(&value, error); + + ret = njs_error_to_string(vm, retval, &value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_backtrace_entry_t)); + if (njs_slow_path(stack == NULL)) { + return NJS_ERROR; + } + + frame = vm->top_frame; + + for ( ;; ) { + if (njs_vm_add_backtrace_entry(vm, stack, frame) != NJS_OK) { + break; + } + + frame = frame->previous; + + if (frame == NULL) { + break; + } + } + + njs_string_get(retval, &string); + + ret = njs_vm_backtrace_to_string(vm, stack, &string); + + njs_arr_destroy(stack); + + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + return njs_string_set(vm, retval, string.start, string.length); +} + + +njs_int_t +njs_error_stack_attach(njs_vm_t *vm, njs_value_t *value) +{ + njs_int_t ret; + njs_object_t *error; + njs_object_prop_t *prop; + njs_lvlhsh_query_t lhq; + + if (njs_slow_path(!njs_is_error(value))) { + return NJS_DECLINED; + } + + if (vm->debug == NULL || vm->start == NULL) { + return NJS_OK; + } + + error = njs_object(value); + + lhq.replace = 0; + lhq.pool = vm->mem_pool; + lhq.proto = &njs_object_hash_proto; + + lhq.key = njs_str_value("stack"); + lhq.key_hash = NJS_STACK_HASH; + + prop = njs_object_prop_alloc(vm, &njs_error_stack_string, + &njs_value_undefined, 1); + if (njs_slow_path(prop == NULL)) { + return NJS_ERROR; + } + + prop->enumerable = 0; + + ret = njs_error_stack_new(vm, error, &prop->value); + if (njs_slow_path(ret == NJS_ERROR)) { + njs_internal_error(vm, "njs_error_stack_new() failed"); + return NJS_ERROR; + } + + if (ret == NJS_OK) { + lhq.value = prop; + + ret = njs_lvlhsh_insert(&error->hash, &lhq); + if (njs_slow_path(ret == NJS_ERROR)) { + njs_internal_error(vm, "lvlhsh insert failed"); + return NJS_ERROR; + } + } + + return NJS_OK; +} + + +njs_int_t +njs_error_stack(njs_vm_t *vm, njs_value_t *value, njs_value_t *stack) +{ + njs_int_t ret; + + ret = njs_value_property(vm, value, njs_value_arg(&njs_error_stack_string), + stack); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (!njs_is_string(stack)) { + return NJS_DECLINED; + } + + return NJS_OK; +} + + njs_object_t * njs_error_alloc(njs_vm_t *vm, njs_object_type_t type, const njs_value_t *name, const njs_value_t *message) @@ -83,11 +205,11 @@ njs_error_alloc(njs_vm_t *vm, njs_object lhq.replace = 0; lhq.pool = vm->mem_pool; + lhq.proto = &njs_object_hash_proto; if (name != NULL) { lhq.key = njs_str_value("name"); lhq.key_hash = NJS_NAME_HASH; - lhq.proto = &njs_object_hash_proto; prop = njs_object_prop_alloc(vm, &njs_error_name_string, name, 1); if (njs_slow_path(prop == NULL)) { @@ -106,7 +228,6 @@ njs_error_alloc(njs_vm_t *vm, njs_object if (message!= NULL) { lhq.key = njs_str_value("message"); lhq.key_hash = NJS_MESSAGE_HASH; - lhq.proto = &njs_object_hash_proto; prop = njs_object_prop_alloc(vm, &njs_error_message_string, message, 1); if (njs_slow_path(prop == NULL)) { @@ -605,20 +726,8 @@ njs_error_prototype_value_of(njs_vm_t *v static njs_int_t -njs_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - if (nargs < 1 || !njs_is_object(&args[0])) { - njs_type_error(vm, "\"this\" argument is not an object"); - return NJS_ERROR; - } - - return njs_error_to_string(vm, &vm->retval, &args[0]); -} - - -njs_int_t -njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error) +njs_error_to_string2(njs_vm_t *vm, njs_value_t *retval, + const njs_value_t *error, njs_bool_t want_stack) { size_t length; u_char *p; @@ -630,6 +739,17 @@ njs_error_to_string(njs_vm_t *vm, njs_va static const njs_value_t default_name = njs_string("Error"); + if (want_stack) { + ret = njs_error_stack(vm, njs_value_arg(error), retval); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (ret == NJS_OK) { + return NJS_OK; + } + } + njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH); ret = njs_object_property(vm, error, &lhq, &value1); @@ -708,6 +828,26 @@ njs_error_to_string(njs_vm_t *vm, njs_va } +static njs_int_t +njs_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + if (nargs < 1 || !njs_is_object(&args[0])) { + njs_type_error(vm, "\"this\" argument is not an object"); + return NJS_ERROR; + } + + return njs_error_to_string2(vm, &vm->retval, &args[0], 0); +} + + +njs_int_t +njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error) +{ + return njs_error_to_string2(vm, retval, error, 1); +} + + static const njs_object_prop_t njs_error_prototype_properties[] = { { diff -r 5bbbc4361799 -r e20023dd991d src/njs_error.h --- a/src/njs_error.h Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs_error.h Fri Nov 29 17:11:11 2019 +0300 @@ -44,6 +44,8 @@ njs_object_t *njs_error_alloc(njs_vm_t * const njs_value_t *name, const njs_value_t *message); njs_int_t njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error); +njs_int_t njs_error_stack(njs_vm_t *vm, njs_value_t *value, njs_value_t *stack); +njs_int_t njs_error_stack_attach(njs_vm_t *vm, njs_value_t *value); extern const njs_object_type_init_t njs_error_type_init; diff -r 5bbbc4361799 -r e20023dd991d src/njs_json.c --- a/src/njs_json.c Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs_json.c Fri Nov 29 17:11:11 2019 +0300 @@ -2144,10 +2144,6 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ njs_property_query_t pq; njs_json_stringify_t *stringify, dump_stringify; - if (njs_vm_backtrace(vm) != NULL) { - goto exception; - } - stringify = &dump_stringify; stringify->vm = vm; diff -r 5bbbc4361799 -r e20023dd991d src/njs_object_hash.h --- a/src/njs_object_hash.h Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs_object_hash.h Fri Nov 29 17:11:11 2019 +0300 @@ -411,6 +411,15 @@ 's'), 'e'), 't') +#define NJS_STACK_HASH \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add(NJS_DJB_HASH_INIT, \ + 's'), 't'), 'a'), 'c'), 'k') + + #define NJS_STRING_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ diff -r 5bbbc4361799 -r e20023dd991d src/njs_shell.c --- a/src/njs_shell.c Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs_shell.c Fri Nov 29 17:11:11 2019 +0300 @@ -722,19 +722,23 @@ njs_output(njs_opts_t *opts, njs_vm_t *v { njs_str_t out; - if (!opts->silent) { + if (opts->silent) { + return; + } + + if (ret == NJS_OK) { if (njs_vm_retval_dump(vm, &out, 1) != NJS_OK) { - out = njs_str_value("failed to get retval from VM"); - ret = NJS_ERROR; + njs_stderror("Shell:failed to get retval from VM\n"); + return; } - if (ret != NJS_OK) { - njs_stderror("%V\n", &out); + if (vm->options.accumulative) { + njs_printf("%V\n", &out); + } - } else if (vm->options.accumulative) { - njs_print(out.start, out.length); - njs_printf("\n"); - } + } else { + njs_vm_retval_string(vm, &out); + njs_stderror("Thrown:\n%V\n", &out); } } diff -r 5bbbc4361799 -r e20023dd991d src/njs_vm.c --- a/src/njs_vm.c Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs_vm.c Fri Nov 29 17:11:11 2019 +0300 @@ -153,10 +153,6 @@ njs_vm_compile(njs_vm_t *vm, u_char **st parser->lexer = &lexer; - if (vm->backtrace != NULL) { - njs_arr_reset(vm->backtrace); - } - njs_set_undefined(&vm->retval); ret = njs_parser(vm, parser, prev); @@ -471,10 +467,6 @@ njs_vm_post_event(njs_vm_t *vm, njs_vm_e njs_int_t njs_vm_run(njs_vm_t *vm) { - if (njs_slow_path(vm->backtrace != NULL)) { - njs_arr_reset(vm->backtrace); - } - return njs_vm_handle_events(vm); } @@ -648,120 +640,11 @@ njs_vm_memory_error(njs_vm_t *vm) } -njs_arr_t * -njs_vm_backtrace(njs_vm_t *vm) -{ - if (vm->backtrace != NULL && !njs_arr_is_empty(vm->backtrace)) { - return vm->backtrace; - } - - return NULL; -} - - -static njs_int_t -njs_vm_backtrace_dump(njs_vm_t *vm, njs_str_t *dst, const njs_value_t *src) +njs_int_t +njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src) { - u_char *p, *start, *end; - size_t len, count; - njs_uint_t i; - njs_arr_t *backtrace; - njs_backtrace_entry_t *be, *prev; - - backtrace = njs_vm_backtrace(vm); - - len = dst->length + 1; - - count = 0; - prev = NULL; - - be = backtrace->start; - - for (i = 0; i < backtrace->items; i++) { - if (i != 0 && prev->name.start == be->name.start - && prev->line == be->line) - { - count++; - - } else { - - if (count != 0) { - len += njs_length(" repeats times\n") - + NJS_INT_T_LEN; - count = 0; - } - - len += be->name.length + njs_length(" at ()\n"); - - if (be->line != 0) { - len += be->file.length + NJS_INT_T_LEN + 1; - - } else { - len += njs_length("native"); - } - } - - prev = be; - be++; - } - - p = njs_mp_alloc(vm->mem_pool, len); - if (p == NULL) { - njs_memory_error(vm); - return NJS_ERROR; - } - - start = p; - end = start + len; - - p = njs_cpymem(p, dst->start, dst->length); - *p++ = '\n'; - - count = 0; - prev = NULL; - - be = backtrace->start; - - for (i = 0; i < backtrace->items; i++) { - if (i != 0 && prev->name.start == be->name.start - && prev->line == be->line) - { - count++; - - } else { - if (count != 0) { - p = njs_sprintf(p, end, " repeats %uz times\n", - count); - count = 0; - } - - p = njs_sprintf(p, end, " at %V ", &be->name); - - if (be->line != 0) { - p = njs_sprintf(p, end, "(%V:%uD)\n", &be->file, - be->line); - - } else { - p = njs_sprintf(p, end, "(native)\n"); - } - } - - prev = be; - be++; - } - - dst->start = start; - dst->length = p - dst->start; - - return NJS_OK; -} - - -njs_int_t -njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, const njs_value_t *src) -{ - njs_int_t ret; - njs_uint_t exception; + njs_int_t ret; + njs_uint_t exception; if (njs_slow_path(src->type == NJS_NUMBER && njs_number(src) == 0 @@ -771,26 +654,17 @@ njs_vm_value_string(njs_vm_t *vm, njs_st return NJS_OK; } - exception = 1; + exception = 0; again: ret = njs_vm_value_to_string(vm, dst, src); - if (njs_fast_path(ret == NJS_OK)) { - - if (njs_vm_backtrace(vm) != NULL) { - ret = njs_vm_backtrace_dump(vm, dst, src); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } - } - return NJS_OK; } - if (exception) { - exception = 0; + if (!exception) { + exception = 1; /* value evaluation threw an exception. */ @@ -966,18 +840,18 @@ njs_vm_object_prop(njs_vm_t *vm, const n njs_int_t -njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst, const njs_value_t *src) +njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src) { u_char *start; size_t size; njs_int_t ret; - njs_value_t value; + njs_value_t value, stack; if (njs_slow_path(src == NULL)) { return NJS_ERROR; } - if (njs_slow_path(njs_is_error(src))) { + if (njs_is_error(src)) { /* MemoryError is a nonextensible internal error. */ @@ -987,6 +861,15 @@ njs_vm_value_to_string(njs_vm_t *vm, njs njs_string_get(&njs_string_memory_error, dst); return NJS_OK; } + + ret = njs_error_stack(vm, src, &stack); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (ret == NJS_OK) { + src = &stack; + } } value = *src; @@ -1020,7 +903,7 @@ njs_vm_value_to_string(njs_vm_t *vm, njs njs_int_t njs_vm_value_string_copy(njs_vm_t *vm, njs_str_t *retval, - const njs_value_t *value, uintptr_t *next) + njs_value_t *value, uintptr_t *next) { uintptr_t n; njs_array_t *array; @@ -1060,31 +943,19 @@ njs_vm_value_string_copy(njs_vm_t *vm, n njs_int_t -njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame) +njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack, + njs_native_frame_t *native_frame) { njs_int_t ret; - njs_arr_t *backtrace; njs_uint_t i; njs_function_t *function; - njs_native_frame_t *native_frame; njs_function_debug_t *debug_entry; njs_function_lambda_t *lambda; njs_backtrace_entry_t *be; - if (njs_slow_path(vm->backtrace == NULL)) { - backtrace = njs_arr_create(vm->mem_pool, 4, - sizeof(njs_backtrace_entry_t)); - if (njs_slow_path(backtrace == NULL)) { - return NJS_ERROR; - } - - vm->backtrace = backtrace; - } - - native_frame = &frame->native; function = native_frame->function; - be = njs_arr_add(vm->backtrace); + be = njs_arr_add(stack); if (njs_slow_path(be == NULL)) { return NJS_ERROR; } @@ -1143,6 +1014,107 @@ njs_vm_add_backtrace_entry(njs_vm_t *vm, } +njs_int_t +njs_vm_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst) +{ + u_char *p, *start, *end; + size_t len, count; + njs_uint_t i; + njs_backtrace_entry_t *be, *prev; + + if (backtrace->items == 0) { + return NJS_OK; + } + + len = dst->length + 1; + + count = 0; + prev = NULL; + + be = backtrace->start; + + for (i = 0; i < backtrace->items; i++) { + if (i != 0 && prev->name.start == be->name.start + && prev->line == be->line) + { + count++; + + } else { + + if (count != 0) { + len += njs_length(" repeats times\n") + + NJS_INT_T_LEN; + count = 0; + } + + len += be->name.length + njs_length(" at ()\n"); + + if (be->line != 0) { + len += be->file.length + NJS_INT_T_LEN + 1; + + } else { + len += njs_length("native"); + } + } + + prev = be; + be++; + } + + p = njs_mp_alloc(vm->mem_pool, len); + if (p == NULL) { + njs_memory_error(vm); + return NJS_ERROR; + } + + start = p; + end = start + len; + + p = njs_cpymem(p, dst->start, dst->length); + *p++ = '\n'; + + count = 0; + prev = NULL; + + be = backtrace->start; + + for (i = 0; i < backtrace->items; i++) { + if (i != 0 && prev->name.start == be->name.start + && prev->line == be->line) + { + count++; + + } else { + if (count != 0) { + p = njs_sprintf(p, end, " repeats %uz times\n", + count); + count = 0; + } + + p = njs_sprintf(p, end, " at %V ", &be->name); + + if (be->line != 0) { + p = njs_sprintf(p, end, "(%V:%uD)\n", &be->file, + be->line); + + } else { + p = njs_sprintf(p, end, "(native)\n"); + } + } + + prev = be; + be++; + } + + dst->start = start; + dst->length = p - dst->start; + + return NJS_OK; +} + + + + void * njs_lvlhsh_alloc(void *data, size_t size) { diff -r 5bbbc4361799 -r e20023dd991d src/njs_vm.h --- a/src/njs_vm.h Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs_vm.h Fri Nov 29 17:11:11 2019 +0300 @@ -224,7 +224,6 @@ struct njs_vm_s { njs_random_t random; njs_arr_t *debug; - njs_arr_t *backtrace; /* * njs_property_query() uses it to store reference to a temporary @@ -272,14 +271,16 @@ struct njs_vm_shared_s { void njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame, njs_native_frame_t *previous); -njs_int_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame); +njs_int_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack, + njs_native_frame_t *native_frame); +njs_int_t njs_vm_backtrace_to_string(njs_vm_t *vm, njs_arr_t *stack, + njs_str_t *dst); njs_int_t njs_builtin_objects_create(njs_vm_t *vm); njs_int_t njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global); njs_int_t njs_builtin_match_native_function(njs_vm_t *vm, njs_function_native_t func, njs_str_t *name); -njs_arr_t *njs_vm_backtrace(njs_vm_t *vm); njs_arr_t *njs_vm_completions(njs_vm_t *vm, njs_str_t *expression); void *njs_lvlhsh_alloc(void *data, size_t size); diff -r 5bbbc4361799 -r e20023dd991d src/njs_vmcode.c --- a/src/njs_vmcode.c Thu Nov 28 13:25:00 2019 +0300 +++ b/src/njs_vmcode.c Fri Nov 29 17:11:11 2019 +0300 @@ -898,6 +898,10 @@ next: error: + if (njs_is_error(&vm->retval)) { + (void) njs_error_stack_attach(vm, &vm->retval); + } + for ( ;; ) { frame = (njs_frame_t *) vm->top_frame; @@ -906,19 +910,9 @@ error: if (catch != NULL) { pc = catch; - if (vm->backtrace != NULL) { - njs_arr_reset(vm->backtrace); - } - goto next; } - if (vm->debug != NULL - && njs_vm_add_backtrace_entry(vm, frame) != NJS_OK) - { - break; - } - previous = frame->native.previous; if (previous == NULL) { break; diff -r 5bbbc4361799 -r e20023dd991d src/test/njs_interactive_test.c --- a/src/test/njs_interactive_test.c Thu Nov 28 13:25:00 2019 +0300 +++ b/src/test/njs_interactive_test.c Fri Nov 29 17:11:11 2019 +0300 @@ -243,10 +243,7 @@ static njs_interactive_test_t njs_test[ " at main (native)\n") }, { njs_str("function f(n) { if (n == 0) { throw 'a'; } return f(n-1); }; f(2)" ENTER), - njs_str("a\n" - " at f (:1)\n" - " repeats 2 times\n" - " at main (native)\n") }, + njs_str("a") }, /* Exception in njs_vm_retval_string() */ diff -r 5bbbc4361799 -r e20023dd991d test/njs_expect_test.exp --- a/test/njs_expect_test.exp Thu Nov 28 13:25:00 2019 +0300 +++ b/test/njs_expect_test.exp Fri Nov 29 17:11:11 2019 +0300 @@ -258,7 +258,7 @@ njs_test { njs_test { {"console.ll()\r\n" - "console.ll()\r\nTypeError: (intermediate value)\\\[\"ll\"] is not a function"} + "console.ll()\r\nThrown:\r\nTypeError: (intermediate value)\\\[\"ll\"] is not a function"} } njs_test { @@ -280,7 +280,7 @@ njs_test { # Backtraces for external objects njs_test { {"console.log(console.a.a)\r\n" - "console.log(console.a.a)\r\nTypeError:*at print (native)"} + "console.log(console.a.a)\r\nThrown:\r\nTypeError:*at print (native)"} } # dumper @@ -321,9 +321,9 @@ njs_test { # Backtraces are reset between invocations njs_test { {"JSON.parse(Error())\r\n" - "JSON.parse(Error())\r\nSyntaxError: Unexpected token at position 0*at JSON.parse (native)"} + "JSON.parse(Error())\r\nThrown:\r\nSyntaxError: Unexpected token at position 0*at JSON.parse (native)"} {"JSON.parse(Error()\r\n" - "JSON.parse(Error()\r\nSyntaxError: Unexpected token \"\" in shell:1"} + "JSON.parse(Error()\r\nThrown:\r\nSyntaxError: Unexpected token \"\" in shell:1"} } njs_test { @@ -335,7 +335,18 @@ njs_test { njs_test { {"(function() { throw 'test' })()\r\n" - "test\r\n at anonymous (shell:1)"} + "Thrown:\r\ntest"} +} + +njs_test { + {"function f() { return ({}.a.a); }\r\n" + "undefined"} + {"var e; try {f()} catch (ee) {e = ee}\r\n" + "undefined"} + {"Object.keys(null)\r\n" + "Thrown:\r\nTypeError: cannot convert null argument to object"} + {"e\r\n" + "TypeError: cannot get property \"a\" of undefined*at f (shell:1)"} } # Non-ASCII characters From ccouzens at gmail.com Fri Nov 29 21:27:46 2019 From: ccouzens at gmail.com (Chris Couzens) Date: Fri, 29 Nov 2019 21:27:46 +0000 Subject: [PATCH] Mime type for Web Assembly Message-ID: # HG changeset patch # User Chris Couzens # Date 1575032353 0 # Fri Nov 29 12:59:13 2019 +0000 # Node ID d826b28f25157901dff4a010cca180a0d13e7c35 # Parent d13eddd9e2529b4bc30dc00aad959bd10ced4c33 Mime.types: Add Web Assembly Web assembly is a web standard and so nginx should support hosting it by default. Not serving the correct mime type causes standard compliant browsers to reject it. https://www.w3.org/TR/wasm-web-api-1/#streaming-modules > If the Response is not CORS-same-origin, does not represent an ok > status, or does not match the `application/wasm` MIME type, the > returned promise will be rejected with a TypeError; diff -r d13eddd9e252 -r d826b28f2515 conf/mime.types --- a/conf/mime.types Tue Nov 19 17:18:58 2019 +0300 +++ b/conf/mime.types Fri Nov 29 12:59:13 2019 +0000 @@ -8,6 +8,7 @@ application/javascript js; application/atom+xml atom; application/rss+xml rss; + application/wasm wasm; text/mathml mml; text/plain txt;