[njs] njs_vm_call() interface to call a function outside.

Igor Sysoev igor at sysoev.ru
Wed Mar 2 12:35:49 UTC 2016


details:   http://hg.nginx.com/njs/rev/0dce9318807a
branches:  
changeset: 82:0dce9318807a
user:      Igor Sysoev <igor at sysoev.ru>
date:      Wed Mar 02 15:10:28 2016 +0300
description:
njs_vm_call() interface to call a function outside.
nginx content and variable handlers interface is

    function name(request, response) { ... }

Variable handler use a value returned by function as value of the variable.

diffstat:

 nginx/ngx_http_js_module.c |  238 +++++++++++++++++++++++++++-----------------
 njs/njs_extern.c           |   41 +++++++
 njs/njs_generator.c        |    3 +
 njs/njs_parser.c           |    8 +-
 njs/njs_parser.h           |    1 +
 njs/njs_vm.h               |   11 +-
 njs/njscript.c             |   55 ++++++++++-
 njs/njscript.h             |   12 ++-
 njs/test/njs_unit_test.c   |   30 ++++-
 9 files changed, 292 insertions(+), 107 deletions(-)

diffs (715 lines):

diff -r 36541f3695b6 -r 0dce9318807a nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c	Thu Feb 25 16:32:47 2016 +0300
+++ b/nginx/ngx_http_js_module.c	Wed Mar 02 15:10:28 2016 +0300
@@ -32,19 +32,28 @@
 
 
 typedef struct {
-    njs_vm_t         *vm;
+    njs_vm_t            *vm;
+    njs_function_t      *function;
+    njs_opaque_value_t   args[2];
+} ngx_http_js_ctx_t;
+
+
+typedef struct {
+    ngx_http_js_ctx_t   js;
 } ngx_http_js_loc_conf_t;
 
 
 typedef struct {
-    ngx_list_part_t  *part;
-    ngx_uint_t        item;
+    ngx_list_part_t     *part;
+    ngx_uint_t           item;
 } ngx_http_js_table_entry_t;
 
 
 static ngx_int_t ngx_http_js_handler(ngx_http_request_t *r);
 static ngx_int_t ngx_http_js_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_js_vm_run(ngx_http_request_t *r,
+    ngx_http_js_ctx_t *js, nxt_str_t *value);
 static void ngx_http_js_cleanup_mem_cache_pool(void *data);
 
 static void *ngx_http_js_alloc(void *mem, size_t size);
@@ -99,7 +108,8 @@ static njs_ret_t ngx_http_js_ext_next_ar
 
 static char *ngx_http_js_run(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
-static njs_vm_t *ngx_http_js_compile(ngx_conf_t *cf, ngx_str_t *script);
+static char *ngx_http_js_compile(ngx_conf_t *cf, ngx_http_js_ctx_t *jctx,
+    ngx_str_t *script);
 static void *ngx_http_js_create_loc_conf(ngx_conf_t *cf);
 static char *ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent,
     void *child);
