[njs] Introduced PCRE2 RegExp backend.

Dmitry Volyntsev xeioex at nginx.com
Thu Nov 11 14:31:04 UTC 2021


details:   https://hg.nginx.org/njs/rev/728c3741f556
branches:  
changeset: 1745:728c3741f556
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu Nov 11 14:27:15 2021 +0000
description:
Introduced PCRE2 RegExp backend.

diffstat:

 auto/options             |    4 +
 auto/pcre                |   76 ++++++++++----
 auto/sources             |   15 ++-
 nginx/config.make        |    2 +-
 src/njs_pcre2.c          |  240 +++++++++++++++++++++++++++++++++++++++++++++++
 src/njs_regex.h          |   28 +++-
 src/test/njs_unit_test.c |   98 +++++++++++++-----
 7 files changed, 399 insertions(+), 64 deletions(-)

diffs (701 lines):

diff -r cf9e73e05aaf -r 728c3741f556 auto/options
--- a/auto/options	Thu Nov 11 14:26:41 2021 +0000
+++ b/auto/options	Thu Nov 11 14:27:15 2021 +0000
@@ -11,6 +11,8 @@ NJS_DEBUG_MEMORY=NO
 NJS_ADDRESS_SANITIZER=NO
 NJS_TEST262=YES
 
+NJS_TRY_PCRE2=YES
+
 NJS_CONFIGURE_OPTIONS=
 
 for njs_option
@@ -31,6 +33,8 @@ do
         --debug-memory=*)                NJS_DEBUG_MEMORY="$value"           ;;
         --test262=*)                     NJS_TEST262="$value"                ;;
 
+        --no-pcre2)                      NJS_TRY_PCRE2=NO                    ;;
+
         --help)
             . auto/help
             exit 0
diff -r cf9e73e05aaf -r 728c3741f556 auto/pcre
--- a/auto/pcre	Thu Nov 11 14:26:41 2021 +0000
+++ b/auto/pcre	Thu Nov 11 14:27:15 2021 +0000
@@ -2,33 +2,67 @@
 # Copyright (C) Igor Sysoev
 # Copyright (C) NGINX, Inc.
 
+njs_found=no
+NJS_HAVE_PCRE2=NO
 
-NJS_PCRE_CFLAGS=
-NJS_PCRE_LIB=
+if [ $NJS_TRY_PCRE2 = YES ]; then
+    if /bin/sh -c "(pcre2-config --version)" >> $NJS_AUTOCONF_ERR 2>&1; then
+
+        NJS_PCRE_CFLAGS=`pcre2-config --cflags`
+        NJS_PCRE_LIB=`pcre2-config --libs8`
 
-njs_found=no
+        njs_feature="PCRE2 library"
+        njs_feature_name=NJS_HAVE_PCRE2
+        njs_feature_run=no
+        njs_feature_incs="-DPCRE2_CODE_UNIT_WIDTH=8 $NJS_PCRE_CFLAGS"
+        njs_feature_libs=$NJS_PCRE_LIB
+        njs_feature_test="#include <pcre2.h>
 
-if /bin/sh -c "(pcre-config --version)" >> $NJS_AUTOCONF_ERR 2>&1; then
+                          int main(void) {
+                              pcre2_code  *re;
 
-    NJS_PCRE_CFLAGS=`pcre-config --cflags`
-    NJS_PCRE_LIB=`pcre-config --libs`
+                              re = pcre2_compile((PCRE2_SPTR)\"\",
+                                                 PCRE2_ZERO_TERMINATED, 0,
+                                                 NULL, NULL, NULL);
+                              return (re == NULL);
+                          }"
+
+        . auto/feature
 
-    njs_feature="PCRE library"
-    njs_feature_name=NJS_HAVE_PCRE
-    njs_feature_run=no
-    njs_feature_incs=$NJS_PCRE_CFLAGS
-    njs_feature_libs=$NJS_PCRE_LIB
-    njs_feature_test="#include <pcre.h>
+        if [ $njs_found = yes ]; then
+            NJS_HAVE_PCRE2=YES
+            echo " + PCRE2 version: `pcre2-config --version`"
+        fi
+    fi
+fi
+
+if [ $njs_found = no ]; then
+    if /bin/sh -c "(pcre-config --version)" >> $NJS_AUTOCONF_ERR 2>&1; then
+
+        NJS_PCRE_CFLAGS=`pcre-config --cflags`
+        NJS_PCRE_LIB=`pcre-config --libs`
 
