[njs] Implemented RegExp getters according to the specification.
Dmitry Volyntsev
xeioex at nginx.com
Mon Jun 7 19:15:39 UTC 2021
details: https://hg.nginx.org/njs/rev/02444445df29
branches:
changeset: 1650:02444445df29
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Sat Jun 05 11:58:00 2021 +0000
description:
Implemented RegExp getters according to the specification.
diffstat:
src/njs_regexp.c | 269 ++++++++++++++++++++++++++++++++++++++--------
src/njs_regexp_pattern.h | 5 +-
src/njs_value.c | 2 +
src/njs_value.h | 1 +
src/test/njs_unit_test.c | 13 +-
5 files changed, 235 insertions(+), 55 deletions(-)
diffs (460 lines):
diff -r 687d9eacbe33 -r 02444445df29 src/njs_regexp.c
--- a/src/njs_regexp.c Wed Jun 02 14:10:03 2021 +0000
+++ b/src/njs_regexp.c Sat Jun 05 11:58:00 2021 +0000
@@ -17,9 +17,8 @@ struct njs_regexp_group_s {
static void *njs_regexp_malloc(size_t size, void *memory_data);
static void njs_regexp_free(void *p, void *memory_data);
-static njs_int_t njs_regexp_prototype_source(njs_vm_t *vm,
- njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
- njs_value_t *retval);
+static njs_int_t njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused);
static int njs_regexp_pattern_compile(njs_vm_t *vm, njs_regex_t *regex,
u_char *source, int options);
static u_char *njs_regexp_compile_trace_handler(njs_trace_t *trace,
@@ -108,11 +107,13 @@ njs_regexp_constructor(njs_vm_t *vm, njs
pattern = njs_arg(args, nargs, 1);
if (njs_is_regexp(pattern)) {
- ret = njs_regexp_prototype_source(vm, NULL, pattern, NULL, &source);
+ ret = njs_regexp_prototype_source(vm, pattern, 1, 0);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
+ source = vm->retval;
+
re_flags = njs_regexp_value_flags(vm, pattern);
pattern = &source;
@@ -634,64 +635,141 @@ njs_regexp_prototype_last_index(njs_vm_t
static njs_int_t
-njs_regexp_prototype_global(njs_vm_t *vm, njs_object_prop_t *prop,
- njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+njs_regexp_prototype_flags(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused)
{
- njs_regexp_pattern_t *pattern;
+ u_char *p;
+ njs_int_t ret;
+ njs_value_t *this, value;
+ u_char dst[3];
+
+ 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");
+
+ this = njs_argument(args, 0);
+ if (njs_slow_path(!njs_is_object(this))) {
+ njs_type_error(vm, "\"this\" argument is not an object");
+ return NJS_ERROR;
+ }
+
+ p = &dst[0];
+
+ ret = njs_value_property(vm, this, njs_value_arg(&string_global),
+ &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
- pattern = njs_regexp_pattern(value);
- *retval = pattern->global ? njs_value_true : njs_value_false;
- njs_release(vm, value);
+ if (njs_bool(&value)) {
+ *p++ = 'g';
+ }
+
+ ret = njs_value_property(vm, this, njs_value_arg(&string_ignore_case),
+ &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
- return NJS_OK;
+ if (njs_bool(&value)) {
+ *p++ = 'i';
+ }
+
+ ret = njs_value_property(vm, this, njs_value_arg(&string_multiline),
+ &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ if (njs_bool(&value)) {
+ *p++ = 'm';
+ }
+
+ return njs_string_new(vm, &vm->retval, dst, p - dst, p - dst);
}
static njs_int_t
-njs_regexp_prototype_ignore_case(njs_vm_t *vm, njs_object_prop_t *prop,
- njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+njs_regexp_prototype_flag(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t flag)
{
+ unsigned yn;
+ njs_value_t *this;
njs_regexp_pattern_t *pattern;
- pattern = njs_regexp_pattern(value);
- *retval = pattern->ignore_case ? njs_value_true : njs_value_false;
- njs_release(vm, value);
+ this = njs_argument(args, 0);
+ if (njs_slow_path(!njs_is_object(this))) {
+ njs_type_error(vm, "\"this\" argument is not an object");
+ return NJS_ERROR;
+ }
+
+ if (njs_slow_path(!njs_is_regexp(this))) {
+ if (njs_object(this) == &vm->prototypes[NJS_OBJ_TYPE_REGEXP].object) {
+ njs_set_undefined(&vm->retval);
+ return NJS_OK;
+ }
+
+ njs_type_error(vm, "\"this\" argument is not a regexp");
+ return NJS_ERROR;
+ }
+
+ pattern = njs_regexp_pattern(this);
+
+ switch (flag) {
+ case NJS_REGEXP_GLOBAL:
+ yn = pattern->global;
+ break;
+
+ case NJS_REGEXP_IGNORE_CASE:
+ yn = pattern->ignore_case;
+ break;
+
+ case NJS_REGEXP_MULTILINE:
+ default:
+ yn = pattern->multiline;
+ break;
+ }
+
+ njs_set_boolean(&vm->retval, yn);
return NJS_OK;
}
static njs_int_t
-njs_regexp_prototype_multiline(njs_vm_t *vm, njs_object_prop_t *prop,
- njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
-{
- njs_regexp_pattern_t *pattern;
-
- pattern = njs_regexp_pattern(value);
- *retval = pattern->multiline ? njs_value_true : njs_value_false;
- njs_release(vm, value);
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_regexp_prototype_source(njs_vm_t *vm, njs_object_prop_t *prop,
- njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused)
{
u_char *source;
int32_t length;
uint32_t size;
+ njs_value_t *this;
njs_regexp_pattern_t *pattern;
- pattern = njs_regexp_pattern(value);
+ this = njs_argument(args, 0);
+ if (njs_slow_path(!njs_is_object(this))) {
+ njs_type_error(vm, "\"this\" argument is not an object");
+ return NJS_ERROR;
+ }
+
+ if (njs_slow_path(!njs_is_regexp(this))) {
+ if (njs_object(this) == &vm->prototypes[NJS_OBJ_TYPE_REGEXP].object) {
+ vm->retval = njs_string_empty_regexp;
+ return NJS_OK;
+ }
+
+ njs_type_error(vm, "\"this\" argument is not a regexp");
+ return NJS_ERROR;
+ }
+
+ pattern = njs_regexp_pattern(this);
/* Skip starting "/". */
source = pattern->source + 1;
size = njs_strlen(source) - pattern->flags;
length = njs_utf8_length(source, size);
- return njs_regexp_string_create(vm, retval, source, size, length);
+ return njs_regexp_string_create(vm, &vm->retval, source, size, length);
}
@@ -699,13 +777,67 @@ static njs_int_t
njs_regexp_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
- if (njs_is_regexp(njs_arg(args, nargs, 0))) {
- return njs_regexp_to_string(vm, &vm->retval, &args[0]);
+ u_char *p;
+ size_t size, length;
+ njs_int_t ret;
+ njs_value_t *r, source, flags;
+ njs_string_prop_t source_string, flags_string;
+
+ static const njs_value_t string_source = njs_string("source");
+ static const njs_value_t string_flags = njs_string("flags");
+
+ r = njs_argument(args, 0);
+
+ if (njs_slow_path(!njs_is_object(r))) {
+ njs_type_error(vm, "\"this\" argument is not an object");
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_property(vm, r, njs_value_arg(&string_source),
+ &source);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_to_string(vm, &source, &source);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
}
- njs_type_error(vm, "\"this\" argument is not a regexp");
+ ret = njs_value_property(vm, r, njs_value_arg(&string_flags),
+ &flags);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_to_string(vm, &flags, &flags);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ (void) njs_string_prop(&source_string, &source);
+ (void) njs_string_prop(&flags_string, &flags);
+
+ size = source_string.size + flags_string.size + njs_length("//");
+ length = source_string.length + flags_string.length + njs_length("//");
- return NJS_ERROR;
+ if (njs_is_byte_string(&source_string)
+ || njs_is_byte_string(&flags_string))
+ {
+ length = 0;
+ }
+
+ p = njs_string_alloc(vm, &vm->retval, size, length);
+ if (njs_slow_path(p == NULL)) {
+ return NJS_ERROR;
+ }
+
+ *p++ = '/';
+ p = njs_cpymem(p, source_string.start, source_string.size);
+ *p++ = '/';
+ memcpy(p, flags_string.start, flags_string.size);
+
+ return NJS_OK;
}
@@ -713,7 +845,7 @@ njs_int_t
njs_regexp_to_string(njs_vm_t *vm, njs_value_t *retval,
const njs_value_t *value)
{
- u_char *source;
+ u_char *p, *source;
int32_t length;
uint32_t size;
njs_regexp_pattern_t *pattern;
@@ -724,7 +856,16 @@ njs_regexp_to_string(njs_vm_t *vm, njs_v
size = njs_strlen(source);
length = njs_utf8_length(source, size);
- return njs_regexp_string_create(vm, retval, source, size, length);
+ length = (length >= 0) ? length: 0;
+
+ p = njs_string_alloc(vm, retval, size, length);
+ if (njs_slow_path(p == NULL)) {
+ return NJS_ERROR;
+ }
+
+ p = njs_cpymem(p, source, size);
+
+ return NJS_OK;
}
@@ -1522,31 +1663,61 @@ static const njs_object_prop_t njs_rege
},
{
- .type = NJS_PROPERTY_HANDLER,
+ .type = NJS_PROPERTY,
+ .name = njs_string("flags"),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function(njs_regexp_prototype_flags, 0),
+ .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+ .writable = NJS_ATTRIBUTE_UNSET,
+ .configurable = 1,
+ .enumerable = 0,
+ },
+
+ {
+ .type = NJS_PROPERTY,
.name = njs_string("global"),
- .value = njs_prop_handler(njs_regexp_prototype_global),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function2(njs_regexp_prototype_flag, 0,
+ NJS_REGEXP_GLOBAL),
+ .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+ .writable = NJS_ATTRIBUTE_UNSET,
.configurable = 1,
+ .enumerable = 0,
},
{
- .type = NJS_PROPERTY_HANDLER,
+ .type = NJS_PROPERTY,
.name = njs_string("ignoreCase"),
- .value = njs_prop_handler(njs_regexp_prototype_ignore_case),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function2(njs_regexp_prototype_flag, 0,
+ NJS_REGEXP_IGNORE_CASE),
+ .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+ .writable = NJS_ATTRIBUTE_UNSET,
.configurable = 1,
+ .enumerable = 0,
},
{
- .type = NJS_PROPERTY_HANDLER,
+ .type = NJS_PROPERTY,
.name = njs_string("multiline"),
- .value = njs_prop_handler(njs_regexp_prototype_multiline),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function2(njs_regexp_prototype_flag, 0,
+ NJS_REGEXP_MULTILINE),
+ .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+ .writable = NJS_ATTRIBUTE_UNSET,
.configurable = 1,
+ .enumerable = 0,
},
{
- .type = NJS_PROPERTY_HANDLER,
+ .type = NJS_PROPERTY,
.name = njs_string("source"),
- .value = njs_prop_handler(njs_regexp_prototype_source),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function(njs_regexp_prototype_source, 0),
+ .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+ .writable = NJS_ATTRIBUTE_UNSET,
.configurable = 1,
+ .enumerable = 0,
},
{
@@ -1610,5 +1781,5 @@ const njs_object_type_init_t njs_regexp
.constructor = njs_native_ctor(njs_regexp_constructor, 2, 0),
.constructor_props = &njs_regexp_constructor_init,
.prototype_props = &njs_regexp_prototype_init,
- .prototype_value = { .object = { .type = NJS_REGEXP } },
+ .prototype_value = { .object = { .type = NJS_OBJECT } },
};
diff -r 687d9eacbe33 -r 02444445df29 src/njs_regexp_pattern.h
--- a/src/njs_regexp_pattern.h Wed Jun 02 14:10:03 2021 +0000
+++ b/src/njs_regexp_pattern.h Sat Jun 05 11:58:00 2021 +0000
@@ -21,8 +21,9 @@ struct njs_regexp_pattern_s {
njs_regex_t regex[2];
/*
- * A pattern source is used by RegExp.toString() method and
- * RegExp.source property. So it is is stored in form "/pattern/flags"
+ * A pattern source is used by RegExp.prototype.toString() method and
+ * RegExp.prototype.source and RegExp.prototype.flags accessor properties.
+ * So it is is stored in form "/pattern/flags"
* and as zero-terminated C string but not as value, because retrieving
* it is very seldom operation. To get just a pattern string for
* RegExp.source property a length of flags part "/flags" is stored
diff -r 687d9eacbe33 -r 02444445df29 src/njs_value.c
--- a/src/njs_value.c Wed Jun 02 14:10:03 2021 +0000
+++ b/src/njs_value.c Sat Jun 05 11:58:00 2021 +0000
@@ -30,6 +30,8 @@ const njs_value_t njs_value_nan =
const njs_value_t njs_value_invalid = njs_value(NJS_INVALID, 0, 0.0);
const njs_value_t njs_string_empty = njs_string("");
+const njs_value_t njs_string_empty_regexp =
+ njs_string("(?:)");
const njs_value_t njs_string_comma = njs_string(",");
const njs_value_t njs_string_null = njs_string("null");
const njs_value_t njs_string_undefined = njs_string("undefined");
diff -r 687d9eacbe33 -r 02444445df29 src/njs_value.h
--- a/src/njs_value.h Wed Jun 02 14:10:03 2021 +0000
+++ b/src/njs_value.h Sat Jun 05 11:58:00 2021 +0000
@@ -802,6 +802,7 @@ extern const njs_value_t njs_value_nan;
extern const njs_value_t njs_value_invalid;
extern const njs_value_t njs_string_empty;
+extern const njs_value_t njs_string_empty_regexp;
extern const njs_value_t njs_string_comma;
extern const njs_value_t njs_string_null;
extern const njs_value_t njs_string_undefined;
diff -r 687d9eacbe33 -r 02444445df29 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Wed Jun 02 14:10:03 2021 +0000
+++ b/src/test/njs_unit_test.c Sat Jun 05 11:58:00 2021 +0000
@@ -10846,6 +10846,11 @@ static njs_unit_test_t njs_test[] =
{ njs_str("var r = (/^.+$/mg); [r.global, r.multiline, r.ignoreCase]"),
njs_str("true,true,false") },
+ { njs_str("['global', 'ignoreCase', 'multiline']"
+ ".map(v => Object.getOwnPropertyDescriptor(RegExp.prototype, v))"
+ ".every(desc => (typeof desc.get === 'function' && typeof desc.set === 'undefined'))"),
+ njs_str("true") },
+
{ njs_str("var r = /./; r"),
njs_str("/./") },
@@ -10855,8 +10860,8 @@ static njs_unit_test_t njs_test[] =
{ njs_str("var r = new RegExp('.'); r"),
njs_str("/./") },
- { njs_str("var r = new RegExp('.', 'ig'); r"),
- njs_str("/./gi") },
+ { njs_str("var r = new RegExp('.', 'igm'); r"),
+ njs_str("/./gim") },
{ njs_str("var r = new RegExp('abc'); r.test('00abc11')"),
njs_str("true") },
@@ -10922,7 +10927,7 @@ static njs_unit_test_t njs_test[] =
njs_str("SyntaxError: pcre_compile(\"\\\") failed: \\ at end of pattern") },
{ njs_str("[0].map(RegExp().toString)"),
- njs_str("TypeError: \"this\" argument is not a regexp") },
+ njs_str("TypeError: \"this\" argument is not an object") },
/* Non-standard ECMA-262 features. */
@@ -12986,7 +12991,7 @@ static njs_unit_test_t njs_test[] =
njs_str("true") },
{ njs_str("Object.prototype.toString.call(RegExp.prototype)"),
- njs_str("[object RegExp]") },
+ njs_str("[object Object]") },
{ njs_str("RegExp.prototype"),
njs_str("/(?:)/") },
More information about the nginx-devel
mailing list