This closes #308 issue on Github.
}
+static njs_int_t
+njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused)
+{
+ u_char *p;
+ int64_t n, last_index, ncaptures, pos, next_pos, size, length;
+ njs_str_t rep, m;
+ 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, retval;
+ njs_function_t *func_replace;
+ njs_string_prop_t s;
+
+ static const njs_value_t string_global = njs_string("global");
+ static const njs_value_t string_groups = njs_string("groups");
+ static const njs_value_t string_index = njs_string("index");
+ static const njs_value_t string_lindex = njs_string("lastIndex");
+
+ rx = njs_argument(args, 0);
+
+ if (njs_slow_path(!njs_is_object(rx))) {
+ njs_type_error(vm, "\"this\" is not object");
+ return NJS_ERROR;
+ }
+
+ string = njs_lvalue_arg(&s_lvalue, args, nargs, 1);
+
+ ret = njs_value_to_string(vm, string, string);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ length = njs_string_prop(&s, string);
+
+ rep.start = NULL;
+ rep.length = 0;
+
+ replace = njs_lvalue_arg(&r_lvalue, args, nargs, 2);
+ func_replace = njs_is_function(replace) ? njs_function(replace) : NULL;
+
+ if (!func_replace) {
+ ret = njs_value_to_string(vm, replace, replace);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
+ ret = njs_value_property(vm, rx, njs_value_arg(&string_global), &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ global = njs_bool(&value);
+
+ if (global) {
+ njs_set_number(&value, 0);
+ ret = njs_value_property_set(vm, rx, njs_value_arg(&string_lindex),
+ &value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+ }
+
+ njs_chb_init(&chain, vm->mem_pool);
+
+ 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)) {
+ return NJS_ERROR;
+ }
+
+ for ( ;; ) {
+ r = njs_arr_add(&results);
+ if (njs_slow_path(r == NULL)) {
+ ret = NJS_ERROR;
+ goto exception;
+ }
+
+ ret = njs_regexp_exec(vm, rx, string, r);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+
+ if (njs_is_null(r) || !global) {
+ break;
+ }
+
+ if (njs_fast_path(njs_is_fast_array(r) && njs_array_len(r) != 0)) {
+ value = njs_array_start(r)[0];
+
+ } else {
+ ret = njs_value_property_i64(vm, r, 0, &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+ }
+
+ ret = njs_value_to_string(vm, &value, &value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+
+ if (njs_string_length(&value) != 0) {
+ continue;
+ }
+
+ ret = njs_value_property(vm, rx, njs_value_arg(&string_lindex), &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+
+ ret = njs_value_to_length(vm, &value, &last_index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+
+ njs_set_number(&value, last_index + 1);
+ ret = njs_value_property_set(vm, rx, njs_value_arg(&string_lindex),
+ &value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+ }
+
+ i = 0;
+ next_pos = 0;
+
+ while (i < results.items) {
+ r = njs_arr_item(&results, i++);
+
+ if (njs_slow_path(njs_is_null(r))) {
+ break;
+ }
+
+ ret = njs_value_property_i64(vm, r, 0, &matched);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+
+ ret = njs_value_to_string(vm, &matched, &matched);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+
+ ret = njs_value_property(vm, r, njs_value_arg(&string_index), &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+
+ ret = njs_value_to_integer(vm, &value, &pos);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+
+ if ((size_t) length != s.size) {
+ /* UTF-8 string. */
+ pos = njs_string_offset(s.start, s.start + s.size, pos) - s.start;
+ }
+
+ pos = njs_max(njs_min(pos, (int64_t) s.size), 0);
+
+ if (njs_fast_path(njs_is_fast_array(r))) {
+ array = njs_array(r);
+
+ arguments = array->start;
+ ncaptures = array->length;
+
+ for (n = 1; n < ncaptures; n++) {
+ if (njs_is_undefined(&arguments[n])) {
+ continue;
+ }
+
+ ret = njs_value_to_string(vm, &arguments[n], &arguments[n]);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+ }
+
+ } else {
+ ret = njs_object_length(vm, r, &ncaptures);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+
+ array = njs_array_alloc(vm, 0, ncaptures, 0);
+ if (njs_slow_path(array == NULL)) {
+ goto exception;
+ }
+
+ arguments = array->start;
+ arguments[0] = matched;
+
+ for (n = 1; n < ncaptures; n++) {
+ ret = njs_value_property_i64(vm, r, n, &arguments[n]);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+
+ if (njs_is_undefined(&arguments[n])) {
+ continue;
+ }
+
+ ret = njs_value_to_string(vm, &arguments[n], &arguments[n]);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+ }
+ }
+
+ ret = njs_value_property(vm, r, njs_value_arg(&string_groups), &groups);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+
+ if (!func_replace) {
+ if (njs_is_defined(&groups)) {
+ ret = njs_value_to_object(vm, &groups);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
+ ret = njs_string_get_substitution(vm, &matched, string, pos,
+ arguments, ncaptures - 1, &groups,
+ replace, &retval);
+
+ } else {
+ ret = njs_array_expand(vm, array, 0,
+ njs_is_defined(&groups) ? 3 : 2);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+
+ arguments = array->start;
+ njs_set_number(&arguments[n++], pos);
+ arguments[n++] = *string;
+
+ if (njs_is_defined(&groups)) {
+ arguments[n++] = groups;
+ }
+
+ ret = njs_function_call(vm, func_replace,
+ njs_value_arg(&njs_value_undefined),
+ arguments, n, &retval);
+ }
+
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_to_string(vm, &retval, &retval);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto exception;
+ }
+
+ if (pos >= next_pos) {
+ njs_chb_append(&chain, &s.start[next_pos], pos - next_pos);
+
+ njs_string_get(&retval, &rep);
+ njs_chb_append_str(&chain, &rep);
+
+ njs_string_get(&matched, &m);
+
+ next_pos = pos + (int64_t) m.length;
+ }
+ }
+
+ if (next_pos < (int64_t) s.size) {
+ njs_chb_append(&chain, &s.start[next_pos], s.size - next_pos);
+ }
+
+ size = njs_chb_size(&chain);
+ if (njs_slow_path(size < 0)) {
+ njs_memory_error(vm);
+ ret = NJS_ERROR;
+ goto exception;
+ }
+
+ length = njs_chb_utf8_length(&chain);
+
+ p = njs_string_alloc(vm, &vm->retval, size, length);
+ if (njs_slow_path(p == NULL)) {
+ ret = NJS_ERROR;
+ goto exception;
+ }
+
+ njs_chb_join_to(&chain, p);
+
+ ret = NJS_OK;
+
+exception:
+
+ njs_chb_destroy(&chain);
+ njs_arr_destroy(&results);
+
+ return ret;
+}
+
+
+
+
static const njs_object_prop_t njs_regexp_constructor_properties[] =
{
{
.writable = 1,
.configurable = 1,
},
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_REPLACE),
+ .value = njs_native_function(njs_regexp_prototype_symbol_replace, 2),
+ .writable = 1,
+ .configurable = 1,
+ },
};
#define NJS_TRIM_END 2
-typedef struct {
- u_char *start;
- size_t size;
- njs_value_t value;
-} njs_string_replace_part_t;
-
-
-#define NJS_SUBST_COPY 255
-#define NJS_SUBST_PRECEDING 254
-#define NJS_SUBST_FOLLOWING 253
-
-
-typedef struct {
- uint32_t type;
- uint32_t size;
- u_char *start;
-} njs_string_subst_t;
-
-
-typedef struct {
- njs_value_t retval;
-
- njs_arr_t parts;
- njs_string_replace_part_t array[3];
- njs_string_replace_part_t *part;
-
- njs_arr_t *substitutions;
- njs_function_t *function;
-
- njs_regex_match_data_t *match_data;
-
- njs_bool_t empty;
-
- njs_utf8_t utf8:8;
- njs_regexp_utf8_t type:8;
-} njs_string_replace_t;
-
-
static void njs_encode_base64_core(njs_str_t *dst, const njs_str_t *src,
const u_char *basis, njs_uint_t padding);
static njs_int_t njs_decode_base64_core(njs_vm_t *vm,
njs_regexp_pattern_t *pattern);
static njs_int_t njs_string_split_part_add(njs_vm_t *vm, njs_array_t *array,
njs_utf8_t utf8, const u_char *start, size_t size);
-static njs_int_t njs_string_replace_regexp(njs_vm_t *vm, njs_value_t *this,
- njs_value_t *regex, njs_string_replace_t *r);
-static njs_int_t njs_string_replace_regexp_function(njs_vm_t *vm,
- njs_value_t *this, njs_value_t *regex, njs_string_replace_t *r,
- int *captures, njs_uint_t n);
-static njs_int_t njs_string_replace_regexp_join(njs_vm_t *vm,
- njs_string_replace_t *r);
-static njs_int_t njs_string_replace_search(njs_vm_t *vm, njs_value_t *this,
- njs_value_t *search, njs_string_replace_t *r);
-static njs_int_t njs_string_replace_search_function(njs_vm_t *vm,
- njs_value_t *this, njs_value_t *search, njs_string_replace_t *r);
-static njs_int_t njs_string_replace_parse(njs_vm_t *vm,
- njs_string_replace_t *r, u_char *p, u_char *end, size_t size,
- njs_uint_t ncaptures);
-static njs_int_t njs_string_replace_substitute(njs_vm_t *vm,
- njs_string_replace_t *r, int *captures);
-static njs_int_t njs_string_replace_join(njs_vm_t *vm, njs_string_replace_t *r);
-static void njs_string_replacement_copy(njs_string_replace_part_t *string,
- const njs_value_t *value);
#define njs_base64_encoded_length(len) (((len + 2) / 3) * 4)
}
-static njs_int_t
-njs_string_prototype_replace(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused)
+njs_int_t
+njs_string_get_substitution(njs_vm_t *vm, njs_value_t *matched,
+ njs_value_t *string, int64_t pos, njs_value_t *captures, int64_t ncaptures,
+ njs_value_t *groups, njs_value_t *replacement, njs_value_t *retval)
{
- u_char *p, *start, *end;
- njs_int_t ret;
- njs_uint_t ncaptures;
- njs_value_t *this, *search, *replace;
- njs_value_t search_lvalue, replace_lvalue;
- njs_regex_t *regex;
- njs_string_prop_t string;
- njs_string_replace_t *r, string_replace;
-
- ret = njs_string_object_validate(vm, njs_arg(args, nargs, 0));
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- this = njs_argument(args, 0);
-
- if (nargs == 1) {
- goto original;
- }
-
- search = njs_lvalue_arg(&search_lvalue, args, nargs, 1);
- replace = njs_lvalue_arg(&replace_lvalue, args, nargs, 2);
-
- (void) njs_string_prop(&string, this);
-
- if (string.size == 0) {
- goto original;
- }
-
- r = &string_replace;
-
- r->utf8 = NJS_STRING_BYTE;
- r->type = NJS_REGEXP_BYTE;
-
- if (string.length != 0) {
- r->utf8 = NJS_STRING_ASCII;
- r->type = NJS_REGEXP_UTF8;
-
- if (string.length != string.size) {
- r->utf8 = NJS_STRING_UTF8;
- }
- }
-
- if (njs_is_regexp(search)) {
- regex = &njs_regexp_pattern(search)->regex[r->type];
-
- if (!njs_regex_is_valid(regex)) {
- goto original;
- }
+ int64_t tail, size, length, n;
+ u_char c, c2, *p, *r, *end;
+ njs_str_t rep, m, str, cap;
+ njs_int_t ret;
+ njs_chb_t chain;
+ njs_value_t name, value;
- ncaptures = njs_regex_ncaptures(regex);
+ njs_string_get(replacement, &rep);
+ p = rep.start;
+ end = rep.start + rep.length;
- } else {
- regex = NULL;
- ncaptures = 1;
+ njs_chb_init(&chain, vm->mem_pool);
- if (!njs_is_string(search)) {
- ret = njs_value_to_string(vm, search, search);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ while (p < end) {
+ r = njs_strlchr(p, end, '$');
+ if (r == NULL || r == &end[-1]) {
+ if (njs_fast_path(p == rep.start)) {
+ *retval = *replacement;
+ return NJS_OK;
}
- }
- }
-
- /* This cannot fail. */
- r->part = njs_arr_init(vm->mem_pool, &r->parts, &r->array,
- 3, sizeof(njs_string_replace_part_t));
-
- r->substitutions = NULL;
- r->function = NULL;
-
- /* A literal replacement is stored in the second part. */
- if (nargs == 2) {
- njs_string_replacement_copy(&r->part[1], &njs_string_undefined);
-
- } else if (njs_is_function(replace)) {
- r->function = njs_function(replace);
-
- } else {
- if (njs_slow_path(!njs_is_string(replace))) {
- ret = njs_value_to_string(vm, replace, replace);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
+ njs_chb_append(&chain, p, end - p);
+ goto done;
}
- njs_string_replacement_copy(&r->part[1], replace);
+ njs_chb_append(&chain, p, r - p);
+ p = r;
- start = r->part[1].start;
+ c = r[1];
- if (start == NULL) {
- start = r->part[1].value.short_string.start;
- }
+ switch (c) {
+ case '$':
+ njs_chb_append_literal(&chain, "$");
+ p += 2;
+ break;
- end = start + r->part[1].size;
+ case '&':
+ njs_string_get(matched, &m);
+ njs_chb_append_str(&chain, &m);
+ p += 2;
+ break;
- for (p = start; p < end; p++) {
- if (*p == '$') {
- ret = njs_string_replace_parse(vm, r, p, end, p - start,
- ncaptures);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
+ case '`':
+ njs_string_get(string, &str);
+ njs_chb_append(&chain, str.start, pos);
+ p += 2;
+ break;
+
+ case '\'':
+ njs_string_get(matched, &m);
+ tail = pos + m.length;
- /* Reset parts array to the subject string only. */
- r->parts.items = 1;
+ njs_string_get(string, &str);
+ njs_chb_append(&chain, &str.start[tail],
+ njs_max((int64_t) str.length - tail, 0));
+ p += 2;
+ break;
+ case '<':
+ r = njs_strlchr(p, end, '>');
+ if (r == NULL) {
+ njs_chb_append(&chain, p, 2);
+ p += 2;
break;
}
- }
- }
- r->part[0].start = string.start;
- r->part[0].size = string.size;
- njs_set_invalid(&r->part[0].value);
+ p += 2;
- if (regex != NULL) {
- r->match_data = njs_regex_match_data(regex, vm->regex_context);
- if (njs_slow_path(r->match_data == NULL)) {
- return NJS_ERROR;
- }
-
- return njs_string_replace_regexp(vm, this, search, r);
- }
-
- return njs_string_replace_search(vm, this, search, r);
-
-original:
-
- njs_string_copy(&vm->retval, this);
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_string_replace_regexp(njs_vm_t *vm, njs_value_t *this, njs_value_t *regex,
- njs_string_replace_t *r)
-{
- int *captures;
- u_char *p, *start;
- njs_int_t ret;
- const u_char *end;
- njs_regexp_pattern_t *pattern;
- njs_string_replace_part_t replace;
-
- pattern = njs_regexp_pattern(regex);
- end = r->part[0].start + r->part[0].size;
-
- replace = r->part[1];
-
- do {
- ret = njs_regexp_match(vm, &pattern->regex[r->type],
- r->part[0].start, 0, r->part[0].size,
- r->match_data);
-
- if (ret < 0) {
- if (njs_slow_path(ret != NJS_REGEX_NOMATCH)) {
- return NJS_ERROR;
+ if (groups == NULL) {
+ break;
}
- break;
- }
-
- captures = njs_regex_captures(r->match_data);
-
- if (r->substitutions != NULL) {
- ret = njs_string_replace_substitute(vm, r, captures);
+ ret = njs_vm_value_string_set(vm, &name, p, r - p);
if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ goto exception;
}
- if (!pattern->global) {
- return njs_string_replace_regexp_join(vm, r);
- }
-
- continue;
- }
-
- if (r->part != r->parts.start) {
- r->part = njs_arr_add(&r->parts);
- if (njs_slow_path(r->part == NULL)) {
- return NJS_ERROR;
- }
+ p = r + 1;
- r->part = njs_arr_add(&r->parts);
- if (njs_slow_path(r->part == NULL)) {
- return NJS_ERROR;
+ ret = njs_value_property(vm, groups, &name, &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
}
- r->part -= 2;
- }
-
- if (captures[1] == 0) {
-
- /* Empty match. */
+ if (njs_is_defined(&value)) {
+ ret = njs_value_to_string(vm, &value, &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
- start = r->part[0].start;
+ njs_string_get(&value, &str);
+ njs_chb_append_str(&chain, &str);
+ }
- if (start < end) {
- p = (r->utf8 != NJS_STRING_BYTE)
- ? (u_char *) njs_utf8_next(start, end) : start + 1;
+ break;
- r->part[1].start = start;
- r->part[1].size = p - start;
+ default:
+ if (c >= '0' && c <= '9') {
+ n = c - '0';
- r->part[2].start = p;
- r->part[2].size = end - p;
+ c2 = (&r[2] < end) ? r[2] : 0;
- } else {
- r->part[1].size = 0;
- r->part[2].size = 0;
+ if (c2 >= '0' && c2 <= '9'
+ && (n * 10 + (c2 - '0')) <= ncaptures)
+ {
+ n = n * 10 + (c2 - '0');
- /* To exit the loop. */
- r->part[2].start = start + 1;
- }
+ } else {
+ c2 = 0;
+ }
- if (r->function != NULL) {
- return njs_string_replace_regexp_function(vm, this, regex, r,
- captures, ret);
- }
+ if (n == 0 || n > ncaptures) {
+ njs_chb_append(&chain, p, (c2 != 0) ? 3 : 2);
+ p += (c2 != 0) ? 3 : 2;
+ break;
+ }
- r->part[0] = replace;
+ p += (c2 != 0) ? 3 : 2;
- } else {
- r->part[2].start = r->part[0].start + captures[1];
- r->part[2].size = r->part[0].size - captures[1];
- njs_set_invalid(&r->part[2].value);
+ if (njs_is_defined(&captures[n])) {
+ njs_string_get(&captures[n], &cap);
+ njs_chb_append_str(&chain, &cap);
+ }
- if (r->function != NULL) {
- return njs_string_replace_regexp_function(vm, this, regex, r,
- captures, ret);
+ break;
}
- r->part[0].size = captures[0];
-
- r->part[1] = replace;
- }
-
- if (!pattern->global) {
- return njs_string_replace_regexp_join(vm, r);
+ njs_chb_append_literal(&chain, "$");
+ p += 1;
+ break;
}
-
- r->part += 2;
-
- } while (r->part[0].start <= end);
-
- if (r->part != r->parts.start) {
- return njs_string_replace_regexp_join(vm, r);
}
- njs_regex_match_data_free(r->match_data, vm->regex_context);
-
- njs_arr_destroy(&r->parts);
-
- njs_string_copy(&vm->retval, this);
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_string_replace_regexp_function(njs_vm_t *vm, njs_value_t *this,
- njs_value_t *regex, njs_string_replace_t *r, int *captures, njs_uint_t n)
-{
- u_char *start;
- size_t size, length;
- njs_int_t ret;
- njs_uint_t i, k;
- njs_value_t *arguments;
- njs_string_prop_t string;
-
- if (njs_slow_path((n + 3) >= UINT32_MAX / sizeof(njs_value_t))) {
- njs_memory_error(vm);
- return NJS_ERROR;
- }
-
- njs_set_invalid(&r->retval);
+done:
- arguments = njs_mp_alloc(vm->mem_pool, (n + 3) * sizeof(njs_value_t));
- if (njs_slow_path(arguments == NULL)) {
+ size = njs_chb_size(&chain);
+ if (njs_slow_path(size < 0)) {
njs_memory_error(vm);
- return NJS_ERROR;
- }
-
- njs_set_undefined(&arguments[0]);
-
- /* Matched substring and parenthesized submatch strings. */
- for (k = 0, i = 1; i <= n; i++) {
-
- start = r->part[0].start + captures[k];
- size = captures[k + 1] - captures[k];
- k += 2;
-
- length = njs_string_calc_length(r->utf8, start, size);
-
- ret = njs_string_new(vm, &arguments[i], start, size, length);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
- }
-
- r->empty = (captures[0] == captures[1]);
-
- /* The offset of the matched substring. */
- njs_set_number(&arguments[n + 1], captures[0]);
-
- /* The whole string being examined. */
- length = njs_string_calc_length(r->utf8, r->part[0].start, r->part[0].size);
-
- (void) njs_string_prop(&string, this);
-
- ret = njs_string_new(vm, &arguments[n + 2], string.start, string.size,
- length);
-
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
+ ret = NJS_ERROR;
+ goto exception;
}
- r->part[0].size = captures[0];
+ length = njs_chb_utf8_length(&chain);
- ret = njs_function_apply(vm, r->function, arguments, n + 3, &r->retval);
- if (njs_slow_path(ret != NJS_OK)) {
+ p = njs_string_alloc(vm, retval, size, length);
+ if (njs_slow_path(p == NULL)) {
+ ret = NJS_ERROR;
goto exception;
}
- if (njs_slow_path(!njs_is_string(&r->retval))) {
- ret = njs_value_to_string(vm, &r->retval, &r->retval);
- if (njs_slow_path(ret != NJS_OK)) {
- goto exception;
- }
- }
-
- njs_string_replacement_copy(&r->part[r->empty ? 0 : 1], &r->retval);
-
- if (njs_regexp_pattern(regex)->global) {
- r->part += 2;
+ njs_chb_join_to(&chain, p);
- if (r->part[0].start > (string.start + string.size)) {
- return njs_string_replace_regexp_join(vm, r);
- }
-
- return njs_string_replace_regexp(vm, this, regex, r);
- }
-
- return njs_string_replace_regexp_join(vm, r);
+ ret = NJS_OK;
exception:
- njs_regex_match_data_free(r->match_data, vm->regex_context);
-
- return NJS_ERROR;
-}
-
-
-static njs_int_t
-njs_string_replace_regexp_join(njs_vm_t *vm, njs_string_replace_t *r)
-{
- njs_regex_match_data_free(r->match_data, vm->regex_context);
+ njs_chb_destroy(&chain);
- return njs_string_replace_join(vm, r);
+ return NJS_OK;
}
static njs_int_t
-njs_string_replace_search(njs_vm_t *vm, njs_value_t *this, njs_value_t *search,
- njs_string_replace_t *r)
+njs_string_prototype_replace(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
{
- int captures[2];
- u_char *p, *end;
- size_t size;
- njs_int_t ret;
- njs_str_t string;
-
- njs_string_get(search, &string);
-
- p = r->part[0].start;
- end = (p + r->part[0].size) - (string.length - 1);
+ u_char *r;
+ size_t length, search_length, ret_length, size;
+ int64_t pos;
+ njs_int_t ret;
+ njs_value_t *this, *search, *replace;
+ njs_value_t search_lvalue, replace_lvalue, replacer, retval,
+ arguments[3];
+ const u_char *p;
+ njs_function_t *func_replace;
+ njs_string_prop_t string, s, ret_string;
- while (p < end) {
- if (memcmp(p, string.start, string.length) == 0) {
+ static const njs_value_t replace_key =
+ njs_wellknown_symbol(NJS_SYMBOL_REPLACE);
- if (r->substitutions != NULL) {
- captures[0] = p - r->part[0].start;
- captures[1] = captures[0] + string.length;
+ this = njs_argument(args, 0);
- ret = njs_string_replace_substitute(vm, r, captures);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
+ if (njs_slow_path(njs_is_null_or_undefined(this))) {
+ njs_type_error(vm, "cannot convert \"%s\"to object",
+ njs_type_string(this->type));
+ return NJS_ERROR;
+ }
- } else {
- r->part[2].start = p + string.length;
- size = p - r->part[0].start;
- r->part[2].size = r->part[0].size - size - string.length;
- r->part[0].size = size;
- njs_set_invalid(&r->part[2].value);
-
- if (r->function != NULL) {
- return njs_string_replace_search_function(vm, this, search,
- r);
- }
- }
+ search = njs_lvalue_arg(&search_lvalue, args, nargs, 1);
+ replace = njs_lvalue_arg(&replace_lvalue, args, nargs, 2);
- return njs_string_replace_join(vm, r);
+ if (!njs_is_null_or_undefined(search)) {
+ ret = njs_value_method(vm, search, njs_value_arg(&replace_key),
+ &replacer);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
- if (r->utf8 < 2) {
- p++;
+ if (njs_is_defined(&replacer)) {
+ arguments[0] = *this;
+ arguments[1] = *replace;
- } else {
- p = (u_char *) njs_utf8_next(p, end);
+ return njs_function_call(vm, njs_function(&replacer), search,
+ arguments, 2, &vm->retval);
}
}
- njs_string_copy(&vm->retval, this);
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_string_replace_search_function(njs_vm_t *vm, njs_value_t *this,
- njs_value_t *search, njs_string_replace_t *r)
-{
- njs_int_t ret;
- njs_value_t string;
- njs_value_t arguments[4];
-
- njs_set_undefined(&arguments[0]);
-
- /* GC, args[0], args[1] */
-
- /* Matched substring, it is the same as the args[1]. */
- arguments[1] = *search;
-
- /* The offset of the matched substring. */
- njs_set_number(&arguments[2], r->part[0].size);
-
- /* The whole string being examined. */
- arguments[3] = *this;
-
- ret = njs_function_apply(vm, r->function, arguments, 4, &r->retval);
-
+ ret = njs_value_to_string(vm, this, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- if (!njs_is_primitive(&r->retval)) {
- ret = njs_value_to_string(vm, &r->retval, &r->retval);
- if (ret != NJS_OK) {
- return ret;
- }
- }
-
- ret = njs_primitive_value_to_string(vm, &string, &r->retval);
+ ret = njs_value_to_string(vm, search, search);
if (njs_slow_path(ret != NJS_OK)) {
- njs_type_error(vm, "cannot convert primitive value to string: %s",
- njs_type_string(r->retval.type));
-
- return NJS_ERROR;
+ return ret;
}
- njs_string_replacement_copy(&r->part[1], &string);
-
- return njs_string_replace_join(vm, r);
-}
-
+ func_replace = njs_is_function(replace) ? njs_function(replace) : NULL;
-static njs_int_t
-njs_string_replace_parse(njs_vm_t *vm, njs_string_replace_t *r, u_char *p,
- u_char *end, size_t size, njs_uint_t ncaptures)
-{
- u_char c;
- uint32_t type;
- njs_string_subst_t *s;
-
- r->substitutions = njs_arr_create(vm->mem_pool, 4,
- sizeof(njs_string_subst_t));
-
- if (njs_slow_path(r->substitutions == NULL)) {
- return NJS_ERROR;
+ if (func_replace == NULL) {
+ ret = njs_value_to_string(vm, replace, replace);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
}
- s = NULL;
+ length = njs_string_prop(&string, this);
+ search_length = njs_string_prop(&s, search);
- if (size == 0) {
- goto skip;
+ pos = njs_string_index_of(&string, &s, 0);
+ if (pos < 0) {
+ vm->retval = *this;
+ return NJS_OK;
}
-copy:
-
- if (s == NULL) {
- s = njs_arr_add(r->substitutions);
- if (njs_slow_path(s == NULL)) {
- return NJS_ERROR;
+ if (func_replace == NULL) {
+ ret = njs_string_get_substitution(vm, search, this, pos, NULL, 0, NULL,
+ replace, &retval);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
- s->type = NJS_SUBST_COPY;
- s->size = size;
- s->start = p - size;
-
} else {
- s->size += size;
- }
-
-skip:
-
- while (p < end) {
- size = 1;
- c = *p++;
+ arguments[0] = *search;
+ njs_set_number(&arguments[1], pos);
+ arguments[2] = *this;
- if (c != '$' || p == end) {
- goto copy;
- }
-
- c = *p++;
-
- if (c == '$') {
- s = NULL;
- goto copy;
- }
-
- size = 2;
-
- if (c >= '1' && c <= '9') {
- type = c - '0';
-
- if (p < end) {
- c = *p;
-
- if (c >= '0' && c <= '9') {
- type = type * 10 + (c - '0');
- p++;
- size = 3;
- }
- }
-
- if (type >= ncaptures) {
- goto copy;
- }
+ ret = njs_function_call(vm, func_replace,
+ njs_value_arg(&njs_value_undefined),
+ arguments, 3, &retval);
- type *= 2;
-
- } else if (c == '`') {
- type = NJS_SUBST_PRECEDING;
-
- } else if (c == '&') {
- type = 0;
-
- } else if (c == '\'') {
- type = NJS_SUBST_FOLLOWING;
-
- } else {
- goto copy;
- }
-
- s = njs_arr_add(r->substitutions);
- if (njs_slow_path(s == NULL)) {
+ ret = njs_value_to_string(vm, &retval, &retval);
+ if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
-
- s->type = type;
- s = NULL;
- }
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_string_replace_substitute(njs_vm_t *vm, njs_string_replace_t *r,
- int *captures)
-{
- int *capture;
- uint32_t i, n, last;
- const u_char *end;
- njs_string_subst_t *s;
- njs_string_replace_part_t *part, *subject;
-
- capture = NULL;
-
- last = r->substitutions->items;
- end = r->part[0].start + r->part[0].size;
-
- part = njs_arr_add_multiple(&r->parts, last + 1);
- if (njs_slow_path(part == NULL)) {
- return NJS_ERROR;
- }
-
- r->part = &part[-1];
-
- part[last].start = r->part[0].start + captures[1];
-
- if (captures[1] == 0) {
-
- /* Empty match. */
-
- if (r->part[0].start < end) {
- captures[1] = njs_utf8_next(r->part[0].start, end)
- - r->part[0].start;
- part[last].start = r->part[0].start + captures[1];
-
- } else {
- /* To exit the loop. */
- part[last].start = r->part[0].start + 1;
- }
}
- part[last].size = r->part[0].size - captures[1];
- njs_set_invalid(&part[last].value);
-
- r->part[0].size = captures[0];
-
- s = r->substitutions->start;
-
- for (i = 0; i < last; i++) {
- n = s[i].type;
-
- switch (n) {
-
- /* Literal text, "$$", and out of range "$n" substitutions. */
- case NJS_SUBST_COPY:
- part->start = s[i].start;
- part->size = s[i].size;
- break;
-
- /* "$`" substitution. */
- case NJS_SUBST_PRECEDING:
- subject = r->parts.start;
- part->start = subject->start;
- part->size = (r->part[0].start - subject->start) + r->part[0].size;
- break;
-
- /* "$'" substitution. */
- case NJS_SUBST_FOLLOWING:
- part->start = r->part[last + 1].start;
- part->size = r->part[last + 1].size;
- break;
-
- /*
- * "$n" and "$&" substitutions.
- */
- default:
- if (captures[n] == captures[n + 1]) {
-
- /* Empty match. */
-
- if (n > 0 && captures[n - 1] == captures[n]) {
-
- /*
- * Consecutive empty matches as in
- * 'ab'.replace(/(z*)(h*)/g, 'x')
- */
-
- part->size = 0;
- break;
- }
-
- capture = &captures[n];
- continue;
- }
-
- if (capture != NULL) {
-
- /*
- * Inserting a single character after a series of
- * (possibly several) empty matches.
- */
-
- if (part->start < end) {
- part->start = r->part[0].start + *capture;
- part->size = njs_utf8_next(part->start, end) - part->start;
-
- } else {
- part->size = 0;
- }
-
- capture = NULL;
- break;
- }
-
- part->start = r->part[0].start + captures[n];
- part->size = captures[n + 1] - captures[n];
- break;
- }
-
- njs_set_invalid(&part->value);
- part++;
- }
-
- if (capture != NULL) {
- part->start = r->part[0].start + *capture;
-
- if (part->start < end) {
- part->size = njs_utf8_next(part->start, end) - part->start;
-
- } else {
- part->size = 0;
- }
+ if (length == string.size) {
+ p = string.start + pos;
- njs_set_invalid(&part->value);
- part++;
+ } else {
+ /* UTF-8 string. */
+ p = njs_string_offset(string.start, string.start + string.size, pos);
}
- r->part = part;
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_string_replace_join(njs_vm_t *vm, njs_string_replace_t *r)
-{
- u_char *p, *string;
- size_t size, length, mask;
- ssize_t len;
- njs_uint_t i, n;
- njs_string_replace_part_t *part;
-
- size = 0;
- length = 0;
- mask = -1;
-
- part = r->parts.start;
- n = r->parts.items;
+ ret_length = njs_string_prop(&ret_string, &retval);
- for (i = 0; i < n; i++) {
- if (part[i].size == 0) {
- continue;
- }
-
- size += part[i].size;
-
- if (part[i].start == NULL) {
- part[i].start = part[i].value.short_string.start;
- }
-
- len = njs_utf8_length(part[i].start, part[i].size);
-
- if (len >= 0) {
- length += len;
+ size = string.size + ret_string.size - s.size;
+ length += ret_length - search_length;
- } else {
- mask = 0;
- }
- }
-
- length &= mask;
-
- string = njs_string_alloc(vm, &vm->retval, size, length);
- if (njs_slow_path(string == NULL)) {
+ r = njs_string_alloc(vm, &vm->retval, size, length);
+ if (njs_slow_path(r == NULL)) {
return NJS_ERROR;
}
- p = string;
-
- for (i = 0; i < n; i++) {
- size = part[i].size;
-
- if (size != 0) {
- p = njs_cpymem(p, part[i].start, size);
- }
-
- /* GC: release valid values. */
- }
-
- njs_arr_destroy(&r->parts);
+ r = njs_cpymem(r, string.start, p - string.start);
+ r = njs_cpymem(r, ret_string.start, ret_string.size);
+ r = njs_cpymem(r, p + s.size, string.size - s.size - (p - string.start));
return NJS_OK;
}
-static void
-njs_string_replacement_copy(njs_string_replace_part_t *string,
- const njs_value_t *value)
-{
- size_t size;
-
- string->value = *value;
-
- size = value->short_string.size;
-
- if (size != NJS_STRING_LONG) {
- string->start = NULL;
-
- } else {
- string->start = value->long_string.data->start;
- size = value->long_string.size;
- }
-
- string->size = size;
-}
-
-
double
njs_string_to_number(const njs_value_t *value, njs_bool_t parse_float)
{
njs_int_t njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused);
+njs_int_t njs_string_get_substitution(njs_vm_t *vm, njs_value_t *matched,
+ njs_value_t *string, int64_t pos, njs_value_t *captures, int64_t ncaptures,
+ njs_value_t *groups, njs_value_t *replacement, njs_value_t *retval);
extern const njs_object_init_t njs_string_instance_init;
return NJS_OK;
}
+
+
+njs_int_t
+njs_value_method(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
+ njs_value_t *retval)
+{
+ njs_int_t ret;
+
+ ret = njs_value_to_object(vm, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_value_property(vm, value, key, retval);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return (ret == NJS_DECLINED) ? NJS_OK : ret;
+ }
+
+ if (njs_slow_path(!njs_is_function(retval))) {
+ njs_type_error(vm, "method is not callable");
+ return NJS_ERROR;
+ }
+
+ return NJS_OK;
+}
njs_int_t njs_value_species_constructor(njs_vm_t *vm, njs_value_t *object,
njs_value_t *default_constructor, njs_value_t *dst);
+njs_int_t njs_value_method(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
+ njs_value_t *retval);
+
njs_inline njs_int_t
njs_value_property_i64(njs_vm_t *vm, njs_value_t *value, int64_t index,
{ njs_str("''.indexOf.call(12345, 45, '0')"),
njs_str("3") },
+ { njs_str("var r = new String('undefined').indexOf(x); var x; r"),
+ njs_str("0") },
+
{ njs_str("'abc'.lastIndexOf('abcdef')"),
njs_str("-1") },
"'123456'.search(r)"),
njs_str("3") },
- { njs_str("'abcdefgh'.replace()"),
- njs_str("abcdefgh") },
+ { njs_str("'abc'.replace()"),
+ njs_str("abc") },
- { njs_str("'abcdefgh'.replace('d')"),
- njs_str("abcundefinedefgh") },
+ { njs_str("'ABC'.replace('B')"),
+ njs_str("AundefinedC") },
- { njs_str("'abcdefgh'.replace('d', undefined)"),
- njs_str("abcundefinedefgh") },
+ { njs_str("'ABC'.replace('B', undefined)"),
+ njs_str("AundefinedC") },
{ njs_str("'a'.repeat(16).replace('a'.repeat(17)) === 'a'.repeat(16)"),
njs_str("true") },
- { njs_str("'abcdefgh'.replace('d', null)"),
- njs_str("abcnullefgh") },
+ { njs_str("'α'.repeat(16).replace('α'.repeat(17)) === 'α'.repeat(16)"),
+ njs_str("true") },
- { njs_str("'abcdefgh'.replace('d', 1)"),
- njs_str("abc1efgh") },
+ { njs_str("'ABC'.replace('B', null)"),
+ njs_str("AnullC") },
- { njs_str("'abcdefghdijklm'.replace('d', 'X')"),
- njs_str("abcXefghdijklm") },
+ { njs_str("'abc'.replace('c', 1)"),
+ njs_str("ab1") },
- { njs_str("'абвгдежгийклм'.replace('г', 'Г')"),
- njs_str("абвГдежгийклм") },
+ { njs_str("'abc'.replace('a', 'X')"),
+ njs_str("Xbc") },
- { njs_str("'abcdefghdijklm'.replace('d',"
- " function(m, o, s) { return '|'+s+'|'+o+'|'+m+'|' })"),
- njs_str("abc|abcdefghdijklm|3|d|efghdijklm") },
+ { njs_str("'abc'.replace('b', 'X')"),
+ njs_str("aXc") },
- { njs_str("'abcdefgh'.replace('', 'X')"),
- njs_str("Xabcdefgh") },
+ { njs_str("('a'.repeat(33) + 'bb').replace('bb', 'CC').slice(31)"),
+ njs_str("aaCC") },
- { njs_str("'abcdefghdijklm'.replace(/d/, 'X')"),
- njs_str("abcXefghdijklm") },
+ { njs_str("var r = 'abc'.replace('c', 'X'); [r, r.length]"),
+ njs_str("abX,3") },
- { njs_str("'abcdefghdijklm'.replace(/d/,"
- " function(m, o, s) { return '|'+s+'|'+o+'|'+m+'|' })"),
- njs_str("abc|abcdefghdijklm|3|d|efghdijklm") },
+ { njs_str("var r = 'αβγ'.replace('α', 'X'); [r, r.length]"),
+ njs_str("Xβγ,3") },
- { njs_str("'abcdefghdijklm'.replace(/(d)/,"
- " function(m, p, o, s)"
- "{ return '|'+s+'|'+o+'|'+m+'|'+p+'|' })"),
- njs_str("abc|abcdefghdijklm|3|d|d|efghdijklm") },
+ { njs_str("var r = 'αβγ'.replace('β', 'X'); [r, r.length]"),
+ njs_str("αXγ,3") },
- { njs_str("'abc'.replace(/b/, ()=>1)"),
- njs_str("a1c") },
+ { njs_str("var r = 'αβγ'.replace('γ', 'X'); [r, r.length]"),
+ njs_str("αβX,3") },
- { njs_str("var n = 0; 'abbbc'.replace(/b/g, function() {return ++n;})"),
- njs_str("a123c") },
+ { njs_str("var r = 'αβγ'.replace('', 'X'); [r, r.length]"),
+ njs_str("Xαβγ,4") },
+
+ { njs_str("'abc'.replace('b', (m, o, s) => `|${s}|${o}|${m}|`)"),
+ njs_str("a|abc|1|b|c") },
+
+ { njs_str("'abcdbe'.replace('b', '|$`X$\\'|')"),
+ njs_str("a|aXcdbe|cdbe") },
+
+ { njs_str("'undefined'.replace(void 0, 'x')"),
+ njs_str("x") },
+
+ { njs_str("'12345'.replace(3, () => 0)"),
+ njs_str("12045") },
+
+ { njs_str("var r = new String('undefined').replace(x, Function('return arguments[1]+42;')); var x; r"),
+ njs_str("42") },
+
+ { njs_str("'123'.replace(3, function() { return {toString: ()=>({})}; })"),
+ njs_str("TypeError: Cannot convert object to primitive value") },
+
+ { njs_str("'12345'.replace(3, () => ({toString: () => 'aaaa'}))"),
+ njs_str("12aaaa45") },
+
+ { njs_str("'abc'.replace(/a/, 'X')"),
+ njs_str("Xbc") },
+
+ { njs_str("'abccd'.replace(/c/, 'X')"),
+ njs_str("abXcd") },
- { njs_str("'abcdefghdijklm'.replace(/x/, 'X')"),
- njs_str("abcdefghdijklm") },
+ { njs_str("'abc'.replace(/c/, 'X')"),
+ njs_str("abX") },
- { njs_str("'abcdefghdijklm'.replace(/x/,"
- " function(m, o, s) { return '|'+s+'|'+o+'|'+m+'|' })"),
- njs_str("abcdefghdijklm") },
+ { njs_str("'abccd'.replace(/c+/, 'X')"),
+ njs_str("abXd") },
- { njs_str("'абвгдежгийклм'.replace(/г/, 'Г')"),
- njs_str("абвГдежгийклм") },
+ { njs_str("'abc'.replace(/f/, 'X')"),
+ njs_str("abc") },
+
+ { njs_str("('a'.repeat(33) + 'bb').replace(/bb/, 'CC').slice(31)"),
+ njs_str("aaCC") },
+
+ { njs_str("'abccd'.replace(/c/g, 'X')"),
+ njs_str("abXXd") },
+
+ { njs_str("('a'.repeat(33) + 'bb').replace(/bb/g, 'CC').slice(31)"),
+ njs_str("aaCC") },
+
+ { njs_str("'abccd'.replace(/[ac]/g, 'X')"),
+ njs_str("XbXXd") },
+
+ { njs_str("'ab'.replace(/q*/g, 'X')"),
+ njs_str("XaXbX") },
+
+ { njs_str("'αβ'.replace(/q*/g, 'X')"),
+ njs_str("XαXβX") },
- { njs_str("'abcdefghdijklm'.replace(/d/g, 'X')"),
- njs_str("abcXefghXijklm") },
+ { njs_str("'αβ'.replace(/(q)*/g, 'X')"),
+ njs_str("XαXβX") },
+
+ { njs_str("'αβ'.replace(/q*/g, 'γ')"),
+ njs_str("γαγβγ") },
+
+ { njs_str("':α:β:γ:'.replace(/:/g, '')"),
+ njs_str("αβγ") },
+
+ { njs_str("':α:β:γ:'.replace(/[αβγ]/g, '')"),
+ njs_str("::::") },
+
+ { njs_str("'aabbccaa'.replace(/a*/g, '')"),
+ njs_str("bbcc") },
- { njs_str("'абвгдежгийклм'.replace(/г/g, 'Г')"),
- njs_str("абвГдежГийклм") },
+ { njs_str("'aabbccaab'.replace(/z*/g, '')"),
+ njs_str("aabbccaab") },
+
+ { njs_str("''.replace(/a*/g, '')"),
+ njs_str("") },
+
+ { njs_str("'abcde'.replace(/d/, (m, o, s) => `|${s}|${o}|${m}|`)"),
+ njs_str("abc|abcde|3|d|e") },
+
+ { njs_str("'abcde'.replace(/(d)/, (m, p, o, s) => `|${s}|${o}|${m}|${p}|`)"),
+ njs_str("abc|abcde|3|d|d|e") },
+
+ { njs_str("'abc'.replace(/b/, () => 1)"),
+ njs_str("a1c") },
+
+ { njs_str("var n = 0; 'abbbc'.replace(/b/g, () => ++n)"),
+ njs_str("a123c") },
+
+ { njs_str("'abc'.replace(/x/, (m, o, s) => `|${s}|${o}|${m}|`)"),
+ njs_str("abc") },
{ njs_str("'abc12345#$*%'.replace(/([^\\d]*)(\\d*)([^\\w]*)/,"
- " function(match, p1, p2, p3) {"
- " return [p1, p2, p3].join('-')})"),
+ " (_, p1, p2, p3) => [p1, p2, p3].join('-'))"),
njs_str("abc-12345-#$*%") },
- { njs_str("'ABCDEFGHDIJKLM'.replace(/[A-Z]/g,"
- " function(match) { return '-' + match.toLowerCase() })"),
- njs_str("-a-b-c-d-e-f-g-h-d-i-j-k-l-m") },
+ { njs_str("'abc'.replace(/(?<named>b)/, (m, p, o, s, gr) => `|${gr.named}|`)"),
+ njs_str("a|b|c") },
+
+ { njs_str("'ABC'.replace(/[A-Z]/g, m => '-' + m.toLowerCase())"),
+ njs_str("-a-b-c") },
+
+ { njs_str("'abc'.replace(/(b)c/g, '|$01|')"),
+ njs_str("a|b|") },
+
+ { njs_str("'abc'.replace(/(b)c/g, '@$0|$01|$00@')"),
+ njs_str("a@$0|b|$00@") },
- { njs_str("'abcdbe'.replace(/(b)/g, '$')"),
- njs_str("a$cd$e") },
+ { njs_str("'abcdeFGHIJ'.replace(/(a)(b)(c)(d)(e)(F)(G)(H)(I)(J)/, '$9|$10|$11|$01')"),
+ njs_str("I|J|a1|a") },
{ njs_str("'abcdbe'.replace(/(b)/g, '$2$23')"),
njs_str("a$2$23cd$2$23e") },
{ njs_str("'abcdbe'.replace(/(b)/g, '$2$23X$$Y')"),
njs_str("a$2$23X$Ycd$2$23X$Ye") },
- { njs_str("'abcdbe'.replace('b', '|$`X$\\'|')"),
- njs_str("a|aXcdbe|cdbe") },
-
{ njs_str("'abcdbe'.replace(/b/, '|$`X$\\'|')"),
njs_str("a|aXcdbe|cdbe") },
{ njs_str("'abcdbefbgh'.replace(/b/g, '|$`X$\\'|')"),
njs_str("a|aXcdbefbgh|cd|abcdXefbgh|ef|abcdbefXgh|gh") },
- { njs_str("'abc12345#$*%'.replace(/([^\\d]*)(\\d*)([^\\w]*)/,"
- " '$1-$2-$3')"),
+ { njs_str("'abc12345#$*%'.replace(/([^\\d]*)(\\d*)([^\\w]*)/, '$1-$2-$3')"),
njs_str("abc-12345-#$*%") },
{ njs_str("'$1,$2'.replace(/(\\$(\\d))/g, '$$1-$1$2')"),
njs_str("$1-$11,$1-$22") },
- { njs_str("('β' + 'α'.repeat(33)+'β').replace(/(α+)(β+)/, function(m, p1) { return p1[32]; })"),
- njs_str("βα") },
+ { njs_str("'ABC'.replace(/(h*)(z*)(g*)/g, '$1@$2α$3')"),
+ njs_str("@αA@αB@αC@α") },
- { njs_str("'abc'.replace(/(h*)(z*)(g*)/g, '$1nn$2zz$3')"),
- njs_str("nnzzannzzbnnzzcnnzz") },
+ { njs_str("'abc'.replace(/(h*)(z*)/g, '$1@$2#$3:')"),
+ njs_str("@#$3:a@#$3:b@#$3:c@#$3:") },
- { njs_str("'abc'.replace(/(h*)(z*)/g, '$1nn$2zz$3yy')"),
- njs_str("nnzz$3yyannzz$3yybnnzz$3yycnnzz$3yy") },
+ { njs_str("/b(c)(z)?(.)/[Symbol.replace]('abcde', '[$1$2$3]')"),
+ njs_str("a[cd]e") },
- { njs_str("'ъ'.replace(/(h*)/g, '$1ЮЙ')"),
- njs_str("ЮЙъЮЙ") },
+ { njs_str("/b(c)(z)?(.)/[Symbol.replace]('abcde', '[$01$02$03$04$00]')"),
+ njs_str("a[cd$04$00]e") },
- { njs_str("'ъg'.replace(/(h*)/g, '$1ЮЙ')"),
- njs_str("ЮЙъЮЙgЮЙ") },
+ { njs_str("'α'.replace(/(h*)/g, '$1βγ')"),
+ njs_str("βγαβγ") },
- { njs_str("'ъg'.replace(/(ъ*)/g, '$1ЮЙ')"),
- njs_str("ъЮЙЮЙgЮЙ") },
+ { njs_str("'αg'.replace(/(h*)/g, '$1βγ')"),
+ njs_str("βγαβγgβγ") },
- { njs_str("'ъg'.replace(/(h*)/g, 'fg$1ЮЙ')"),
- njs_str("fgЮЙъfgЮЙgfgЮЙ") },
+ { njs_str("'αg'.replace(/(α*)/g, '$1βγ')"),
+ njs_str("αβγβγgβγ") },
- { njs_str("'юgёfя'.replace(/(gё)/g, 'n$1i')"),
- njs_str("юngёifя") },
+ { njs_str("'αg'.replace(/(h*)/g, 'fg$1βγ')"),
+ njs_str("fgβγαfgβγgfgβγ") },
- { njs_str("'aabbccaa'.replace(/a*/g, '')"),
- njs_str("bbcc") },
-
- { njs_str("'aabbccaab'.replace(/z*/g, '')"),
- njs_str("aabbccaab") },
-
- { njs_str("'αβγ'.replace(/z*/g, '|')"),
- njs_str("|α|β|γ|") },
-
- { njs_str("''.replace(/a*/g, '')"),
- njs_str("") },
-
- { njs_str("'12345'.replace(3, () => 0)"),
- njs_str("12045") },
-
- { njs_str("'123'.replace(3, function() { return {toString: ()=>({})}; })"),
- njs_str("TypeError: Cannot convert object to primitive value") },
-
- { njs_str("'12345'.replace(3, () => ({toString: () => 'aaaa'}))"),
- njs_str("12aaaa45") },
+ { njs_str("'αgβfγ'.replace(/(gβ)/g, 'n$1i')"),
+ njs_str("αngβifγ") },
- { njs_str("'abc'.replace(/(z*)/g, function v0() {return '124'})"),
- njs_str("124a124b124c124") },
+ { njs_str("'abc'.replace(/b/g, '|$&|')"),
+ njs_str("a|b|c") },
- { njs_str("'abc'.replace(/(a*)/g, function v0() {return '124'})"),
- njs_str("124124b124c124") },
+ { njs_str("'ABC'.replace(/((A)B)/g, '($1|$&|$2)')"),
+ njs_str("(AB|AB|A)C") },
{ njs_str("'abc'.replace(/b/g, '$0')"),
njs_str("a$0c") },
{ njs_str("typeof String.bytesFrom(Array(15).fill(0xE3)).replace(/^/g, 1)"),
njs_str("string") },
-#if 0 /* FIXME: PCRE limitation */
{ njs_str("'abc'.replace(/^/g, '|$&|')"),
njs_str("||abc") },
-#endif
- { njs_str("'abc'.replace(/b/g, '|$&|')"),
- njs_str("a|b|c") },
+ { njs_str("var uri ='/u/v1/Aa/bB?type=m3u8&mt=42';"
+ "uri.replace(/^\\/u\\/v1\\/[^/]*\\/([^\?]*)\\?.*(mt=[^&]*).*$/, '$1|$2')"),
+ njs_str("bB|mt=42") },
- { njs_str("'ABC'.replace(/((A)B)/g, '($1|$&|$2)')"),
- njs_str("(AB|AB|A)C") },
+ { njs_str("'ABC'.replace(/(?<b>B)/, '|$<b>|@$<a>@')"),
+ njs_str("A|B|@@C") },
- { njs_str("'undefined'.replace(void 0, 'x')"),
- njs_str("x") },
+ { njs_str("'ABC'.replace(/(?<b>B)/, '|$<BB|')"),
+ njs_str("A|$<BB|C") },
+
+ { njs_str("'ABC'.replace(/(?<b>B)/, '|$<BB$$|>@')"),
+ njs_str("A|@C") },
+
+ { njs_str("('β' + 'α'.repeat(33)+'β').replace(/(α+)(β+)/, (m, p1) => p1[32])"),
+ njs_str("βα") },
+
+ { njs_str("'abc'.replace(/(z*)/g, () => '@')"),
+ njs_str("@a@b@c@") },
+
+ { njs_str("'abc'.replace(/(a*)/g, () => '@')"),
+ njs_str("@@b@c@") },
+
+ { njs_str("var O = RegExp.prototype[Symbol.replace];"
+ "RegExp.prototype[Symbol.replace] = function (s, rep) { return O.call(this, s, `|${rep}|`); };"
+ "'ABC'.replace(/B/, '+')"),
+ njs_str("A|+|C") },
+
+ { njs_str("var O = RegExp.prototype.exec;"
+ "function mangled(s) { var r = O.call(this, s); Object.defineProperty(r, '0', {enumerable:false}); "
+ " return r; };"
+ "RegExp.prototype.exec = mangled;"
+ "'ABC'.replace(/(B)/, (m, p1, off, s) => `@${m}|${p1}|${off}|${s}@`)"),
+ njs_str("A@B|B|1|ABC@C") },
+
+ { njs_str("RegExp.prototype[Symbol.replace].call()"),
+ njs_str("TypeError: \"this\" is not object") },
+
+ { njs_str("RegExp.prototype[Symbol.replace].call(1)"),
+ njs_str("TypeError: \"this\" is not object") },
+
+ { njs_str("RegExp.prototype[Symbol.replace].call(/b/, 'abc','B')"),
+ njs_str("aBc") },
{ njs_str("/]/"),
njs_str("/\\]/") },
{ njs_str("/./['exec'] === RegExp.prototype.exec"),
njs_str("true") },
+ { njs_str("/./[Symbol.replace] === RegExp.prototype[Symbol.replace]"),
+ njs_str("true") },
+
{ njs_str("/^[A-Za-z0-9+/]{4}$/.test('////')"),
njs_str("true") },
{ njs_str("var r = /a/.exec('a'); ['groups' in r, typeof r.groups]"),
njs_str("true,undefined") },
-#if (!NJS_HAVE_MEMORY_SANITIZER) /* PCRE bug in groups code */
{ njs_str("var r = /(?<m>[0-9]{2})\\/(?<d>[0-9]{2})\\/(?<y>[0-9]{4})/;"
- "var g = r.exec('12/31/1986').groups;"
- "g.d + '.' + g.m + '.' + g.y"),
+ "var g = r.exec('12/31/1986').groups;"
+ "g.d + '.' + g.m + '.' + g.y"),
njs_str("31.12.1986") },
+#if (!NJS_HAVE_MEMORY_SANITIZER) /* PCRE bug in groups code */
+
{ njs_str("var g = /(?<r>(?<no>no)?(?<yes>yes)?)/.exec('yes').groups;"
- "[Object.keys(g).length,'no' in g, typeof g.no, g.yes, g.r]"),
+ "[Object.keys(g).length,'no' in g, typeof g.no, g.yes, g.r]"),
njs_str("3,true,undefined,yes,yes") },
#endif
" function(m) {return m.a.a})" ENTER),
njs_str("TypeError: cannot get property \"a\" of undefined\n"
" at anonymous (:1)\n"
+ " at RegExp.prototype[Symbol.replace] (native)\n"
" at String.prototype.replace (native)\n"
" at main (:1)\n") },