[njs] Modules: introduced engine API.

noreply at nginx.com noreply at nginx.com
Wed Sep 18 01:06:02 UTC 2024


details:   https://github.com/nginx/njs/commit/9b6744129ce4c18e74b5e41cd1da66f2cbadcf36
branches:  master
commit:    9b6744129ce4c18e74b5e41cd1da66f2cbadcf36
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon, 10 Jun 2024 22:58:24 -0700
description:
Modules: introduced engine API.

No functional changes.

---
 nginx/ngx_http_js_module.c   | 337 ++++++++++++++-------------
 nginx/ngx_js.c               | 533 ++++++++++++++++++++++++++++++++-----------
 nginx/ngx_js.h               |  98 ++++++--
 nginx/ngx_js_fetch.c         |  12 +-
 nginx/ngx_stream_js_module.c | 385 +++++++++++++++++--------------
 5 files changed, 873 insertions(+), 492 deletions(-)

diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c
index 7f2eded2..35f988d0 100644
--- a/nginx/ngx_http_js_module.c
+++ b/nginx/ngx_http_js_module.c
@@ -48,14 +48,13 @@ typedef struct {
 #define NJS_HEADER_ARRAY       0x4
 
 
-typedef struct {
+typedef struct ngx_http_js_ctx_s  ngx_http_js_ctx_t;
+
+struct ngx_http_js_ctx_s {
     NGX_JS_COMMON_CTX;
-    ngx_log_t             *log;
     ngx_uint_t             done;
     ngx_int_t              status;
-    njs_opaque_value_t     retval;
-    njs_opaque_value_t     request;
-    njs_opaque_value_t     args;
+    njs_opaque_value_t     rargs;
     njs_opaque_value_t     request_body;
     njs_opaque_value_t     response_body;
     ngx_str_t              redirect_uri;
@@ -65,9 +64,13 @@ typedef struct {
     ngx_chain_t          **last_out;
     ngx_chain_t           *free;
     ngx_chain_t           *busy;
+    ngx_int_t            (*body_filter)(ngx_http_request_t *r,
+                                        ngx_http_js_loc_conf_t *jlcf,
+                                        ngx_http_js_ctx_t *ctx,
+                                        ngx_chain_t *in);
 
     ngx_js_periodic_t     *periodic;
-} ngx_http_js_ctx_t;
+};
 
 
 typedef struct {
@@ -299,6 +302,11 @@ static ngx_flag_t ngx_http_js_ssl_verify(ngx_http_request_t *r);
 static ngx_int_t ngx_http_js_parse_unsafe_uri(ngx_http_request_t *r,
     njs_str_t *uri, njs_str_t *args);
 
+static ngx_conf_bitmask_t  ngx_http_js_engines[] = {
+    { ngx_string("njs"), NGX_ENGINE_NJS },
+    { ngx_null_string, 0 }
+};
+
 #if (NGX_HTTP_SSL)
 
 static ngx_conf_bitmask_t  ngx_http_js_ssl_protocols[] = {
@@ -313,6 +321,13 @@ static ngx_conf_bitmask_t  ngx_http_js_ssl_protocols[] = {
 
 static ngx_command_t  ngx_http_js_commands[] = {
 
+    { ngx_string("js_engine"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_js_engine,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_js_loc_conf_t, type),
+      &ngx_http_js_engines },
+
     { ngx_string("js_import"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13,
       ngx_js_import,
@@ -960,8 +975,8 @@ ngx_http_js_content_event_handler(ngx_http_request_t *r)
 
     ctx->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
 
-    rc = ngx_js_name_call(ctx->vm, &jlcf->content, r->connection->log,
-                          &ctx->request, 1);
+    rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->content, &ctx->args[0],
+                           1);
 
     if (rc == NGX_ERROR) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -990,7 +1005,7 @@ ngx_http_js_content_write_event_handler(ngx_http_request_t *r)
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
 
-    if (!ngx_vm_pending(ctx)) {
+    if (!ngx_js_ctx_pending(ctx)) {
         ngx_http_js_content_finalize(r, ctx);
         return;
     }
@@ -1086,13 +1101,13 @@ ngx_http_js_header_filter(ngx_http_request_t *r)
     ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
 
     ctx->filter = 1;
-    pending = ngx_vm_pending(ctx);
+    pending = ngx_js_ctx_pending(ctx);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http js header call \"%V\"", &jlcf->header_filter);
 
-    rc = ngx_js_name_call(ctx->vm, &jlcf->header_filter, r->connection->log,
-                          &ctx->request, 1);
+    rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->header_filter,
+                           &ctx->args[0], 1);
 
     if (rc == NGX_ERROR) {
         return NGX_ERROR;
@@ -1110,51 +1125,28 @@ ngx_http_js_header_filter(ngx_http_request_t *r)
 
 
 static ngx_int_t
-ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+ngx_http_njs_body_filter(ngx_http_request_t *r, ngx_http_js_loc_conf_t *jlcf,
+    ngx_http_js_ctx_t *ctx, ngx_chain_t *in)
 {
-    size_t                   len;
-    u_char                  *p;
-    ngx_int_t                rc;
-    njs_int_t                ret, pending;
-    ngx_buf_t               *b;
-    ngx_chain_t             *out, *cl;
-    ngx_connection_t        *c;
-    ngx_http_js_ctx_t       *ctx;
-    njs_opaque_value_t       last_key, last;
-    ngx_http_js_loc_conf_t  *jlcf;
-    njs_opaque_value_t       arguments[3];
+    size_t               len;
+    u_char              *p;
+    njs_vm_t            *vm;
+    ngx_int_t            rc;
+    njs_int_t            ret, pending;
+    ngx_buf_t           *b;
+    ngx_chain_t         *cl;
+    ngx_connection_t    *c;
+    njs_opaque_value_t   last_key, last;
+    njs_opaque_value_t   arguments[3];
 
     static const njs_str_t last_str = njs_str("last");
 
-    jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
-
-    if (jlcf->body_filter.len == 0 || in == NULL) {
-        return ngx_http_next_body_filter(r, in);
-    }
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http js body filter");
-
-    rc = ngx_http_js_init_vm(r, ngx_http_js_request_proto_id);
-
-    if (rc == NGX_ERROR || rc == NGX_DECLINED) {
-        return NGX_ERROR;
-    }
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
-
-    if (ctx->done) {
-        return ngx_http_next_body_filter(r, in);
-    }
-
     c = r->connection;
+    vm = ctx->engine->u.njs.vm;
 
-    ctx->filter = 1;
-    ctx->last_out = &out;
+    njs_value_assign(&arguments[0], &ctx->args[0]);
 
-    njs_value_assign(&arguments[0], &ctx->request);
-
-    njs_vm_value_string_create(ctx->vm, njs_value_arg(&last_key),
+    njs_vm_value_string_create(vm, njs_value_arg(&last_key),
                                last_str.start, last_str.length);
 
     while (in != NULL) {
@@ -1166,7 +1158,7 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 
             p = ngx_pnalloc(r->pool, len);
             if (p == NULL) {
-                njs_vm_memory_error(ctx->vm);
+                njs_vm_memory_error(vm);
                 return NJS_ERROR;
             }
 
@@ -1174,7 +1166,7 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
                 ngx_memcpy(p, b->pos, len);
             }
 
-            ret = ngx_js_prop(ctx->vm, jlcf->buffer_type,
+            ret = ngx_js_prop(vm, jlcf->buffer_type,
                               njs_value_arg(&arguments[1]), p, len);
             if (ret != NJS_OK) {
                 return ret;
@@ -1182,20 +1174,20 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 
             njs_value_boolean_set(njs_value_arg(&last), b->last_buf);
 
-            ret = njs_vm_object_alloc(ctx->vm, njs_value_arg(&arguments[2]),
+            ret = njs_vm_object_alloc(vm, njs_value_arg(&arguments[2]),
                                        njs_value_arg(&last_key),
                                        njs_value_arg(&last), NULL);
             if (ret != NJS_OK) {
                 return ret;
             }
 
-            pending = ngx_vm_pending(ctx);
+            pending = ngx_js_ctx_pending(ctx);
 
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http js body call \"%V\"", &jlcf->body_filter);
 
-            rc = ngx_js_name_call(ctx->vm, &jlcf->body_filter, c->log,
-                                  &arguments[0], 3);
+            rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->body_filter,
+                                   &arguments[0], 3);
 
             if (rc == NGX_ERROR) {
                 return NGX_ERROR;
@@ -1225,13 +1217,54 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
         in = in->next;
     }
 
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_int_t                rc;
+    ngx_chain_t             *out;
+    ngx_http_js_ctx_t       *ctx;
+    ngx_http_js_loc_conf_t  *jlcf;
+
+    jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
+
+    if (jlcf->body_filter.len == 0 || in == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http js body filter");
+
+    rc = ngx_http_js_init_vm(r, ngx_http_js_request_proto_id);
+
+    if (rc == NGX_ERROR || rc == NGX_DECLINED) {
+        return NGX_ERROR;
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
+
+    if (ctx->done) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx->filter = 1;
+    ctx->last_out = &out;
+
+    rc = ctx->body_filter(r, jlcf, ctx, in);
+    if (rc != NGX_OK) {
+        return NGX_ERROR;
+    }
+
     *ctx->last_out = NULL;
 
-    if (out != NULL || c->buffered) {
+    if (out != NULL || r->connection->buffered) {
         rc = ngx_http_next_body_filter(r, out);
 
-        ngx_chain_update_chains(c->pool, &ctx->free, &ctx->busy, &out,
-                                (ngx_buf_tag_t) &ngx_http_js_module);
+        ngx_chain_update_chains(r->connection->pool, &ctx->free, &ctx->busy,
+                                &out, (ngx_buf_tag_t) &ngx_http_js_module);
 
     } else {
         rc = NGX_OK;
@@ -1249,8 +1282,7 @@ ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v,
 
     ngx_int_t           rc;
     njs_int_t           pending;
-    ngx_str_t          *fname;
-    njs_str_t           value;
+    ngx_str_t          *fname, value;
     ngx_http_js_ctx_t  *ctx;
 
     fname = &vdata->fname;
@@ -1271,10 +1303,9 @@ ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v,
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
 
-    pending = ngx_vm_pending(ctx);
+    pending = ngx_js_ctx_pending(ctx);
 
-    rc = ngx_js_name_invoke(ctx->vm, fname, r->connection->log, &ctx->request,
-                            1, &ctx->retval);
+    rc = ctx->engine->call((ngx_js_ctx_t *) ctx, fname, &ctx->args[0], 1);
 
     if (rc == NGX_ERROR) {
         v->not_found = 1;
@@ -1287,15 +1318,15 @@ ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v,
         return NGX_ERROR;
     }
 
-    if (ngx_js_string(ctx->vm, njs_value_arg(&ctx->retval), &value) != NGX_OK) {
+    if (ctx->engine->string(ctx->engine, &ctx->retval, &value) != NGX_OK) {
         return NGX_ERROR;
     }
 
-    v->len = value.length;
+    v->len = value.len;
     v->valid = 1;
     v->no_cacheable = vdata->flags & NGX_NJS_VAR_NOCACHE;
     v->not_found = 0;
-    v->data = value.start;
+    v->data = value.data;
 
     return NGX_OK;
 }
@@ -1331,18 +1362,12 @@ ngx_http_js_variable_var(ngx_http_request_t *r, ngx_http_variable_value_t *v,
 static ngx_int_t
 ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id)
 {
-    njs_int_t                rc;
-    ngx_str_t                exception;
-    njs_str_t                key;
-    ngx_uint_t               i;
     ngx_http_js_ctx_t       *ctx;
-    njs_opaque_value_t       retval;
     ngx_pool_cleanup_t      *cln;
-    ngx_js_named_path_t     *preload;
     ngx_http_js_loc_conf_t  *jlcf;
 
     jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
-    if (jlcf->vm == NULL) {
+    if (jlcf->engine == NULL) {
         return NGX_DECLINED;
     }
 
@@ -1354,71 +1379,33 @@ ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id)
             return NGX_ERROR;
         }
 
-        ngx_js_ctx_init((ngx_js_ctx_t *) ctx);
-
-        njs_value_invalid_set(njs_value_arg(&ctx->retval));
+        ngx_js_ctx_init((ngx_js_ctx_t *) ctx, r->connection->log);
 
         ngx_http_set_ctx(r, ctx, ngx_http_js_module);
     }
 
-    if (ctx->vm) {
+    if (ctx->engine) {
         return NGX_OK;
     }
 
-    ctx->vm = njs_vm_clone(jlcf->vm, r);
-    if (ctx->vm == NULL) {
+    ctx->engine = jlcf->engine->clone((ngx_js_ctx_t *) ctx,
+                                      (ngx_js_loc_conf_t *) jlcf, proto_id, r);
+    if (ctx->engine == NULL) {
         return NGX_ERROR;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http js vm clone: %p from: %p", ctx->vm, jlcf->vm);
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+                   "http js vm clone %s: %p from: %p", jlcf->engine->name,
+                   ctx->engine, jlcf->engine);
 
     cln = ngx_pool_cleanup_add(r->pool, 0);
     if (cln == NULL) {
         return NGX_ERROR;
     }
 
-    ctx->log = r->connection->log;
-
     cln->handler = ngx_http_js_cleanup_ctx;
     cln->data = ctx;
 
-    /* bind objects from preload vm */
-
-    if (jlcf->preload_objects != NGX_CONF_UNSET_PTR) {
-        preload = jlcf->preload_objects->elts;
-
-        for (i = 0; i < jlcf->preload_objects->nelts; i++) {
-            key.start = preload[i].name.data;
-            key.length = preload[i].name.len;
-
-            rc = njs_vm_value(jlcf->preload_vm, &key, njs_value_arg(&retval));
-            if (rc != NJS_OK) {
-                return NGX_ERROR;
-            }
-
-            rc = njs_vm_bind(ctx->vm, &key, njs_value_arg(&retval), 0);
-            if (rc != NJS_OK) {
-                return NGX_ERROR;
-            }
-        }
-    }
-
-    if (njs_vm_start(ctx->vm, njs_value_arg(&retval)) == NJS_ERROR) {
-        ngx_js_exception(ctx->vm, &exception);
-
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "js exception: %V", &exception);
-
-        return NGX_ERROR;
-    }
-
-    rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->request),
-                                proto_id, r, 0);
-    if (rc != NJS_OK) {
-        return NGX_ERROR;
-    }
-
     return NGX_OK;
 }
 
@@ -1426,16 +1413,22 @@ ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id)
 static void
 ngx_http_js_cleanup_ctx(void *data)
 {
-    ngx_http_js_ctx_t *ctx = data;
+    ngx_http_request_t      *r;
+    ngx_http_js_loc_conf_t  *jlcf;
+
+    ngx_http_js_ctx_t        *ctx = data;
 
-    if (ngx_vm_pending(ctx)) {
+    if (ngx_js_ctx_pending(ctx)) {
         ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events");
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http js vm destroy: %p",
-                   ctx->vm);
+                   ctx->engine);
+
+    r = ngx_js_ctx_external(ctx);
+    jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
 
-    ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx);
+    ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx, (ngx_js_loc_conf_t *) jlcf);
 }
 
 
@@ -2657,7 +2650,7 @@ ngx_http_js_ext_get_args(njs_vm_t *vm, njs_object_prop_t *prop,
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
 
-    args = njs_value_arg(&ctx->args);
+    args = njs_value_arg(&ctx->rargs);
 
     if (njs_value_is_null(args)) {
         data = (r->args.len != 0) ? r->args.data : (u_char *) "";
@@ -3109,9 +3102,8 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_str_t                    uri, rargs;
     ngx_uint_t                   method, methods_max, has_body, detached,
                                  promise;
-    njs_value_t                 *value, *arg, *options;
+    njs_value_t                 *value, *arg, *options, *callback;
     ngx_js_event_t              *event;
-    njs_function_t              *callback;
     ngx_http_js_ctx_t           *ctx;
     njs_opaque_value_t           lvalue;
     ngx_http_request_t          *r, *sr;
@@ -3168,7 +3160,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
 
     } else if (njs_value_is_function(arg)) {
-        callback = njs_value_function(arg);
+        callback = arg;
 
     } else if (njs_value_is_object(arg)) {
         options = arg;
@@ -3237,7 +3229,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
             return NJS_ERROR;
 
         } else {
-            callback = njs_value_function(arg);
+            callback = arg;
         }
     }
 
@@ -3254,35 +3246,33 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     if (!detached) {
         ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
         if (ps == NULL) {
-            njs_vm_memory_error(ctx->vm);
+            njs_vm_memory_error(vm);
             return NJS_ERROR;
         }
 
         promise = !!(callback == NULL);
 
-        event = njs_mp_zalloc(njs_vm_memory_pool(ctx->vm),
+        event = njs_mp_zalloc(njs_vm_memory_pool(vm),
                               sizeof(ngx_js_event_t)
                               + promise * (sizeof(njs_opaque_value_t) * 2));
         if (njs_slow_path(event == NULL)) {
-            njs_vm_memory_error(ctx->vm);
+            njs_vm_memory_error(vm);
             return NJS_ERROR;
         }
 
-        event->vm = ctx->vm;
         event->fd = ctx->event_id++;
 
         if (promise) {
-            event->args = (njs_value_t *) &event[1];
-            rc = njs_vm_promise_create(ctx->vm, retval,
-                                       njs_value_arg(event->args));
+            event->args = (njs_opaque_value_t *) &event[1];
+            rc = njs_vm_promise_create(vm, retval, njs_value_arg(event->args));
             if (rc != NJS_OK) {
                 return NJS_ERROR;
             }
 
-            callback = njs_value_function(njs_value_arg(event->args));
+            callback = njs_value_arg(event->args);
         }
 
-        event->function = callback;
+        njs_value_assign(&event->function, callback);
 
         ps->handler = ngx_http_js_subrequest_done;
         ps->data = event;
@@ -3303,7 +3293,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     if (ngx_http_subrequest(r, &uri, rargs.len ? &rargs : NULL, &sr, ps, flags)
         != NGX_OK)
     {
-        njs_vm_error(ctx->vm, "subrequest creation failed");
+        njs_vm_error(vm, "subrequest creation failed");
         return NJS_ERROR;
     }
 
@@ -3358,7 +3348,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 memory_error:
 
-    njs_vm_error(ctx->vm, "internal error");
+    njs_vm_error(vm, "internal error");
 
     return NJS_ERROR;
 }
@@ -3370,6 +3360,7 @@ ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
     ngx_js_event_t  *event = data;
 
     njs_int_t            ret;
+    njs_vm_t            *vm;
     ngx_http_js_ctx_t   *ctx;
     njs_opaque_value_t   reply;
 
@@ -3407,7 +3398,9 @@ ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
         return NGX_ERROR;
     }
 
-    ret = njs_vm_external_create(ctx->vm, njs_value_arg(&reply),
+    vm = ctx->engine->u.njs.vm;
+
+    ret = njs_vm_external_create(vm, njs_value_arg(&reply),
                                  ngx_http_js_request_proto_id, r, 0);
     if (ret != NJS_OK) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -3416,7 +3409,8 @@ ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
         return NGX_ERROR;
     }
 
-    rc = ngx_js_call(ctx->vm, event->function, njs_value_arg(&reply), 1);
+    rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)),
+                     &reply, 1);
 
     ngx_js_del_event(ctx, event);
 
@@ -3447,7 +3441,7 @@ ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_object_prop_t *prop,
         return NJS_DECLINED;
     }
 
-    njs_value_assign(retval, njs_value_arg(&ctx->request));
+    njs_value_assign(retval, njs_value_arg(&ctx->args[0]));
 
     return NJS_OK;
 }
@@ -4182,8 +4176,8 @@ ngx_http_js_periodic_handler(ngx_event_t *ev)
 
     r->count++;
 
-    rc = ngx_js_name_invoke(ctx->vm, &periodic->method, &periodic->log,
-                            &ctx->request, 1, &ctx->retval);
+    rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &periodic->method,
+                           &ctx->args[0], 1);
 
     if (rc == NGX_AGAIN) {
         rc = NGX_OK;
@@ -4210,7 +4204,7 @@ ngx_http_js_periodic_write_handler(ngx_event_t *ev)
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
 
-    if (!ngx_vm_pending(ctx)) {
+    if (!ngx_js_ctx_pending(ctx)) {
         ngx_http_js_periodic_finalize(r, NGX_OK);
         return;
     }
@@ -4247,9 +4241,9 @@ ngx_http_js_periodic_finalize(ngx_http_request_t *r, ngx_int_t rc)
     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http js periodic finalize: \"%V\" rc: %i c: %i pending: %i",
                    &ctx->periodic->method, rc, r->count,
-                   ngx_vm_pending(ctx));
+                   ngx_js_ctx_pending(ctx));
 
-    if (r->count > 1 || (rc == NGX_OK && ngx_vm_pending(ctx))) {
+    if (r->count > 1 || (rc == NGX_OK && ngx_js_ctx_pending(ctx))) {
         return;
     }
 
@@ -4426,22 +4420,51 @@ ngx_js_http_init(njs_vm_t *vm)
 }
 
 
+static ngx_engine_t *
+ngx_engine_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf,
+    njs_int_t proto_id, void *external)
+{
+    njs_int_t           rc;
+    ngx_engine_t       *engine;
+    ngx_http_js_ctx_t  *hctx;
+
+    engine = ngx_njs_clone(ctx, cf, external);
+    if (engine == NULL) {
+        return NULL;
+    }
+
+    rc = njs_vm_external_create(engine->u.njs.vm, njs_value_arg(&ctx->args[0]),
+                                proto_id, njs_vm_external_ptr(engine->u.njs.vm),
+                                0);
+    if (rc != NJS_OK) {
+        return NULL;
+    }
+
+    hctx = (ngx_http_js_ctx_t *) ctx;
+    hctx->body_filter = ngx_http_njs_body_filter;
+
+    return engine;
+}
+
+
 static ngx_int_t
 ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)
 {
-    njs_vm_opt_t         options;
+    ngx_engine_opts_t    options;
     ngx_js_main_conf_t  *jmcf;
 
-    njs_vm_opt_init(&options);
+    memset(&options, 0, sizeof(ngx_engine_opts_t));
 
-    jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module);
-    ngx_http_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf;
+    options.engine = conf->type;
 
