static u_char *njs_regexp_match_trace_handler(njs_trace_t *trace,
njs_trace_data_t *td, u_char *start);
static njs_int_t njs_regexp_exec_result(njs_vm_t *vm, njs_regexp_t *regexp,
- njs_utf8_t utf8, u_char *string, njs_regex_match_data_t *match_data);
+ njs_utf8_t utf8, u_char *string, njs_regex_match_data_t *match_data,
+ uint32_t last_index);
static njs_int_t njs_regexp_string_create(njs_vm_t *vm, njs_value_t *value,
u_char *start, uint32_t size, int32_t length);
if (njs_fast_path(regexp != NULL)) {
njs_lvlhsh_init(®exp->object.hash);
- njs_lvlhsh_init(®exp->object.shared_hash);
+ regexp->object.shared_hash = vm->shared->regexp_instance_hash;
regexp->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_REGEXP].object;
regexp->object.type = NJS_REGEXP;
regexp->object.shared = 0;
regexp->object.extensible = 1;
- regexp->last_index = 0;
+ njs_set_number(®exp->last_index, 0);
regexp->pattern = pattern;
+ njs_string_short_set(®exp->string, 0, 0);
return regexp;
}
static njs_int_t
-njs_regexp_prototype_last_index(njs_vm_t *vm, njs_object_prop_t *prop,
+njs_regexp_prototype_last_index(njs_vm_t *vm, njs_object_prop_t *unused,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
- uint32_t index;
+ uint32_t index, last_index;
njs_regexp_t *regexp;
njs_string_prop_t string;
- njs_release(vm, value);
+ regexp = njs_object_proto_lookup(njs_object(value), NJS_REGEXP,
+ njs_regexp_t);
+ if (njs_slow_path(regexp == NULL)) {
+ njs_set_undefined(retval);
+ return NJS_DECLINED;
+ }
- regexp = njs_regexp(value);
+ if (setval != NULL) {
+ regexp->last_index = *setval;
+ *retval = *setval;
+
+ return NJS_OK;
+ }
+
+ if (njs_slow_path(!njs_is_number(®exp->last_index))) {
+ *retval = regexp->last_index;
+ return NJS_OK;
+ }
(void) njs_string_prop(&string, ®exp->string);
- index = njs_string_index(&string, regexp->last_index);
+ last_index = njs_number(®exp->last_index);
+
+ if (njs_slow_path(string.size < last_index)) {
+ *retval = regexp->last_index;
+ return NJS_OK;
+ }
+
+ index = njs_string_index(&string, last_index);
njs_set_number(retval, index);
return NJS_OK;
njs_regexp_prototype_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
- njs_int_t ret;
+ int *captures;
+ uint32_t last_index;
+ njs_int_t ret, match;
njs_uint_t n;
njs_regex_t *regex;
+ njs_regexp_t *regexp;
njs_value_t *value, lvalue;
const njs_value_t *retval;
njs_string_prop_t string;
n = (string.length != 0);
+ regexp = njs_regexp(njs_argument(args, 0));
pattern = njs_regexp_pattern(&args[0]);
regex = &pattern->regex[n];
}
}
- ret = njs_regexp_match(vm, regex, string.start, string.size,
+ match = njs_regexp_match(vm, regex, string.start, string.size,
match_data);
- if (ret >= 0) {
+ if (match >= 0) {
retval = &njs_value_true;
- } else if (ret != NJS_REGEX_NOMATCH) {
+ } else if (match != NJS_REGEX_NOMATCH) {
ret = NJS_ERROR;
goto done;
}
+
+ if (pattern->global) {
+ ret = njs_value_to_length(vm, ®exp->last_index, &last_index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ if (match >= 0) {
+ captures = njs_regex_captures(match_data);
+ last_index += captures[1];
+
+ } else {
+ last_index = 0;
+ }
+
+ njs_set_number(®exp->last_index, last_index);
+ }
}
ret = NJS_OK;
njs_regexp_prototype_exec(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
+ uint32_t last_index;
njs_int_t ret;
njs_utf8_t utf8;
njs_value_t *value, lvalue;
regexp = njs_regexp(&args[0]);
regexp->string = *value;
+ pattern = regexp->pattern;
+
+ ret = njs_value_to_length(vm, ®exp->last_index, &last_index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ if (!pattern->global) {
+ last_index = 0;
+ }
(void) njs_string_prop(&string, value);
- if (string.size >= regexp->last_index) {
+ if (string.size >= last_index) {
utf8 = NJS_STRING_BYTE;
type = NJS_REGEXP_BYTE;
pattern = regexp->pattern;
if (njs_regex_is_valid(&pattern->regex[type])) {
- string.start += regexp->last_index;
- string.size -= regexp->last_index;
+ string.start += last_index;
+ string.size -= last_index;
match_data = njs_regex_match_data(&pattern->regex[type],
vm->regex_context);
string.size, match_data);
if (ret >= 0) {
return njs_regexp_exec_result(vm, regexp, utf8, string.start,
- match_data);
+ match_data, last_index);
}
if (njs_slow_path(ret != NJS_REGEX_NOMATCH)) {
}
}
- regexp->last_index = 0;
+ if (pattern->global) {
+ njs_set_number(®exp->last_index, 0);
+ }
+
vm->retval = njs_value_null;
return NJS_OK;
static njs_int_t
njs_regexp_exec_result(njs_vm_t *vm, njs_regexp_t *regexp, njs_utf8_t utf8,
- u_char *string, njs_regex_match_data_t *match_data)
+ u_char *string, njs_regex_match_data_t *match_data, uint32_t last_index)
{
int *captures;
u_char *start;
/* TODO: Non UTF-8 position */
- njs_set_number(&prop->value, regexp->last_index + captures[0]);
+ njs_set_number(&prop->value, last_index + captures[0]);
if (regexp->pattern->global) {
- regexp->last_index += captures[1];
+ njs_set_number(®exp->last_index, last_index + captures[1]);
}
lhq.key_hash = NJS_INDEX_HASH;
.configurable = 1,
},
- {
- .type = NJS_PROPERTY_HANDLER,
- .name = njs_string("lastIndex"),
- .value = njs_prop_handler(njs_regexp_prototype_last_index),
- },
-
{
.type = NJS_PROPERTY_HANDLER,
.name = njs_string("global"),
};
+const njs_object_prop_t njs_regexp_instance_properties[] =
+{
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("lastIndex"),
+ .value = njs_prop_handler(njs_regexp_prototype_last_index),
+ .writable = 1,
+ },
+};
+
+
+const njs_object_init_t njs_regexp_instance_init = {
+ njs_regexp_instance_properties,
+ njs_nitems(njs_regexp_instance_properties),
+};
+
+
const njs_object_init_t njs_regexp_prototype_init = {
njs_regexp_prototype_properties,
njs_nitems(njs_regexp_prototype_properties),
"r.lastIndex +' '+ r.source +' '+ r.source.length +' '+ r"),
njs_str("1 \\x80 4 /\\x80/g") },
+ { njs_str("var descs = Object.getOwnPropertyDescriptors(RegExp('a'));"
+ "Object.keys(descs)"),
+ njs_str("lastIndex") },
+
+ { njs_str("var props = Object.getOwnPropertyDescriptor(RegExp('a'), 'lastIndex');"
+ "props.writable && !props.enumerable && !props.configurable"),
+ njs_str("true") },
+
+ { njs_str("var re = /a/; re.lastIndex"),
+ njs_str("0") },
+
+ { njs_str("var re = /aα/g; re.exec('aα'.repeat(32)); re.lastIndex"),
+ njs_str("2") },
+
+ { njs_str("var re = new RegExp('α'.repeat(33), 'g'); re.exec('α'.repeat(33)); re.lastIndex"),
+ njs_str("33") },
+
+ { njs_str("var re = new RegExp('α'.repeat(33), 'g'); re.exec('α'.repeat(33)); "
+ "re.lastIndex = 67; re.lastIndex"),
+ njs_str("67") },
+
+ { njs_str("var re = /a/; re.lastIndex = 4; Object.create(re).lastIndex"),
+ njs_str("4") },
+
+ { njs_str("var re = /a/g; re.lastIndex = {valueOf(){throw 'Oops'}}; typeof re.lastIndex"),
+ njs_str("object") },
+
+ { njs_str("var re = /a/g; re.lastIndex = {valueOf(){throw 'Oops'}}; re.exec('a')"),
+ njs_str("Oops") },
+
+ { njs_str("var re = /a/; Object.defineProperty(re, 'lastIndex', {value:'qq'}); re.lastIndex"),
+ njs_str("qq") },
+
+ { njs_str("var re = /a/; re.lastIndex = 'qq'; Object.create(re).lastIndex"),
+ njs_str("qq") },
+
+ { njs_str("var re = /(?:ab|cd)\\d?/g; re.lastIndex=-1; re.test('aacd22 '); re.lastIndex"),
+ njs_str("5") },
+
+ { njs_str("var re = /(?:ab|cd)\\d?/g; re.lastIndex=-1; re.test('@@'); re.lastIndex"),
+ njs_str("0") },
+
/*
* It seems that "/стоп/ig" fails on early PCRE versions.
* It fails at least in 8.1 and works at least in 8.31.