[njs] Introduced njs_vm_bind().

Dmitry Volyntsev xeioex at nginx.com
Tue Nov 26 12:42:35 UTC 2019


details:   https://hg.nginx.org/njs/rev/68c9971d7366
branches:  
changeset: 1259:68c9971d7366
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Nov 26 15:09:45 2019 +0300
description:
Introduced njs_vm_bind().

This is a generic replacement for njs_vm_external_bind().

diffstat:

 src/njs.h                 |   5 +-
 src/njs_builtin.c         |  84 ++++++----------------------------------------
 src/njs_extern.c          |  83 ----------------------------------------------
 src/njs_extern.h          |   2 -
 src/njs_parser_terminal.c |  10 -----
 src/njs_shell.c           |  12 +++---
 src/njs_variable.c        |   6 ---
 src/njs_vm.c              |  41 ++++++++++++++++++++++-
 src/njs_vm.h              |   1 -
 src/test/njs_unit_test.c  |  77 ++++++++++++++++++++++++++++++++++--------
 test/njs_expect_test.exp  |  11 +++--
 11 files changed, 129 insertions(+), 203 deletions(-)

diffs (609 lines):

diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs.h
--- a/src/njs.h	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs.h	Tue Nov 26 15:09:45 2019 +0300
@@ -235,16 +235,17 @@ NJS_EXPORT const njs_extern_t *njs_vm_ex
     njs_external_t *external);
 NJS_EXPORT njs_int_t njs_vm_external_create(njs_vm_t *vm,
     njs_value_t *value, const njs_extern_t *proto, njs_external_ptr_t object);
-NJS_EXPORT njs_int_t njs_vm_external_bind(njs_vm_t *vm,
-    const njs_str_t *var_name, const njs_value_t *value);
 NJS_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm,
     const njs_value_t *value);
 
 NJS_EXPORT void njs_disassembler(njs_vm_t *vm);
 NJS_EXPORT void njs_disassemble(u_char *start, u_char *end);
 
+NJS_EXPORT njs_int_t njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name,
+    const njs_value_t *value, njs_bool_t shared);
 NJS_EXPORT const njs_value_t *njs_vm_value(njs_vm_t *vm, const njs_str_t *name);
 NJS_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, const njs_str_t *name);
+
 NJS_EXPORT njs_value_t *njs_vm_retval(njs_vm_t *vm);
 NJS_EXPORT void njs_vm_retval_set(njs_vm_t *vm, const njs_value_t *value);
 
diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_builtin.c
--- a/src/njs_builtin.c	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs_builtin.c	Tue Nov 26 15:09:45 2019 +0300
@@ -176,6 +176,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
             return NJS_ERROR;
         }
 
+        object->type = NJS_OBJECT;
         object->shared = 1;
         object->extensible = 1;
 
@@ -260,13 +261,6 @@ njs_builtin_objects_create(njs_vm_t *vm)
     shared->prototypes[NJS_OBJ_TYPE_REGEXP].regexp.pattern =
                                               shared->empty_regexp_pattern;
 
-    string_object = &shared->string_object;
-    njs_lvlhsh_init(&string_object->hash);
-    string_object->shared_hash = shared->string_instance_hash;
-    string_object->type = NJS_OBJECT_STRING;
-    string_object->shared = 1;
-    string_object->extensible = 0;
-
     constructor = shared->constructors;
 
     for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) {
@@ -286,6 +280,16 @@ njs_builtin_objects_create(njs_vm_t *vm)
         }
     }
 
+    vm->global_object = shared->objects[0];
+    vm->global_object.shared = 0;
+
+    string_object = &shared->string_object;
+    njs_lvlhsh_init(&string_object->hash);
+    string_object->shared_hash = shared->string_instance_hash;
+    string_object->type = NJS_OBJECT_STRING;
+    string_object->shared = 1;
+    string_object->extensible = 0;
+
     vm->shared = shared;
 
     return NJS_OK;
@@ -333,10 +337,7 @@ njs_builtin_objects_clone(njs_vm_t *vm, 
         vm->constructors[i].object.__proto__ = error_constructor;
     }
 
-    vm->global_object = vm->shared->objects[0];
     vm->global_object.__proto__ = object_prototype;
-    vm->global_object.shared = 0;
-
     njs_set_object(global, &vm->global_object);
 
     vm->string_object = vm->shared->string_object;
