diff --git a/external/njs_regex.c b/external/njs_regex.c index cd45afc05..3b4e33391 100644 --- a/external/njs_regex.c +++ b/external/njs_regex.c @@ -573,9 +573,10 @@ njs_regex_is_valid(njs_regex_t *regex) njs_int_t -njs_regex_named_captures(njs_regex_t *regex, njs_str_t *name, int n) +njs_regex_named_capture(njs_regex_t *regex, njs_str_t *name, int n, + uint32_t *capture) { - char *entry; + u_char *entry; if (name == NULL) { return regex->nentries; @@ -587,10 +588,12 @@ njs_regex_named_captures(njs_regex_t *regex, njs_str_t *name, int n) entry = regex->entries + regex->entry_size * n; - name->start = (u_char *) entry + 2; + name->start = entry + 2; name->length = njs_strlen(name->start); - return (entry[0] << 8) + entry[1]; + *capture = (entry[0] << 8) + entry[1]; + + return NJS_OK; } @@ -744,4 +747,3 @@ njs_pcre_free(void *p) #endif - diff --git a/src/njs_array.c b/src/njs_array.c index 5c30e833d..aea9c32b6 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -1961,7 +1961,7 @@ njs_array_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - njs_set_invalid(&value); + continue; } ret = njs_value_property_i64_set(vm, &this, length, @@ -2917,8 +2917,11 @@ static njs_int_t njs_array_prototype_sort(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { + double idx; int64_t i, nslots, nunds, length; + uint32_t n; njs_int_t ret; + njs_array_t *keys; njs_value_t *this, *comparefn; njs_function_t *compare; njs_array_sort_slot_t *slots; @@ -2982,11 +2985,35 @@ njs_array_prototype_sort(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } } - for (; i < length; i++) { - ret = njs_value_property_i64_delete(vm, this, i, NULL); - if (njs_slow_path(ret == NJS_ERROR)) { + if (njs_is_fast_array(this)) { + for (; i < length; i++) { + ret = njs_value_property_i64_delete(vm, this, i, NULL); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; + } + } + + } else { + keys = njs_array_indices(vm, this); + if (njs_slow_path(keys == NULL)) { goto exception; } + + for (n = 0; n < keys->length; n++) { + idx = njs_string_to_index(&keys->start[n]); + + if (idx >= i && idx < length) { + ret = njs_value_property_delete(vm, this, + keys->start[n].atom_id, + NULL, 1); + if (njs_slow_path(ret == NJS_ERROR)) { + njs_array_destroy(vm, keys); + goto exception; + } + } + } + + njs_array_destroy(vm, keys); } done: diff --git a/src/njs_object.c b/src/njs_object.c index fbaf2208c..65964070a 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -1140,7 +1140,7 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, } entry = NULL; - njs_set_object(&value, (njs_object_t *) object); + njs_set_type_object(&value, (njs_object_t *) object, object->type); for (i = 0; i< items_sorted->length; i++) { ret = njs_value_property_val(vm, &value, &items_sorted->start[i], diff --git a/src/njs_object.h b/src/njs_object.h index 814964aa9..319de1dbf 100644 --- a/src/njs_object.h +++ b/src/njs_object.h @@ -19,7 +19,6 @@ typedef enum { NJS_OBJECT_PROP_CONFIGURABLE = 16, NJS_OBJECT_PROP_WRITABLE = 32, NJS_OBJECT_PROP_UNSET = 64, - NJS_OBJECT_PROP_IS_STRING = 128, #define NJS_OBJECT_PROP_VALUE_ECW (NJS_OBJECT_PROP_VALUE \ | NJS_OBJECT_PROP_ENUMERABLE \ | NJS_OBJECT_PROP_CONFIGURABLE \ @@ -264,10 +263,6 @@ njs_object_prop_define_val(njs_vm_t *vm, njs_value_t *object, njs_value_t *name, } } - if (njs_is_string(name)) { - flags |= NJS_OBJECT_PROP_IS_STRING; - } - return njs_object_prop_define(vm, object, name->atom_id, value, flags); } diff --git a/src/njs_object_prop.c b/src/njs_object_prop.c index aeaeb71ba..6b3181d56 100644 --- a/src/njs_object_prop.c +++ b/src/njs_object_prop.c @@ -136,27 +136,17 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, unsigned atom_id, uint32_t length, index, set_enumerable, set_configurable, set_writable; njs_int_t ret; + njs_str_t string; njs_array_t *array; njs_value_t key, retval; njs_object_prop_t _prop; - njs_object_prop_t *prop = &_prop, *prev, *obj_prop; + njs_object_prop_t *prop, *prev, *obj_prop; njs_property_query_t pq; -again: - set_enumerable = 1; set_configurable = 1; set_writable = 1; - - njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1); - - ret = (flags & NJS_OBJECT_PROP_CREATE) - ? NJS_DECLINED - : njs_property_query(vm, &pq, object, atom_id); - - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } + prop = &_prop; switch (njs_prop_type(flags)) { case NJS_OBJECT_PROP_DESCRIPTOR: @@ -208,6 +198,18 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, unsigned atom_id, break; } +again: + + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1); + + ret = (flags & NJS_OBJECT_PROP_CREATE) + ? NJS_DECLINED + : njs_property_query(vm, &pq, object, atom_id); + + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + if (njs_fast_path(ret == NJS_DECLINED)) { set_prop: @@ -219,22 +221,38 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, unsigned atom_id, return NJS_ERROR; } - if (njs_slow_path(njs_is_typed_array(object) && - (flags & NJS_OBJECT_PROP_IS_STRING))) - { + if (njs_slow_path(njs_is_typed_array(object))) { /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */ + if (njs_atom_is_number(atom_id)) { + index = njs_atom_number(atom_id); + + if (index < njs_typed_array_length(njs_typed_array(object))) { + goto complete; + } + + goto invalid_index; + } + ret = njs_atom_to_value(vm, &key, atom_id); if (njs_slow_path(ret != NJS_OK)) { return ret; } - if (!isnan(njs_string_to_index(&key))) { - njs_type_error(vm, "Invalid typed array index"); - return NJS_ERROR; + if (njs_is_string(&key)) { + njs_string_get(vm, &key, &string); + + if (!isnan(njs_string_to_index(&key)) + || (string.length == 3 + && memcmp(string.start, "NaN", 3) == 0)) + { + goto invalid_index; + } } } +complete: + /* 6.2.5.6 CompletePropertyDescriptor */ if (njs_is_accessor_descriptor(prop)) { @@ -558,6 +576,12 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, unsigned atom_id, njs_atom_string_get(vm, atom_id, &pq.fhq.key); njs_type_error(vm, "Cannot redefine property: \"%V\"", &pq.fhq.key); + return NJS_ERROR; + +invalid_index: + + njs_type_error(vm, "Invalid typed array index"); + return NJS_ERROR; } diff --git a/src/njs_regex.h b/src/njs_regex.h index 5cf09cf28..0039ba1fb 100644 --- a/src/njs_regex.h +++ b/src/njs_regex.h @@ -32,7 +32,7 @@ typedef struct { int backrefmax; int nentries; int entry_size; - char *entries; + u_char *entries; } njs_regex_t; @@ -76,8 +76,8 @@ NJS_EXPORT njs_int_t njs_regex_compile(njs_regex_t *regex, u_char *source, size_t len, njs_regex_flags_t flags, njs_regex_compile_ctx_t *ctx, njs_trace_t *trace); NJS_EXPORT njs_bool_t njs_regex_is_valid(njs_regex_t *regex); -NJS_EXPORT njs_int_t njs_regex_named_captures(njs_regex_t *regex, - njs_str_t *name, int n); +NJS_EXPORT njs_int_t njs_regex_named_capture(njs_regex_t *regex, + njs_str_t *name, int n, uint32_t *capture); NJS_EXPORT njs_regex_match_data_t *njs_regex_match_data(njs_regex_t *regex, njs_regex_generic_ctx_t *ctx); NJS_EXPORT void njs_regex_match_data_free(njs_regex_match_data_t *match_data, diff --git a/src/njs_regexp.c b/src/njs_regexp.c index e902dc7b3..3506451cd 100644 --- a/src/njs_regexp.c +++ b/src/njs_regexp.c @@ -27,11 +27,17 @@ static u_char *njs_regexp_match_trace_handler(njs_trace_t *trace, njs_trace_data_t *td, u_char *start); #define NJS_REGEXP_FLAG_TEST 1 static njs_int_t njs_regexp_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s, - unsigned flags, njs_value_t *retval); + unsigned flags, njs_value_t *retval, njs_bool_t *builtin); static njs_array_t *njs_regexp_exec_result(njs_vm_t *vm, njs_value_t *r, njs_utf8_t utf8, njs_string_prop_t *string, njs_regex_match_data_t *data); +typedef struct { + njs_value_t value; + njs_bool_t builtin; +} njs_regexp_result_t; + + njs_int_t njs_regexp_init(njs_vm_t *vm) { @@ -377,7 +383,7 @@ njs_regexp_pattern_create(njs_vm_t *vm, u_char *start, size_t length, goto fail; } - pattern->ngroups = njs_regex_named_captures(regex, NULL, 0); + pattern->ngroups = njs_regex_named_capture(regex, NULL, 0, NULL); if (pattern->ngroups != 0) { size = sizeof(njs_regexp_group_t) * pattern->ngroups; @@ -393,7 +399,15 @@ njs_regexp_pattern_create(njs_vm_t *vm, u_char *start, size_t length, do { group = &pattern->groups[n]; - group->capture = njs_regex_named_captures(regex, &group->name, n); + ret = njs_regex_named_capture(regex, &group->name, n, + &group->capture); + if (njs_slow_path(ret != NJS_OK + || group->capture >= pattern->ncaptures)) + { + njs_internal_error(vm, "invalid named capture index"); + goto fail; + } + group->hash = njs_djb_hash(group->name.start, group->name.length); n++; @@ -830,7 +844,7 @@ njs_regexp_prototype_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - ret = njs_regexp_exec(vm, r, string, NJS_REGEXP_FLAG_TEST, &value); + ret = njs_regexp_exec(vm, r, string, NJS_REGEXP_FLAG_TEST, &value, NULL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -1206,11 +1220,17 @@ njs_regexp_prototype_exec(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, static njs_int_t njs_regexp_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s, unsigned flags, - njs_value_t *retval) + njs_value_t *retval, njs_bool_t *builtin) { - njs_int_t ret; - njs_value_t exec; - njs_value_t arguments[2]; + njs_int_t ret; + njs_bool_t builtin_exec; + njs_value_t exec; + njs_function_t *function; + njs_value_t arguments[2]; + + if (builtin != NULL) { + *builtin = 0; + } ret = njs_value_property(vm, r, NJS_ATOM_STRING_exec, &exec); if (njs_slow_path(ret == NJS_ERROR)) { @@ -1218,14 +1238,18 @@ njs_regexp_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s, unsigned flags, } if (njs_is_function(&exec)) { + function = njs_function(&exec); + builtin_exec = function->native + && function->u.native == njs_regexp_prototype_exec; + njs_value_assign(&arguments[0], s); if (flags) { njs_set_number(&arguments[1], flags); } - ret = njs_function_call(vm, njs_function(&exec), r, arguments, - flags ? 2 : 1, retval); + ret = njs_function_call(vm, function, r, arguments, flags ? 2 : 1, + retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } @@ -1247,6 +1271,10 @@ njs_regexp_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s, unsigned flags, return NJS_ERROR; } + if (builtin != NULL && builtin_exec && !njs_is_null(retval)) { + *builtin = 1; + } + return NJS_OK; } @@ -1255,7 +1283,13 @@ njs_regexp_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s, unsigned flags, return NJS_ERROR; } - return njs_regexp_builtin_exec(vm, r, s, flags, retval); + ret = njs_regexp_builtin_exec(vm, r, s, flags, retval); + + if (ret == NJS_OK && builtin != NULL && !njs_is_null(retval)) { + *builtin = 1; + } + + return ret; } @@ -1263,19 +1297,20 @@ njs_int_t njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { - int64_t n, last_index, ncaptures, pos, length, next, mlength; - const u_char *p, *start; - njs_str_t rep, m, head, tail; - njs_int_t ret; - njs_arr_t results; - njs_chb_t chain; - njs_uint_t i; - njs_bool_t global; - njs_array_t *array; - njs_value_t *arguments, *r, *rx, *string, *replace; - njs_value_t s_lvalue, r_lvalue, value, matched, groups; - njs_function_t *func_replace; - njs_string_prop_t s, ms; + int64_t n, last_index, ncaptures, pos, length, next, mlength; + const u_char *p, *start; + njs_str_t rep, m, head, tail; + njs_int_t ret; + njs_arr_t results; + njs_chb_t chain; + njs_uint_t i; + njs_bool_t global; + njs_array_t *array; + njs_value_t *arguments, *r, *rx, *string, *replace; + njs_value_t s_lvalue, r_lvalue, value, matched, groups; + njs_function_t *func_replace; + njs_regexp_result_t *result; + njs_string_prop_t s, ms; rx = njs_argument(args, 0); @@ -1326,19 +1361,22 @@ njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, results.separate = 0; results.pointer = 0; - r = njs_arr_init(vm->mem_pool, &results, NULL, 4, sizeof(njs_value_t)); - if (njs_slow_path(r == NULL)) { + result = njs_arr_init(vm->mem_pool, &results, NULL, 4, + sizeof(njs_regexp_result_t)); + if (njs_slow_path(result == NULL)) { return NJS_ERROR; } for ( ;; ) { - r = njs_arr_add(&results); - if (njs_slow_path(r == NULL)) { + result = njs_arr_add(&results); + if (njs_slow_path(result == NULL)) { ret = NJS_ERROR; goto exception; } - ret = njs_regexp_exec(vm, rx, string, 0, r); + r = &result->value; + + ret = njs_regexp_exec(vm, rx, string, 0, r, &result->builtin); if (njs_slow_path(ret != NJS_OK)) { goto exception; } @@ -1393,7 +1431,8 @@ njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, next = 0; while (i < results.items) { - r = njs_arr_item(&results, i++); + result = njs_arr_item(&results, i++); + r = &result->value; if (njs_slow_path(njs_is_null(r))) { break; @@ -1524,7 +1563,7 @@ njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, next = pos + mlength; } - if (!func_replace && njs_object_slots(r)) { + if (!func_replace && result->builtin) { /* * Doing free here ONLY for non-function replace, because * otherwise we cannot be certain the result of match @@ -1565,7 +1604,7 @@ njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args, ssize_t len; uint32_t limit; njs_int_t ret; - njs_bool_t sticky; + njs_bool_t builtin, sticky; njs_utf8_t utf8; njs_array_t *array; njs_value_t *rx, *string, *value; @@ -1664,7 +1703,8 @@ njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args, length = njs_string_prop(vm, &s, string); if (njs_slow_path(s.size == 0)) { - ret = njs_regexp_exec(vm, rx, string, NJS_REGEXP_FLAG_TEST, &z); + ret = njs_regexp_exec(vm, rx, string, NJS_REGEXP_FLAG_TEST, &z, + NULL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -1692,7 +1732,7 @@ njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args, return NJS_ERROR; } - ret = njs_regexp_exec(vm, rx, string, 0, &z); + ret = njs_regexp_exec(vm, rx, string, 0, &z, &builtin); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -1715,7 +1755,7 @@ njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args, e = njs_min(e, length); if (e == p) { - if (njs_object_slots(&z)) { + if (builtin) { njs_regexp_exec_result_free(vm, njs_array(&z)); } @@ -1740,7 +1780,7 @@ njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args, } if (array->length == limit) { - if (njs_object_slots(&z)) { + if (builtin) { njs_regexp_exec_result_free(vm, njs_array(&z)); } @@ -1768,7 +1808,7 @@ njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args, } if (array->length == limit) { - if (njs_object_slots(&z)) { + if (builtin) { njs_regexp_exec_result_free(vm, njs_array(&z)); } @@ -1776,7 +1816,7 @@ njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args, } } - if (njs_object_slots(&z)) { + if (builtin) { njs_regexp_exec_result_free(vm, njs_array(&z)); } diff --git a/src/njs_value.c b/src/njs_value.c index 2a7f7fd57..ae2f206aa 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -37,7 +37,7 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, { njs_int_t ret; njs_uint_t tries, force_ordinary; - njs_value_t method, retval, arguments[2]; + njs_value_t input, method, retval, arguments[2]; njs_flathsh_query_t fhq; static const uint32_t atoms[] = { @@ -56,6 +56,9 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, return NJS_OK; } + input = *value; + value = &input; + tries = 0; fhq.proto = &njs_object_hash_proto; @@ -1075,9 +1078,7 @@ njs_value_property(njs_vm_t *vm, njs_value_t *value, uint32_t atom_id, return NJS_OK; } - if (njs_slow_path(!(njs_is_object(value) - && njs_object(value)->fast_array))) - { + if (njs_slow_path(!njs_is_fast_array(value))) { goto slow_path; } @@ -1194,9 +1195,7 @@ njs_value_property_set(njs_vm_t *vm, njs_value_t *value, uint32_t atom_id, return NJS_OK; } - if (njs_slow_path(!(njs_is_object(value) - && njs_object(value)->fast_array))) - { + if (njs_slow_path(!njs_is_fast_array(value))) { goto slow_path; } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 4d104d949..3c01172d8 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -5193,6 +5193,15 @@ static njs_unit_test_t njs_test[] = "njs.dump(a2)"), njs_str("[3,1,<1111110 empty items>,2,4]") }, + { njs_str("var a = []; a[2**32 - 7] = 2; a[2**32 - 2] = 1;" + "a.sort(); [a.length, a[0], a[1], Object.keys(a)]"), + njs_str("4294967295,1,2,0,1") }, + + { njs_str("var a = []; a[100000] = 1;" + "var b = [].concat(a, [,, 5]);" + "[b.length, Object.keys(b), b[100001], b.sort().length]"), + njs_str("100004,100000,100003,,100004") }, + { njs_str("var re = /abc/; re[Symbol.isConcatSpreadable] = true;" "re[0] = 1, re[1] = 2, re[2] = 3, re.length = 3;" "[].concat(re)"), @@ -6996,6 +7005,35 @@ static njs_unit_test_t njs_test[] = " try {a.set(init,Infinity)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, + { njs_str("var a = new Uint8Array([1]);" + "Object.defineProperty(a, 'x', {enumerable: true," + " get() { return this === a && this[0]; }});" + "Object.values(a)"), + njs_str("1,1") }, + + { njs_str("var a = new Uint8Array([1]);" + "Object.defineProperty(a, 'x', {enumerable: true," + " get() { this[0] = 2; return this[0]; }});" + "Object.values(a); a[0]"), + njs_str("2") }, + + { njs_str("[257, '257', NaN, 'NaN', Infinity, 'Infinity', -0, '-0']" + ".every(k => { try {" + " Object.defineProperty(new Uint8Array(), k, {value: 1});" + "} catch (e) { return e.name == 'TypeError'; } })"), + njs_str("true") }, + + { njs_str("['01', ' 1', '0x10', '1e3'].every(k => {" + "var a = new Uint8Array();" + "Object.defineProperty(a, k, {value: 1}); return a[k] == 1; })"), + njs_str("true") }, + + { njs_str("var a = Buffer.from([1]);" + "Object.defineProperty(a, 'x', {enumerable: true," + " get() { return this === a && this[0]; }});" + "Object.values(a)"), + njs_str("1,1") }, + { njs_str(NJS_INT_TYPED_ARRAY_LIST ".map(v=>{try { var a = new v(1); $262.detachArrayBuffer(a.buffer); Object.entries(a)} " "catch (e) {return e.name}}).every(v=>v === 'TypeError')"), @@ -10060,6 +10098,22 @@ static njs_unit_test_t njs_test[] = "r[Symbol.replace]('ABCD', 'b')"), njs_str("b") }, + { njs_str("var r = /./; r.exec = () => globalThis;" + "r[Symbol.replace]('', '') === ''"), + njs_str("true") }, + + { njs_str("var exec = RegExp.prototype.exec, saved;" + "var r = /a/; r.exec = function(s) {" + " saved = exec.call(this, s); return saved; };" + "r[Symbol.replace]('a', 'b'); saved[0]"), + njs_str("a") }, + + { njs_str("var exec = RegExp.prototype.exec, saved;" + "RegExp.prototype.exec = function(s) {" + " saved = exec.call(this, s); return saved; };" + "'a'.split(/a/); saved[0]"), + njs_str("a") }, + { njs_str("'α'.replace(/(h*)/g, '$1βγ')"), njs_str("βγαβγ") }, @@ -10149,6 +10203,14 @@ static njs_unit_test_t njs_test[] = { njs_str("'ABC'.replace(/(?B)/, '|$@')"), njs_str("A|@C") }, + { njs_str("var s = '(?:' + '()'.repeat(127) + '){0}(?a)';" + "new RegExp(s).exec('a').groups.x"), + njs_str("a") }, + + { njs_str("var s = '(?:' + '()'.repeat(383) + '){0}(?a)';" + "new RegExp(s).exec('a').groups.x"), + njs_str("a") }, + { njs_str("'ABCB'.replaceAll(/(?B)/g, '|$@')"), njs_str("A|@C|@") }, @@ -14309,6 +14371,15 @@ static njs_unit_test_t njs_test[] = { njs_str("false.__proto__ === Boolean.prototype"), njs_str("true") }, + { njs_str("var o = {valueOf() { o = 0; return {}; }," + " toString() { return '7'; }}; +o"), + njs_str("7") }, + + { njs_str("var original = {toString() { return '7'; }}, o = original;" + "Object.defineProperty(original, 'valueOf', {get() {" + " o = 0; return () => ({}); }}); +o"), + njs_str("7") }, + { njs_str("var b = Boolean(1); b.__proto__ === Boolean.prototype"), njs_str("true") }, @@ -15963,6 +16034,18 @@ static njs_unit_test_t njs_test[] = "njs.dump(arr)"), njs_str("[1,'[Getter]']") }, + { njs_str("var a = [1], n = 0;" + "Object.defineProperty(a, 0, {" + " get get() { n++; a.unshift(0); return function() {}; }});" + "n"), + njs_str("1") }, + + { njs_str("var a = [1], n = 0;" + "Object.defineProperty(a, 'length', {" + " get writable() { n++; return false; }});" + "n"), + njs_str("1") }, + { njs_str("Object.defineProperties()"), njs_str("TypeError: Object.defineProperties is called on non-object") },