-    options.backtrace = 1;
-    options.metas = &ngx_http_js_metas;
-    options.addons = njs_http_js_addon_modules;
-    options.argv = ngx_argv;
-    options.argc = ngx_argc;
+    if (conf->type == NGX_ENGINE_NJS) {
+        jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module);
+        ngx_http_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf;
+
+        options.u.njs.metas = &ngx_http_js_metas;
+        options.u.njs.addons = njs_http_js_addon_modules;
+        options.clone = ngx_engine_njs_clone;
+    }
 
     return ngx_js_init_conf_vm(cf, conf, &options);
 }
diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c
index 4c1f29f3..ce4988f9 100644
--- a/nginx/ngx_js.c
+++ b/nginx/ngx_js.c
@@ -24,7 +24,8 @@ typedef struct {
 
 
 typedef struct {
-    void                *promise;
+    void                *promise_obj;
+    njs_opaque_value_t   promise;
     njs_opaque_value_t   message;
 } ngx_js_rejected_promise_t;
 
@@ -43,6 +44,19 @@ typedef struct {
 } njs_module_info_t;
 
 
+static ngx_int_t ngx_engine_njs_init(ngx_engine_t *engine,
+    ngx_engine_opts_t *opts);
+static ngx_int_t ngx_engine_njs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log,
+    u_char *start, size_t size);
+static ngx_int_t ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname,
+    njs_opaque_value_t *args, njs_uint_t nargs);
+static void *ngx_engine_njs_external(ngx_engine_t *engine);
+static ngx_int_t ngx_engine_njs_pending(ngx_engine_t *engine);
+static ngx_int_t ngx_engine_njs_string(ngx_engine_t *e,
+    njs_opaque_value_t *value, ngx_str_t *str);
+static void ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
+    ngx_js_loc_conf_t *conf);
+
 static njs_int_t ngx_js_ext_build(njs_vm_t *vm, njs_object_prop_t *prop,
     njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
 static njs_int_t ngx_js_ext_conf_file_path(njs_vm_t *vm,
@@ -70,6 +84,15 @@ static njs_int_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args,
 static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
 static njs_int_t ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx);
+static void ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused,
+    njs_bool_t is_handled, njs_value_t *promise, njs_value_t *reason);
+static njs_mod_t *ngx_js_module_loader(njs_vm_t *vm,
+    njs_external_ptr_t external, njs_str_t *name);
+static njs_int_t ngx_js_module_lookup(ngx_js_loc_conf_t *conf,
+    njs_module_info_t *info);
+static njs_int_t ngx_js_module_read(njs_mp_t *mp, int fd, njs_str_t *text);
+static njs_int_t ngx_js_set_cwd(njs_mp_t *mp, ngx_js_loc_conf_t *conf,
+    njs_str_t *path);
 static void ngx_js_cleanup_vm(void *data);
 
 static njs_int_t ngx_js_core_init(njs_vm_t *vm);
