[njs] Fixed String.prototype.replace() according to the specification.
Dmitry Volyntsev
xeioex at nginx.com
Thu Jul 2 14:01:44 UTC 2020
details: https://hg.nginx.org/njs/rev/1c729f765cfb
branches:
changeset: 1448:1c729f765cfb
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Thu Jul 02 14:00:16 2020 +0000
description:
Fixed String.prototype.replace() according to the specification.
This closes #308 issue on Github.
diffstat:
src/njs_regexp.c | 316 +++++++++++++
src/njs_string.c | 1055 ++++++++++-----------------------------------
src/njs_string.h | 3 +
src/njs_value.c | 25 +
src/njs_value.h | 3 +
src/test/njs_unit_test.c | 365 ++++++++++-----
6 files changed, 820 insertions(+), 947 deletions(-)
diffs (truncated from 1960 to 1000 lines):
diff -r f9082cd59ba6 -r 1c729f765cfb src/njs_regexp.c
--- a/src/njs_regexp.c Thu Jul 02 13:59:33 2020 +0000
+++ b/src/njs_regexp.c Thu Jul 02 14:00:16 2020 +0000
@@ -1161,6 +1161,314 @@ njs_regexp_string_create(njs_vm_t *vm, n
}
+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[] =
{
{
@@ -1252,6 +1560,14 @@ static const njs_object_prop_t njs_rege
.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,
+ },
};
diff -r f9082cd59ba6 -r 1c729f765cfb src/njs_string.c
--- a/src/njs_string.c Thu Jul 02 13:59:33 2020 +0000
+++ b/src/njs_string.c Thu Jul 02 14:00:16 2020 +0000
@@ -12,44 +12,6 @@
#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,
@@ -72,25 +34,6 @@ static njs_int_t njs_string_match_multip
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)
@@ -3406,806 +3349,294 @@ njs_string_split_part_add(njs_vm_t *vm,
}
-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;
+
+ njs_string_get(replacement, &rep);
+ p = rep.start;
+ end = rep.start + rep.length;
+
+ njs_chb_init(&chain, vm->mem_pool);
+
+ 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;
+ }
+
+ njs_chb_append(&chain, p, end - p);
+ goto done;
}
- ncaptures = njs_regex_ncaptures(regex);
-
- } else {
- regex = NULL;
- ncaptures = 1;
-
- if (!njs_is_string(search)) {
- ret = njs_value_to_string(vm, search, search);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ njs_chb_append(&chain, p, r - p);
+ p = r;
+
+ c = r[1];
+
+ switch (c) {
+ case '$':
+ njs_chb_append_literal(&chain, "$");
+ p += 2;
+ break;
+
+ case '&':
+ njs_string_get(matched, &m);
+ njs_chb_append_str(&chain, &m);
+ p += 2;
+ break;
+
+ 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;
+
+ 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;
+ }
+
+ p += 2;
+
+ if (groups == NULL) {
+ break;
}
- }
- }
-
- /* 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);
+
+ ret = njs_vm_value_string_set(vm, &name, p, r - p);
if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ goto exception;
+ }
+
+ p = r + 1;
+
+ ret = njs_value_property(vm, groups, &name, &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+
+ if (njs_is_defined(&value)) {
+ ret = njs_value_to_string(vm, &value, &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto exception;
+ }
+
+ njs_string_get(&value, &str);
+ njs_chb_append_str(&chain, &str);
}
- }
-
- njs_string_replacement_copy(&r->part[1], replace);
-
- start = r->part[1].start;
-
- if (start == NULL) {
- start = r->part[1].value.short_string.start;
- }
-
- end = start + r->part[1].size;
-
- 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;
+
+ break;
+
+ default:
+ if (c >= '0' && c <= '9') {
+ n = c - '0';
+
+ c2 = (&r[2] < end) ? r[2] : 0;
+
+ if (c2 >= '0' && c2 <= '9'
+ && (n * 10 + (c2 - '0')) <= ncaptures)
+ {
+ n = n * 10 + (c2 - '0');
+
+ } else {
+ c2 = 0;
}
- /* Reset parts array to the subject string only. */
- r->parts.items = 1;
+ if (n == 0 || n > ncaptures) {
+ njs_chb_append(&chain, p, (c2 != 0) ? 3 : 2);
+ p += (c2 != 0) ? 3 : 2;
+ break;
+ }
+
+ p += (c2 != 0) ? 3 : 2;
+
+ if (njs_is_defined(&captures[n])) {
+ njs_string_get(&captures[n], &cap);
+ njs_chb_append_str(&chain, &cap);
+ }
break;
}
- }
- }
-
- r->part[0].start = string.start;
- r->part[0].size = string.size;
- njs_set_invalid(&r->part[0].value);
-
- 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;
- }
-
+
+ njs_chb_append_literal(&chain, "$");
+ p += 1;
break;
}
-
- captures = njs_regex_captures(r->match_data);
-
- if (r->substitutions != NULL) {
- ret = njs_string_replace_substitute(vm, r, captures);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- 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;
- }
-
- r->part = njs_arr_add(&r->parts);
- if (njs_slow_path(r->part == NULL)) {
- return NJS_ERROR;
- }
-
- r->part -= 2;
- }
-
- if (captures[1] == 0) {
-
- /* Empty match. */
-
- start = r->part[0].start;
-
- if (start < end) {
- p = (r->utf8 != NJS_STRING_BYTE)
- ? (u_char *) njs_utf8_next(start, end) : start + 1;
-
- r->part[1].start = start;
- r->part[1].size = p - start;
-
- r->part[2].start = p;
- r->part[2].size = end - p;
-
- } else {
- r->part[1].size = 0;
- r->part[2].size = 0;
-
- /* To exit the loop. */
- r->part[2].start = start + 1;
- }
-
- if (r->function != NULL) {
- return njs_string_replace_regexp_function(vm, this, regex, r,
- captures, ret);
- }
-
- r->part[0] = replace;
-
- } 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 (r->function != NULL) {
- return njs_string_replace_regexp_function(vm, this, regex, r,
- captures, ret);
- }
-
- r->part[0].size = captures[0];
-
- r->part[1] = replace;
- }
-
- if (!pattern->global) {
- return njs_string_replace_regexp_join(vm, r);
- }
-
- r->part += 2;
-
- } while (r->part[0].start <= end);
-
- if (r->part != r->parts.start) {
- return njs_string_replace_regexp_join(vm, r);
+ }
+
+done:
+
+ size = njs_chb_size(&chain);
+ if (njs_slow_path(size < 0)) {
+ njs_memory_error(vm);
+ ret = NJS_ERROR;
+ goto exception;
}
- njs_regex_match_data_free(r->match_data, vm->regex_context);
-
- njs_arr_destroy(&r->parts);
-
- njs_string_copy(&vm->retval, this);
+ length = njs_chb_utf8_length(&chain);
+
+ p = njs_string_alloc(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);
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)
+njs_string_prototype_replace(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
{
- u_char *start;
- size_t size, length;
+ u_char *r;
+ size_t length, search_length, ret_length, size;
+ int64_t pos;
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);
+ 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;
+
+ static const njs_value_t replace_key =
+ njs_wellknown_symbol(NJS_SYMBOL_REPLACE);
+
+ this = njs_argument(args, 0);
+
+ 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;
}
- njs_set_invalid(&r->retval);
-
- arguments = njs_mp_alloc(vm->mem_pool, (n + 3) * sizeof(njs_value_t));
- if (njs_slow_path(arguments == NULL)) {
- njs_memory_error(vm);
- return NJS_ERROR;
+ search = njs_lvalue_arg(&search_lvalue, args, nargs, 1);
+ replace = njs_lvalue_arg(&replace_lvalue, args, nargs, 2);
+
+ 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 (njs_is_defined(&replacer)) {
+ arguments[0] = *this;
+ arguments[1] = *replace;
+
+ return njs_function_call(vm, njs_function(&replacer), search,
+ arguments, 2, &vm->retval);
+ }
+ }
+
+ ret = njs_value_to_string(vm, this, this);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_value_to_string(vm, search, search);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
- 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);
+ func_replace = njs_is_function(replace) ? njs_function(replace) : NULL;
+
+ if (func_replace == NULL) {
+ ret = njs_value_to_string(vm, replace, replace);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
+ length = njs_string_prop(&string, this);
+ search_length = njs_string_prop(&s, search);
+
+ pos = njs_string_index_of(&string, &s, 0);
+ if (pos < 0) {
+ vm->retval = *this;
+ return NJS_OK;
+ }
+
+ 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;
+ }
+
+ } else {
+ arguments[0] = *search;
+ njs_set_number(&arguments[1], pos);
+ arguments[2] = *this;
+
+ ret = njs_function_call(vm, func_replace,
+ njs_value_arg(&njs_value_undefined),
+ arguments, 3, &retval);
+
+ ret = njs_value_to_string(vm, &retval, &retval);
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;
- }
-
- r->part[0].size = captures[0];
-
- ret = njs_function_apply(vm, r->function, arguments, n + 3, &r->retval);
- if (njs_slow_path(ret != NJS_OK)) {
- 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;
-
- 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);
-
-exception:
-
- njs_regex_match_data_free(r->match_data, vm->regex_context);
-
- return NJS_ERROR;
-}
-
-
More information about the nginx-devel
mailing list