Modules: introduced global "ngx" object.
diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c
index 842c6b5..8685be8 100644
--- a/nginx/ngx_http_js_module.c
+++ b/nginx/ngx_http_js_module.c
@@ -122,9 +122,6 @@
 static njs_int_t ngx_http_js_ext_internal_redirect(njs_vm_t *vm,
     njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
 
-static njs_int_t ngx_http_js_ext_log(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t level);
-
 static njs_int_t ngx_http_js_ext_get_http_version(njs_vm_t *vm,
     njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
     njs_value_t *retval);
@@ -429,7 +426,7 @@
         .configurable = 1,
         .enumerable = 1,
         .u.method = {
-            .native = ngx_http_js_ext_log,
+            .native = ngx_js_ext_log,
             .magic8 = NGX_LOG_INFO,
         }
     },
@@ -441,7 +438,7 @@
         .configurable = 1,
         .enumerable = 1,
         .u.method = {
-            .native = ngx_http_js_ext_log,
+            .native = ngx_js_ext_log,
             .magic8 = NGX_LOG_WARN,
         }
     },
@@ -453,7 +450,7 @@
         .configurable = 1,
         .enumerable = 1,
         .u.method = {
-            .native = ngx_http_js_ext_log,
+            .native = ngx_js_ext_log,
             .magic8 = NGX_LOG_ERR,
         }
     },
@@ -522,6 +519,17 @@
 };
 
 
+static uintptr_t ngx_http_js_uptr[] = {
+    offsetof(ngx_http_request_t, connection),
+};
+
+
+static njs_vm_meta_t ngx_http_js_metas = {
+    .size = 1,
+    .values = ngx_http_js_uptr
+};
+
+
 static ngx_int_t
 ngx_http_js_content_handler(ngx_http_request_t *r)
 {
@@ -1789,41 +1797,6 @@
 
 
 static njs_int_t
-ngx_http_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t level)
-{
-    njs_str_t            msg;
-    ngx_connection_t    *c;
-    ngx_log_handler_pt   handler;
-    ngx_http_request_t  *r;
-
-    r = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (r == NULL) {
-        return NJS_ERROR;
-    }
-
-    c = r->connection;
-
-    if (njs_vm_value_to_string(vm, &msg, njs_arg(args, nargs, 1))
-        == NJS_ERROR)
-    {
-        return NJS_ERROR;
-    }
-
-    handler = c->log->handler;
-    c->log->handler = NULL;
-
-    ngx_log_error(level, c->log, 0, "js: %*s", msg.length, msg.start);
-
-    c->log->handler = handler;
-
-    njs_value_undefined_set(njs_vm_retval(vm));
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
 ngx_http_js_ext_get_http_version(njs_vm_t *vm, njs_object_prop_t *prop,
     njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
@@ -2946,6 +2919,7 @@
     options.backtrace = 1;
     options.unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW;
     options.ops = &ngx_http_js_ops;
+    options.metas = &ngx_http_js_metas;
     options.argv = ngx_argv;
     options.argc = ngx_argc;
 
@@ -3011,6 +2985,12 @@
     }
 
     jmcf->req_proto = proto;
+
+    rc = ngx_js_core_init(jmcf->vm, cf->log);
+    if (njs_slow_path(rc != NJS_OK)) {
+        return NGX_CONF_ERROR;
+    }
+
     end = start + size;
 
     rc = njs_vm_compile(jmcf->vm, &start, end);
diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c
index d5e9a4e..03c7877 100644
--- a/nginx/ngx_js.c
+++ b/nginx/ngx_js.c
@@ -11,6 +11,48 @@
 #include "ngx_js.h"
 
 
+static njs_external_t  ngx_js_ext_core[] = {
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("log"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_js_ext_log,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("INFO"),
+        .u.property = {
+            .handler = ngx_js_ext_constant,
+            .magic32 = NGX_LOG_INFO,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("WARN"),
+        .u.property = {
+            .handler = ngx_js_ext_constant,
+            .magic32 = NGX_LOG_WARN,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("ERR"),
+        .u.property = {
+            .handler = ngx_js_ext_constant,
+            .magic32 = NGX_LOG_ERR,
+        }
+    },
+};
+
+
 ngx_int_t
 ngx_js_call(njs_vm_t *vm, ngx_str_t *fname, njs_opaque_value_t *value,
     ngx_log_t *log)
@@ -45,25 +87,6 @@
 }
 
 
-njs_int_t
-ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
-    njs_value_t *setval, njs_value_t *retval)
-{
-    char       *p;
-    ngx_str_t  *field;
-
-    p = njs_vm_external(vm, value);
-    if (p == NULL) {
-        njs_value_undefined_set(retval);
-        return NJS_DECLINED;
-    }
-
-    field = (ngx_str_t *) (p + njs_vm_prop_magic32(prop));
-
-    return njs_vm_value_string_set(vm, retval, field->data, field->len);
-}
-
-
 ngx_int_t
 ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n)
 {
@@ -93,3 +116,114 @@
 
     return NGX_OK;
 }