@@ -362,15 +372,70 @@ static njs_external_t  ngx_http_js_exter
 static ngx_int_t
 ngx_http_js_handler(ngx_http_request_t *r)
 {
-    nxt_str_t                value;
-    njs_vm_t                *nvm;
-    ngx_pool_cleanup_t      *cln;
-    nxt_mem_cache_pool_t    *mcp;
+    ngx_int_t                rc;
     ngx_http_js_loc_conf_t  *jlcf;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http js handler");
 
+    jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
+
+    rc = ngx_http_js_vm_run(r, &jlcf->js, NULL);
+
+    if (rc == NGX_OK) {
+        return rc;
+    }
+
+    return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_js_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    ngx_http_js_ctx_t *js = (ngx_http_js_ctx_t *) data;
+
+    ngx_int_t  rc;
+    nxt_str_t  value;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http js variable handler");
+
+    rc = ngx_http_js_vm_run(r, js, &value);
+
+    if (rc == NXT_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_OK) {
+        v->len = value.len;
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = value.data;
+
+    } else {
+        v->not_found = 1;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http js variable done");
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_js_vm_run(ngx_http_request_t *r, ngx_http_js_ctx_t *js,
+    nxt_str_t *value)
+{
+    njs_vm_t              *nvm;
+    nxt_int_t              ret;
+    nxt_str_t              exception;
+    ngx_pool_cleanup_t    *cln;
+    nxt_mem_cache_pool_t  *mcp;
+
     mcp = ngx_http_js_create_mem_cache_pool();
     if (mcp == NULL) {
         return NGX_ERROR;
@@ -384,85 +449,39 @@ ngx_http_js_handler(ngx_http_request_t *
     cln->handler = ngx_http_js_cleanup_mem_cache_pool;
     cln->data = mcp;
 
-    jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
-
-    nvm = njs_vm_clone(jlcf->vm, mcp, (void **) &r);
+    nvm = njs_vm_clone(js->vm, mcp, (void **) &r);
     if (nvm == NULL) {
         return NGX_ERROR;
     }
 
-    if (njs_vm_run(nvm) != NJS_OK) {
-        njs_vm_exception(nvm, &value);
+    if (js->function) {
+        ret = njs_vm_call(nvm, js->function, js->args, 2);
+
+    } else {
+        ret = njs_vm_run(nvm);
+    }
+
+    if (ret == NJS_OK) {
+
+        if (value != NULL) {
+            if (njs_vm_retval(nvm, value) != NJS_OK) {
+                return NGX_ERROR;
+            }
+        }
+
+    } else {
+        njs_vm_exception(nvm, &exception);
 
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "js exception: %*s", value.len, value.data);
+                      "js exception: %*s", exception.len, exception.data);
 
-        return NGX_ERROR;
+        return NGX_DECLINED;
     }
 
     return NGX_OK;
 }
 
 
-static ngx_int_t
-ngx_http_js_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
-    uintptr_t data)
-{
-    njs_vm_t *vm = (njs_vm_t *) data;
-
-    nxt_str_t              value;
-    njs_vm_t              *nvm;
-    ngx_pool_cleanup_t    *cln;
-    nxt_mem_cache_pool_t  *mcp;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http js variable handler");
-
-    mcp = ngx_http_js_create_mem_cache_pool();
-    if (mcp == NULL) {
-        return NGX_ERROR;
-    }
-
-    cln = ngx_pool_cleanup_add(r->pool, 0);
-    if (cln == NULL) {
-        return NGX_ERROR;
-    }
-
-    cln->handler = ngx_http_js_cleanup_mem_cache_pool;
-    cln->data = mcp;
-
-    nvm = njs_vm_clone(vm, mcp, (void **) &r);
-    if (nvm == NULL) {
-        return NGX_ERROR;
-    }
-
-    if (njs_vm_run(nvm) == NJS_OK) {
-        if (njs_vm_retval(nvm, &value) != NJS_OK) {
-            return NGX_ERROR;
-        }
-
-        v->len = value.len;
-        v->valid = 1;
-        v->no_cacheable = 0;
-        v->not_found = 0;
-        v->data = value.data;
-
-    } else {
-        njs_vm_exception(nvm, &value);
-
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "js exception: %*s", value.len, value.data);
-
-        v->not_found = 1;
-    }
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http js variable done");
-
-    return NGX_OK;
-}
-
-
 static void
 ngx_http_js_cleanup_mem_cache_pool(void *data)
 {
@@ -1046,20 +1065,21 @@ ngx_http_js_run(ngx_conf_t *cf, ngx_comm
 {
     ngx_http_js_loc_conf_t *jlcf = conf;
 
+    char                      *ret;
     ngx_str_t                 *value;
     ngx_http_core_loc_conf_t  *clcf;
 
     value = cf->args->elts;
 
-    if (jlcf->vm) {
+    if (jlcf->js.vm) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "duplicate js handler \"%V\"", &value[1]);
         return NGX_CONF_ERROR;
     }
 
-    jlcf->vm = ngx_http_js_compile(cf, &value[1]);
-    if (jlcf->vm == NULL) {
-        return NGX_CONF_ERROR;
+    ret = ngx_http_js_compile(cf, &jlcf->js, &value[1]);
+    if (ret != NGX_CONF_OK) {
+        return ret;
     }
 
     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
@@ -1072,8 +1092,9 @@ ngx_http_js_run(ngx_conf_t *cf, ngx_comm
 static char *
 ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    njs_vm_t             *vm;
+    char                 *ret;
     ngx_str_t            *value;
+    ngx_http_js_ctx_t    *js;
     ngx_http_variable_t  *v;
 
     value = cf->args->elts;
@@ -1092,32 +1113,38 @@ ngx_http_js_set(ngx_conf_t *cf, ngx_comm
         return NGX_CONF_ERROR;
     }
 
-    vm = ngx_http_js_compile(cf, &value[2]);
-    if (vm == NULL) {
+    js = ngx_palloc(cf->pool, sizeof(ngx_http_js_ctx_t));
+    if (js == NULL) {
         return NGX_CONF_ERROR;
     }
 
+    ret = ngx_http_js_compile(cf, js, &value[2]);
+    if (ret != NGX_CONF_OK) {
+        return ret;
+    }
+
     v->get_handler = ngx_http_js_variable;
-    v->data = (uintptr_t) vm;
+    v->data = (uintptr_t) js;
 
     return NGX_CONF_OK;
 }
 
 
-static njs_vm_t *
-ngx_http_js_compile(ngx_conf_t *cf, ngx_str_t *script)
+static char *
+ngx_http_js_compile(ngx_conf_t *cf, ngx_http_js_ctx_t *js, ngx_str_t *script)
 {
     u_char                *start, *end;
     nxt_int_t              rc;
-    nxt_str_t              s;
+    nxt_str_t              s, name;
     njs_vm_t              *vm;
     nxt_lvlhsh_t           externals;
+    njs_function_t        *function;
     njs_vm_shared_t       *shared;
     nxt_mem_cache_pool_t  *mcp;
 
     mcp = ngx_http_js_create_mem_cache_pool();
     if (mcp == NULL) {
-        return NULL;
+        return NGX_CONF_ERROR;
     }
 
     shared = NULL;
@@ -1129,35 +1156,58 @@ ngx_http_js_compile(ngx_conf_t *cf, ngx_
         != NJS_OK)
     {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "could not add js externals");
-        return NULL;
+        return NGX_CONF_ERROR;
     }
 
     vm = njs_vm_create(mcp, &shared, &externals);
     if (vm == NULL) {
-        return NULL;
+        return NGX_CONF_ERROR;
     }
 
     start = script->data;
     end = start + script->len;
 
-    rc = njs_vm_compile(vm, &start, end);
+    rc = njs_vm_compile(vm, &start, end, &function);
 
     if (rc != NJS_OK) {
         njs_vm_exception(vm, &s);
 
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "js compilation error: \"%*s\"", s.len, s.data);
-        return NULL;
+        return NGX_CONF_ERROR;
     }
 
     if (start != end) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "extra characters in js script: \"%*s\"",
                            end - start, start);
-        return NULL;
+        return NGX_CONF_ERROR;
     }
 
-    return vm;
+    js->vm = vm;
+    js->function = function;
+
+    if (function) {
+        ngx_str_set(&name, "$r");
+
+        rc = njs_external_get(vm, NULL, &name, &js->args[0]);
+        if (rc != NXT_OK) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "could not get $r external");
+            return NGX_CONF_ERROR;
+        }
+
+        ngx_str_set(&name, "response");
+
+        rc = njs_external_get(vm, &js->args[0], &name, &js->args[1]);
+        if (rc != NXT_OK) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "could not get $r.response external");
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    return NGX_CONF_OK;
 }
 
 