@@ -354,82 +377,234 @@ njs_module_t *njs_js_addon_modules_shared[] = {
 static njs_int_t      ngx_js_console_proto_id;
 
 
-ngx_int_t
-ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_value_t *args,
-    njs_uint_t nargs)
+static ngx_engine_t *
+ngx_create_engine(ngx_engine_opts_t *opts)
 {
-    njs_int_t          ret;
-    ngx_str_t          exception;
-    ngx_connection_t  *c;
+    njs_mp_t      *mp;
+    ngx_int_t      rc;
+    ngx_engine_t  *engine;
 
-    ret = njs_vm_call(vm, func, args, nargs);
-    if (ret == NJS_ERROR) {
-        ngx_js_exception(vm, &exception);
+    mp = njs_mp_fast_create(2 * getpagesize(), 128, 512, 16);
+    if (mp == NULL) {
+        return NULL;
+    }
 
-        c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
+    engine = njs_mp_zalloc(mp, sizeof(ngx_engine_t));
+    if (engine == NULL) {
+        return NULL;
+    }
 
-        ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                      "js exception: %V", &exception);
+    engine->pool = mp;
+    engine->clone = opts->clone;
+
+    switch (opts->engine) {
+    case NGX_ENGINE_NJS:
+        rc = ngx_engine_njs_init(engine, opts);
+        if (rc != NGX_OK) {
+            return NULL;
+        }
+
+        engine->name = "njs";
+        engine->type = NGX_ENGINE_NJS;
+        engine->compile = ngx_engine_njs_compile;
+        engine->call = ngx_engine_njs_call;
+        engine->external = ngx_engine_njs_external;
+        engine->pending = ngx_engine_njs_pending;
+        engine->string = ngx_engine_njs_string;
+        engine->destroy = opts->destroy ? opts->destroy
+                                        : ngx_engine_njs_destroy;
+        break;
+
+    default:
+        return NULL;
+    }
+
+    return engine;
+}
+
+
+static ngx_int_t
+ngx_engine_njs_init(ngx_engine_t *engine, ngx_engine_opts_t *opts)
+{
+    njs_vm_t      *vm;
+    ngx_int_t      rc;
+    njs_vm_opt_t   vm_options;
+
+    njs_vm_opt_init(&vm_options);
+
+    vm_options.backtrace = 1;
+    vm_options.addons = opts->u.njs.addons;
+    vm_options.metas = opts->u.njs.metas;
+    vm_options.file = opts->file;
+    vm_options.argv = ngx_argv;
+    vm_options.argc = ngx_argc;
+
+    vm = njs_vm_create(&vm_options);
+    if (vm == NULL) {
         return NGX_ERROR;
     }
 
-    for ( ;; ) {
-        ret = njs_vm_execute_pending_job(vm);
-        if (ret <= NJS_OK) {
-            c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
+    njs_vm_set_rejection_tracker(vm, ngx_js_rejection_tracker, NULL);
 
-            if (ret == NJS_ERROR) {
-                ngx_js_exception(vm, &exception);
+    rc = ngx_js_set_cwd(njs_vm_memory_pool(vm), opts->conf, &vm_options.file);
+    if (rc != NGX_OK) {
+        return NGX_ERROR;
+    }
 
-                ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                              "js job exception: %V", &exception);
-                return NGX_ERROR;
-            }
+    njs_vm_set_module_loader(vm, ngx_js_module_loader, opts->conf);
 
-            break;
+    engine->u.njs.vm = vm;
+
+    return NJS_OK;
+}
+
+
+static ngx_int_t
+ngx_engine_njs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start,
+    size_t size)
+{
+    u_char               *end;
+    njs_vm_t             *vm;
+    njs_int_t             rc;
+    njs_str_t             text;
+    ngx_uint_t            i;
+    njs_value_t          *value;
+    njs_opaque_value_t    exception, lvalue;
+    ngx_js_named_path_t  *import;
+
+    static const njs_str_t line_number_key = njs_str("lineNumber");
+    static const njs_str_t file_name_key = njs_str("fileName");
+
+    vm = conf->engine->u.njs.vm;
+    end = start + size;
+
+    rc = njs_vm_compile(vm, &start, end);
+
+    if (rc != NJS_OK) {
+        njs_vm_exception_get(vm, njs_value_arg(&exception));
+        njs_vm_value_string(vm, &text, njs_value_arg(&exception));
+
+        value = njs_vm_object_prop(vm, njs_value_arg(&exception),
+                                   &file_name_key, &lvalue);
+        if (value == NULL) {
+            value = njs_vm_object_prop(vm, njs_value_arg(&exception),
+                                       &line_number_key, &lvalue);
+
+            if (value != NULL) {
+                i = njs_value_number(value) - 1;
+
+                if (i < conf->imports->nelts) {
+                    import = conf->imports->elts;
+                    ngx_log_error(NGX_LOG_EMERG, log, 0,
+                                  "%*s, included in %s:%ui", text.length,
+                                  text.start, import[i].file, import[i].line);
+                    return NGX_ERROR;
+                }
+            }
         }
+
+        ngx_log_error(NGX_LOG_EMERG, log, 0, "%*s", text.length, text.start);
+        return NGX_ERROR;
+    }
+
+    if (start != end) {
+        ngx_log_error(NGX_LOG_EMERG, log, 0,
+                      "extra characters in js script: \"%*s\"",
+                      end - start, start);
+        return NGX_ERROR;
     }
 
     return NGX_OK;
 }
 
 
-ngx_int_t
-ngx_js_name_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
-    njs_opaque_value_t *args, njs_uint_t nargs)
+ngx_engine_t *
+ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external)
 {
-    njs_opaque_value_t  unused;
+    njs_vm_t             *vm;
+    njs_int_t             rc;
+    njs_str_t             key;
+    ngx_str_t             exception;
+    ngx_uint_t            i;
+    ngx_engine_t         *engine;
+    njs_opaque_value_t    retval;
+    ngx_js_named_path_t  *preload;
+
+    vm = njs_vm_clone(cf->engine->u.njs.vm, external);
+    if (vm == NULL) {
+        return NULL;
+    }
+
+    engine = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(ngx_engine_t));
+    if (engine == NULL) {
+        return NULL;
+    }
 