-                     int main(void) {
-                         pcre  *re;
+        njs_feature="PCRE library"
+        njs_feature_name=NJS_HAVE_PCRE
+        njs_feature_run=no
+        njs_feature_incs=$NJS_PCRE_CFLAGS
+        njs_feature_libs=$NJS_PCRE_LIB
+        njs_feature_test="#include <pcre.h>
+
+                         int main(void) {
+                             pcre  *re;
 
-                         re = pcre_compile(NULL, 0, NULL, 0, NULL);
-                         if (re == NULL)
-                             return 1;
-                         return 0;
-                     }"
-    . auto/feature
+                             re = pcre_compile(NULL, 0, NULL, 0, NULL);
+                             if (re == NULL)
+                                 return 1;
+                             return 0;
+                         }"
+        . auto/feature
+
+        if [ $njs_found = yes ]; then
+            echo " + PCRE version: `pcre-config --version`"
+        fi
+    fi
 fi
 
 if [ $njs_found = no ]; then
@@ -37,5 +71,3 @@ if [ $njs_found = no ]; then
     echo
     exit 1;
 fi
-
-echo " + PCRE version: `pcre-config --version`"
diff -r cf9e73e05aaf -r 728c3741f556 auto/sources
--- a/auto/sources	Thu Nov 11 14:26:41 2021 +0000
+++ b/auto/sources	Thu Nov 11 14:27:15 2021 +0000
@@ -16,7 +16,6 @@ NJS_LIB_SRCS=" \
    src/njs_md5.c \
    src/njs_sha1.c \
    src/njs_sha2.c \
-   src/njs_pcre.c \
    src/njs_time.c \
    src/njs_file.c \
    src/njs_malloc.c \
@@ -64,6 +63,14 @@ NJS_LIB_SRCS=" \
    src/njs_async.c \
 "
 
+NJS_LIB_PCRE_SRCS=" \
+   src/njs_pcre.c \
+"
+
+NJS_LIB_PCRE2_SRCS=" \
+   src/njs_pcre2.c \
+"
+
 NJS_LIB_TEST_SRCS=" \
    src/test/lvlhsh_unit_test.c \
    src/test/random_unit_test.c \
@@ -76,6 +83,12 @@ NJS_TEST_SRCS=" \
    src/test/njs_benchmark.c \
 "
 
+if [ "$NJS_HAVE_PCRE2" = "YES" ]; then
+	NJS_LIB_SRCS="$NJS_LIB_SRCS $NJS_LIB_PCRE2_SRCS"
+else
+	NJS_LIB_SRCS="$NJS_LIB_SRCS $NJS_LIB_PCRE_SRCS"
+fi
+
 NJS_TS_SRCS=$(find ts/ -name "*.d.ts" -o -name "*.json")
 
 NJS_TEST_TS_SRCS=$(find test/ts/ -name "*.ts" -o -name "*.json")
diff -r cf9e73e05aaf -r 728c3741f556 nginx/config.make
--- a/nginx/config.make	Thu Nov 11 14:26:41 2021 +0000
+++ b/nginx/config.make	Thu Nov 11 14:27:15 2021 +0000
@@ -3,7 +3,7 @@ cat << END                              
 $ngx_addon_dir/../build/libnjs.a: $NGX_MAKEFILE
 	cd $ngx_addon_dir/.. \\
 	&& if [ -f build/Makefile ]; then \$(MAKE) clean; fi \\
-	&& CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure \\
+	&& CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-pcre2 \\
 	&& \$(MAKE)
 
 END