@@ -452,15 +453,11 @@ njs_builtin_traverse(njs_vm_t *vm, njs_t
 static njs_arr_t *
 njs_builtin_completions(njs_vm_t *vm)
 {
-    u_char                   *compl;
-    size_t                   len;
     njs_arr_t                *array;
     njs_str_t                *completion;
     njs_int_t                ret;
     njs_keyword_t            *keyword;
-    njs_lvlhsh_each_t        lhe, lhe_prop;
-    njs_extern_value_t       *ev;
-    const njs_extern_t       *ext_proto, *ext_prop;
+    njs_lvlhsh_each_t        lhe;
     njs_builtin_traverse_t   ctx;
     const njs_object_prop_t  *prop;
 
@@ -516,63 +513,6 @@ njs_builtin_completions(njs_vm_t *vm)
         njs_string_get(&prop->name, completion);
     }
 
-    /* Externals completions. */
-
-    njs_lvlhsh_each_init(&lhe, &njs_extern_value_hash_proto);
-
-    for ( ;; ) {
-        ev = njs_lvlhsh_each(&vm->externals_hash, &lhe);
-
-        if (ev == NULL) {
-            break;
-        }
-
-        ext_proto = ev->value.external.proto;
-
-        njs_lvlhsh_each_init(&lhe_prop, &njs_extern_hash_proto);
-
-        len = ev->name.length + 1;
-        compl = njs_mp_zalloc(vm->mem_pool, len);
-        if (compl == NULL) {
-            return NULL;
-        }
-
-        njs_sprintf(compl, compl + len, "%V%Z", &ev->name);
-
-        completion = njs_arr_add(array);
-        if (njs_slow_path(completion == NULL)) {
-            return NULL;
-        }
-
-        completion->length = len;
-        completion->start = (u_char *) compl;
-
-        for ( ;; ) {
-            ext_prop = njs_lvlhsh_each(&ext_proto->hash, &lhe_prop);
-
-            if (ext_prop == NULL) {
-                break;
-            }
-
-            len = ev->name.length + ev->name.length + 2;
-            compl = njs_mp_zalloc(vm->mem_pool, len);
-            if (compl == NULL) {
-                return NULL;
-            }
-
-            njs_sprintf(compl, compl + len, "%V.%V%Z", &ev->name,
-                        &ext_prop->name);
-
-            completion = njs_arr_add(array);
-            if (njs_slow_path(completion == NULL)) {
-                return NULL;
-            }
-
-            completion->length = len;
-            completion->start = (u_char *) compl;
-        }
-    }
-
     return array;
 }
 
diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_extern.c
--- a/src/njs_extern.c	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs_extern.c	Tue Nov 26 15:09:45 2019 +0300
@@ -31,21 +31,6 @@ njs_extern_hash_test(njs_lvlhsh_query_t 
 }
 
 
-static njs_int_t
-njs_extern_value_hash_test(njs_lvlhsh_query_t *lhq, void *data)
-{
-    njs_extern_value_t  *ev;
-
-    ev = (njs_extern_value_t *) data;
-
-    if (njs_strstr_eq(&lhq->key, &ev->name)) {
-        return NJS_OK;
-    }
-
-    return NJS_DECLINED;
-}
-
-
 const njs_lvlhsh_proto_t  njs_extern_hash_proto
     njs_aligned(64) =
 {
@@ -56,16 +41,6 @@ const njs_lvlhsh_proto_t  njs_extern_has
 };
 
 
-const njs_lvlhsh_proto_t  njs_extern_value_hash_proto
-    njs_aligned(64) =
-{
-    NJS_LVLHSH_DEFAULT,
-    njs_extern_value_hash_test,
-    njs_lvlhsh_alloc,
-    njs_lvlhsh_free,
-};
-
-
 static njs_extern_t *
 njs_vm_external_add(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_external_t *external,
     njs_uint_t n)
@@ -215,45 +190,6 @@ njs_vm_external_create(njs_vm_t *vm, njs
 }
 
 