-    return ngx_js_name_invoke(vm, fname, log, args, nargs, &unused);
+    memcpy(engine, cf->engine, sizeof(ngx_engine_t));
+    engine->pool = njs_vm_memory_pool(vm);
+    engine->u.njs.vm = vm;
+
+    /* bind objects from preload vm */
+
+    if (cf->preload_objects != NGX_CONF_UNSET_PTR) {
+        preload = cf->preload_objects->elts;
+
+        for (i = 0; i < cf->preload_objects->nelts; i++) {
+            key.start = preload[i].name.data;
+            key.length = preload[i].name.len;
+
+            rc = njs_vm_value(cf->preload_vm, &key, njs_value_arg(&retval));
+            if (rc != NJS_OK) {
+                return NULL;
+            }
+
+            rc = njs_vm_bind(vm, &key, njs_value_arg(&retval), 0);
+            if (rc != NJS_OK) {
+                return NULL;
+            }
+        }
+    }
+
+    if (njs_vm_start(vm, njs_value_arg(&retval)) == NJS_ERROR) {
+        ngx_js_exception(vm, &exception);
+
+        ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception);
+
+        return NULL;
+    }
+
+    return engine;
 }
 
 
-ngx_int_t
-ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
-    njs_opaque_value_t *args, njs_uint_t nargs, njs_opaque_value_t *retval)
+static ngx_int_t
+ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname,
+    njs_opaque_value_t *args, njs_uint_t nargs)
 {
+    njs_vm_t        *vm;
     njs_int_t        ret;
     njs_str_t        name;
     ngx_str_t        exception;
-    ngx_js_ctx_t    *ctx;
     njs_function_t  *func;
 
     name.start = fname->data;
     name.length = fname->len;
 
+    vm = ctx->engine->u.njs.vm;
+
     func = njs_vm_function(vm, &name);
     if (func == NULL) {
-        ngx_log_error(NGX_LOG_ERR, log, 0,
+        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                       "js function \"%V\" not found", fname);
         return NGX_ERROR;
     }
 
     ret = njs_vm_invoke(vm, func, njs_value_arg(args), nargs,
-                        njs_value_arg(retval));
+                        njs_value_arg(&ctx->retval));
     if (ret == NJS_ERROR) {
         ngx_js_exception(vm, &exception);
 
-        ngx_log_error(NGX_LOG_ERR, log, 0,
+        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                       "js exception: %V", &exception);
 
         return NGX_ERROR;
@@ -441,7 +616,7 @@ ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
             if (ret == NJS_ERROR) {
                 ngx_js_exception(vm, &exception);
 
-                ngx_log_error(NGX_LOG_ERR, log, 0,
+                ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                               "js job exception: %V", &exception);
                 return NGX_ERROR;
             }
@@ -450,12 +625,10 @@ ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
         }
     }
 
-    ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
-
     if (ngx_js_unhandled_rejection(ctx)) {
         ngx_js_exception(vm, &exception);
 
-        ngx_log_error(NGX_LOG_ERR, log, 0, "js exception: %V", &exception);
+        ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception);
         return NGX_ERROR;
     }
 
@@ -463,6 +636,110 @@ ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
 }
 
 
+static void *
+ngx_engine_njs_external(ngx_engine_t *engine)
+{
+    return njs_vm_external_ptr(engine->u.njs.vm);
+}
+
+static ngx_int_t
+ngx_engine_njs_pending(ngx_engine_t *e)
+{
+    return njs_vm_pending(e->u.njs.vm);
+}
+
+
+static ngx_int_t
+ngx_engine_njs_string(ngx_engine_t *e, njs_opaque_value_t *value,
+    ngx_str_t *str)
+{
+    ngx_int_t  rc;
+    njs_str_t  s;
+
+    rc = ngx_js_string(e->u.njs.vm, njs_value_arg(value), &s);
+
+    str->data = s.start;
+    str->len = s.length;
+
+    return rc;
+}
+
+
+static void
+ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
+    ngx_js_loc_conf_t *conf)
+{
+    ngx_js_event_t     *event;
+    njs_rbtree_node_t  *node;
+
+    if (ctx != NULL) {
+        node = njs_rbtree_min(&ctx->waiting_events);
+
+        while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) {
+            event = (ngx_js_event_t *) ((u_char *) node
+                                        - offsetof(ngx_js_event_t, node));
+
+            if (event->destructor != NULL) {
+                event->destructor(event);
+            }
+
+            node = njs_rbtree_node_successor(&ctx->waiting_events, node);
+        }
+    }
+
+    njs_vm_destroy(e->u.njs.vm);
+
+    /*
+     * when ctx !=NULL e->pool is vm pool, in such case it is destroyed
+     * by njs_vm_destroy().
+     */
+
+    if (ctx == NULL) {
+        njs_mp_destroy(e->pool);
+    }
+}
+
+
+ngx_int_t
+ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_opaque_value_t *args,
+    njs_uint_t nargs)
+{
+    njs_int_t          ret;
+    ngx_str_t          exception;
+    ngx_connection_t  *c;
+
+    ret = njs_vm_call(vm, func, njs_value_arg(args), nargs);
+    if (ret == NJS_ERROR) {
+        ngx_js_exception(vm, &exception);
+
+        c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
+
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "js exception: %V", &exception);
+        return NGX_ERROR;
+    }
+
+    for ( ;; ) {
+        ret = njs_vm_execute_pending_job(vm);
+        if (ret <= NJS_OK) {
+            c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
+
+            if (ret == NJS_ERROR) {
+                ngx_js_exception(vm, &exception);
+
+                ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                              "js job exception: %V", &exception);
+                return NGX_ERROR;
+            }
+
+            break;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_js_exception(njs_vm_t *vm, ngx_str_t *s)
 {
@@ -554,33 +831,18 @@ ngx_js_event_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2)
 
 
 void
-ngx_js_ctx_init(ngx_js_ctx_t *ctx)
+ngx_js_ctx_init(ngx_js_ctx_t *ctx, ngx_log_t *log)
 {
+    ctx->log = log;
     ctx->event_id = 0;
     njs_rbtree_init(&ctx->waiting_events, ngx_js_event_rbtree_compare);
 }
 
 
 void
-ngx_js_ctx_destroy(ngx_js_ctx_t *ctx)
+ngx_js_ctx_destroy(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf)
 {
-    ngx_js_event_t     *event;
-    njs_rbtree_node_t  *node;
-
-    node = njs_rbtree_min(&ctx->waiting_events);
-
-    while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) {
-        event = (ngx_js_event_t *) ((u_char *) node
-                                    - offsetof(ngx_js_event_t, node));
-
-        if (event->destructor != NULL) {
-            event->destructor(njs_vm_external_ptr(event->vm), event);
-        }
-
-        node = njs_rbtree_node_successor(&ctx->waiting_events, node);
-    }
-
-    njs_vm_destroy(ctx->vm);
+    ctx->engine->destroy(ctx->engine, ctx, conf);
 }
 
 
@@ -1047,9 +1309,10 @@ ngx_js_timer_handler(ngx_event_t *ev)
 
     event = (ngx_js_event_t *) ((u_char *) ev - offsetof(ngx_js_event_t, ev));
 
-    vm = event->vm;
+    vm = event->ctx;
 
-    rc = ngx_js_call(vm, event->function, event->args, event->nargs);
+    rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)),
+                     event->args, event->nargs);
 
     ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
     ngx_js_del_event(ctx, event);
@@ -1059,7 +1322,7 @@ ngx_js_timer_handler(ngx_event_t *ev)
 
 
 static void