+
+
+ngx_int_t
+ngx_js_core_init(njs_vm_t *vm, ngx_log_t *log)
+{
+    njs_int_t             ret;
+    njs_str_t             name;
+    njs_opaque_value_t    value;
+    njs_external_proto_t  proto;
+
+    proto = njs_vm_external_prototype(vm, ngx_js_ext_core,
+                                      njs_nitems(ngx_js_ext_core));
+    if (proto == NULL) {
+        ngx_log_error(NGX_LOG_EMERG, log, 0, "failed to add js core proto");
+        return NGX_ERROR;
+    }
+
+    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto, NULL, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        ngx_log_error(NGX_LOG_EMERG, log, 0,
+                      "njs_vm_external_create() failed\n");
+        return NGX_ERROR;
+    }
+
+    name.length = 3;
+    name.start = (u_char *) "ngx";
+
+    ret = njs_vm_bind(vm, &name, njs_value_arg(&value), 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        ngx_log_error(NGX_LOG_EMERG, log, 0, "njs_vm_bind() failed\n");
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+njs_int_t
+ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
+    njs_value_t *setval, njs_value_t *retval)
+{
+    char       *p;
+    ngx_str_t  *field;
+
+    p = njs_vm_external(vm, value);
+    if (p == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    field = (ngx_str_t *) (p + njs_vm_prop_magic32(prop));
+
+    return njs_vm_value_string_set(vm, retval, field->data, field->len);
+}
+
+
+njs_int_t
+ngx_js_ext_constant(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+    njs_value_number_set(retval, njs_vm_prop_magic32(prop));
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+ngx_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t level)
+{
+    char                *p;
+    ngx_int_t            lvl;
+    njs_str_t            msg;
+    njs_value_t         *value;
+    ngx_connection_t    *c;
+    ngx_log_handler_pt   handler;
+
+    p = njs_vm_external(vm, njs_arg(args, nargs, 0));
+    if (p == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
+        return NJS_ERROR;
+    }
+
+    value =  njs_arg(args, nargs, (level != 0) ? 1 : 2);
+
+    if (level == 0) {
+        if (ngx_js_integer(vm, njs_arg(args, nargs, 1), &lvl) != NGX_OK) {
+            return NJS_ERROR;
+        }
+
+        level = lvl;
+    }
+
+    if (ngx_js_string(vm, value, &msg) != NGX_OK) {
+        return NJS_ERROR;
+    }
+
+    c = ngx_external_connection(vm, p);
+    handler = c->log->handler;
+    c->log->handler = NULL;
+
+    ngx_log_error(level, c->log, 0, "js: %*s", msg.length, msg.start);
+
+    c->log->handler = handler;
+
+    njs_value_undefined_set(njs_vm_retval(vm));
+
+    return NJS_OK;
+}
+
+
diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h
index f56cc0f..2bd90e2 100644
--- a/nginx/ngx_js.h
+++ b/nginx/ngx_js.h
@@ -15,11 +15,22 @@
 #include <njs.h>
 
 
+#define ngx_external_connection(vm, ext)                                    \
+    (*((ngx_connection_t **) ((u_char *) ext + njs_vm_meta(vm, 0))))
+
+
 ngx_int_t ngx_js_call(njs_vm_t *vm, ngx_str_t *s, njs_opaque_value_t *value,
     ngx_log_t *log);
 
+njs_int_t ngx_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t level);
+
 njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop,
     njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+njs_int_t ngx_js_ext_constant(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+
+ngx_int_t ngx_js_core_init(njs_vm_t *vm, ngx_log_t *log);
 
 ngx_int_t ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str);
 ngx_int_t ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n);
diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c
index 1777d76..e177b93 100644
--- a/nginx/ngx_stream_js_module.c
+++ b/nginx/ngx_stream_js_module.c
@@ -89,8 +89,6 @@
 static njs_int_t ngx_stream_js_ext_done(njs_vm_t *vm, njs_value_t *args,
      njs_uint_t nargs, njs_index_t unused);
 
-static njs_int_t ngx_stream_js_ext_log(njs_vm_t *vm, njs_value_t *args,
-     njs_uint_t nargs, njs_index_t unused);
 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);
 static njs_int_t ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args,
@@ -291,7 +289,7 @@
         .configurable = 1,
         .enumerable = 1,
         .u.method = {
-            .native = ngx_stream_js_ext_log,
+            .native = ngx_js_ext_log,
             .magic8 = NGX_LOG_INFO,
         }
     },