diff -r cf9e73e05aaf -r 728c3741f556 src/njs_pcre2.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_pcre2.c	Thu Nov 11 14:27:15 2021 +0000
@@ -0,0 +1,240 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
+
+
+static const u_char* njs_regex_pcre2_error(int errcode, u_char buffer[128]);
+
+
+njs_regex_generic_ctx_t *
+njs_regex_generic_ctx_create(njs_pcre_malloc_t private_malloc,
+    njs_pcre_free_t private_free, void *memory_data)
+{
+    return pcre2_general_context_create(private_malloc, private_free,
+                                        memory_data);
+}
+
+
+njs_regex_compile_ctx_t *
+njs_regex_compile_ctx_create(njs_regex_generic_ctx_t *ctx)
+{
+    return pcre2_compile_context_create(ctx);
+}
+
+
+njs_int_t
+njs_regex_escape(njs_mp_t *mp, njs_str_t *text)
+{
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_regex_compile(njs_regex_t *regex, u_char *source, size_t len,
+    njs_regex_flags_t flags, njs_regex_compile_ctx_t *ctx, njs_trace_t *trace)
+{
+    int         ret;
+    u_char      *error;
+    size_t      erroff;
+    njs_uint_t  options;
+    u_char      errstr[128];
+
+    options = PCRE2_ALT_BSUX | PCRE2_MATCH_UNSET_BACKREF;
+
+    if ((flags & NJS_REGEX_IGNORE_CASE)) {
+         options |= PCRE2_CASELESS;
+    }
+
+    if ((flags & NJS_REGEX_MULTILINE)) {
+         options |= PCRE2_MULTILINE;
+    }
+
+    if ((flags & NJS_REGEX_STICKY)) {
+         options |= PCRE2_ANCHORED;
+    }
+
+    if ((flags & NJS_REGEX_UTF8)) {
+         options |= PCRE2_UTF;
+    }
+
+    regex->code = pcre2_compile(source, len, options, &ret, &erroff, ctx);
+
+    if (njs_slow_path(regex->code == NULL)) {
+        error = &source[erroff];
+
+        njs_alert(trace, NJS_LEVEL_ERROR,
+                  "pcre_compile2(\"%s\") failed: %s at \"%s\"",
+                  source, njs_regex_pcre2_error(ret, errstr), error);
+
+        return NJS_DECLINED;
+    }
+
+    ret = pcre2_pattern_info(regex->code, PCRE2_INFO_CAPTURECOUNT,
+                             &regex->ncaptures);
+
+    if (njs_slow_path(ret < 0)) {
+        njs_alert(trace, NJS_LEVEL_ERROR,
+               "pcre2_pattern_info(\"%s\", PCRE2_INFO_CAPTURECOUNT) failed: %s",
+               source, njs_regex_pcre2_error(ret, errstr));
+
+        return NJS_ERROR;
+    }
+
+    ret = pcre2_pattern_info(regex->code, PCRE2_INFO_BACKREFMAX,
+                             &regex->backrefmax);
+
+    if (njs_slow_path(ret < 0)) {
+        njs_alert(trace, NJS_LEVEL_ERROR,
+                 "pcre2_pattern_info(\"%s\", PCRE2_INFO_BACKREFMAX) failed: %s",
+                 source, njs_regex_pcre2_error(ret, errstr));
+
+        return NJS_ERROR;
+    }
+
+    /* Reserve additional elements for the first "$0" capture. */
+    regex->ncaptures++;
+
+    if (regex->ncaptures > 1) {
+        ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMECOUNT,
+                                 &regex->nentries);
+
+        if (njs_slow_path(ret < 0)) {
+            njs_alert(trace, NJS_LEVEL_ERROR,
+                  "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMECOUNT) failed: %s",
+                   source, njs_regex_pcre2_error(ret, errstr));
+
+            return NJS_ERROR;
+        }
+
+        if (regex->nentries != 0) {
+            ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMEENTRYSIZE,
+                                     &regex->entry_size);
+
+            if (njs_slow_path(ret < 0)) {
+                njs_alert(trace, NJS_LEVEL_ERROR,
+                          "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMEENTRYSIZE)"
+                          " failed: %s", source,
+                          njs_regex_pcre2_error(ret, errstr));
+
+                return NJS_ERROR;
+            }
+
+            ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMETABLE,
+                                     &regex->entries);
+
+            if (njs_slow_path(ret < 0)) {
+                njs_alert(trace, NJS_LEVEL_ERROR,
+                          "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMETABLE) "
+                          "failed: %s", source,
+                          njs_regex_pcre2_error(ret, errstr));
+
+                return NJS_ERROR;
+            }
+        }
+    }
+
+    return NJS_OK;
+}
+
+
+njs_bool_t
+njs_regex_is_valid(njs_regex_t *regex)
+{
+    return (regex->code != NULL);
+}
+
+
+njs_int_t
+njs_regex_named_captures(njs_regex_t *regex, njs_str_t *name, int n)
+{
+    char  *entry;
+
+    if (name == NULL) {
+        return regex->nentries;
+    }
+
+    if (n >= regex->nentries) {
+        return NJS_ERROR;
+    }
+
+    entry = regex->entries + regex->entry_size * n;
+
+    name->start = (u_char *) entry + 2;
+    name->length = njs_strlen(name->start);
+
+    return (entry[0] << 8) + entry[1];
+}
+
+
+njs_regex_match_data_t *
+njs_regex_match_data(njs_regex_t *regex, njs_regex_generic_ctx_t *ctx)
+{
+    if (regex != NULL) {
+        return pcre2_match_data_create_from_pattern(regex->code, ctx);
+    }
+
+    return pcre2_match_data_create(0, ctx);
+}
+
+
+void
+njs_regex_match_data_free(njs_regex_match_data_t *match_data,
+    njs_regex_generic_ctx_t *unused)
+{
+    pcre2_match_data_free(match_data);
+}
+
+
+njs_int_t
+njs_regex_match(njs_regex_t *regex, const u_char *subject, size_t off,
+    size_t len, njs_regex_match_data_t *match_data, njs_trace_t *trace)
+{
+    int     ret;
+    u_char  errstr[128];
+
+    ret = pcre2_match(regex->code, subject, len, off, 0, match_data, NULL);
+
+    if (ret < 0) {
+        if (ret == PCRE2_ERROR_NOMATCH) {
+            return NJS_DECLINED;
+        }
+
+        njs_alert(trace, NJS_LEVEL_ERROR, "pcre2_match() failed: %s",
+                  njs_regex_pcre2_error(ret, errstr));
+        return NJS_ERROR;
+    }
+
+    return ret;
+}
+
+
+size_t
+njs_regex_capture(njs_regex_match_data_t *match_data, njs_uint_t n)
+{
+    size_t  c;
+
+    c = pcre2_get_ovector_pointer(match_data)[n];
+
+    if (c == PCRE2_UNSET) {
+        return NJS_REGEX_UNSET;
+    }
+
+    return c;
+}
+
+
+static const u_char *
+njs_regex_pcre2_error(int errcode, u_char buffer[128])
+{
+    pcre2_get_error_message(errcode, buffer, 128);
+
+    return buffer;
+}
diff -r cf9e73e05aaf -r 728c3741f556 src/njs_regex.h
--- a/src/njs_regex.h	Thu Nov 11 14:26:41 2021 +0000
+++ b/src/njs_regex.h	Thu Nov 11 14:27:15 2021 +0000
@@ -26,16 +26,6 @@ typedef void (*njs_pcre_free_t)(void *p,
 
 
 typedef struct {
-    njs_pcre_malloc_t  private_malloc;
-    njs_pcre_free_t    private_free;
-    void               *memory_data;
-} njs_regex_generic_ctx_t;
-
-
-#define njs_regex_compile_ctx_t  void
-
-
-typedef struct {
     void        *code;
     void        *extra;
     int         ncaptures;
@@ -46,6 +36,22 @@ typedef struct {
 } njs_regex_t;
 
 
+#ifdef NJS_HAVE_PCRE2
+
+#define njs_regex_generic_ctx_t  void
+#define njs_regex_compile_ctx_t  void
+#define njs_regex_match_data_t   void
+
+#else
+
+typedef struct {
+    njs_pcre_malloc_t  private_malloc;
+    njs_pcre_free_t    private_free;
+    void               *memory_data;
+} njs_regex_generic_ctx_t;
+
+#define njs_regex_compile_ctx_t  void
+
 typedef struct {
     int         ncaptures;
     /*
@@ -57,6 +63,8 @@ typedef struct {
     int         captures[3];
 } njs_regex_match_data_t;
 
+#endif
+
 
 NJS_EXPORT njs_regex_generic_ctx_t *
     njs_regex_generic_ctx_create(njs_pcre_malloc_t private_malloc,
diff -r cf9e73e05aaf -r 728c3741f556 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Thu Nov 11 14:26:41 2021 +0000
+++ b/src/test/njs_unit_test.c	Thu Nov 11 14:27:15 2021 +0000
@@ -6,7 +6,9 @@
 
 #include <njs_main.h>
 
+#ifndef NJS_HAVE_PCRE2
 #include <pcre.h>
+#endif
 
 #include "njs_externals_test.h"
 
@@ -19,6 +21,12 @@
 #define njs_evar(little, big) big
 #endif
 
+#ifdef NJS_HAVE_PCRE2
+#define njs_pcre_var(pcre2, pcre) pcre2
+#else
+#define njs_pcre_var(pcre2, pcre) pcre
+#endif
+
 
 #define njs_declare_sparse_array(nm, sz)                                      \
     "var " nm " = Array(" njs_stringify(sz) "); "                             \
@@ -8632,9 +8640,6 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("String.bytesFrom([255,149,15,97,95]).replace(/_/g, 'X')[4]"),
       njs_str("X") },
 
-    { njs_str("/]/"),
-      njs_str("/\\]/") },
-
     { njs_str("/=/"),
       njs_str("/=/") },
 
@@ -8647,12 +8652,13 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("/\\s*;\\s*/"),
       njs_str("/\\s*;\\s*/") },
 
+#ifndef NJS_HAVE_PCRE2
+    { njs_str("/]/"),
+      njs_str("/\\]/") },
+
     { njs_str("RegExp(']')"),
       njs_str("/\\]/") },
 
-    { njs_str("RegExp('[\\\\')"),
-      njs_str("SyntaxError: pcre_compile(\"[\\\") failed: \\ at end of pattern") },
-
     { njs_str("RegExp('[\\\\\\\\]]')"),
       njs_str("/[\\\\]\\]/") },
 
@@ -8673,6 +8679,12 @@ static njs_unit_test_t  njs_test[] =
 
     { njs_str("/]cd/"),
       njs_str("/\\]cd/") },
+#endif
+
+    { njs_str("RegExp('[\\\\')"),
+      njs_str("SyntaxError: "
+              njs_pcre_var("pcre_compile2(\"[\\\") failed: \\ at end of pattern at \"\"",
+                           "pcre_compile(\"[\\\") failed: \\ at end of pattern")) },
 
     { njs_str("RegExp('\\\\0').source[1]"),
       njs_str("0") },
@@ -10698,8 +10710,10 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("/a\r/"),
       njs_str("SyntaxError: Unterminated RegExp \"/a\" in 1") },
 
+#ifndef NJS_HAVE_PCRE2
     { njs_str("/a\\q/"),
       njs_str("/a\\q/") },
+#endif
 
     { njs_str("/\\\\/"),
       njs_str("/\\\\/") },
@@ -10750,17 +10764,23 @@ static njs_unit_test_t  njs_test[] =
                  ".every(ch=>/[\\]\\[!\"#$%&'()*+,.\\/:;<=>?@\\^_`{|}-]/.test(ch))"),
       njs_str("true") },
 