-ngx_js_clear_timer(njs_external_ptr_t external, ngx_js_event_t *event)
+ngx_js_clear_timer(ngx_js_event_t *event)
 {
     if (event->ev.timer_set) {
         ngx_del_timer(&event->ev);
@@ -1106,10 +1369,10 @@ njs_set_timer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    event->vm = vm;
-    event->function = njs_value_function(njs_argument(args, 1));
+    event->ctx = vm;
+    njs_value_assign(&event->function, njs_argument(args, 1));
     event->nargs = nargs;
-    event->args = (njs_value_t *) ((u_char *) event + sizeof(ngx_js_event_t));
+    event->args = (njs_opaque_value_t *) &event[1];
     event->destructor = ngx_js_clear_timer;
 
     ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
@@ -1343,6 +1606,47 @@ ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 }
 
 
+char *
+ngx_js_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    char  *p = conf;
+
+    ngx_str_t           *value;
+    ngx_uint_t          *type, m;
+    ngx_conf_bitmask_t  *mask;
+
+    type = (size_t *) (p + cmd->offset);
+    if (*type != NGX_CONF_UNSET_UINT) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+    mask = cmd->post;
+
+    for (m = 0; mask[m].name.len != 0; m++) {
+
+        if (mask[m].name.len != value[1].len
+            || ngx_strcasecmp(mask[m].name.data, value[1].data) != 0)
+        {
+            continue;
+        }
+
+        *type = mask[m].mask;
+
+        break;
+    }
+
+    if (mask[m].name.len == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid value \"%s\"", value[1].data);
+
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 char *
 ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
@@ -1552,14 +1856,16 @@ ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf,
     ngx_js_named_path_t  *import, *pi, *pij, *preload;
 
     if (conf->imports == NGX_CONF_UNSET_PTR
+        && conf->type == NGX_CONF_UNSET_UINT
         && conf->paths == NGX_CONF_UNSET_PTR
         && conf->preload_objects == NGX_CONF_UNSET_PTR)
     {
-        if (prev->vm != NULL) {
+        if (prev->engine != NULL) {
             conf->preload_objects = prev->preload_objects;
             conf->imports = prev->imports;
+            conf->type = prev->type;
             conf->paths = prev->paths;
-            conf->vm = prev->vm;
+            conf->engine = prev->engine;
 
             conf->preload_vm = prev->preload_vm;
 
@@ -1703,9 +2009,10 @@ ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused,
         promise_obj = njs_value_ptr(promise);
 
         for (i = 0; i < length; i++) {
-            if (rejected_promise[i].promise == promise_obj) {
-                njs_arr_remove(ctx->rejected_promises,
-                               &rejected_promise[i]);
+            if (njs_value_ptr(njs_value_arg(&rejected_promise[i].promise))
+                == promise_obj)
+            {
+                njs_arr_remove(ctx->rejected_promises, &rejected_promise[i]);
 
                 break;
             }
@@ -1727,7 +2034,7 @@ ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused,
         return;
     }
 
-    rejected_promise->promise = njs_value_ptr(promise);
+    njs_value_assign(&rejected_promise->promise, promise);
     njs_value_assign(&rejected_promise->message, reason);
 }
 
@@ -1918,13 +2225,13 @@ current_dir:
 
 
 static njs_int_t
-ngx_js_set_cwd(njs_vm_t *vm, ngx_js_loc_conf_t *conf, njs_str_t *path)
+ngx_js_set_cwd(njs_mp_t *mp, ngx_js_loc_conf_t *conf, njs_str_t *path)
 {
     ngx_str_t  cwd;
 
     ngx_js_file_dirname(path, &cwd);
 
-    conf->cwd.data = njs_mp_alloc(njs_vm_memory_pool(vm), cwd.len);
+    conf->cwd.data = njs_mp_alloc(mp, cwd.len);
     if (conf->cwd.data == NULL) {
         return NJS_ERROR;
     }
@@ -1969,7 +2276,7 @@ ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name)
 
     prev_cwd = conf->cwd;
 
-    ret = ngx_js_set_cwd(vm, conf, &info.file);
+    ret = ngx_js_set_cwd(njs_vm_memory_pool(vm), conf, &info.file);
     if (ret != NJS_OK) {
         njs_vm_internal_error(vm, "while setting cwd for \"%V\" module",
                               &info.file);
@@ -1992,22 +2299,15 @@ ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name)
 
 ngx_int_t
 ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf,
-    njs_vm_opt_t *options)
+    ngx_engine_opts_t *options)
 {
+    u_char               *start, *p;
     size_t                size;
-    u_char               *start, *end, *p;
     ngx_str_t            *m, file;
-    njs_int_t             rc;
-    njs_str_t             text;
     ngx_uint_t            i;
-    njs_value_t          *value;
     ngx_pool_cleanup_t   *cln;
-    njs_opaque_value_t    lvalue, exception;
     ngx_js_named_path_t  *import;
 
-    static const njs_str_t line_number_key = njs_str("lineNumber");
-    static const njs_str_t file_name_key = njs_str("fileName");
-
     if (conf->preload_objects != NGX_CONF_UNSET_PTR) {
        if (ngx_js_init_preload_vm(cf, (ngx_js_loc_conf_t *)conf) != NGX_OK) {
            return NGX_ERROR;
@@ -2054,9 +2354,10 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf,
 
     options->file.start = file.data;
     options->file.length = file.len;
+    options->conf = conf;
 
-    conf->vm = njs_vm_create(options);
-    if (conf->vm == NULL) {
+    conf->engine = ngx_create_engine(options);
+    if (conf->engine == NULL) {
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to create js VM");
         return NGX_ERROR;
     }
@@ -2069,17 +2370,6 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf,
     cln->handler = ngx_js_cleanup_vm;
     cln->data = conf;
 
-    njs_vm_set_rejection_tracker(conf->vm, ngx_js_rejection_tracker,
-                                 NULL);
-
-    rc = ngx_js_set_cwd(conf->vm, conf, &options->file);
-    if (rc != NJS_OK) {
-        ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to set cwd");
-        return NGX_ERROR;
-    }
-
-    njs_vm_set_module_loader(conf->vm, ngx_js_module_loader, conf);
-
     if (conf->paths != NGX_CONF_UNSET_PTR) {
         m = conf->paths->elts;
 
@@ -2090,52 +2380,14 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf,
         }
     }
 
-    end = start + size;
-
-    rc = njs_vm_compile(conf->vm, &start, end);
-
-    if (rc != NJS_OK) {
-        njs_vm_exception_get(conf->vm, njs_value_arg(&exception));
-        njs_vm_value_string(conf->vm, &text, njs_value_arg(&exception));
-
-        value = njs_vm_object_prop(conf->vm, njs_value_arg(&exception),
-                                   &file_name_key, &lvalue);
-        if (value == NULL) {
-            value = njs_vm_object_prop(conf->vm, njs_value_arg(&exception),
-                                       &line_number_key, &lvalue);
-
-            if (value != NULL) {
-                i = njs_value_number(value) - 1;
-
-                if (i < conf->imports->nelts) {
-                    import = conf->imports->elts;
-                    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                                  "%*s, included in %s:%ui", text.length,
-                                  text.start, import[i].file, import[i].line);
-                    return NGX_ERROR;
-                }
-            }
-        }
-
-        ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "%*s", text.length,
-                      text.start);
-        return NGX_ERROR;
-    }
-
-    if (start != end) {
-        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                      "extra characters in js script: \"%*s\"",
-                      end - start, start);
-        return NGX_ERROR;
-    }
-
-    return NGX_OK;
+    return conf->engine->compile(conf, cf->log, start, size);
 }
 
 
 static njs_int_t
 ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx)
 {
+    njs_vm_t                   *vm;
     njs_int_t                   ret;
     njs_str_t                   message;
     ngx_js_rejected_promise_t  *rejected_promise;
@@ -2146,15 +2398,16 @@ ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx)
         return 0;
     }
 
+    vm = ctx->engine->u.njs.vm;
     rejected_promise = ctx->rejected_promises->start;
 
-    ret = njs_vm_value_to_string(ctx->vm, &message,
+    ret = njs_vm_value_to_string(vm, &message,
                                  njs_value_arg(&rejected_promise->message));
     if (njs_slow_path(ret != NJS_OK)) {
         return -1;
     }
 
-    njs_vm_error(ctx->vm, "unhandled promise rejection: %V", &message);
+    njs_vm_error(vm, "unhandled promise rejection: %V", &message);
 
     njs_arr_destroy(ctx->rejected_promises);
     ctx->rejected_promises = NULL;
@@ -2168,7 +2421,7 @@ ngx_js_cleanup_vm(void *data)
 {
     ngx_js_loc_conf_t  *jscf = data;
 
-    njs_vm_destroy(jscf->vm);
+    jscf->engine->destroy(jscf->engine, NULL, NULL);
 
     if (jscf->preload_objects != NGX_CONF_UNSET_PTR) {
         njs_vm_destroy(jscf->preload_vm);
@@ -2187,6 +2440,7 @@ ngx_js_create_conf(ngx_conf_t *cf, size_t size)
     }
 
     conf->paths = NGX_CONF_UNSET_PTR;
+    conf->type = NGX_CONF_UNSET_UINT;
     conf->imports = NGX_CONF_UNSET_PTR;
     conf->preload_objects = NGX_CONF_UNSET_PTR;
 
@@ -2251,6 +2505,7 @@ ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child,
     ngx_js_loc_conf_t *prev = parent;
     ngx_js_loc_conf_t *conf = child;
 
+    ngx_conf_merge_uint_value(conf->type, prev->type, NGX_ENGINE_NJS);
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 16384);
     ngx_conf_merge_size_value(conf->max_response_body_size,
diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h
index 373dd499..a3bbd541 100644
--- a/nginx/ngx_js.h
+++ b/nginx/ngx_js.h
@@ -20,6 +20,8 @@
 #include "ngx_js_shared_dict.h"
 
 
+#define NGX_ENGINE_NJS      1
+
 #define NGX_JS_UNSET        0
 #define NGX_JS_DEPRECATED   1
 #define NGX_JS_STRING       2
@@ -36,9 +38,11 @@
 #define ngx_js_buffer_type(btype) ((btype) & ~NGX_JS_DEPRECATED)
 
 
+typedef struct ngx_js_loc_conf_s ngx_js_loc_conf_t;
 typedef struct ngx_js_event_s ngx_js_event_t;
 typedef struct ngx_js_dict_s  ngx_js_dict_t;
 typedef struct ngx_js_ctx_s  ngx_js_ctx_t;
+typedef struct ngx_engine_s  ngx_engine_t;
 
 
 typedef ngx_pool_t *(*ngx_external_pool_pt)(njs_external_ptr_t e);
@@ -60,14 +64,13 @@ typedef struct {
 
 
 struct ngx_js_event_s {
-    njs_vm_t            *vm;
-    njs_function_t      *function;
-    njs_value_t         *args;
+    void                *ctx;
+    njs_opaque_value_t   function;
+    njs_opaque_value_t  *args;
     ngx_socket_t         fd;
     NJS_RBTREE_NODE     (node);
     njs_uint_t           nargs;
-    void               (*destructor)(njs_external_ptr_t external,
-                                     ngx_js_event_t *event);
+    void               (*destructor)(ngx_js_event_t *event);
     ngx_event_t          ev;
     void                *data;
 };
@@ -79,7 +82,8 @@ struct ngx_js_event_s {
 
 
 #define _NGX_JS_COMMON_LOC_CONF                                               \
-    njs_vm_t              *vm;                                                \
+    ngx_uint_t             type;                                              \
+    ngx_engine_t          *engine;                                            \
     ngx_str_t              cwd;                                               \
     ngx_array_t           *imports;                                           \
     ngx_array_t           *paths;                                             \
@@ -109,7 +113,10 @@ struct ngx_js_event_s {
 
 
 #define NGX_JS_COMMON_CTX                                                     \
-    njs_vm_t              *vm;                                                \
+    ngx_engine_t          *engine;                                            \
+    ngx_log_t             *log;                                               \
+    njs_opaque_value_t     args[3];                                           \
+    njs_opaque_value_t     retval;                                            \
     njs_arr_t             *rejected_promises;                                 \
     njs_rbtree_t           waiting_events;                                    \
     ngx_socket_t           event_id
@@ -122,7 +129,7 @@ struct ngx_js_event_s {
 #define ngx_js_del_event(ctx, event)                                          \
     do {                                                                      \
         if ((event)->destructor) {                                            \
-            (event)->destructor(njs_vm_external_ptr((event)->vm), event);     \
+            (event)->destructor(event);                                       \
         }                                                                     \
                                                                               \
         njs_rbtree_delete(&(ctx)->waiting_events, &(event)->node);            \
@@ -134,9 +141,9 @@ typedef struct {
 } ngx_js_main_conf_t;
 
 
-typedef struct {
+struct ngx_js_loc_conf_s {
     NGX_JS_COMMON_LOC_CONF;
-} ngx_js_loc_conf_t;
+};
 
 
 typedef struct {
@@ -150,6 +157,54 @@ struct ngx_js_ctx_s {
 };
 
 
+typedef struct ngx_engine_opts_s {
+    unsigned                    engine;
+    union {
+        struct {
+            njs_vm_meta_t      *metas;
+            njs_module_t      **addons;
+        } njs;
+    } u;
+
+    njs_str_t                   file;
+    ngx_js_loc_conf_t          *conf;
+    ngx_engine_t             *(*clone)(ngx_js_ctx_t *ctx,
+                                        ngx_js_loc_conf_t *cf, njs_int_t pr_id,
+                                        void *external);
+    void                      (*destroy)(ngx_engine_t *e, ngx_js_ctx_t *ctx,
+                                        ngx_js_loc_conf_t *conf);
+} ngx_engine_opts_t;
+
+
+struct ngx_engine_s {
+    union {
+        struct {
+            njs_vm_t           *vm;
+        } njs;
+    } u;
+
+    ngx_int_t                 (*compile)(ngx_js_loc_conf_t *conf, ngx_log_t *lg,
+                                         u_char *start, size_t size);
+    ngx_int_t                 (*call)(ngx_js_ctx_t *ctx, ngx_str_t *fname,
+                                      njs_opaque_value_t *args,
+                                      njs_uint_t nargs);
+    ngx_engine_t             *(*clone)(ngx_js_ctx_t *ctx,
+                                       ngx_js_loc_conf_t *cf, njs_int_t pr_id,
+                                       void *external);
+    void                     *(*external)(ngx_engine_t *e);
+    ngx_int_t                 (*pending)(ngx_engine_t *e);
+    ngx_int_t                 (*string)(ngx_engine_t *e,
+                                        njs_opaque_value_t *value,
+                                        ngx_str_t *str);
+    void                      (*destroy)(ngx_engine_t *e, ngx_js_ctx_t *ctx,
+                                         ngx_js_loc_conf_t *conf);
+
+    unsigned                    type;
+    const char                 *name;
+    njs_mp_t                   *pool;
+};
+
+
 #define ngx_external_connection(vm, e)                                        \
     (*((ngx_connection_t **) ((u_char *) (e) + njs_vm_meta(vm, 0))))
 #define ngx_external_pool(vm, e)                                              \
@@ -182,19 +237,21 @@ struct ngx_js_ctx_s {
                              : njs_vm_value_buffer_set(vm, value, start, len))
 
 
-#define ngx_vm_pending(ctx)                                                   \
-    (njs_vm_pending((ctx)->vm) || !njs_rbtree_is_empty(&(ctx)->waiting_events))
+void ngx_js_ctx_init(ngx_js_ctx_t *ctx, ngx_log_t *log);
+
+#define ngx_js_ctx_pending(ctx)                                               \
+    ((ctx)->engine->pending(ctx->engine)                                      \
+     || !njs_rbtree_is_empty(&(ctx)->waiting_events))
 
+#define ngx_js_ctx_external(ctx)                                              \
+    ((ctx)->engine->external(ctx->engine))
 
-void ngx_js_ctx_init(ngx_js_ctx_t *ctx);
-void ngx_js_ctx_destroy(ngx_js_ctx_t *ctx);
-ngx_int_t ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_value_t *args,
-    njs_uint_t nargs);
-ngx_int_t ngx_js_name_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
+void ngx_js_ctx_destroy(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf);
+ngx_int_t ngx_js_call(njs_vm_t *vm, njs_function_t *func,
     njs_opaque_value_t *args, njs_uint_t nargs);
-ngx_int_t ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
-    njs_opaque_value_t *args, njs_uint_t nargs, njs_opaque_value_t *retval);
 ngx_int_t ngx_js_exception(njs_vm_t *vm, ngx_str_t *s);
+ngx_engine_t *ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf,
+    void *external);
 
 njs_int_t ngx_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t level, njs_value_t *retval);
@@ -203,13 +260,14 @@ void ngx_js_log(njs_vm_t *vm, njs_external_ptr_t external,
 void ngx_js_logger(ngx_connection_t *c, ngx_uint_t level,
     const u_char *start, size_t length);
 char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char * ngx_js_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 ngx_int_t ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf);
 ngx_int_t ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf,
     ngx_js_loc_conf_t *prev,
     ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf));
 ngx_int_t ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf,
-    njs_vm_opt_t *options);
+    ngx_engine_opts_t *opt);
 ngx_js_loc_conf_t *ngx_js_create_conf(ngx_conf_t *cf, size_t size);
 char * ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child,
    ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf));
diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c
index cea80469..1c5a961d 100644
--- a/nginx/ngx_js_fetch.c
+++ b/nginx/ngx_js_fetch.c
@@ -174,8 +174,7 @@ static njs_int_t ngx_js_headers_fill(njs_vm_t *vm, ngx_js_headers_t *headers,
     njs_value_t *init);
 static ngx_js_http_t *ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool,
     ngx_log_t *log);
-static void njs_js_http_destructor(njs_external_ptr_t external,
-    ngx_js_event_t *event);
+static void njs_js_http_destructor(ngx_js_event_t *event);
 static void ngx_js_resolve_handler(ngx_resolver_ctx_t *ctx);
 static njs_int_t ngx_js_fetch_promissified_result(njs_vm_t *vm,
     njs_value_t *result, njs_int_t rc, njs_value_t *retval);
@@ -1309,8 +1308,8 @@ ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log)
 
     ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
 
-    event->vm = vm;
-    event->function = callback;
+    event->ctx = vm;
+    njs_value_function_set(njs_value_arg(&event->function), callback);
     event->destructor = njs_js_http_destructor;
     event->fd = ctx->event_id++;
     event->data = http;
@@ -1439,7 +1438,7 @@ ngx_js_http_close_connection(ngx_connection_t *c)
 
 
 static void
-njs_js_http_destructor(njs_external_ptr_t external, ngx_js_event_t *event)
+njs_js_http_destructor(ngx_js_event_t *event)
 {
     ngx_js_http_t  *http;
 
@@ -1530,7 +1529,8 @@ ngx_js_http_fetch_done(ngx_js_http_t *http, njs_opaque_value_t *retval,
         vm = http->vm;
         event = http->event;
 
-        rc = ngx_js_call(vm, event->function, njs_value_arg(&arguments), 2);
+        rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)),
+                         &arguments[0], 2);
 
         ctx = ngx_external_ctx(vm,  njs_vm_external_ptr(vm));
         ngx_js_del_event(ctx, event);
diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c
index 295b2fcf..565f4e66 100644
--- a/nginx/ngx_stream_js_module.c
+++ b/nginx/ngx_stream_js_module.c
@@ -12,6 +12,8 @@
 #include "ngx_js.h"
 
 
