[njs] Externals refactored.

Dmitry Volyntsev xeioex at nginx.com
Tue Feb 20 16:19:28 UTC 2018


details:   http://hg.nginx.org/njs/rev/e7bc9d328a20
branches:  
changeset: 448:e7bc9d328a20
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Feb 20 19:12:53 2018 +0300
description:
Externals refactored.

Public API is rectified to allow the creation of external objects in
runtime.
    1) njs_vm_external_add() is replaced with njs_vm_external_prototype().
    The later functions returns a pointer to a prototype object which can
    be used to create a value with such a prototype in runtime.

    2) njs_vm_external() is split into njs_vm_external_create() and
    njs_vm_external_bind(). The former creates a variable with a specified
    prototype and associates it with an external pointer. The latter binds
    a variable to a name in the global namespace.

diffstat:

 nginx/ngx_http_js_module.c      |  212 ++++++---------------
 nginx/ngx_stream_js_module.c    |  176 ++++-------------
 njs/njs.c                       |   75 +++----
 njs/njs_builtin.c               |   26 +-
 njs/njs_extern.c                |  209 +++++++++++++-------
 njs/njs_extern.h                |   14 +-
 njs/njs_parser.c                |    6 +-
 njs/njs_parser.h                |    3 +-
 njs/njs_vm.c                    |  168 ++++++++++-------
 njs/njs_vm.h                    |   19 +-
 njs/njscript.c                  |   71 ++++--
 njs/njscript.h                  |   22 +-
 njs/test/njs_benchmark.c        |  114 ++++-------
 njs/test/njs_interactive_test.c |   27 +-
 njs/test/njs_unit_test.c        |  381 +++++++++++++++++++++++++++------------
 nxt/nxt_array.h                 |    4 +
 nxt/nxt_lvlhsh.c                |    4 +-
 nxt/nxt_lvlhsh.h                |    6 +-
 18 files changed, 800 insertions(+), 737 deletions(-)

diffs (truncated from 2773 to 1000 lines):

diff -r 0a3645d22d22 -r e7bc9d328a20 nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c	Tue Feb 20 19:12:52 2018 +0300
+++ b/nginx/ngx_http_js_module.c	Tue Feb 20 19:12:53 2018 +0300
@@ -15,37 +15,21 @@
 #include <nxt_string.h>
 #include <nxt_stub.h>
 #include <nxt_array.h>
-#include <nxt_random.h>
-#include <nxt_lvlhsh.h>
-#include <nxt_mem_cache_pool.h>
 
 #include <njscript.h>
 
 
-#define NGX_HTTP_JS_MCP_CLUSTER_SIZE    (2 * ngx_pagesize)
-#define NGX_HTTP_JS_MCP_PAGE_ALIGNMENT  128
-#define NGX_HTTP_JS_MCP_PAGE_SIZE       512
-#define NGX_HTTP_JS_MCP_MIN_CHUNK_SIZE  16
-
-
-#define ngx_http_js_create_mem_cache_pool()                                   \
-    nxt_mem_cache_pool_create(&ngx_http_js_mem_cache_pool_proto, NULL, NULL,  \
-                              NGX_HTTP_JS_MCP_CLUSTER_SIZE,                   \
-                              NGX_HTTP_JS_MCP_PAGE_ALIGNMENT,                 \
-                              NGX_HTTP_JS_MCP_PAGE_SIZE,                      \
-                              NGX_HTTP_JS_MCP_MIN_CHUNK_SIZE)
+typedef struct {
+    njs_vm_t            *vm;
+    ngx_str_t            content;
+    const njs_extern_t  *req_proto;
+    const njs_extern_t  *res_proto;
+} ngx_http_js_loc_conf_t;
 
 
 typedef struct {
     njs_vm_t            *vm;
     njs_opaque_value_t   args[2];
-    ngx_str_t            content;
-} ngx_http_js_loc_conf_t;
-
-
-typedef struct {
-    njs_vm_t            *vm;
-    njs_opaque_value_t  *args;
 } ngx_http_js_ctx_t;
 
 
@@ -59,12 +43,7 @@ static ngx_int_t ngx_http_js_handler(ngx
 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_init_vm(ngx_http_request_t *r);
-static void ngx_http_js_cleanup_mem_cache_pool(void *data);
-
-static void *ngx_http_js_alloc(void *mem, size_t size);
-static void *ngx_http_js_calloc(void *mem, size_t size);
-static void *ngx_http_js_memalign(void *mem, size_t alignment, size_t size);
-static void ngx_http_js_free(void *mem, void *p);
+static void ngx_http_js_cleanup_vm(void *data);
 
 static njs_ret_t ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value,
     void *obj, uintptr_t data);
@@ -183,17 +162,6 @@ ngx_module_t  ngx_http_js_module = {
 };
 
 