+#ifndef NJS_HAVE_PCRE2
     { njs_str("/a\\q/.test('a\\q')"),
       njs_str("true") },
+#endif
 
     { njs_str("/(\\.(?!com|org)|\\/)/.test('ah.info')"),
       njs_str("true") },
 
     { njs_str("/(/.test('')"),
-      njs_str("SyntaxError: pcre_compile(\"(\") failed: missing ) in 1") },
+      njs_str("SyntaxError: "
+              njs_pcre_var("pcre_compile2(\"(\") failed: missing closing parenthesis at \"\" in 1",
+                           "pcre_compile(\"(\") failed: missing ) in 1")) },
 
     { njs_str("/+/.test('')"),
-      njs_str("SyntaxError: pcre_compile(\"+\") failed: nothing to repeat at \"+\" in 1") },
+      njs_str("SyntaxError: "
+              njs_pcre_var("pcre_compile2(\"+\") failed: quantifier does not follow a repeatable item at \"+\" in 1",
+                           "pcre_compile(\"+\") failed: nothing to repeat at \"+\" in 1")) },
 
     { njs_str("/^$/.test('')"),
       njs_str("true") },
@@ -11040,17 +11060,27 @@ static njs_unit_test_t  njs_test[] =
       njs_str("true") },
 
     { njs_str("new RegExp('[')"),
-      njs_str("SyntaxError: pcre_compile(\"[\") failed: missing terminating ] for character class") },
+      njs_str("SyntaxError: "
+              njs_pcre_var("pcre_compile2(\"[\") failed: missing terminating ] for character class at \"\"",
+                           "pcre_compile(\"[\") failed: missing terminating ] for character class")) },
 
     { njs_str("new RegExp('['.repeat(16))"),
-      njs_str("SyntaxError: pcre_compile(\"[[[[[[[[[[[[[[[[\") failed: missing terminating ] for character class") },
+      njs_str("SyntaxError: "
+              njs_pcre_var("pcre_compile2(\"[[[[[[[[[[[[[[[[\") failed: missing terminating ] for character class at \"\"",
+                           "pcre_compile(\"[[[[[[[[[[[[[[[[\") failed: missing terminating ] for character class")) },
 
     { njs_str("new RegExp('\\\\')"),
-      njs_str("SyntaxError: pcre_compile(\"\\\") failed: \\ at end of pattern") },
+      njs_str("SyntaxError: "
+              njs_pcre_var("pcre_compile2(\"\\\") failed: \\ at end of pattern at \"\"",
+                           "pcre_compile(\"\\\") failed: \\ at end of pattern")) },
 
     { njs_str("[0].map(RegExp().toString)"),
       njs_str("TypeError: \"this\" argument is not an object") },
 