-njs_int_t
-njs_vm_external_bind(njs_vm_t *vm, const njs_str_t *var_name,
-    const njs_value_t *value)
-{
-    njs_int_t           ret;
-    njs_extern_value_t  *ev;
-    njs_lvlhsh_query_t  lhq;
-
-    if (njs_slow_path(!njs_is_external(value))) {
-        return NJS_ERROR;
-    }
-
-    ev = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
-                      sizeof(njs_extern_value_t));
-    if (njs_slow_path(ev == NULL)) {
-        njs_memory_error(vm);
-        return NJS_ERROR;
-    }
-
-    ev->value = *value;
-    ev->name = *var_name;
-
-    lhq.key = *var_name;
-    lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
-    lhq.proto = &njs_extern_value_hash_proto;
-    lhq.value = ev;
-    lhq.replace = 0;
-    lhq.pool = vm->mem_pool;
-
-    ret = njs_lvlhsh_insert(&vm->externals_hash, &lhq);
-    if (njs_slow_path(ret != NJS_OK)) {
-        njs_internal_error(vm, "lvlhsh insert failed");
-        return ret;
-    }
-
-    return NJS_OK;
-}
-
-
 njs_external_ptr_t
 njs_vm_external(njs_vm_t *vm, const njs_value_t *value)
 {
@@ -321,25 +257,6 @@ njs_extern_keys_array(njs_vm_t *vm, cons
 }
 
 
-njs_value_t *
-njs_external_lookup(njs_vm_t *vm, njs_str_t *name, uint32_t hash)
-{
-    njs_lvlhsh_query_t  lhq;
-    njs_extern_value_t  *ev;
-
-    lhq.key_hash = hash;
-    lhq.key = *name;
-    lhq.proto = &njs_extern_value_hash_proto;
-
-    if (njs_lvlhsh_find(&vm->externals_hash, &lhq) == NJS_OK) {
-        ev = (njs_extern_value_t *) lhq.value;
-        return &ev->value;
-    }
-
-    return NULL;
-}
-
-
 static njs_int_t
 njs_external_match(njs_vm_t *vm, njs_function_native_t func, njs_extern_t *ext,
     njs_str_t *name, njs_extern_part_t *head, njs_extern_part_t *ppart)
diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_extern.h
--- a/src/njs_extern.h	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs_extern.h	Tue Nov 26 15:09:45 2019 +0300
@@ -41,13 +41,11 @@ typedef struct {
 
 
 njs_array_t *njs_extern_keys_array(njs_vm_t *vm, const njs_extern_t *external);
-njs_value_t *njs_external_lookup(njs_vm_t *vm, njs_str_t *name, uint32_t hash);
 njs_int_t njs_external_match_native_function(njs_vm_t *vm,
     njs_function_native_t func, njs_str_t *name);
 
 
 extern const njs_lvlhsh_proto_t  njs_extern_hash_proto;
-extern const njs_lvlhsh_proto_t  njs_extern_value_hash_proto;
 
 
 #endif /* _NJS_EXTERN_H_INCLUDED_ */
diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_parser_terminal.c
--- a/src/njs_parser_terminal.c	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs_parser_terminal.c	Tue Nov 26 15:09:45 2019 +0300
@@ -203,7 +203,6 @@ njs_parser_reference(njs_vm_t *vm, njs_p
     njs_str_t *name, uint32_t hash, uint32_t token_line)
 {
     njs_int_t           ret;
-    njs_value_t         *ext;
     njs_variable_t      *var;
     njs_parser_node_t   *node;
     njs_parser_scope_t  *scope;
@@ -291,15 +290,6 @@ njs_parser_reference(njs_vm_t *vm, njs_p
 
         node->token_line = token_line;
 
-        ext = njs_external_lookup(vm, name, hash);
-
-        if (ext != NULL) {
-            node->token = NJS_TOKEN_EXTERNAL;
-            node->u.value = *ext;
-            node->index = (njs_index_t) ext;
-            break;
-        }
-
         ret = njs_variable_reference(vm, parser->scope, node, name, hash,
                                      NJS_REFERENCE);
         if (njs_slow_path(ret != NJS_OK)) {
diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_shell.c
--- a/src/njs_shell.c	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs_shell.c	Tue Nov 26 15:09:45 2019 +0300
@@ -613,28 +613,28 @@ njs_externals_init(njs_vm_t *vm, njs_con
     static const njs_str_t name = njs_str("console");
 
     proto = njs_vm_external_prototype(vm, &njs_externals[0]);
-    if (proto == NULL) {
+    if (njs_slow_path(proto == NULL)) {
         njs_stderror("failed to add console proto\n");
         return NJS_ERROR;
     }
 
     value = njs_mp_zalloc(vm->mem_pool, sizeof(njs_opaque_value_t));
-    if (value == NULL) {
+    if (njs_slow_path(value == NULL)) {
         return NJS_ERROR;
     }
 
     ret = njs_vm_external_create(vm, value, proto, console);
-    if (ret != NJS_OK) {
+    if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
-    ret = njs_vm_external_bind(vm, &name, value);
-    if (ret != NJS_OK) {
+    ret = njs_vm_bind(vm, &name, value, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
     ret = njs_console_init(vm, console);
-    if (ret != NJS_OK) {
+    if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_variable.c
--- a/src/njs_variable.c	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs_variable.c	Tue Nov 26 15:09:45 2019 +0300
@@ -683,12 +683,6 @@ njs_vm_value(njs_vm_t *vm, const njs_str
         return njs_vmcode_operand(vm, ((njs_variable_t *) lhq.value)->index);
     }
 
-    lhq.proto = &njs_extern_value_hash_proto;
-
-    if (njs_lvlhsh_find(&vm->externals_hash, &lhq) == NJS_OK) {
-        return &((njs_extern_value_t *) lhq.value)->value;
-    }
-
     return &njs_value_undefined;
 }
 
diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_vm.c
--- a/src/njs_vm.c	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs_vm.c	Tue Nov 26 15:09:45 2019 +0300
@@ -65,7 +65,6 @@ njs_vm_create(njs_vm_opt_t *options)
         return NULL;
     }
 
-    njs_lvlhsh_init(&vm->externals_hash);
     njs_lvlhsh_init(&vm->external_prototypes_hash);
 
     vm->trace.level = NJS_LEVEL_TRACE;
@@ -570,6 +569,46 @@ njs_vm_retval_set(njs_vm_t *vm, const nj
 
 
 njs_int_t
+njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name, const njs_value_t *value,
+    njs_bool_t shared)
+{
+    njs_int_t           ret;
+    njs_object_t        *global;
+    njs_lvlhsh_t        *hash;
+    njs_object_prop_t   *prop;
+    njs_lvlhsh_query_t  lhq;
+
+    prop = njs_object_prop_alloc(vm, &njs_value_undefined, value, 1);
+    if (njs_slow_path(prop == NULL)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_string_new(vm, &prop->name, var_name->start, var_name->length, 0);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    lhq.value = prop;
+    lhq.key = *var_name;
+    lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
+    lhq.replace = 1;
+    lhq.pool = vm->mem_pool;
+    lhq.proto = &njs_object_hash_proto;
+
+    global = &vm->global_object;
+    hash = shared ? &global->shared_hash : &global->hash;
+
+    ret = njs_lvlhsh_insert(hash, &lhq);
+    if (njs_slow_path(ret != NJS_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return ret;
+    }
+
+    return NJS_OK;
+}
+
+
+njs_int_t
 njs_vm_value_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
     uint32_t size)
 {
diff -r 8a41cab86cc2 -r 68c9971d7366 src/njs_vm.h
--- a/src/njs_vm.h	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/njs_vm.h	Tue Nov 26 15:09:45 2019 +0300
@@ -175,7 +175,6 @@ struct njs_vm_s {
 
     njs_arr_t                *external_objects; /* of njs_external_ptr_t */
 
-    njs_lvlhsh_t             externals_hash;
     njs_lvlhsh_t             external_prototypes_hash;
 
     njs_lvlhsh_t             variables_hash;
diff -r 8a41cab86cc2 -r 68c9971d7366 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Tue Nov 26 15:01:02 2019 +0300
+++ b/src/test/njs_unit_test.c	Tue Nov 26 15:09:45 2019 +0300
@@ -9352,8 +9352,17 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("Object.getOwnPropertyNames(this).includes('NaN')"),
       njs_str("true") },
 
-    { njs_str("Object.keys(this)"),
-      njs_str("njs,process") },
+    { njs_str("Object.keys(this).sort()"),
+      njs_str("$r,$r2,$r3,njs,process") },
+
+    { njs_str("var r = njs.dump(this); "
+              "['$r', 'global', njs.version].every(v=>r.includes(v))"),
+      njs_str("true") },
+
+    { njs_str("var r = JSON.stringify(this); "
+              "['$r', njs.version].every(v=>r.includes(v))"),
+      njs_str("true") },
+
 
     { njs_str("this.a = 1; this.a"),
       njs_str("1") },
@@ -13842,9 +13851,6 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("this.njs = 1; njs"),
       njs_str("1") },
 
-    { njs_str("njs.dump(this) === `global {njs:njs {version:'${njs.version}'},process:process {}}`"),
-      njs_str("true") },
-
     { njs_str("process === process"),
       njs_str("true") },
 
@@ -15233,6 +15239,15 @@ static njs_unit_test_t  njs_shared_test[
 
     { njs_str("var r; for (var i = 0; i < 2**10; i++) {r = $r.create('XXX').uri;}"),
       njs_str("undefined") },
+
+    { njs_str("delete $r3.vars.p; $r3.vars.p"),
+      njs_str("undefined") },
+
+    { njs_str("var sr = $r.create('XXX'); sr.vars.p = 'a'; sr.vars.p"),
+      njs_str("a") },
+
+    { njs_str("$r.bind('XXX', 37); XXX"),
+      njs_str("37") },
 };
 
 
@@ -15793,6 +15808,26 @@ memory_error:
 }
 
 
+static njs_int_t
+njs_unit_test_bind_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
+{
+    njs_str_t            name;
+    njs_unit_test_req_t  *r;
+
+    r = njs_vm_external(vm, njs_arg(args, nargs, 0));
+    if (njs_slow_path(r == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (njs_vm_value_to_string(vm, &name, njs_arg(args, nargs, 1)) != NJS_OK) {
+        return NJS_ERROR;
+    }
+
+    return njs_vm_bind(vm, &name, njs_arg(args, nargs, 2), 0);
+}
+
+
 static njs_external_t  njs_unit_test_r_props[] = {
 
     { njs_str("a"),
@@ -15909,6 +15944,17 @@ static njs_external_t  njs_unit_test_r_e
       njs_unit_test_create_external,
       0 },
 
+    { njs_str("bind"),
+      NJS_EXTERN_METHOD,
+      NULL,
+      0,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      njs_unit_test_bind_external,
+      0 },
+
 };
 
 
@@ -15982,15 +16028,14 @@ njs_externals_init(njs_vm_t *vm)
     njs_unit_test_prop_t  *prop;
 
     proto = njs_vm_external_prototype(vm, &njs_test_external[0]);
-    if (proto == NULL) {
+    if (njs_slow_path(proto == NULL)) {
         njs_printf("njs_vm_external_prototype() failed\n");
         return NJS_ERROR;
     }
 
-    requests = njs_mp_zalloc(vm->mem_pool,
-                             njs_nitems(njs_test_requests)
+    requests = njs_mp_zalloc(vm->mem_pool, njs_nitems(njs_test_requests)
                              * sizeof(njs_unit_test_req_t));
-    if (requests == NULL) {
+    if (njs_slow_path(requests == NULL)) {
         return NJS_ERROR;
     }
 
@@ -16002,15 +16047,15 @@ njs_externals_init(njs_vm_t *vm)
 
         ret = njs_vm_external_create(vm, njs_value_arg(&requests[i].value),
                                      proto, &requests[i]);
-        if (ret != NJS_OK) {
+        if (njs_slow_path(ret != NJS_OK)) {
             njs_printf("njs_vm_external_create() failed\n");
             return NJS_ERROR;
         }
 
-        ret = njs_vm_external_bind(vm, &njs_test_requests[i].name,
-                                   njs_value_arg(&requests[i].value));
-        if (ret != NJS_OK) {
-            njs_printf("njs_vm_external_bind() failed\n");
+        ret = njs_vm_bind(vm, &njs_test_requests[i].name,
+                          njs_value_arg(&requests[i].value), 1);
+        if (njs_slow_path(ret != NJS_OK)) {
+            njs_printf("njs_vm_bind() failed\n");
             return NJS_ERROR;
         }
 
@@ -16019,13 +16064,13 @@ njs_externals_init(njs_vm_t *vm)
                                           &njs_test_requests[i].props[j].name,
                                           &njs_test_requests[i].props[j].value);
 
-            if (prop == NULL) {
+            if (njs_slow_path(prop == NULL)) {
                 njs_printf("lvlhsh_unit_test_alloc() failed\n");
                 return NJS_ERROR;
             }
 
             ret = lvlhsh_unit_test_add(&requests[i], prop);
-            if (ret != NJS_OK) {
+            if (njs_slow_path(ret != NJS_OK)) {
                 njs_printf("lvlhsh_unit_test_add() failed\n");
                 return NJS_ERROR;
             }
diff -r 8a41cab86cc2 -r 68c9971d7366 test/njs_expect_test.exp
--- a/test/njs_expect_test.exp	Tue Nov 26 15:01:02 2019 +0300
+++ b/test/njs_expect_test.exp	Tue Nov 26 15:09:45 2019 +0300
@@ -87,10 +87,13 @@ njs_test {
      "Ma\a*th"}
 }
 
-njs_test {
-    {"conso\t"
-     "conso\a*le"}
-}
+# FIXME: completions for external objects
+# are not supported
+
+# njs_test {
+#     {"conso\t"
+#     "conso\a*le"}
+# }
 
 # Global completions, multiple partial match
 njs_test {


More information about the nginx-devel mailing list