@@ -303,7 +301,7 @@
         .configurable = 1,
         .enumerable = 1,
         .u.method = {
-            .native = ngx_stream_js_ext_log,
+            .native = ngx_js_ext_log,
             .magic8 = NGX_LOG_WARN,
         }
     },
@@ -315,7 +313,7 @@
         .configurable = 1,
         .enumerable = 1,
         .u.method = {
-            .native = ngx_stream_js_ext_log,
+            .native = ngx_js_ext_log,
             .magic8 = NGX_LOG_ERR,
         }
     },
@@ -362,6 +360,17 @@
 };
 
 
+static uintptr_t ngx_stream_js_uptr[] = {
+    offsetof(ngx_stream_session_t, connection),
+};
+
+
+static njs_vm_meta_t ngx_stream_js_metas = {
+    .size = 1,
+    .values = ngx_stream_js_uptr
+};
+
+
 static ngx_stream_filter_pt  ngx_stream_next_filter;
 
 
@@ -919,42 +928,6 @@
 
 
 static njs_int_t
-ngx_stream_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t level)
-{
-    njs_str_t              msg;
-    ngx_connection_t      *c;
-    ngx_log_handler_pt     handler;
-    ngx_stream_session_t  *s;
-
-    s = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (s == NULL) {
-        njs_vm_error(vm, "\"this\" is not an external");
-        return NJS_ERROR;
-    }
-
-    c = s->connection;
-
-    if (njs_vm_value_to_string(vm, &msg, njs_arg(args, nargs, 1))
-        == NJS_ERROR)
-    {
-        return NJS_ERROR;
-    }
-
-    handler = c->log->handler;
-    c->log->handler = NULL;
-
-    ngx_log_error(level, c->log, 0, "js: %*s", msg.length, msg.start);
-
-    c->log->handler = handler;
-
-    njs_value_undefined_set(njs_vm_retval(vm));
-
-    return NJS_OK;
-}
-
-
-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)
 {
@@ -1418,6 +1391,7 @@
     options.backtrace = 1;
     options.unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW;
     options.ops = &ngx_stream_js_ops;
+    options.metas = &ngx_stream_js_metas;
     options.argv = ngx_argv;
     options.argc = ngx_argc;
 
diff --git a/src/njs.h b/src/njs.h
index aae7844..00d4e0e 100644
--- a/src/njs.h
+++ b/src/njs.h
@@ -187,9 +187,16 @@
 
 
 typedef struct {
+    size_t                          size;
+    uintptr_t                       *values;
+} njs_vm_meta_t;
+
+
+typedef struct {
     njs_external_ptr_t              external;
     njs_vm_shared_t                 *shared;
     njs_vm_ops_t                    *ops;
+    njs_vm_meta_t                   *metas;
     njs_str_t                       file;
 
     char                            **argv;
@@ -295,6 +302,7 @@
     njs_external_proto_t proto, njs_external_ptr_t external, njs_bool_t shared);
 NJS_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm,
     const njs_value_t *value);
+NJS_EXPORT uintptr_t njs_vm_meta(njs_vm_t *vm, njs_uint_t index);
 
 NJS_EXPORT njs_function_t *njs_vm_function_alloc(njs_vm_t *vm,
     njs_function_native_t native);
diff --git a/src/njs_extern.c b/src/njs_extern.c
index 04bd587..8d0ddf5 100644
--- a/src/njs_extern.c
+++ b/src/njs_extern.c
@@ -322,8 +322,15 @@
 njs_external_ptr_t
 njs_vm_external(njs_vm_t *vm, const njs_value_t *value)
 {
+    njs_external_ptr_t  external;
+
     if (njs_fast_path(njs_is_object_data(value, NJS_DATA_TAG_EXTERNAL))) {
-        return njs_object_data(value);
+        external = njs_object_data(value);
+        if (external == NULL) {
+            external = vm->external;
+        }
+
+        return external;
     }
 
     return NULL;
diff --git a/src/njs_vm.c b/src/njs_vm.c
index a574060..de85bc9 100644
--- a/src/njs_vm.c
+++ b/src/njs_vm.c
@@ -612,6 +612,20 @@
 }
 
 
+uintptr_t
+njs_vm_meta(njs_vm_t *vm, njs_uint_t index)
+{
+    njs_vm_meta_t  *metas;
+
+    metas = vm->options.metas;
+    if (njs_slow_path(metas == NULL || metas->size <= index)) {
+        return -1;
+    }
+
+    return metas->values[index];
+}
+
+
 void
 njs_vm_retval_set(njs_vm_t *vm, const njs_value_t *value)
 {