+    { njs_str("var arr = /\\1(A)/.exec('AA');"
+              "[arr[0], arr[1]]"),
+      njs_str("A,A") },
+
     /* Non-standard ECMA-262 features. */
 
     /* 0x10400 is not a surrogate pair of 0xD801 and 0xDC00. */
@@ -21230,7 +21260,7 @@ static njs_unit_test_t  njs_tz_test[] =
 };
 
 
-static njs_unit_test_t  njs_regexp_test[] =
+static njs_unit_test_t  njs_regexp_optional_tests[] =
 {
     { njs_str("/[\\\\u02E0-\\\\u02E4]/"),
       njs_str("/[\\\\u02E0-\\\\u02E4]/") },
@@ -21262,6 +21292,7 @@ static njs_unit_test_t  njs_regexp_test[
     { njs_str("RegExp('\x00').test('\0')"),
       njs_str("true") },
 
+#ifndef NJS_HAVE_PCRE2
     { njs_str("RegExp('\x00\\\\x00').source"),
       njs_str("\\u0000\\x00") },
 
@@ -21270,6 +21301,7 @@ static njs_unit_test_t  njs_regexp_test[
 
     { njs_str("RegExp('\\\\\\0').source"),
       njs_str("\\\\u0000") },
+#endif
 
     { njs_str("RegExp('[\0]').test('\0')"),
       njs_str("true") },
@@ -22154,9 +22186,11 @@ static njs_int_t
 njs_regexp_optional_test(njs_unit_test_t tests[], size_t num, njs_str_t *name,
     njs_opts_t *opts, njs_stat_t *stat)
 {
+    njs_bool_t  safe;
+
+#ifndef NJS_HAVE_PCRE2
     int         erroff;
     pcre        *re1, *re2;
-    njs_int_t   ret;
     const char  *errstr;
 
     /*
@@ -22169,6 +22203,10 @@ njs_regexp_optional_test(njs_unit_test_t
     re1 = pcre_compile("/[\\u0410]/", PCRE_JAVASCRIPT_COMPAT, &errstr, &erroff,
                       NULL);
 
+    if (re1 != NULL) {
+        pcre_free(re1);
+    }
+
     /*
      * pcre-7.8 fails to compile unicode escape codes inside square brackets
      * even when PCRE_UTF8 option is provided.
@@ -22176,24 +22214,24 @@ njs_regexp_optional_test(njs_unit_test_t
     re2 = pcre_compile("/[\\u0410]/", PCRE_JAVASCRIPT_COMPAT | PCRE_UTF8,
                        &errstr, &erroff, NULL);
 
-    if (re1 == NULL && re2 != NULL) {
-        ret = njs_unit_test(tests, num, name, opts, stat);
-        if (ret != NJS_OK) {
-            return ret;
-        }
-
-    } else {
-        njs_printf("njs unicode regexp tests skipped, libpcre fails\n");
-    }
-
-    if (re1 != NULL) {
-        pcre_free(re1);
-    }
-
     if (re2 != NULL) {
         pcre_free(re2);
     }
 
+    safe = (re1 == NULL && re2 != NULL);
+
+#else
+
+    safe = 1;
+
+#endif
+
+    if (safe) {
+        return njs_unit_test(tests, num, name, opts, stat);
+    }
+
+    njs_printf("regexp optional tests skipped\n");
+
     return NJS_OK;
 }
 
@@ -23331,10 +23369,10 @@ static njs_test_suite_t  njs_suites[] =
       njs_nitems(njs_tz_test),
       njs_timezone_optional_test },
 
-    { njs_str("regexp"),
+    { njs_str("regexp optional"),
       { .repeat = 1, .unsafe = 1 },
-      njs_regexp_test,
-      njs_nitems(njs_regexp_test),
+      njs_regexp_optional_tests,
+      njs_nitems(njs_regexp_optional_tests),
       njs_regexp_optional_test },
 
     { njs_str("vm_json"),


More information about the nginx-devel mailing list