[njs] Introduced RegExp.prototype.sticky support.
Dmitry Volyntsev
xeioex at nginx.com
Tue Jun 8 18:00:00 UTC 2021
details: https://hg.nginx.org/njs/rev/913a69870b49
branches:
changeset: 1654:913a69870b49
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Sat Jun 05 11:55:08 2021 +0000
description:
Introduced RegExp.prototype.sticky support.
diffstat:
src/njs_regexp.c | 60 +++++++++++++++++++++++++++++++++++++++++------
src/njs_regexp.h | 1 +
src/njs_regexp_pattern.h | 1 +
src/test/njs_unit_test.c | 33 +++++++++++++++++++++++---
4 files changed, 83 insertions(+), 12 deletions(-)
diffs (252 lines):
diff -r 06204b343066 -r 913a69870b49 src/njs_regexp.c
--- a/src/njs_regexp.c Tue Jun 08 12:43:13 2021 +0000
+++ b/src/njs_regexp.c Sat Jun 05 11:55:08 2021 +0000
@@ -93,6 +93,10 @@ njs_regexp_value_flags(njs_vm_t *vm, con
flags |= NJS_REGEXP_MULTILINE;
}
+ if (pattern->sticky) {
+ flags |= NJS_REGEXP_STICKY;
+ }
+
return flags;
}
@@ -332,6 +336,10 @@ njs_regexp_flags(u_char **start, u_char
flag = NJS_REGEXP_MULTILINE;
break;
+ case 'y':
+ flag = NJS_REGEXP_STICKY;
+ break;
+
default:
if (*p >= 'a' && *p <= 'z') {
goto invalid;
@@ -429,6 +437,11 @@ njs_regexp_pattern_create(njs_vm_t *vm,
options |= PCRE_MULTILINE;
}
+ pattern->sticky = ((flags & NJS_REGEXP_STICKY) != 0);
+ if (pattern->sticky) {
+ options |= PCRE_ANCHORED;
+ }
+
*p++ = '\0';
ret = njs_regexp_pattern_compile(vm, &pattern->regex[0],
@@ -644,11 +657,12 @@ njs_regexp_prototype_flags(njs_vm_t *vm,
u_char *p;
njs_int_t ret;
njs_value_t *this, value;
- u_char dst[3];
+ u_char dst[4];
static const njs_value_t string_global = njs_string("global");
static const njs_value_t string_ignore_case = njs_string("ignoreCase");
static const njs_value_t string_multiline = njs_string("multiline");
+ static const njs_value_t string_sticky = njs_string("sticky");
this = njs_argument(args, 0);
if (njs_slow_path(!njs_is_object(this))) {
@@ -688,6 +702,16 @@ njs_regexp_prototype_flags(njs_vm_t *vm,
*p++ = 'm';
}
+ ret = njs_value_property(vm, this, njs_value_arg(&string_sticky),
+ &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ if (njs_bool(&value)) {
+ *p++ = 'y';
+ }
+
return njs_string_new(vm, &vm->retval, dst, p - dst, p - dst);
}
@@ -728,8 +752,12 @@ njs_regexp_prototype_flag(njs_vm_t *vm,
break;
case NJS_REGEXP_MULTILINE:
+ yn = pattern->multiline;
+ break;
+
+ case NJS_REGEXP_STICKY:
default:
- yn = pattern->multiline;
+ yn = pattern->sticky;
break;
}
@@ -859,15 +887,19 @@ njs_regexp_to_string(njs_vm_t *vm, njs_v
size = njs_strlen(source);
length = njs_utf8_length(source, size);
- length = (length >= 0) ? length: 0;
+ length = (length >= 0) ? (length + (pattern->sticky != 0)): 0;
- p = njs_string_alloc(vm, retval, size, length);
+ p = njs_string_alloc(vm, retval, size + (pattern->sticky != 0), length);
if (njs_slow_path(p == NULL)) {
return NJS_ERROR;
}
p = njs_cpymem(p, source, size);
+ if (pattern->sticky) {
+ *p++ = 'y';
+ }
+
return NJS_OK;
}
@@ -905,7 +937,7 @@ njs_regexp_prototype_test(njs_vm_t *vm,
/**
- * TODO: sticky, unicode flags.
+ * TODO: unicode flags.
*/
static njs_int_t
njs_regexp_builtin_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s,
@@ -937,7 +969,7 @@ njs_regexp_builtin_exec(njs_vm_t *vm, nj
return NJS_ERROR;
}
- if (!pattern->global) {
+ if (!pattern->global && !pattern->sticky) {
last_index = 0;
}
@@ -995,7 +1027,7 @@ njs_regexp_builtin_exec(njs_vm_t *vm, nj
not_found:
- if (pattern->global) {
+ if (pattern->global || pattern->sticky) {
njs_set_number(&value, 0);
ret = njs_value_property_set(vm, r, njs_value_arg(&njs_string_lindex),
&value);
@@ -1082,7 +1114,7 @@ njs_regexp_exec_result(njs_vm_t *vm, njs
njs_set_number(&prop->value, index);
- if (pattern->global) {
+ if (pattern->global || pattern->sticky) {
if (type == NJS_REGEXP_UTF8) {
index = njs_string_index(string, captures[1]);
@@ -1682,6 +1714,18 @@ static const njs_object_prop_t njs_rege
{
.type = NJS_PROPERTY,
+ .name = njs_string("sticky"),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function2(njs_regexp_prototype_flag, 0,
+ NJS_REGEXP_STICKY),
+ .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+ .writable = NJS_ATTRIBUTE_UNSET,
+ .configurable = 1,
+ .enumerable = 0,
+ },
+
+ {
+ .type = NJS_PROPERTY,
.name = njs_string("toString"),
.value = njs_native_function(njs_regexp_prototype_to_string, 0),
.writable = 1,
diff -r 06204b343066 -r 913a69870b49 src/njs_regexp.h
--- a/src/njs_regexp.h Tue Jun 08 12:43:13 2021 +0000
+++ b/src/njs_regexp.h Sat Jun 05 11:55:08 2021 +0000
@@ -14,6 +14,7 @@ typedef enum {
NJS_REGEXP_GLOBAL = 1,
NJS_REGEXP_IGNORE_CASE = 2,
NJS_REGEXP_MULTILINE = 4,
+ NJS_REGEXP_STICKY = 8,
} njs_regexp_flags_t;
diff -r 06204b343066 -r 913a69870b49 src/njs_regexp_pattern.h
--- a/src/njs_regexp_pattern.h Tue Jun 08 12:43:13 2021 +0000
+++ b/src/njs_regexp_pattern.h Sat Jun 05 11:55:08 2021 +0000
@@ -38,6 +38,7 @@ struct njs_regexp_pattern_s {
uint8_t global; /* 1 bit */
uint8_t ignore_case; /* 1 bit */
uint8_t multiline; /* 1 bit */
+ uint8_t sticky; /* 1 bit */
njs_regexp_group_t *groups;
};
diff -r 06204b343066 -r 913a69870b49 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Tue Jun 08 12:43:13 2021 +0000
+++ b/src/test/njs_unit_test.c Sat Jun 05 11:55:08 2021 +0000
@@ -10631,6 +10631,12 @@ static njs_unit_test_t njs_test[] =
{ njs_str("/[A-Z/]/"),
njs_str("/[A-Z/]/") },
+ { njs_str("/a/gim"),
+ njs_str("/a/gim") },
+
+ { njs_str("/a/y"),
+ njs_str("/a/y") },
+
{ njs_str("/[A-Z\n]/"),
njs_str("SyntaxError: Unterminated RegExp \"/[A-Z\" in 1") },
@@ -10720,6 +10726,9 @@ static njs_unit_test_t njs_test[] =
{ njs_str("/α/.test('\\u00CE\\u00B1'.toBytes())"),
njs_str("true") },
+ { njs_str("var r = /abc/y; r.test('abc'); r.lastIndex"),
+ njs_str("3") },
+
{ njs_str("/\\d/.exec('123')"),
njs_str("1") },
@@ -10849,8 +10858,21 @@ static njs_unit_test_t njs_test[] =
{ njs_str("var r = (/^.+$/mg), s = 'ab\\nc'; [r.exec(s), r.exec(s)]"),
njs_str("ab,c") },
- { njs_str("var r = (/^.+$/mg); [r.global, r.multiline, r.ignoreCase]"),
- njs_str("true,true,false") },
+ { njs_str("var r = (/^.+$/mg); [r.global, r.multiline, r.ignoreCase, r.sticky]"),
+ njs_str("true,true,false,false") },
+
+ { njs_str("['global', 'ignoreCase', 'multiline', 'sticky']"
+ ".map(v => Object.getOwnPropertyDescriptor(RegExp.prototype, v))"
+ ".every(desc => (typeof desc.get === 'function' && typeof desc.set === 'undefined'))"),
+ njs_str("true") },
+
+ { njs_str("var re = /./, re2 = /./y; re.lastIndex = 1; re2.lastIndex = 1;"
+ "[re.exec('abc')[0], re2.exec('abc')[0]]"),
+ njs_str("a,b") },
+
+ { njs_str("var re = /c/, re2 = /c/y;"
+ "njs.dump([re.exec('abc')[0], re2.exec('abc')])"),
+ njs_str("['c',null]") },
{ njs_str("['global', 'ignoreCase', 'multiline']"
".map(v => Object.getOwnPropertyDescriptor(RegExp.prototype, v))"
@@ -10869,6 +10891,9 @@ static njs_unit_test_t njs_test[] =
{ njs_str("var r = new RegExp('.', 'igm'); r"),
njs_str("/./gim") },
+ { njs_str("var r = new RegExp('.', 'y'); r"),
+ njs_str("/./y") },
+
{ njs_str("var r = new RegExp('abc'); r.test('00abc11')"),
njs_str("true") },
@@ -17340,8 +17365,8 @@ static njs_unit_test_t njs_test[] =
{ njs_str("njs.dump({a:1, b:[1,,2,{c:new Boolean(1)}]})"),
njs_str("{a:1,b:[1,<empty>,2,{c:[Boolean: true]}]}") },
- { njs_str("njs.dump([InternalError(),TypeError('msg'), new RegExp(), /^undef$/m, new Date(0)])"),
- njs_str("[InternalError,TypeError: msg,/(?:)/,/^undef$/m,1970-01-01T00:00:00.000Z]") },
+ { njs_str("njs.dump([InternalError(),TypeError('msg'), new RegExp(), /^undef$/my, new Date(0)])"),
+ njs_str("[InternalError,TypeError: msg,/(?:)/,/^undef$/my,1970-01-01T00:00:00.000Z]") },
{ njs_str("njs.dump(Array.prototype.slice.call({'1':'b', length:2}))"),
njs_str("[<empty>,'b']") },
More information about the nginx-devel
mailing list