+typedef struct ngx_stream_js_ctx_s  ngx_stream_js_ctx_t;
+
 typedef struct {
     NGX_JS_COMMON_LOC_CONF;
 
@@ -22,7 +24,7 @@ typedef struct {
 
 
 typedef struct {
-    njs_function_t         *function;
+    njs_opaque_value_t      function;
     ngx_uint_t              data_type;
 } ngx_stream_js_ev_t;
 
@@ -47,30 +49,34 @@ typedef struct {
 } ngx_js_periodic_t;
 
 
-typedef struct {
+struct ngx_stream_js_ctx_s {
     NGX_JS_COMMON_CTX;
-    njs_opaque_value_t      retval;
-    njs_opaque_value_t      args[3];
     ngx_buf_t              *buf;
     ngx_chain_t           **last_out;
     ngx_chain_t            *free;
     ngx_chain_t            *upstream_busy;
     ngx_chain_t            *downstream_busy;
     ngx_int_t               status;
+    ngx_int_t             (*run_event)(ngx_stream_session_t *s,
+                                       ngx_stream_js_ctx_t *ctx,
+                                       ngx_stream_js_ev_t *event,
+                                       ngx_uint_t from_upstream);
+    ngx_int_t             (*body_filter)(ngx_stream_session_t *s,
+                                         ngx_stream_js_ctx_t *ctx,
+                                         ngx_chain_t *in,
+                                         ngx_uint_t from_upstream);
 #define NGX_JS_EVENT_UPLOAD   0
 #define NGX_JS_EVENT_DOWNLOAD 1
 #define NGX_JS_EVENT_MAX      2
-    ngx_stream_js_ev_t      events[2];
+    ngx_stream_js_ev_t      events[NGX_JS_EVENT_MAX];
     unsigned                filter:1;
     unsigned                in_progress:1;
     ngx_js_periodic_t      *periodic;
-} ngx_stream_js_ctx_t;
+};
 
 
 #define ngx_stream_pending(ctx)                                               \
-    (ngx_vm_pending(ctx)                                                      \
-     || (ctx)->events[NGX_JS_EVENT_UPLOAD].function != NULL                   \
-     || (ctx)->events[NGX_JS_EVENT_DOWNLOAD].function != NULL)
+    (ngx_js_ctx_pending(ctx) || ngx_stream_js_pending_events(ctx))
 
 
 static ngx_int_t ngx_stream_js_access_handler(ngx_stream_session_t *s);
@@ -79,6 +85,8 @@ static ngx_int_t ngx_stream_js_phase_handler(ngx_stream_session_t *s,
     ngx_str_t *name);
 static ngx_int_t ngx_stream_js_body_filter(ngx_stream_session_t *s,
     ngx_chain_t *in, ngx_uint_t from_upstream);
+static ngx_int_t ngx_stream_njs_body_filter(ngx_stream_session_t *s,
+    ngx_stream_js_ctx_t *ctx, ngx_chain_t *in, ngx_uint_t from_upstream);
 static ngx_int_t ngx_stream_js_next_filter(ngx_stream_session_t *s,
     ngx_stream_js_ctx_t *ctx, ngx_chain_t *out, ngx_uint_t from_upstream);
 static ngx_int_t ngx_stream_js_variable_set(ngx_stream_session_t *s,
@@ -87,12 +95,13 @@ static ngx_int_t ngx_stream_js_variable_var(ngx_stream_session_t *s,
     ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s,
     njs_int_t proto_id);
+static ngx_int_t ngx_stream_js_pending_events(ngx_stream_js_ctx_t *ctx);
 static void ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx);
 static void ngx_stream_js_cleanup(void *data);
-static njs_int_t ngx_stream_js_run_event(ngx_stream_session_t *s,
+static ngx_int_t ngx_stream_js_run_event(ngx_stream_session_t *s,
     ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event,
     ngx_uint_t from_upstream);
-static njs_function_t **ngx_stream_js_event(ngx_stream_session_t *s,
+static ngx_stream_js_ev_t *ngx_stream_js_event(ngx_stream_session_t *s,
     njs_str_t *event);
 
 static njs_int_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm,
@@ -156,6 +165,11 @@ static char *ngx_stream_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd,
 static ngx_ssl_t *ngx_stream_js_ssl(ngx_stream_session_t *s);
 static ngx_flag_t ngx_stream_js_ssl_verify(ngx_stream_session_t *s);
 
+static ngx_conf_bitmask_t  ngx_stream_js_engines[] = {
+    { ngx_string("njs"), NGX_ENGINE_NJS },
+    { ngx_null_string, 0 }
+};
+
 #if (NGX_STREAM_SSL)
 
 static ngx_conf_bitmask_t  ngx_stream_js_ssl_protocols[] = {
@@ -170,6 +184,13 @@ static ngx_conf_bitmask_t  ngx_stream_js_ssl_protocols[] = {
 
 static ngx_command_t  ngx_stream_js_commands[] = {
 
+    { ngx_string("js_engine"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+      ngx_js_engine,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_js_srv_conf_t, type),
+      &ngx_stream_js_engines },
+
     { ngx_string("js_import"),
       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE13,
       ngx_js_import,
@@ -693,10 +714,7 @@ ngx_stream_js_preread_handler(ngx_stream_session_t *s)
 static ngx_int_t
 ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name)
 {
-    ngx_str_t             exception;
-    njs_int_t             ret;
     ngx_int_t             rc;
-    ngx_connection_t     *c;
     ngx_stream_js_ctx_t  *ctx;
 
     if (name->len == 0) {
@@ -711,8 +729,6 @@ ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name)
         return rc;
     }
 
-    c = s->connection;
-
     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
 
     if (!ctx->in_progress) {
@@ -723,36 +739,33 @@ ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name)
 
         ctx->status = NGX_ERROR;
 
-        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
                        "stream js phase call \"%V\"", name);
 
-        rc = ngx_js_name_call(ctx->vm, name, c->log, &ctx->args[0], 1);
+        rc = ctx->engine->call((ngx_js_ctx_t *) ctx, name, &ctx->args[0], 1);
 
         if (rc == NGX_ERROR) {
             return rc;
         }
     }
 
-    ret = ngx_stream_js_run_event(s, ctx, &ctx->events[NGX_JS_EVENT_UPLOAD], 0);
-    if (ret != NJS_OK) {
-        ngx_js_exception(ctx->vm, &exception);
-
-        ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V",
-                      &exception);
-
+    rc = ctx->run_event(s, ctx, &ctx->events[NGX_JS_EVENT_UPLOAD], 0);
+    if (rc != NGX_OK) {
         return NGX_ERROR;
     }
 
     if (ngx_stream_pending(ctx)) {
         ctx->in_progress = 1;
-        rc = ctx->events[NGX_JS_EVENT_UPLOAD].function ? NGX_AGAIN : NGX_DONE;
+        rc = (ctx->events[NGX_JS_EVENT_UPLOAD].data_type != NGX_JS_UNSET)
+                                                        ? NGX_AGAIN
+                                                        : NGX_DONE;
 
     } else {
         ctx->in_progress = 0;
         rc = ctx->status;
     }
 
-    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream js phase rc: %i",
+    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "stream js phase rc: %i",
                    rc);
 
     return rc;
@@ -769,11 +782,8 @@ ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in,
     ngx_uint_t from_upstream)
 {
     ngx_int_t                  rc;
-    ngx_str_t                  exception;
-    njs_int_t                  ret;
-    ngx_chain_t               *out, *cl;
+    ngx_chain_t               *out;
     ngx_connection_t          *c;
-    ngx_stream_js_ev_t        *event;
     ngx_stream_js_ctx_t       *ctx;
     ngx_stream_js_srv_conf_t  *jscf;
 
@@ -803,7 +813,8 @@ ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in,
         ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
                        "stream js filter call \"%V\"" , &jscf->filter);
 
-        rc = ngx_js_name_call(ctx->vm, &jscf->filter, c->log, &ctx->args[0], 1);
+        rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jscf->filter,
+                               &ctx->args[0], 1);
 
         if (rc == NGX_ERROR) {
             return rc;
@@ -814,37 +825,9 @@ ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in,
 
     ctx->last_out = &out;
 
-    while (in) {
-        ctx->buf = in->buf;
-
-        event = ngx_stream_event(from_upstream);
-
-        if (event->function != NULL) {
-            ret = ngx_stream_js_run_event(s, ctx, event, from_upstream);
-            if (ret != NJS_OK) {
-                ngx_js_exception(ctx->vm, &exception);
-
-                ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V",
-                              &exception);
-
-                return NGX_ERROR;
-            }
-
-            ctx->buf->pos = ctx->buf->last;
-
-        } else {
-            cl = ngx_alloc_chain_link(c->pool);
-            if (cl == NULL) {
-                return NGX_ERROR;
-            }
-
-            cl->buf = ctx->buf;
-
-            *ctx->last_out = cl;
-            ctx->last_out = &cl->next;
-        }
-
-        in = in->next;
+    rc = ctx->body_filter(s, ctx, in, from_upstream);
+    if (rc != NGX_OK) {
+        return NGX_ERROR;
     }
 
     ctx->buf = NULL;
@@ -895,8 +878,7 @@ ngx_stream_js_variable_set(ngx_stream_session_t *s,
 
     ngx_int_t             rc;
     njs_int_t             pending;
-    ngx_str_t            *fname;
-    njs_str_t             value;
+    ngx_str_t            *fname, value;
     ngx_stream_js_ctx_t  *ctx;
 
     fname = &vdata->fname;
@@ -919,8 +901,7 @@ ngx_stream_js_variable_set(ngx_stream_session_t *s,
 
     pending = ngx_stream_pending(ctx);
 
-    rc = ngx_js_name_invoke(ctx->vm, fname, s->connection->log, &ctx->args[0],
-                            1, &ctx->retval);
+    rc = ctx->engine->call((ngx_js_ctx_t *) ctx, fname, &ctx->args[0], 1);
 
     if (rc == NGX_ERROR) {
         v->not_found = 1;
@@ -933,15 +914,15 @@ ngx_stream_js_variable_set(ngx_stream_session_t *s,
         return NGX_ERROR;
     }
 
-    if (ngx_js_string(ctx->vm, njs_value_arg(&ctx->retval), &value) != NGX_OK) {
+    if (ctx->engine->string(ctx->engine, &ctx->retval, &value) != NGX_OK) {
         return NGX_ERROR;
     }
 
-    v->len = value.length;
+    v->len = value.len;
     v->valid = 1;
     v->no_cacheable = vdata->flags & NGX_NJS_VAR_NOCACHE;
     v->not_found = 0;
-    v->data = value.start;
+    v->data = value.data;
 
     return NGX_OK;
 }
@@ -977,18 +958,12 @@ ngx_stream_js_variable_var(ngx_stream_session_t *s,
 static ngx_int_t
 ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id)
 {
-    njs_int_t                  rc;
-    njs_str_t                  key;
-    ngx_str_t                  exception;
-    ngx_uint_t                 i;
-    njs_opaque_value_t         retval;
     ngx_pool_cleanup_t        *cln;
-    ngx_js_named_path_t       *preload;
     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) {
+    if (jscf->engine == NULL) {
         return NGX_DECLINED;
     }
 
@@ -1000,24 +975,24 @@ ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id)
             return NGX_ERROR;
         }
 
-        ngx_js_ctx_init((ngx_js_ctx_t *) ctx);
-
-        njs_value_invalid_set(njs_value_arg(&ctx->retval));
+        ngx_js_ctx_init((ngx_js_ctx_t *) ctx, s->connection->log);
 
         ngx_stream_set_ctx(s, ctx, ngx_stream_js_module);
     }
 
-    if (ctx->vm) {
+    if (ctx->engine) {
         return NGX_OK;
     }
 
-    ctx->vm = njs_vm_clone(jscf->vm, s);
-    if (ctx->vm == NULL) {
+    ctx->engine = jscf->engine->clone((ngx_js_ctx_t *) ctx,
+                                      (ngx_js_loc_conf_t *) jscf, proto_id, s);
+    if (ctx->engine == NULL) {
         return NGX_ERROR;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
-                   "stream js vm clone: %p from: %p", ctx->vm, jscf->vm);
+    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+                   "stream js vm clone: %p from: %p", ctx->engine,
+                   jscf->engine);
 
     cln = ngx_pool_cleanup_add(s->connection->pool, 0);
     if (cln == NULL) {
@@ -1027,43 +1002,22 @@ ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id)
     cln->handler = ngx_stream_js_cleanup;
     cln->data = s;
 
-    /* bind objects from preload vm */
-
-    if (jscf->preload_objects != NGX_CONF_UNSET_PTR) {
-        preload = jscf->preload_objects->elts;
+    return NGX_OK;
+}
 
-        for (i = 0; i < jscf->preload_objects->nelts; i++) {
-            key.start = preload[i].name.data;
-            key.length = preload[i].name.len;
 
-            rc = njs_vm_value(jscf->preload_vm, &key, njs_value_arg(&retval));
-            if (rc != NJS_OK) {
-                return NGX_ERROR;
-            }
+static ngx_int_t
+ngx_stream_js_pending_events(ngx_stream_js_ctx_t *ctx)
+{
+    ngx_uint_t  i;
 
-            rc = njs_vm_bind(ctx->vm, &key, njs_value_arg(&retval), 0);
-            if (rc != NJS_OK) {
-                return NGX_ERROR;
-            }
+    for (i = 0; i < NGX_JS_EVENT_MAX; i++) {
+        if (ctx->events[i].data_type != NGX_JS_UNSET) {
+            return 1;
         }
     }
 
-    if (njs_vm_start(ctx->vm, njs_value_arg(&retval)) == NJS_ERROR) {
-        ngx_js_exception(ctx->vm, &exception);
-
-        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
-                      "js exception: %V", &exception);
-
-        return NGX_ERROR;
-    }
-
-    rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->args[0]),
-                                proto_id, s, 0);
-    if (rc != NJS_OK) {
-        return NGX_ERROR;
-    }
-
-    return NGX_OK;
+    return 0;
 }
 
 
@@ -1073,9 +1027,11 @@ ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx)
     ngx_uint_t  i;
 
     for (i = 0; i < NGX_JS_EVENT_MAX; i++) {
-        if (ctx->events[i].function != NULL) {
-            ctx->events[i].function = NULL;
-        }
+        /*
+         * event[i].data_type = NGX_JS_UNSET
+         * event[i].function = JS_NULL
+         */
+        memset(&ctx->events[i], 0, sizeof(ngx_stream_js_ev_t));
     }
 }
 
@@ -1083,38 +1039,41 @@ ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx)
 static void
 ngx_stream_js_cleanup(void *data)
 {
-    ngx_stream_js_ctx_t  *ctx;
+    ngx_stream_js_ctx_t       *ctx;
+    ngx_stream_js_srv_conf_t  *jscf;
 
     ngx_stream_session_t *s = data;
 
     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
 
-    ngx_stream_js_drop_events(ctx);
-
-    if (ngx_vm_pending(ctx)) {
-        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "pending events");
+    if (ngx_js_ctx_pending(ctx)) {
+        ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events");
     }
 
-    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
-                   "stream js vm destroy: %p", ctx->vm);
+    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+                   "stream js vm destroy: %p", ctx->engine);
+
+    jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
 