-static const nxt_mem_proto_t  ngx_http_js_mem_cache_pool_proto = {
-    ngx_http_js_alloc,
-    ngx_http_js_calloc,
-    ngx_http_js_memalign,
-    NULL,
-    ngx_http_js_free,
-    NULL,
-    NULL,
-};
-
-
 static njs_external_t  ngx_http_js_ext_response[] = {
 
     { nxt_string("headers"),
@@ -284,18 +252,6 @@ static njs_external_t  ngx_http_js_ext_r
 
 static njs_external_t  ngx_http_js_ext_request[] = {
 
-    { nxt_string("response"),
-      NJS_EXTERN_OBJECT,
-      ngx_http_js_ext_response,
-      nxt_nitems(ngx_http_js_ext_response),
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
     { nxt_string("log"),
       NJS_EXTERN_METHOD,
       NULL,
@@ -396,7 +352,7 @@ static njs_external_t  ngx_http_js_ext_r
 
 static njs_external_t  ngx_http_js_externals[] = {
 
-    { nxt_string("$r"),
+    { nxt_string("request"),
       NJS_EXTERN_OBJECT,
       ngx_http_js_ext_request,
       nxt_nitems(ngx_http_js_ext_request),
@@ -407,6 +363,18 @@ static njs_external_t  ngx_http_js_exter
       NULL,
       NULL,
       0 },
+
+    { nxt_string("response"),
+      NJS_EXTERN_OBJECT,
+      ngx_http_js_ext_response,
+      nxt_nitems(ngx_http_js_ext_response),
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      0 },
 };
 
 
@@ -520,11 +488,10 @@ ngx_http_js_variable(ngx_http_request_t 
 static ngx_int_t
 ngx_http_js_init_vm(ngx_http_request_t *r)
 {
-    void                    **ext;
-    ngx_http_js_ctx_t        *ctx;
-    ngx_pool_cleanup_t       *cln;
-    nxt_mem_cache_pool_t     *mcp;
-    ngx_http_js_loc_conf_t   *jlcf;
+    nxt_int_t                rc;
+    ngx_http_js_ctx_t       *ctx;
+    ngx_pool_cleanup_t      *cln;
+    ngx_http_js_loc_conf_t  *jlcf;
 
     jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
     if (jlcf->vm == NULL) {
@@ -546,8 +513,8 @@ ngx_http_js_init_vm(ngx_http_request_t *
         return NGX_OK;
     }
 
-    mcp = ngx_http_js_create_mem_cache_pool();
-    if (mcp == NULL) {
+    ctx->vm = njs_vm_clone(jlcf->vm, r);
+    if (ctx->vm == NULL) {
         return NGX_ERROR;
     }
 
@@ -556,65 +523,33 @@ ngx_http_js_init_vm(ngx_http_request_t *
         return NGX_ERROR;
     }
 
-    cln->handler = ngx_http_js_cleanup_mem_cache_pool;
-    cln->data = mcp;
-
-    ext = ngx_palloc(r->pool, sizeof(void *));
-    if (ext == NULL) {
-        return NGX_ERROR;
-    }
-
-    *ext = r;
-
-    ctx->vm = njs_vm_clone(jlcf->vm, mcp, ext);
-    if (ctx->vm == NULL) {
-        return NGX_ERROR;
-    }
+    cln->handler = ngx_http_js_cleanup_vm;
+    cln->data = ctx->vm;
 
     if (njs_vm_run(ctx->vm) != NJS_OK) {
         return NGX_ERROR;
     }
 
-    ctx->args = &jlcf->args[0];
+    rc = njs_vm_external_create(ctx->vm, &ctx->args[0], jlcf->req_proto, r);
+    if (rc != NXT_OK) {
+        return NGX_ERROR;
+    }
+
+    rc = njs_vm_external_create(ctx->vm, &ctx->args[1], jlcf->res_proto, r);
+    if (rc != NXT_OK) {
+        return NGX_ERROR;
+    }
 
     return NGX_OK;
 }
 
 
 static void
-ngx_http_js_cleanup_mem_cache_pool(void *data)
+ngx_http_js_cleanup_vm(void *data)
 {
-    nxt_mem_cache_pool_t *mcp = data;
-
-    nxt_mem_cache_pool_destroy(mcp);
-}
-
-
-static void *
-ngx_http_js_alloc(void *mem, size_t size)
-{
-    return ngx_alloc(size, ngx_cycle->log);
-}
-
+    njs_vm_t *vm = data;
 
-static void *
-ngx_http_js_calloc(void *mem, size_t size)
-{
-    return ngx_calloc(size, ngx_cycle->log);
-}
-
-
-static void *
-ngx_http_js_memalign(void *mem, size_t alignment, size_t size)
-{
-    return ngx_memalign(alignment, size, ngx_cycle->log);
-}
-
-
-static void
-ngx_http_js_free(void *mem, void *p)
-{
-    ngx_free(p);
+    njs_vm_destroy(vm);
 }
 
 
@@ -1229,12 +1164,10 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_
     ngx_fd_t               fd;
     ngx_str_t             *value, file;
     nxt_int_t              rc;
-    nxt_str_t              text, ext;
+    nxt_str_t              text;
     njs_vm_opt_t           options;
-    nxt_lvlhsh_t           externals;
     ngx_file_info_t        fi;
     ngx_pool_cleanup_t    *cln;
-    nxt_mem_cache_pool_t  *mcp;
 
     if (jlcf->vm) {
         return "is duplicate";
@@ -1295,8 +1228,13 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_
 
     end = start + size;
 
-    mcp = ngx_http_js_create_mem_cache_pool();
-    if (mcp == NULL) {
+    ngx_memzero(&options, sizeof(njs_vm_opt_t));
+
+    options.backtrace = 1;
+
+    jlcf->vm = njs_vm_create(&options);
+    if (jlcf->vm == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM");
         return NGX_CONF_ERROR;
     }
 
@@ -1305,28 +1243,21 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_
         return NGX_CONF_ERROR;
     }
 
-    cln->handler = ngx_http_js_cleanup_mem_cache_pool;
-    cln->data = mcp;
-
-    nxt_lvlhsh_init(&externals);
+    cln->handler = ngx_http_js_cleanup_vm;
+    cln->data = jlcf->vm;
 
-    if (njs_vm_external_add(&externals, mcp, 0, ngx_http_js_externals,
-                            nxt_nitems(ngx_http_js_externals))
-        != NJS_OK)
-    {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "could not add js externals");
+    jlcf->req_proto = njs_vm_external_prototype(jlcf->vm,
+                                                &ngx_http_js_externals[0]);
+    if (jlcf->req_proto == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add request proto");
         return NGX_CONF_ERROR;
     }
 
-    ngx_memzero(&options, sizeof(njs_vm_opt_t));
-
-    options.mcp = mcp;
-    options.backtrace = 1;
-    options.externals_hash = &externals;
-
-    jlcf->vm = njs_vm_create(&options);
-    if (jlcf->vm == NULL) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM");
+    jlcf->res_proto = njs_vm_external_prototype(jlcf->vm,
+                                                &ngx_http_js_externals[1]);
+    if (jlcf->res_proto == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "failed to add response proto");
         return NGX_CONF_ERROR;
     }
 
@@ -1348,25 +1279,6 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_
         return NGX_CONF_ERROR;
     }
 
-    ext = nxt_string_value("$r");
-
-    if (njs_vm_external(jlcf->vm, NULL, &ext, &jlcf->args[0]) != NJS_OK) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "js external \"%*s\" not found",
-                           ext.length, ext.start);
-        return NGX_CONF_ERROR;
-    }
-
-    ext = nxt_string_value("response");
-
-    rc = njs_vm_external(jlcf->vm, &jlcf->args[0], &ext, &jlcf->args[1]);
-    if (rc != NXT_OK) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "js external \"$r.%*s\" not found",
-                           ext.length, ext.start);
-        return NGX_CONF_ERROR;
-    }
-
     return NGX_CONF_OK;
 }
 
@@ -1443,6 +1355,8 @@ ngx_http_js_create_loc_conf(ngx_conf_t *
      * set by ngx_pcalloc():
      *
      *     conf->vm = NULL;
+     *     conf->req_proto = NULL;
+     *     conf->res_proto = NULL;
      */
 
     return conf;
@@ -1457,8 +1371,8 @@ ngx_http_js_merge_loc_conf(ngx_conf_t *c
 
     if (conf->vm == NULL) {
         conf->vm = prev->vm;
-        conf->args[0] = prev->args[0];
-        conf->args[1] = prev->args[1];
+        conf->req_proto = prev->req_proto;
+        conf->res_proto = prev->res_proto;
     }
 
     return NGX_CONF_OK;
diff -r 0a3645d22d22 -r e7bc9d328a20 nginx/ngx_stream_js_module.c
--- a/nginx/ngx_stream_js_module.c	Tue Feb 20 19:12:52 2018 +0300
+++ b/nginx/ngx_stream_js_module.c	Tue Feb 20 19:12:53 2018 +0300
@@ -15,39 +15,22 @@
 #include <nxt_string.h>
 #include <nxt_stub.h>
 #include <nxt_array.h>
-#include <nxt_random.h>
-#include <nxt_lvlhsh.h>
-#include <nxt_mem_cache_pool.h>
 
 #include <njscript.h>
 
 
-#define NGX_STREAM_JS_MCP_CLUSTER_SIZE    (2 * ngx_pagesize)
-#define NGX_STREAM_JS_MCP_PAGE_ALIGNMENT  128
-#define NGX_STREAM_JS_MCP_PAGE_SIZE       512
-#define NGX_STREAM_JS_MCP_MIN_CHUNK_SIZE  16
-
-
-#define ngx_stream_js_create_mem_cache_pool()                                 \
-    nxt_mem_cache_pool_create(&ngx_stream_js_mem_cache_pool_proto, NULL, NULL,\
-                              NGX_STREAM_JS_MCP_CLUSTER_SIZE,                 \
-                              NGX_STREAM_JS_MCP_PAGE_ALIGNMENT,               \
-                              NGX_STREAM_JS_MCP_PAGE_SIZE,                    \
-                              NGX_STREAM_JS_MCP_MIN_CHUNK_SIZE)
+typedef struct {
+    njs_vm_t              *vm;
+    ngx_str_t              access;
+    ngx_str_t              preread;
+    ngx_str_t              filter;
+    const njs_extern_t    *proto;
+} ngx_stream_js_srv_conf_t;
 
 
 typedef struct {
     njs_vm_t              *vm;
     njs_opaque_value_t     arg;
-    ngx_str_t              access;
-    ngx_str_t              preread;
-    ngx_str_t              filter;
-} ngx_stream_js_srv_conf_t;
-
-
-typedef struct {
-    njs_vm_t              *vm;
-    njs_opaque_value_t    *arg;
     ngx_buf_t             *buf;
     ngx_chain_t           *free;
     ngx_chain_t           *busy;
@@ -65,13 +48,8 @@ static ngx_int_t ngx_stream_js_body_filt
     ngx_chain_t *in, ngx_uint_t from_upstream);
 static ngx_int_t ngx_stream_js_variable(ngx_stream_session_t *s,
     ngx_stream_variable_value_t *v, uintptr_t data);
-static void ngx_stream_js_cleanup_mem_cache_pool(void *data);
 static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s);
-
-static void *ngx_stream_js_alloc(void *mem, size_t size);
-static void *ngx_stream_js_calloc(void *mem, size_t size);
-static void *ngx_stream_js_memalign(void *mem, size_t alignment, size_t size);
-static void ngx_stream_js_free(void *mem, void *p);
+static void ngx_stream_js_cleanup_vm(void *data);
 
 static njs_ret_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm,
     njs_value_t *value, void *obj, uintptr_t data);
@@ -169,17 +147,6 @@ ngx_module_t  ngx_stream_js_module = {
 };
 
 
-static const nxt_mem_proto_t  ngx_stream_js_mem_cache_pool_proto = {
-    ngx_stream_js_alloc,
-    ngx_stream_js_calloc,
-    ngx_stream_js_memalign,
-    NULL,
-    ngx_stream_js_free,
-    NULL,
-    NULL,
-};
-
-
 static njs_external_t  ngx_stream_js_ext_session[] = {
 
     { nxt_string("remoteAddress"),
@@ -318,7 +285,7 @@ static njs_external_t  ngx_stream_js_ext
 
 static njs_external_t  ngx_stream_js_externals[] = {
 
-    { nxt_string("$s"),
+    { nxt_string("stream"),
       NJS_EXTERN_OBJECT,
       ngx_stream_js_ext_session,
       nxt_nitems(ngx_stream_js_ext_session),
@@ -405,7 +372,7 @@ ngx_stream_js_phase_handler(ngx_stream_s
         return NGX_ERROR;
     }
 
-    if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) {
+    if (njs_vm_call(ctx->vm, func, &ctx->arg, 1) != NJS_OK) {
         njs_vm_retval_to_ext_string(ctx->vm, &exception);
 
         ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %*s",
@@ -492,7 +459,7 @@ ngx_stream_js_body_filter(ngx_stream_ses
     while (in) {
         ctx->buf = in->buf;
 
-        if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) {
+        if (njs_vm_call(ctx->vm, func, &ctx->arg, 1) != NJS_OK) {
             njs_vm_retval_to_ext_string(ctx->vm, &exception);
 
             ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %*s",
@@ -590,7 +557,7 @@ ngx_stream_js_variable(ngx_stream_sessio
         return NGX_OK;
     }
 
-    if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) {
+    if (njs_vm_call(ctx->vm, func, &ctx->arg, 1) != NJS_OK) {
         njs_vm_retval_to_ext_string(ctx->vm, &exception);
 
         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
@@ -617,11 +584,10 @@ ngx_stream_js_variable(ngx_stream_sessio
 static ngx_int_t
 ngx_stream_js_init_vm(ngx_stream_session_t *s)
 {
-    void                      **ext;
-    ngx_pool_cleanup_t         *cln;
-    nxt_mem_cache_pool_t       *mcp;
-    ngx_stream_js_ctx_t        *ctx;
-    ngx_stream_js_srv_conf_t   *jscf;
+    nxt_int_t                  rc;
+    ngx_pool_cleanup_t        *cln;
+    ngx_stream_js_ctx_t       *ctx;
+    ngx_stream_js_srv_conf_t  *jscf;
 
     jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
     if (jscf->vm == NULL) {
@@ -643,8 +609,8 @@ ngx_stream_js_init_vm(ngx_stream_session
         return NGX_OK;
     }
 
-    mcp = ngx_stream_js_create_mem_cache_pool();
-    if (mcp == NULL) {
+    ctx->vm = njs_vm_clone(jscf->vm, s);
+    if (ctx->vm == NULL) {
         return NGX_ERROR;
     }
 
@@ -653,65 +619,28 @@ ngx_stream_js_init_vm(ngx_stream_session
         return NGX_ERROR;
     }
 
-    cln->handler = ngx_stream_js_cleanup_mem_cache_pool;
-    cln->data = mcp;
-
-    ext = ngx_palloc(s->connection->pool, sizeof(void *));
-    if (ext == NULL) {
-        return NGX_ERROR;
-    }
-
-    *ext = s;
-
-    ctx->vm = njs_vm_clone(jscf->vm, mcp, ext);
-    if (ctx->vm == NULL) {
-        return NGX_ERROR;
-    }
+    cln->handler = ngx_stream_js_cleanup_vm;
+    cln->data = ctx->vm;
 
     if (njs_vm_run(ctx->vm) != NJS_OK) {
         return NGX_ERROR;
     }
 
-    ctx->arg = &jscf->arg;
+    rc = njs_vm_external_create(ctx->vm, &ctx->arg, jscf->proto, s);
+    if (rc != NXT_OK) {
+        return NGX_ERROR;
+    }
 
     return NGX_OK;
 }
 
 
 static void
-ngx_stream_js_cleanup_mem_cache_pool(void *data)
+ngx_stream_js_cleanup_vm(void *data)
 {
-    nxt_mem_cache_pool_t *mcp = data;
-
-    nxt_mem_cache_pool_destroy(mcp);
-}
-
-
-static void *
-ngx_stream_js_alloc(void *mem, size_t size)
-{
-    return ngx_alloc(size, ngx_cycle->log);
-}
-
+    njs_vm_t *vm = data;
 
-static void *
-ngx_stream_js_calloc(void *mem, size_t size)
-{
-    return ngx_calloc(size, ngx_cycle->log);
-}
-
-
-static void *
-ngx_stream_js_memalign(void *mem, size_t alignment, size_t size)
-{
-    return ngx_memalign(alignment, size, ngx_cycle->log);
-}
-
-
-static void
-ngx_stream_js_free(void *mem, void *p)
-{
-    ngx_free(p);
+    njs_vm_destroy(vm);
 }
 
 
@@ -937,12 +866,10 @@ ngx_stream_js_include(ngx_conf_t *cf, ng
     ngx_fd_t               fd;
     ngx_str_t             *value, file;
     nxt_int_t              rc;
-    nxt_str_t              text, ext;
+    nxt_str_t              text;
     njs_vm_opt_t           options;
-    nxt_lvlhsh_t           externals;
     ngx_file_info_t        fi;
     ngx_pool_cleanup_t    *cln;
-    nxt_mem_cache_pool_t  *mcp;
 
     if (jscf->vm) {
         return "is duplicate";
@@ -1003,8 +930,13 @@ ngx_stream_js_include(ngx_conf_t *cf, ng
 
     end = start + size;
 
-    mcp = ngx_stream_js_create_mem_cache_pool();
-    if (mcp == NULL) {
+    ngx_memzero(&options, sizeof(njs_vm_opt_t));
+
+    options.backtrace = 1;
+
+    jscf->vm = njs_vm_create(&options);
+    if (jscf->vm == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM");
         return NGX_CONF_ERROR;
     }
 
@@ -1013,28 +945,14 @@ ngx_stream_js_include(ngx_conf_t *cf, ng
         return NGX_CONF_ERROR;
     }
 
-    cln->handler = ngx_stream_js_cleanup_mem_cache_pool;
-    cln->data = mcp;
-
-    nxt_lvlhsh_init(&externals);
+    cln->handler = ngx_stream_js_cleanup_vm;
+    cln->data = jscf->vm;
 
-    if (njs_vm_external_add(&externals, mcp, 0, ngx_stream_js_externals,
-                            nxt_nitems(ngx_stream_js_externals))
-        != NJS_OK)
-    {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "could not add js externals");
-        return NGX_CONF_ERROR;
-    }
+    jscf->proto = njs_vm_external_prototype(jscf->vm,
+                                            &ngx_stream_js_externals[0]);
 
-    ngx_memzero(&options, sizeof(njs_vm_opt_t));
-
-    options.mcp = mcp;
-    options.backtrace = 1;
-    options.externals_hash = &externals;
-
-    jscf->vm = njs_vm_create(&options);
-    if (jscf->vm == NULL) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM");
+    if (jscf->proto == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add stream proto");
         return NGX_CONF_ERROR;
     }
 
@@ -1056,14 +974,6 @@ ngx_stream_js_include(ngx_conf_t *cf, ng
         return NGX_CONF_ERROR;
     }
 
-    ext = nxt_string_value("$s");
-
-    if (njs_vm_external(jscf->vm, NULL, &ext, &jscf->arg) != NJS_OK) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                       "js external \"%*s\" not found", ext.length, ext.start);
-        return NGX_CONF_ERROR;
-    }
-
     return NGX_CONF_OK;
 }
 
@@ -1118,7 +1028,7 @@ ngx_stream_js_create_srv_conf(ngx_conf_t
      * set by ngx_pcalloc():
      *
      *     conf->vm = NULL;
-     *     conf->arg = NULL;
+     *     conf->proto = NULL;
      *     conf->access = { 0, NULL };
      *     conf->preread = { 0, NULL };
      *     conf->filter = { 0, NULL };
@@ -1136,7 +1046,7 @@ ngx_stream_js_merge_srv_conf(ngx_conf_t 
 
     if (conf->vm == NULL) {
         conf->vm = prev->vm;
-        conf->arg = prev->arg;
+        conf->proto = prev->proto;
     }
 
     ngx_conf_merge_str_value(conf->access, prev->access, "");
diff -r 0a3645d22d22 -r e7bc9d328a20 njs/njs.c
--- a/njs/njs.c	Tue Feb 20 19:12:52 2018 +0300
+++ b/njs/njs.c	Tue Feb 20 19:12:53 2018 +0300
@@ -60,7 +60,7 @@ typedef struct {
 
 
 static nxt_int_t njs_get_options(njs_opts_t *opts, int argc, char **argv);
-static nxt_int_t njs_externals_init(njs_opts_t *opts, njs_vm_opt_t *vm_options);
+static nxt_int_t njs_externals_init(njs_vm_t *vm);
 static nxt_int_t njs_interactive_shell(njs_opts_t *opts,
     njs_vm_opt_t *vm_options);
 static nxt_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options);
@@ -119,17 +119,15 @@ static njs_external_t  njs_externals[] =
 };
 
 
-static nxt_lvlhsh_t      njs_externals_hash;
 static njs_completion_t  njs_completion;
 
 
 int
 main(int argc, char **argv)
 {
-    nxt_int_t             ret;
-    njs_opts_t            opts;
-    njs_vm_opt_t          vm_options;
-    nxt_mem_cache_pool_t  *mcp;
+    nxt_int_t     ret;
+    njs_opts_t    opts;
+    njs_vm_opt_t  vm_options;
 
     memset(&opts, 0, sizeof(njs_opts_t));
     opts.interactive = 1;
@@ -144,22 +142,11 @@ main(int argc, char **argv)
         return EXIT_SUCCESS;
     }
 
-    mcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL,
-                                    NULL, 2 * nxt_pagesize(), 128, 512, 16);
-    if (nxt_slow_path(mcp == NULL)) {
-        return EXIT_FAILURE;
-    }
-
     memset(&vm_options, 0, sizeof(njs_vm_opt_t));
 
-    vm_options.mcp = mcp;
     vm_options.accumulative = 1;
     vm_options.backtrace = 1;
 
-    if (njs_externals_init(&opts, &vm_options) != NXT_OK) {
-        return EXIT_FAILURE;
-    }
-
     if (opts.interactive) {
         ret = njs_interactive_shell(&opts, &vm_options);
 
@@ -218,30 +205,35 @@ njs_get_options(njs_opts_t *opts, int ar
 
 
 static nxt_int_t
-njs_externals_init(njs_opts_t *opts, njs_vm_opt_t *vm_options)
+njs_externals_init(njs_vm_t *vm)
 {
-    void        **ext;
-    nxt_uint_t  i;
-
-    nxt_lvlhsh_init(&njs_externals_hash);
+    nxt_uint_t          ret;
+    const njs_extern_t  *proto;
+    njs_opaque_value_t  *value;
 
-    for (i = 0; i < nxt_nitems(njs_externals); i++) {
-        if (njs_vm_external_add(&njs_externals_hash, vm_options->mcp,
-                                (uintptr_t) i, &njs_externals[i], 1)
-            != NXT_OK)
-        {
-            fprintf(stderr, "could not add external objects\n");
-            return NXT_ERROR;
-        }
-    }
+    static const nxt_str_t name = nxt_string_value("console");
 
-    ext = nxt_mem_cache_zalloc(vm_options->mcp, sizeof(void *) * i);
-    if (ext == NULL) {
+    proto = njs_vm_external_prototype(vm, &njs_externals[0]);
+    if (proto == NULL) {
+        fprintf(stderr, "failed to add console proto\n");
         return NXT_ERROR;
     }
 
-    vm_options->external = ext;
-    vm_options->externals_hash = &njs_externals_hash;
+    value = nxt_mem_cache_zalloc(vm->mem_cache_pool,
+                                 sizeof(njs_opaque_value_t));
+    if (value == NULL) {
+        return NXT_ERROR;
+    }
+
+    ret = njs_vm_external_create(vm, value, proto, NULL);
+    if (ret != NXT_OK) {
+        return NXT_ERROR;
+    }
+
+    ret = njs_vm_external_bind(vm, &name, value);
+    if (ret != NXT_OK) {
+        return NXT_ERROR;
+    }
 
     return NXT_OK;
 }
@@ -260,6 +252,10 @@ njs_interactive_shell(njs_opts_t *opts, 
         return NXT_ERROR;
     }
 
+    if (njs_externals_init(vm) != NXT_OK) {
+        return NXT_ERROR;
+    }
+
     if (njs_editline_init(vm) != NXT_OK) {
         fprintf(stderr, "failed to init completions\n");
         return NXT_ERROR;
@@ -393,6 +389,10 @@ njs_process_file(njs_opts_t *opts, njs_v
         goto done;
     }
 
+    if (njs_externals_init(vm) != NXT_OK) {
+        return NXT_ERROR;
+    }
+
     ret = njs_process_script(vm, opts, &script, &out);
     if (ret != NXT_OK) {
         fprintf(stderr, "failed to get retval from VM\n");
@@ -652,10 +652,7 @@ njs_ext_console_help(njs_vm_t *vm, njs_v
     }
 
     printf("\nEmbedded objects:\n");
-    for (i = 0; i < nxt_nitems(njs_externals); i++) {
-        printf("  %.*s\n", (int) njs_externals[i].name.length,
-               njs_externals[i].name.start);
-    }
+    printf("  console\n");
 
     printf("\n");
 
diff -r 0a3645d22d22 -r e7bc9d328a20 njs/njs_builtin.c
--- a/njs/njs_builtin.c	Tue Feb 20 19:12:52 2018 +0300
+++ b/njs/njs_builtin.c	Tue Feb 20 19:12:53 2018 +0300
@@ -513,12 +513,13 @@ njs_builtin_completions(njs_vm_t *vm, si
     size_t                  n, len;
     nxt_str_t               string;
     nxt_uint_t              i, k;
-    njs_extern_t            *ext_object, *ext_prop;
     njs_object_t            *objects;
     njs_keyword_t           *keyword;
     njs_function_t          *constructors;
     njs_object_prop_t       *prop;
     nxt_lvlhsh_each_t       lhe, lhe_prop;
+    njs_extern_value_t      *ev;
+    const njs_extern_t      *ext_proto, *ext_prop;
     njs_object_prototype_t  *prototypes;
 
     n = 0;
@@ -652,26 +653,27 @@ njs_builtin_completions(njs_vm_t *vm, si
         }
     }
 
-    nxt_lvlhsh_each_init(&lhe, &njs_extern_hash_proto);
+    nxt_lvlhsh_each_init(&lhe, &njs_extern_value_hash_proto);
 
     for ( ;; ) {
-        ext_object = nxt_lvlhsh_each(&vm->externals_hash, &lhe);
+        ev = nxt_lvlhsh_each(&vm->externals_hash, &lhe);
 
-        if (ext_object == NULL) {
+        if (ev == NULL) {
             break;
         }
 
+        ext_proto = ev->value->external.proto;
+
         nxt_lvlhsh_each_init(&lhe_prop, &njs_extern_hash_proto);
 
         if (completions != NULL) {
-            len = ext_object->name.length + 1;
+            len = ev->name.length + 1;
             compl = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
             if (compl == NULL) {
                 return NXT_ERROR;
             }
 
-            snprintf(compl, len, "%.*s",
-                     (int) ext_object->name.length, ext_object->name.start);
+            snprintf(compl, len, "%.*s", (int) ev->name.length, ev->name.start);
 
             completions[n].length = len;
             completions[n++].start = (u_char *) compl;
@@ -681,22 +683,22 @@ njs_builtin_completions(njs_vm_t *vm, si
         }
 
         for ( ;; ) {
-            ext_prop = nxt_lvlhsh_each(&ext_object->hash, &lhe_prop);
+            ext_prop = nxt_lvlhsh_each(&ext_proto->hash, &lhe_prop);
 
             if (ext_prop == NULL) {
                 break;
             }
 
             if (completions != NULL) {
-                len = ext_object->name.length + ext_prop->name.length + 2;
+                len = ev->name.length + ev->name.length + 2;
                 compl = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
                 if (compl == NULL) {
                     return NXT_ERROR;
                 }
 
-                snprintf(compl, len, "%.*s.%.*s",
-                         (int) ext_object->name.length, ext_object->name.start,
-                         (int) ext_prop->name.length, ext_prop->name.start);
+                snprintf(compl, len, "%.*s.%.*s", (int) ev->name.length,
+                         ev->name.start, (int) ext_prop->name.length,
+                         ext_prop->name.start);
 
                 completions[n].length = len;
                 completions[n++].start = (u_char *) compl;
diff -r 0a3645d22d22 -r e7bc9d328a20 njs/njs_extern.c
--- a/njs/njs_extern.c	Tue Feb 20 19:12:52 2018 +0300
+++ b/njs/njs_extern.c	Tue Feb 20 19:12:53 2018 +0300
@@ -29,11 +29,24 @@ njs_extern_hash_test(nxt_lvlhsh_query_t 
 {
     njs_extern_t  *ext;
 
-    ext = data;
+    ext = (njs_extern_t *) data;
+
+    if (nxt_strstr_eq(&lhq->key, &ext->name)) {
+        return NXT_OK;
+    }
+
+    return NXT_DECLINED;
+}
 
-// STUB
-//    if (nxt_strcasestr_eq(&lhq->key, &ext->name)) {
-    if (nxt_strstr_eq(&lhq->key, &ext->name)) {
+
+static nxt_int_t
+njs_extern_value_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+    njs_extern_value_t  *ev;
+
+    ev = (njs_extern_value_t *) data;
+
+    if (nxt_strstr_eq(&lhq->key, &ev->name)) {
         return NXT_OK;
     }
 
@@ -52,71 +65,78 @@ const nxt_lvlhsh_proto_t  njs_extern_has
 };
 
 
-nxt_int_t
-njs_vm_external_add(nxt_lvlhsh_t *hash, nxt_mem_cache_pool_t *mcp,
-    uintptr_t object, njs_external_t *external, nxt_uint_t n)
+const nxt_lvlhsh_proto_t  njs_extern_value_hash_proto
+    nxt_aligned(64) =
+{
+    NXT_LVLHSH_DEFAULT,
+    NXT_LVLHSH_BATCH_ALLOC,
+    njs_extern_value_hash_test,
+    njs_lvlhsh_alloc,
+    njs_lvlhsh_free,
+};
+
+
+static njs_extern_t *
+njs_vm_external_add(njs_vm_t *vm, nxt_lvlhsh_t *hash, njs_external_t *external,
+    nxt_uint_t n)
 {
     nxt_int_t           ret;
-    njs_extern_t        *ext;
+    njs_extern_t        *ext, *child;
     nxt_lvlhsh_query_t  lhq;
 
     do {
-        ext = nxt_mem_cache_align(mcp, sizeof(njs_value_t),
-                                 sizeof(njs_extern_t));
+        ext = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_extern_t));
         if (nxt_slow_path(ext == NULL)) {
-            return NXT_ERROR;
-        }
-
-        ext->name.length = external->name.length;
-        ext->name.start = nxt_mem_cache_alloc(mcp, external->name.length);
-        if (nxt_slow_path(ext->name.start == NULL)) {
-            return NXT_ERROR;
+            return NULL;
         }
 
-        memcpy(ext->name.start, external->name.start, external->name.length);
-
-        ext->value.type = NJS_EXTERNAL;
-        ext->value.data.truth = 1;
-        ext->value.data.u.external = ext;
-
-        if (external->method != NULL) {
-            ext->function = nxt_mem_cache_zalloc(mcp, sizeof(njs_function_t));
-            if (nxt_slow_path(ext->function == NULL)) {
-                return NXT_ERROR;
-            }
-
-            ext->function->native = 1;
-            ext->function->args_offset = 1;
-            ext->function->u.native = external->method;
-        }
+        ext->name = external->name;
 
         nxt_lvlhsh_init(&ext->hash);
+
         ext->type = external->type;
         ext->get = external->get;
         ext->set = external->set;
         ext->find = external->find;
         ext->foreach = external->foreach;
         ext->next = external->next;
-        ext->object = object;
         ext->data = external->data;
 
-        lhq.key_hash = nxt_djb_hash(external->name.start, external->name.length);
-        lhq.key = ext->name;
-        lhq.replace = 0;
-        lhq.value = ext;
-        lhq.pool = mcp;


More information about the nginx-devel mailing list