@@ -1187,8 +1237,8 @@ ngx_http_js_merge_loc_conf(ngx_conf_t *c
     ngx_http_js_loc_conf_t *prev = parent;
     ngx_http_js_loc_conf_t *conf = child;
 
-    if (conf->vm == NULL) {
-        conf->vm = prev->vm;
+    if (conf->js.vm == NULL) {
+        conf->js = prev->js;
     }
 
     return NGX_CONF_OK;
diff -r 36541f3695b6 -r 0dce9318807a njs/njs_extern.c
--- a/njs/njs_extern.c	Thu Feb 25 16:32:47 2016 +0300
+++ b/njs/njs_extern.c	Wed Mar 02 15:10:28 2016 +0300
@@ -126,6 +126,47 @@ njs_add_external(nxt_lvlhsh_t *hash, nxt
 }
 
 
+nxt_int_t
+njs_external_get(njs_vm_t *vm, njs_opaque_value_t *obj, nxt_str_t *property,
+    njs_opaque_value_t *value)
+{
+    uint32_t            (*key_hash)(const void *, size_t);
+    njs_value_t         *object;
+    njs_extern_t        *ext;
+    nxt_lvlhsh_t        hash;
+    nxt_lvlhsh_query_t  lhq;
+
+    object = (njs_value_t *) obj;
+
+    key_hash = nxt_djb_hash;
+    hash = vm->externals_hash;
+
+    if (object != NULL) {
+        if (!njs_is_external(object)) {
+            return NXT_ERROR;
+        }
+
+        ext = object->data.u.external;
+        hash = ext->hash;
+
+        if (ext->type == NJS_EXTERN_CASELESS_OBJECT) {
+            key_hash = nxt_djb_hash_lowcase;
+        }
+    }
+
+    lhq.key_hash = key_hash(property->data, property->len);
+    lhq.key = *property;
+    lhq.proto = &njs_extern_hash_proto;
+
+    if (nxt_lvlhsh_find(&hash, &lhq) == NXT_OK) {
+        *value = *(njs_opaque_value_t *) lhq.value;
+        return NXT_OK;
+    }
+
+    return NXT_ERROR;
+}
+
+
 njs_extern_t *
 njs_parser_external(njs_vm_t *vm, njs_parser_t *parser)
 {
diff -r 36541f3695b6 -r 0dce9318807a njs/njs_generator.c
--- a/njs/njs_generator.c	Thu Feb 25 16:32:47 2016 +0300
+++ b/njs/njs_generator.c	Wed Mar 02 15:10:28 2016 +0300
@@ -163,6 +163,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t
     case NJS_TOKEN_END:
         return njs_generate_stop_statement(vm, parser, node);
 
+    case NJS_TOKEN_CALL:
+        return njs_generate_children(vm, parser, node);
+
     case NJS_TOKEN_COMMA:
         return njs_generate_comma_expression(vm, parser, node);
 
diff -r 36541f3695b6 -r 0dce9318807a njs/njs_parser.c
--- a/njs/njs_parser.c	Thu Feb 25 16:32:47 2016 +0300
+++ b/njs/njs_parser.c	Wed Mar 02 15:10:28 2016 +0300
@@ -99,7 +99,13 @@ njs_parser(njs_vm_t *vm, njs_parser_t *p
 
     node = parser->node;
 
-    if (node == NULL) {
+    if (node != NULL && node->right != NULL) {
+        if (node->right->token == NJS_TOKEN_FUNCTION) {
+            node->token = NJS_TOKEN_CALL;
+            return node;
+        }
+
+    } else {
         /* Empty string, just semicolons or variables declarations. */
 
         node = njs_parser_node_alloc(vm);
diff -r 36541f3695b6 -r 0dce9318807a njs/njs_parser.h
--- a/njs/njs_parser.h	Thu Feb 25 16:32:47 2016 +0300
+++ b/njs/njs_parser.h	Wed Mar 02 15:10:28 2016 +0300
@@ -14,6 +14,7 @@ typedef enum {
     NJS_TOKEN_ILLEGAL = 0,
 
     NJS_TOKEN_END,
+    NJS_TOKEN_CALL,
     NJS_TOKEN_SPACE,
     NJS_TOKEN_LINE_END,
 
diff -r 36541f3695b6 -r 0dce9318807a njs/njs_vm.h
--- a/njs/njs_vm.h	Thu Feb 25 16:32:47 2016 +0300
+++ b/njs/njs_vm.h	Wed Mar 02 15:10:28 2016 +0300
@@ -130,7 +130,7 @@ struct njs_object_s {
 
 #define NJS_ARGS_TYPES_MAX            3
 
-typedef struct {
+struct njs_function_s {
     njs_object_t                      object;
 
     uint8_t                           args_types[NJS_ARGS_TYPES_MAX];
@@ -158,7 +158,7 @@ typedef struct {
     } u;
 
     njs_value_t                       *bound;
-} njs_function_t;
+};
 
 
 typedef struct njs_continuation_s     njs_continuation_t;
@@ -684,10 +684,10 @@ enum njs_functions_e {
 
 
 #define njs_scope_index(value)                                                \
-    ((njs_index_t) (value << NJS_SCOPE_SHIFT))
+    ((njs_index_t) ((value) << NJS_SCOPE_SHIFT))
 
 #define njs_global_scope_index(value)                                         \
-    ((njs_index_t) ((value << NJS_SCOPE_SHIFT) | NJS_SCOPE_GLOBAL))
+    ((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | NJS_SCOPE_GLOBAL))
 
 
 #define NJS_INDEX_OBJECT         njs_global_scope_index(NJS_FUNCTION_OBJECT)
@@ -699,7 +699,8 @@ enum njs_functions_e {
 #define NJS_INDEX_REGEXP         njs_global_scope_index(NJS_FUNCTION_REGEXP)
 #define NJS_INDEX_EVAL           njs_global_scope_index(NJS_FUNCTION_EVAL)
 
-#define NJS_INDEX_GLOBAL_OFFSET  njs_scope_index(NJS_FUNCTION_MAX)
+#define NJS_INDEX_GLOBAL_RETVAL  njs_global_scope_index(NJS_FUNCTION_MAX)
+#define NJS_INDEX_GLOBAL_OFFSET  njs_scope_index(NJS_FUNCTION_MAX + 1)
 
 
 #define njs_offset(index)                                                     \
diff -r 36541f3695b6 -r 0dce9318807a njs/njscript.c
--- a/njs/njscript.c	Thu Feb 25 16:32:47 2016 +0300
+++ b/njs/njscript.c	Wed Mar 02 15:10:28 2016 +0300
@@ -167,10 +167,12 @@ njs_vm_destroy(njs_vm_t *vm)
 
 
 nxt_int_t
-njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
+njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end,
+    njs_function_t **function)
 {
     nxt_int_t          ret;
     njs_lexer_t        *lexer;
+    njs_value_t        *value;
     njs_parser_t       *parser;
     njs_parser_node_t  *node;
 
@@ -216,6 +218,16 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
         return NJS_ERROR;
     }
 
+    if (function != NULL) {
+        if (node->token == NJS_TOKEN_CALL) {
+            value = njs_variable_value(parser, node->right->index);
+            *function = value->data.u.function;
+
+        } else {
+            *function = NULL;
+        }
+    }
+
     *start = parser->lexer->start;
 
     ret = njs_generate_scope(vm, parser, node);
@@ -318,6 +330,47 @@ fail:
 
 
 nxt_int_t
+njs_vm_call(njs_vm_t *vm, njs_function_t *function, njs_opaque_value_t *args,
+    nxt_uint_t nargs)
+{
+    u_char       *current;
+    njs_ret_t    ret;
+    njs_value_t  *this;
+
+    static const njs_vmcode_stop_t  stop[] = {
+        { .code = { .operation = njs_vmcode_stop,
+                    .operands =  NJS_VMCODE_1OPERAND,
+                    .retval = NJS_VMCODE_NO_RETVAL },
+          .retval = NJS_INDEX_GLOBAL_RETVAL },
+    };
+
+    this = (njs_value_t *) &njs_value_void;
+
+    ret = njs_function_frame(vm, function, this,
+                             (njs_value_t *) args, nargs, 0);
+
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    current = vm->current;
+    vm->current = (u_char *) stop;
+
+    (void) njs_function_call(vm, NJS_INDEX_GLOBAL_RETVAL, 0);
+
+    ret = njs_vmcode_interpreter(vm);
+
+    vm->current = current;
+
+    if (ret == NJS_STOP) {
+        ret = NXT_OK;
+    }
+
+    return ret;
+}
+
+
+nxt_int_t
 njs_vm_run(njs_vm_t *vm)
 {
     nxt_str_t  s;
diff -r 36541f3695b6 -r 0dce9318807a njs/njscript.h
--- a/njs/njscript.h	Thu Feb 25 16:32:47 2016 +0300
+++ b/njs/njscript.h	Wed Mar 02 15:10:28 2016 +0300
@@ -12,8 +12,13 @@ typedef intptr_t                    njs_
 typedef uintptr_t                   njs_index_t;
 typedef struct njs_vm_s             njs_vm_t;
 typedef union  njs_value_s          njs_value_t;
+typedef struct njs_function_s       njs_function_t;
 typedef struct njs_vm_shared_s      njs_vm_shared_t;
 
+typedef struct {
+    uint64_t                        filler[2];
+} njs_opaque_value_t;
+
 
 /* sizeof(njs_value_t) is 16 bytes. */
 #define njs_argument(args, n)                                                 \
@@ -71,14 +76,19 @@ struct njs_external_s {
 NXT_EXPORT nxt_int_t njs_add_external(nxt_lvlhsh_t *hash,
     nxt_mem_cache_pool_t *mcp, uintptr_t object, njs_external_t *external,
     nxt_uint_t n);
+NXT_EXPORT nxt_int_t njs_external_get(njs_vm_t *vm, njs_opaque_value_t *object,
+    nxt_str_t *property, njs_opaque_value_t *value);
 
 NXT_EXPORT njs_vm_t *njs_vm_create(nxt_mem_cache_pool_t *mcp,
     njs_vm_shared_t **shared, nxt_lvlhsh_t *externals);
 NXT_EXPORT void njs_vm_destroy(njs_vm_t *vm);
 
-NXT_EXPORT nxt_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end);
+NXT_EXPORT nxt_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end,
+    njs_function_t **function);
 NXT_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp,
     void **external);
+NXT_EXPORT nxt_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function,
+    njs_opaque_value_t *args, nxt_uint_t nargs);
 NXT_EXPORT nxt_int_t njs_vm_run(njs_vm_t *vm);
 
 NXT_EXPORT njs_ret_t njs_vm_return_string(njs_vm_t *vm, u_char *start,
diff -r 36541f3695b6 -r 0dce9318807a njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Thu Feb 25 16:32:47 2016 +0300
+++ b/njs/test/njs_unit_test.c	Wed Mar 02 15:10:28 2016 +0300
@@ -3899,6 +3899,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("eval()"),
       nxt_string("") },
 
+    { nxt_string("function f(req) { return req.uri }"),
+      nxt_string("АБВ") },
+
     /* Trick: number to boolean. */
 
     { nxt_string("var a = 0; !!a"),
@@ -4192,12 +4195,14 @@ njs_unit_test(nxt_bool_t disassemble)
     u_char                *start;
     njs_vm_t              *vm, *nvm;
     nxt_int_t             ret;
-    nxt_str_t             s;
+    nxt_str_t             s, r_name;
     nxt_uint_t            i;
     nxt_bool_t            success;
     nxt_lvlhsh_t          externals;
+    njs_function_t        *function;
     njs_vm_shared_t       *shared;
     njs_unit_test_req     r;
+    njs_opaque_value_t    value;
     nxt_mem_cache_pool_t  *mcp;
 
     shared = NULL;
@@ -4230,7 +4235,8 @@ njs_unit_test(nxt_bool_t disassemble)
 
         start = njs_test[i].script.data;
 
-        ret = njs_vm_compile(vm, &start, start + njs_test[i].script.len);
+        ret = njs_vm_compile(vm, &start, start + njs_test[i].script.len,
+                             &function);
 
         if (ret == NXT_OK) {
             if (disassemble) {
@@ -4245,8 +4251,22 @@ njs_unit_test(nxt_bool_t disassemble)
             r.uri.len = 6;
             r.uri.data = (u_char *) "АБВ";
 
-            if (njs_vm_run(nvm) == NXT_OK) {
-
+            if (function != NULL) {
+                r_name.len = 2;
+                r_name.data = (u_char *) "$r";
+
+                ret = njs_external_get(vm, NULL, &r_name, &value);
+                if (ret != NXT_OK) {
+                    return NXT_ERROR;
+                }
+
+                ret = njs_vm_call(nvm, function, &value, 1);
+
+            } else {
+                ret = njs_vm_run(nvm);
+            }
+
+            if (ret == NXT_OK) {
                 if (njs_vm_retval(nvm, &s) != NXT_OK) {
                     return NXT_ERROR;
                 }
@@ -4329,7 +4349,7 @@ njs_unit_test_benchmark(nxt_str_t *scrip
 
     start = script->data;
 
-    ret = njs_vm_compile(vm, &start, start + script->len);
+    ret = njs_vm_compile(vm, &start, start + script->len, NULL);
     if (ret != NXT_OK) {
         return NXT_ERROR;
     }


More information about the nginx-devel mailing list