-    ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx);
+    ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx, (ngx_js_loc_conf_t *) jscf);
 }
 
 
-static njs_int_t
+static ngx_int_t
 ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx,
     ngx_stream_js_ev_t *event, ngx_uint_t from_upstream)
 {
     size_t             len;
     u_char            *p;
+    njs_vm_t          *vm;
     njs_int_t          ret;
+    ngx_str_t          exception;
     ngx_buf_t         *b;
     uintptr_t          flags;
     ngx_connection_t  *c;
 
-    if (event->function == NULL) {
-        return NJS_OK;
+    if (!njs_value_is_function(njs_value_arg(&event->function))) {
+        return NGX_OK;
     }
 
     c = s->connection;
@@ -1122,36 +1081,90 @@ ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx,
 
     len = b ? b->last - b->pos : 0;
 
+    vm = ctx->engine->u.njs.vm;
+
     p = ngx_pnalloc(c->pool, len);
     if (p == NULL) {
-        njs_vm_memory_error(ctx->vm);
-        return NJS_ERROR;
+        njs_vm_memory_error(vm);
+        goto error;
     }
 
     if (len) {
         ngx_memcpy(p, b->pos, len);
     }
 
-    ret = ngx_js_prop(ctx->vm, event->data_type, njs_value_arg(&ctx->args[1]),
+    ret = ngx_js_prop(vm, event->data_type, njs_value_arg(&ctx->args[1]),
                       p, len);
     if (ret != NJS_OK) {
-        return ret;
+        goto error;
     }
 
     flags = from_upstream << 1 | (uintptr_t) (b && b->last_buf);
 
-    ret = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->args[2]),
+    ret = njs_vm_external_create(vm, njs_value_arg(&ctx->args[2]),
                        ngx_stream_js_session_flags_proto_id, (void *) flags, 0);
     if (ret != NJS_OK) {
+        goto error;
+    }
+
+    ret = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)),
+                      &ctx->args[1], 2);
+
+    if (ret == NJS_ERROR) {
+error:
+        ngx_js_exception(vm, &exception);
+
+        ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V",
+                      &exception);
+
         return NGX_ERROR;
     }
 
-    return ngx_js_call(ctx->vm, event->function, njs_value_arg(&ctx->args[1]),
-                       2);
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_njs_body_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx,
+    ngx_chain_t *in, ngx_uint_t from_upstream)
+{
+    ngx_int_t            rc;
+    ngx_chain_t         *cl;
+    ngx_stream_js_ev_t  *event;
+
+    while (in) {
+        ctx->buf = in->buf;
+
+        event = ngx_stream_event(from_upstream);
+
+        if (njs_value_is_function(njs_value_arg(&event->function))) {
+            rc = ngx_stream_js_run_event(s, ctx, event, from_upstream);
+            if (rc != NGX_OK) {
+                return NGX_ERROR;
+            }
+
+            ctx->buf->pos = ctx->buf->last;
+
+        } else {
+            cl = ngx_alloc_chain_link(s->connection->pool);
+            if (cl == NULL) {
+                return NGX_ERROR;
+            }
+
+            cl->buf = ctx->buf;
+
+            *ctx->last_out = cl;
+            ctx->last_out = &cl->next;
+        }
+
+        in = in->next;
+    }
+
+    return NGX_OK;
 }
 
 
-static njs_function_t **
+static ngx_stream_js_ev_t *
 ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event)
 {
     ngx_uint_t            i, n, type;
@@ -1204,7 +1217,7 @@ ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event)
     }
 
     if (i == n) {
-        njs_vm_error(ctx->vm, "unknown event \"%V\"", event);
+        njs_vm_error(ctx->engine->u.njs.vm, "unknown event \"%V\"", event);
         return NULL;
     }
 
@@ -1213,13 +1226,13 @@ ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event)
     for (n = 0; n < NGX_JS_EVENT_MAX; n++) {
         type = ctx->events[n].data_type;
         if (type != NGX_JS_UNSET && type != events[i].data_type) {
-            njs_vm_error(ctx->vm, "mixing string and buffer events"
-                         " is not allowed");
+            njs_vm_error(ctx->engine->u.njs.vm, "mixing string and buffer"
+                         " events is not allowed");
             return NULL;
         }
     }
 
-    return &ctx->events[events[i].id].function;
+    return &ctx->events[events[i].id];
 }
 
 
@@ -1305,10 +1318,10 @@ static njs_int_t
 ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
 {
-    njs_str_t               name;
-    njs_value_t            *callback;
-    njs_function_t        **cb;
-    ngx_stream_session_t   *s;
+    njs_str_t              name;
+    njs_value_t           *callback;
+    ngx_stream_js_ev_t    *event;
+    ngx_stream_session_t  *s;
 
     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
                         njs_argument(args, 0));
@@ -1328,17 +1341,17 @@ ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    cb = ngx_stream_js_event(s, &name);
-    if (cb == NULL) {
+    event = ngx_stream_js_event(s, &name);
+    if (event == NULL) {
         return NJS_ERROR;
     }
 
-    if (*cb != NULL) {
+    if (njs_value_is_function(njs_value_arg(&event->function))) {
         njs_vm_error(vm, "event handler \"%V\" is already set", &name);
         return NJS_ERROR;
     }
 
-    *cb = njs_value_function(callback);
+    njs_value_assign(&event->function, callback);
 
     njs_value_undefined_set(retval);
 
@@ -1350,9 +1363,9 @@ static njs_int_t
 ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
 {
-    njs_str_t               name;
-    njs_function_t        **callback;
-    ngx_stream_session_t   *s;
+    njs_str_t              name;
+    ngx_stream_js_ev_t    *event;
+    ngx_stream_session_t  *s;
 
     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
                         njs_argument(args, 0));
@@ -1366,12 +1379,13 @@ ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    callback = ngx_stream_js_event(s, &name);
-    if (callback == NULL) {
+    event = ngx_stream_js_event(s, &name);
+    if (event == NULL) {
         return NJS_ERROR;
     }
 
-    *callback = NULL;
+    njs_value_null_set(njs_value_arg(&event->function));
+    event->data_type = NGX_JS_UNSET;
 
     njs_value_undefined_set(retval);
 
@@ -1764,22 +1778,52 @@ ngx_js_stream_init(njs_vm_t *vm)
 }
 
 
+static ngx_engine_t *
+ngx_engine_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf,
+    njs_int_t proto_id, void *external)
+{
+    njs_int_t             rc;
+    ngx_engine_t         *engine;
+    ngx_stream_js_ctx_t  *sctx;
+
+    engine = ngx_njs_clone(ctx, cf, external);
+    if (engine == NULL) {
+        return NULL;
+    }
+
+    sctx = (ngx_stream_js_ctx_t *) ctx;
+    sctx->run_event = ngx_stream_js_run_event;
+    sctx->body_filter = ngx_stream_njs_body_filter;
+
+    rc = njs_vm_external_create(engine->u.njs.vm, njs_value_arg(&ctx->args[0]),
+                                proto_id, njs_vm_external_ptr(engine->u.njs.vm),
+                                0);
+    if (rc != NJS_OK) {
+        return NULL;
+    }
+
+    return engine;
+}
+
+
 static ngx_int_t
 ngx_stream_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)
 {
-    njs_vm_opt_t        options;
+    ngx_engine_opts_t    options;
     ngx_js_main_conf_t  *jmcf;
 
-    njs_vm_opt_init(&options);
+    memset(&options, 0, sizeof(ngx_engine_opts_t));
 
-    jmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_js_module);
-    ngx_stream_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf;
+    options.engine = conf->type;
 
-    options.backtrace = 1;
-    options.metas = &ngx_stream_js_metas;
-    options.addons = njs_stream_js_addon_modules;
-    options.argv = ngx_argv;
-    options.argc = ngx_argc;
+    if (conf->type == NGX_ENGINE_NJS) {
+        jmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_js_module);
+        ngx_stream_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf;
+
+        options.u.njs.metas = &ngx_stream_js_metas;
+        options.u.njs.addons = njs_stream_js_addon_modules;
+        options.clone = ngx_engine_njs_clone;
+    }
 
     return ngx_js_init_conf_vm(cf, conf, &options);
 }
@@ -1881,8 +1925,8 @@ ngx_stream_js_periodic_handler(ngx_event_t *ev)
 
     s->received++;
 
-    rc = ngx_js_name_invoke(ctx->vm, &periodic->method, &periodic->log,
-                            &ctx->args[0], 1, &ctx->retval);
+    rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &periodic->method,
+                           &ctx->args[0], 1);
 
     if (rc == NGX_AGAIN) {
         rc = NGX_OK;
@@ -1925,7 +1969,7 @@ ngx_stream_js_periodic_event_handler(ngx_event_t *ev)
 
     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
 
-    if (!ngx_vm_pending(ctx)) {
+    if (!ngx_js_ctx_pending(ctx)) {
         ngx_stream_js_periodic_finalize(s, NGX_OK);
         return;
     }
@@ -1942,9 +1986,9 @@ ngx_stream_js_periodic_finalize(ngx_stream_session_t *s, ngx_int_t rc)
     ngx_log_debug4(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
                    "stream js periodic finalize: \"%V\" rc: %i c: %i "
                    "pending: %i", &ctx->periodic->method, rc, s->received,
-                   ngx_vm_pending(ctx));
+                   ngx_js_ctx_pending(ctx));
 
-    if (s->received > 1 || (rc == NGX_OK && ngx_vm_pending(ctx))) {
+    if (s->received > 1 || (rc == NGX_OK && ngx_js_ctx_pending(ctx))) {
         return;
     }
 
@@ -2348,6 +2392,7 @@ ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_stream_js_srv_conf_t *prev = parent;
     ngx_stream_js_srv_conf_t *conf = child;
 
+    ngx_conf_merge_uint_value(conf->type, prev->type, NGX_ENGINE_NJS);
     ngx_conf_merge_str_value(conf->access, prev->access, "");
     ngx_conf_merge_str_value(conf->preread, prev->preread, "");
     ngx_conf_merge_str_value(conf->filter, prev->filter, "");


More information about the nginx-devel mailing list