]> git.kaiwu.me - njs.git/commitdiff
Refactored externals.
authorDmitry Volyntsev <xeioex@nginx.com>
Fri, 20 Mar 2020 14:33:06 +0000 (17:33 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Fri, 20 Mar 2020 14:33:06 +0000 (17:33 +0300)
This closes #16 issue on Github.

32 files changed:
nginx/ngx_http_js_module.c
nginx/ngx_stream_js_module.c
src/njs.h
src/njs_array.c
src/njs_array.h
src/njs_array_buffer.c
src/njs_crypto.c
src/njs_date.c
src/njs_error.c
src/njs_extern.c
src/njs_extern.h [deleted file]
src/njs_function.c
src/njs_function.h
src/njs_json.c
src/njs_main.h
src/njs_module.c
src/njs_object.c
src/njs_object_prop.c
src/njs_promise.c
src/njs_regexp.c
src/njs_shell.c
src/njs_symbol.h
src/njs_value.c
src/njs_value.h
src/njs_vm.c
src/njs_vm.h
src/test/njs_benchmark.c
src/test/njs_externals_test.c
src/test/njs_externals_test.h
src/test/njs_interactive_test.c
src/test/njs_unit_test.c
test/njs_expect_test.exp

index 5af7319fa93f06f28eb2a8dd4801fe7ab3b10cc1..a286be9cdf69ce8461cdad9763dad1cec47ff4bc 100644 (file)
 
 
 typedef struct {
-    njs_vm_t            *vm;
-    ngx_array_t         *paths;
-    const njs_extern_t  *req_proto;
+    njs_vm_t              *vm;
+    ngx_array_t           *paths;
+    njs_external_proto_t   req_proto;
 } ngx_http_js_main_conf_t;
 
 
 typedef struct {
-    ngx_str_t            content;
+    ngx_str_t              content;
 } ngx_http_js_loc_conf_t;
 
 
 typedef struct {
-    njs_vm_t            *vm;
-    ngx_log_t           *log;
-    ngx_uint_t           done;
-    ngx_int_t            status;
-    njs_opaque_value_t   request;
-    njs_opaque_value_t   request_body;
-    ngx_str_t            redirect_uri;
-    njs_opaque_value_t   promise_callbacks[2];
+    njs_vm_t              *vm;
+    ngx_log_t             *log;
+    ngx_uint_t             done;
+    ngx_int_t              status;
+    njs_opaque_value_t     request;
+    njs_opaque_value_t     request_body;
+    ngx_str_t              redirect_uri;
+    njs_opaque_value_t     promise_callbacks[2];
 } ngx_http_js_ctx_t;
 
 
 typedef struct {
-    ngx_http_request_t  *request;
-    njs_vm_event_t       vm_event;
-    void                *unused;
-    ngx_int_t            ident;
+    ngx_http_request_t    *request;
+    njs_vm_event_t         vm_event;
+    void                  *unused;
+    ngx_int_t              ident;
 } ngx_http_js_event_t;
 
 
@@ -55,24 +55,20 @@ static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r);
 static void ngx_http_js_cleanup_ctx(void *data);
 static void ngx_http_js_cleanup_vm(void *data);
 
-static njs_int_t ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value,
-    void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_keys_header(njs_vm_t *vm, void *obj,
+static njs_int_t ngx_http_js_ext_get_string(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_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *keys, uintptr_t data);
 static ngx_table_elt_t *ngx_http_js_get_header(ngx_list_part_t *part,
     u_char *data, size_t len);
-static njs_int_t ngx_http_js_ext_get_header_out(njs_vm_t *vm,
-    njs_value_t *value, void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj,
-    uintptr_t data, njs_str_t *value);
-static njs_int_t ngx_http_js_ext_delete_header_out(njs_vm_t *vm, void *obj,
-    uintptr_t data, njs_bool_t delete);
-static njs_int_t ngx_http_js_ext_keys_header_out(njs_vm_t *vm, void *obj,
-    njs_value_t *keys); /*FIXME*/
-static njs_int_t ngx_http_js_ext_get_status(njs_vm_t *vm, njs_value_t *value,
-    void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_set_status(njs_vm_t *vm, void *obj,
-    uintptr_t data, njs_str_t *value);
+static njs_int_t ngx_http_js_ext_header_out(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_http_js_ext_keys_header_out(njs_vm_t *vm,
+    njs_value_t *value, njs_value_t *keys);
+static njs_int_t ngx_http_js_ext_status(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_http_js_ext_send_header(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 static njs_int_t ngx_http_js_ext_send(njs_vm_t *vm, njs_value_t *args,
@@ -85,32 +81,30 @@ 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 unused);
-static njs_int_t ngx_http_js_ext_warn(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
-static njs_int_t ngx_http_js_ext_error(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_core(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, ngx_uint_t level);
+    njs_uint_t nargs, njs_index_t level);
 
 static njs_int_t ngx_http_js_ext_get_http_version(njs_vm_t *vm,
-    njs_value_t *value, void *obj, uintptr_t data);
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
 static njs_int_t ngx_http_js_ext_get_remote_address(njs_vm_t *vm,
-    njs_value_t *value, void *obj, uintptr_t data);
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
 static njs_int_t ngx_http_js_ext_get_request_body(njs_vm_t *vm,
-    njs_value_t *value, void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_get_header_in(njs_vm_t *vm, njs_value_t *value,
-    void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_keys_header_in(njs_vm_t *vm, void *obj,
-    njs_value_t *keys); /*FIXME*/
-static njs_int_t ngx_http_js_ext_get_arg(njs_vm_t *vm, njs_value_t *value,
-    void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_keys_arg(njs_vm_t *vm, void *obj,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
+static njs_int_t ngx_http_js_ext_get_header_in(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_http_js_ext_keys_header_in(njs_vm_t *vm,
+    njs_value_t *value, njs_value_t *keys);
+static njs_int_t ngx_http_js_ext_get_arg(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_http_js_ext_keys_arg(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *keys);
-static njs_int_t ngx_http_js_ext_get_variable(njs_vm_t *vm, njs_value_t *value,
-    void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_set_variable(njs_vm_t *vm, void *obj,
-       uintptr_t data, njs_str_t *value);
+static njs_int_t ngx_http_js_ext_variables(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_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 static ngx_int_t ngx_http_js_subrequest(ngx_http_request_t *r,
@@ -118,10 +112,12 @@ static ngx_int_t ngx_http_js_subrequest(ngx_http_request_t *r,
     ngx_http_request_t **sr);
 static ngx_int_t ngx_http_js_subrequest_done(ngx_http_request_t *r,
     void *data, ngx_int_t rc);
-static njs_int_t ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_value_t *value,
-    void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_get_reply_body(njs_vm_t *vm,
-    njs_value_t *value, void *obj, uintptr_t data);
+static njs_int_t ngx_http_js_ext_get_parent(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_http_js_ext_get_response_body(njs_vm_t *vm,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
 
 static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external,
     uint64_t delay, njs_vm_event_t vm_event);
@@ -211,251 +207,234 @@ ngx_module_t  ngx_http_js_module = {
 
 static njs_external_t  ngx_http_js_ext_request[] = {
 
-    { njs_str("uri"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_http_js_ext_get_string,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      offsetof(ngx_http_request_t, uri) },
-
-    { njs_str("method"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_http_js_ext_get_string,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      offsetof(ngx_http_request_t, method_name) },
-
-    { njs_str("httpVersion"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_http_js_ext_get_http_version,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("remoteAddress"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_http_js_ext_get_remote_address,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("parent"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_http_js_ext_get_parent,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("requestBody"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_http_js_ext_get_request_body,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("responseBody"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_http_js_ext_get_reply_body,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("headersIn"),
-      NJS_EXTERN_OBJECT,
-      NULL,
-      0,
-      ngx_http_js_ext_get_header_in,
-      NULL,
-      NULL,
-      ngx_http_js_ext_keys_header_in,
-      NULL,
-      0 },
-
-    { njs_str("args"),
-      NJS_EXTERN_OBJECT,
-      NULL,
-      0,
-      ngx_http_js_ext_get_arg,
-      NULL,
-      NULL,
-      ngx_http_js_ext_keys_arg,
-      NULL,
-      0 },
-
-    { njs_str("variables"),
-      NJS_EXTERN_OBJECT,
-      NULL,
-      0,
-      ngx_http_js_ext_get_variable,
-      ngx_http_js_ext_set_variable,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("status"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_http_js_ext_get_status,
-      ngx_http_js_ext_set_status,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("headersOut"),
-      NJS_EXTERN_OBJECT,
-      NULL,
-      0,
-      ngx_http_js_ext_get_header_out,
-      ngx_http_js_ext_set_header_out,
-      ngx_http_js_ext_delete_header_out,
-      ngx_http_js_ext_keys_header_out,
-      NULL,
-      0 },
-
-    { njs_str("subrequest"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_subrequest,
-      0 },
-
-    { njs_str("log"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_log,
-      0 },
-
-    { njs_str("warn"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_warn,
-      0 },
-
-    { njs_str("error"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_error,
-      0 },
-
-    { njs_str("sendHeader"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_send_header,
-      0 },
-
-    { njs_str("send"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_send,
-      0 },
-
-    { njs_str("finish"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_finish,
-      0 },
-
-    { njs_str("return"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_return,
-      0 },
-
-    { njs_str("internalRedirect"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_http_js_ext_internal_redirect,
-      0 },
-};
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Request",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("uri"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = ngx_http_js_ext_get_string,
+            .magic32 = offsetof(ngx_http_request_t, uri),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("method"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = ngx_http_js_ext_get_string,
+            .magic32 = offsetof(ngx_http_request_t, method_name),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("httpVersion"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = ngx_http_js_ext_get_http_version,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("remoteAddress"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = ngx_http_js_ext_get_remote_address,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("requestBody"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = ngx_http_js_ext_get_request_body,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("parent"),
+        .u.property = {
+            .handler = ngx_http_js_ext_get_parent,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("responseBody"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = ngx_http_js_ext_get_response_body,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("headersIn"),
+        .enumerable = 1,
+        .u.object = {
+            .enumerable = 1,
+            .prop_handler = ngx_http_js_ext_get_header_in,
+            .keys = ngx_http_js_ext_keys_header_in,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("args"),
+        .enumerable = 1,
+        .u.object = {
+            .enumerable = 1,
+            .prop_handler = ngx_http_js_ext_get_arg,
+            .keys = ngx_http_js_ext_keys_arg,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("variables"),
+        .u.object = {
+            .writable = 1,
+            .prop_handler = ngx_http_js_ext_variables,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("status"),
+        .writable = 1,
+        .enumerable = 1,
+        .u.property = {
+            .handler = ngx_http_js_ext_status,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("headersOut"),
+        .enumerable = 1,
+        .u.object = {
+            .writable = 1,
+            .configurable = 1,
+            .enumerable = 1,
+            .prop_handler = ngx_http_js_ext_header_out,
+            .keys = ngx_http_js_ext_keys_header_out,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("subrequest"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_subrequest,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("log"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_log,
+            .magic8 = NGX_LOG_INFO,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("warn"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_log,
+            .magic8 = NGX_LOG_WARN,
+        }
+    },
 
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("error"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_log,
+            .magic8 = NGX_LOG_ERR,
+        }
+    },
 
-static njs_external_t  ngx_http_js_externals[] = {
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("sendHeader"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_send_header,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("send"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_send,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("finish"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_finish,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("return"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_return,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("internalRedirect"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_http_js_ext_internal_redirect,
+        }
+    },
 
-    { njs_str("request"),
-      NJS_EXTERN_OBJECT,
-      ngx_http_js_ext_request,
-      njs_nitems(ngx_http_js_ext_request),
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
 };
 
 
@@ -745,7 +724,7 @@ ngx_http_js_init_vm(ngx_http_request_t *r)
     }
 
     rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->request),
-                                jmcf->req_proto, r);
+                                jmcf->req_proto, r, 0);
     if (rc != NJS_OK) {
         return NGX_ERROR;
     }
@@ -777,29 +756,32 @@ ngx_http_js_cleanup_vm(void *data)
 
 
 static njs_int_t
-ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_get_string(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
-    char *p = obj;
-
+    char       *p;
     ngx_str_t  *field;
 
-    field = (ngx_str_t *) (p + data);
+    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, value, field->data, field->len);
+    return njs_vm_value_string_set(vm, retval, field->data, field->len);
 }
 
 
 static njs_int_t
-ngx_http_js_ext_keys_header(njs_vm_t *vm, void *obj, njs_value_t *keys,
+ngx_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys,
     uintptr_t data)
 {
-    char *p = obj;
-
+    char             *p;
     njs_int_t         rc, cookie, x_for;
     ngx_uint_t        item;
     ngx_list_t       *headers;
-    njs_value_t      *value;
     ngx_list_part_t  *part;
     ngx_table_elt_t  *header, *h;
 
@@ -808,6 +790,11 @@ ngx_http_js_ext_keys_header(njs_vm_t *vm, void *obj, njs_value_t *keys,
         return NJS_ERROR;
     }
 
+    p = njs_vm_external(vm, value);
+    if (p == NULL) {
+        return NJS_OK;
+    }
+
     headers = (ngx_list_t *) (p + data);
     part = &headers->part;
     item = 0;
@@ -903,78 +890,96 @@ ngx_http_js_get_header(ngx_list_part_t *part, u_char *data, size_t len)
 
 
 static njs_int_t
-ngx_http_js_ext_get_header_out(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_header_out(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
     u_char              *p, *start;
-    njs_str_t           *v;
+    njs_int_t            rc;
+    ngx_int_t            n;
+    njs_str_t           *v, s, name;
     ngx_str_t           *hdr;
     ngx_table_elt_t     *h;
     ngx_http_request_t  *r;
     u_char               content_len[NGX_OFF_T_LEN];
 
-    r = (ngx_http_request_t *) obj;
-    v = (njs_str_t *) data;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        if (retval != NULL) {
+            njs_value_undefined_set(retval);
+        }
 
-    if (v->length == njs_length("Content-Type")
-        && ngx_strncasecmp(v->start, (u_char *) "Content-Type",
-                           v->length) == 0)
-    {
-        hdr = &r->headers_out.content_type;
-        return njs_vm_value_string_set(vm, value, hdr->data, hdr->len);
+        return NJS_DECLINED;
     }
 
-    if (v->length == njs_length("Content-Length")
-        && ngx_strncasecmp(v->start, (u_char *) "Content-Length",
-                           v->length) == 0)
-    {
-        if (r->headers_out.content_length == NULL
-            && r->headers_out.content_length_n >= 0)
-        {
-            p = ngx_sprintf(content_len, "%O", r->headers_out.content_length_n);
+    rc = njs_vm_prop_name(vm, prop, &name);
+    if (rc != NJS_OK) {
+        if (retval != NULL) {
+            njs_value_undefined_set(retval);
+        }
 
-            start = njs_vm_value_string_alloc(vm, value, p - content_len);
-            if (start == NULL) {
-                return NJS_ERROR;
-            }
+        return NJS_DECLINED;
+    }
 
-            ngx_memcpy(start, content_len, p - content_len);
+    v = &name;
 
-            return NJS_OK;
+    if (retval != NULL && setval == NULL) {
+        if (v->length == njs_length("Content-Type")
+            && ngx_strncasecmp(v->start, (u_char *) "Content-Type",
+                               v->length) == 0)
+        {
+            hdr = &r->headers_out.content_type;
+            return njs_vm_value_string_set(vm, retval, hdr->data, hdr->len);
         }
-    }
 
-    h = ngx_http_js_get_header(&r->headers_out.headers.part, v->start,
-                               v->length);
-    if (h == NULL) {
-        njs_value_undefined_set(value);
-        return NJS_OK;
-    }
+        if (v->length == njs_length("Content-Length")
+            && ngx_strncasecmp(v->start, (u_char *) "Content-Length",
+                               v->length) == 0)
+        {
+            if (r->headers_out.content_length == NULL
+                && r->headers_out.content_length_n >= 0)
+            {
+                p = ngx_sprintf(content_len, "%O",
+                                r->headers_out.content_length_n);
+
+                start = njs_vm_value_string_alloc(vm, retval, p - content_len);
+                if (start == NULL) {
+                    return NJS_ERROR;
+                }
 
-    return njs_vm_value_string_set(vm, value, h->value.data, h->value.len);
-}
+                ngx_memcpy(start, content_len, p - content_len);
 
+                return NJS_OK;
+            }
+        }
 
-static njs_int_t
-ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_str_t *value)
-{
-    u_char              *p;
-    ngx_int_t            n;
-    njs_str_t           *v;
-    ngx_table_elt_t     *h;
-    ngx_http_request_t  *r;
+        h = ngx_http_js_get_header(&r->headers_out.headers.part, v->start,
+                                   v->length);
+        if (h == NULL) {
+            njs_value_undefined_set(retval);
+            return NJS_DECLINED;
+        }
 
-    r = (ngx_http_request_t *) obj;
-    v = (njs_str_t *) data;
+        return njs_vm_value_string_set(vm, retval, h->value.data, h->value.len);
+    }
+
+    if (setval != NULL) {
+        rc = ngx_http_js_string(vm, setval, &s);
+        if (rc != NJS_OK) {
+            return NJS_ERROR;
+        }
+
+    } else {
+        s.length = 0;
+        s.start = NULL;
+    }
 
     if (v->length == njs_length("Content-Type")
         && ngx_strncasecmp(v->start, (u_char *) "Content-Type",
                            v->length) == 0)
     {
-        r->headers_out.content_type.len = value->length;
+        r->headers_out.content_type.len = s.length;
         r->headers_out.content_type_len = r->headers_out.content_type.len;
-        r->headers_out.content_type.data = value->start;
+        r->headers_out.content_type.data = s.start;
         r->headers_out.content_type_lowcase = NULL;
 
         return NJS_OK;
@@ -983,12 +988,12 @@ ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj, uintptr_t data,
     h = ngx_http_js_get_header(&r->headers_out.headers.part, v->start,
                                v->length);
 
-    if (h != NULL && value->length == 0) {
+    if (h != NULL && s.length == 0) {
         h->hash = 0;
         h = NULL;
     }
 
-    if (h == NULL && value->length != 0) {
+    if (h == NULL && s.length != 0) {
         h = ngx_list_push(&r->headers_out.headers);
         if (h == NULL) {
             return NJS_ERROR;
@@ -1006,15 +1011,15 @@ ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj, uintptr_t data,
     }
 
     if (h != NULL) {
-        p = ngx_pnalloc(r->pool, value->length);
+        p = ngx_pnalloc(r->pool, s.length);
         if (p == NULL) {
             return NJS_ERROR;
         }
 
-        ngx_memcpy(p, value->start, value->length);
+        ngx_memcpy(p, s.start, s.length);
 
         h->value.data = p;
-        h->value.len = value->length;
+        h->value.len = s.length;
         h->hash = 1;
     }
 
@@ -1030,7 +1035,7 @@ ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj, uintptr_t data,
                            v->length) == 0)
     {
         if (h != NULL) {
-            n = ngx_atoi(value->start, value->length);
+            n = ngx_atoi(s.start, s.length);
             if (n == NGX_ERROR) {
                 h->hash = 0;
                 njs_vm_error(vm, "failed converting argument to integer");
@@ -1050,53 +1055,44 @@ ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj, uintptr_t data,
 
 
 static njs_int_t
-ngx_http_js_ext_delete_header_out(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_bool_t unused)
+ngx_http_js_ext_keys_header_out(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *keys)
 {
-    njs_str_t  value;
-
-    value = njs_str_value("");
-
-    return ngx_http_js_ext_set_header_out(vm, obj, data, &value);
-}
-
-
-static njs_int_t
-ngx_http_js_ext_keys_header_out(njs_vm_t *vm, void *obj, njs_value_t *keys)
-{
-    return ngx_http_js_ext_keys_header(vm, obj, keys,
+    return ngx_http_js_ext_keys_header(vm, value, keys,
                              offsetof(ngx_http_request_t, headers_out.headers));
 }
 
 
 static njs_int_t
-ngx_http_js_ext_get_status(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_status(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
+    njs_int_t            rc;
+    ngx_int_t            n;
+    njs_str_t            s;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
-
-    njs_value_number_set(value, r->headers_out.status);
-
-    return NJS_OK;
-}
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
+    if (setval == NULL) {
+        njs_value_number_set(retval, r->headers_out.status);
+        return NJS_OK;
+    }
 
-static njs_int_t
-ngx_http_js_ext_set_status(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_str_t *value)
-{
-    ngx_int_t            n;
-    ngx_http_request_t  *r;
+    rc = ngx_http_js_string(vm, setval, &s);
+    if (rc != NJS_OK) {
+        return NJS_ERROR;
+    }
 
-    n = ngx_atoi(value->start, value->length);
+    n = ngx_atoi(s.start, s.length);
     if (n == NGX_ERROR) {
         return NJS_ERROR;
     }
 
-    r = (ngx_http_request_t *) obj;
-
     r->headers_out.status = n;
 
     return NJS_OK;
@@ -1110,7 +1106,8 @@ ngx_http_js_ext_send_header(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_http_request_t  *r;
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(r == NULL)) {
+    if (r == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1139,7 +1136,8 @@ ngx_http_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_http_request_t  *r;
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(r == NULL)) {
+    if (r == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1208,7 +1206,8 @@ ngx_http_js_ext_finish(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_http_request_t  *r;
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(r == NULL)) {
+    if (r == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1236,7 +1235,8 @@ ngx_http_js_ext_return(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_http_complex_value_t   cv;
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(r == NULL)) {
+    if (r == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1290,7 +1290,8 @@ ngx_http_js_ext_internal_redirect(njs_vm_t *vm, njs_value_t *args,
     ngx_http_request_t  *r;
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(r == NULL)) {
+    if (r == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1317,31 +1318,7 @@ ngx_http_js_ext_internal_redirect(njs_vm_t *vm, njs_value_t *args,
 
 static njs_int_t
 ngx_http_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    return ngx_http_js_ext_log_core(vm, args, nargs, NGX_LOG_INFO);
-}
-
-
-static njs_int_t
-ngx_http_js_ext_warn(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    return ngx_http_js_ext_log_core(vm, args, nargs, NGX_LOG_WARN);
-}
-
-
-static njs_int_t
-ngx_http_js_ext_error(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    return ngx_http_js_ext_log_core(vm, args, nargs, NGX_LOG_ERR);
-}
-
-
-static njs_int_t
-ngx_http_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    ngx_uint_t level)
+    njs_index_t level)
 {
     njs_str_t            msg;
     ngx_connection_t    *c;
@@ -1349,7 +1326,7 @@ ngx_http_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_http_request_t  *r;
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(r == NULL)) {
+    if (r == NULL) {
         return NJS_ERROR;
     }
 
@@ -1373,13 +1350,17 @@ ngx_http_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 
 static njs_int_t
-ngx_http_js_ext_get_http_version(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+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)
 {
     ngx_str_t            v;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
     switch (r->http_version) {
 
@@ -1396,28 +1377,33 @@ ngx_http_js_ext_get_http_version(njs_vm_t *vm, njs_value_t *value, void *obj,
         break;
     }
 
-    return njs_vm_value_string_set(vm, value, v.data, v.len);
+    return njs_vm_value_string_set(vm, retval, v.data, v.len);
 }
 
 
 static njs_int_t
-ngx_http_js_ext_get_remote_address(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_get_remote_address(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
     ngx_connection_t    *c;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
     c = r->connection;
 
-    return njs_vm_value_string_set(vm, value, c->addr_text.data,
+    return njs_vm_value_string_set(vm, retval, c->addr_text.data,
                                    c->addr_text.len);
 }
 
 
 static njs_int_t
-ngx_http_js_ext_get_request_body(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_get_request_body(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
     u_char              *p, *body;
     size_t               len;
@@ -1428,19 +1414,23 @@ ngx_http_js_ext_get_request_body(njs_vm_t *vm, njs_value_t *value, void *obj,
     ngx_http_js_ctx_t   *ctx;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
     request_body = (njs_value_t *) &ctx->request_body;
 
     if (!njs_value_is_null(request_body)) {
-        njs_value_assign(value, request_body);
+        njs_value_assign(retval, request_body);
         return NJS_OK;
     }
 
     if (r->request_body == NULL || r->request_body->bufs == NULL) {
-        njs_value_undefined_set(value);
-        return NJS_OK;
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
     }
 
     if (r->request_body->temp_file) {
@@ -1488,26 +1478,38 @@ done:
         return NJS_ERROR;
     }
 
-    njs_value_assign(value, request_body);
+    njs_value_assign(retval, request_body);
 
     return NJS_OK;
 }
 
 
 static njs_int_t
-ngx_http_js_ext_get_header_in(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_get_header_in(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
     u_char              *p, *end, sep;
     size_t               len;
-    njs_str_t           *v;
+    njs_int_t            rc;
+    njs_str_t           *v, name;
     ngx_uint_t           i, n;
     ngx_array_t         *a;
     ngx_table_elt_t     *h, **hh;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
-    v = (njs_str_t *) data;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    rc = njs_vm_prop_name(vm, prop, &name);
+    if (rc != NJS_OK) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    v = &name;
 
     if (v->length == njs_length("Cookie")
         && ngx_strncasecmp(v->start, (u_char *) "Cookie",
@@ -1532,11 +1534,11 @@ ngx_http_js_ext_get_header_in(njs_vm_t *vm, njs_value_t *value, void *obj,
     h = ngx_http_js_get_header(&r->headers_in.headers.part, v->start,
                                v->length);
     if (h == NULL) {
-        njs_value_undefined_set(value);
-        return NJS_OK;
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
     }
 
-    return njs_vm_value_string_set(vm, value, h->value.data, h->value.len);
+    return njs_vm_value_string_set(vm, retval, h->value.data, h->value.len);
 
 multi:
 
@@ -1552,18 +1554,18 @@ multi:
     }
 
     if (len == 0) {
-        njs_value_undefined_set(value);
-        return NJS_OK;
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
     }
 
     len -= 2;
 
     if (n == 1) {
-        return njs_vm_value_string_set(vm, value, (*hh)->value.data,
+        return njs_vm_value_string_set(vm, retval, (*hh)->value.data,
                                        (*hh)->value.len);
     }
 
-    p = njs_vm_value_string_alloc(vm, value, len);
+    p = njs_vm_value_string_alloc(vm, retval, len);
     if (p == NULL) {
         return NJS_ERROR;
     }
@@ -1588,48 +1590,63 @@ multi:
 
 
 static njs_int_t
-ngx_http_js_ext_keys_header_in(njs_vm_t *vm, void *obj, njs_value_t *keys)
+ngx_http_js_ext_keys_header_in(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *keys)
 {
-    return ngx_http_js_ext_keys_header(vm, obj, keys,
+    return ngx_http_js_ext_keys_header(vm, value, keys,
                               offsetof(ngx_http_request_t, headers_in.headers));
 }
 
 static njs_int_t
-ngx_http_js_ext_get_arg(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_get_arg(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
-    njs_str_t           *v;
+    njs_int_t            rc;
+    njs_str_t           *v, key;
     ngx_str_t            arg;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
-    v = (njs_str_t *) data;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    rc = njs_vm_prop_name(vm, prop, &key);
+    if (rc != NJS_OK) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    v = &key;
 
     if (ngx_http_arg(r, v->start, v->length, &arg) == NGX_OK) {
-        return njs_vm_value_string_set(vm, value, arg.data, arg.len);
+        return njs_vm_value_string_set(vm, retval, arg.data, arg.len);
     }
 
-    njs_value_undefined_set(value);
+    njs_value_undefined_set(retval);
 
-    return NJS_OK;
+    return NJS_DECLINED;
 }
 
 
 static njs_int_t
-ngx_http_js_ext_keys_arg(njs_vm_t *vm, void *obj, njs_value_t *keys)
+ngx_http_js_ext_keys_arg(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys)
 {
     u_char              *v, *p, *start, *end;
     njs_int_t            rc;
-    njs_value_t         *value;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
-
     rc = njs_vm_array_alloc(vm, keys, 8);
     if (rc != NJS_OK) {
         return NJS_ERROR;
     }
 
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        return NJS_OK;
+    }
+
     start = r->args.data;
     end = start + r->args.len;
 
@@ -1664,50 +1681,44 @@ ngx_http_js_ext_keys_arg(njs_vm_t *vm, void *obj, njs_value_t *keys)
 
 
 static njs_int_t
-ngx_http_js_ext_get_variable(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_variables(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
-    njs_str_t                  *v;
+    njs_int_t                   rc;
+    njs_str_t                   val, s;
     ngx_str_t                   name;
     ngx_uint_t                  key;
     ngx_http_request_t         *r;
+    ngx_http_variable_t        *v;
+    ngx_http_core_main_conf_t  *cmcf;
     ngx_http_variable_value_t  *vv;
 
-    r = (ngx_http_request_t *) obj;
-    v = (njs_str_t *) data;
-
-    name.data = v->start;
-    name.len = v->length;
-
-    key = ngx_hash_strlow(name.data, name.data, name.len);
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
-    vv = ngx_http_get_variable(r, &name, key);
-    if (vv == NULL || vv->not_found) {
-        njs_value_undefined_set(value);
-        return NJS_OK;
+    rc = njs_vm_prop_name(vm, prop, &val);
+    if (rc != NJS_OK) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
     }
 
-    return njs_vm_value_string_set(vm, value, vv->data, vv->len);
-}
+    name.data = val.start;
+    name.len = val.length;
 
+    if (setval == NULL) {
+        key = ngx_hash_strlow(name.data, name.data, name.len);
 
-static njs_int_t
-ngx_http_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_str_t *value)
-{
-    njs_str_t                  *val;
-    ngx_str_t                   name;
-    ngx_uint_t                  key;
-    ngx_http_request_t         *r;
-    ngx_http_variable_t        *v;
-    ngx_http_variable_value_t  *vv;
-    ngx_http_core_main_conf_t  *cmcf;
-
-    r = (ngx_http_request_t *) obj;
-    val = (njs_str_t *) data;
+        vv = ngx_http_get_variable(r, &name, key);
+        if (vv == NULL || vv->not_found) {
+            njs_value_undefined_set(retval);
+            return NJS_DECLINED;
+        }
 
-    name.data = val->start;
-    name.len = val->length;
+        return njs_vm_value_string_set(vm, retval, vv->data, vv->len);
+    }
 
     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 
@@ -1720,6 +1731,11 @@ ngx_http_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data,
         return NJS_ERROR;
     }
 
+    rc = ngx_http_js_string(vm, setval, &s);
+    if (rc != NJS_OK) {
+        return NJS_ERROR;
+    }
+
     if (v->set_handler != NULL) {
         vv = ngx_pcalloc(r->pool, sizeof(ngx_http_variable_value_t));
         if (vv == NULL) {
@@ -1729,8 +1745,8 @@ ngx_http_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data,
 
         vv->valid = 1;
         vv->not_found = 0;
-        vv->data = value->start;
-        vv->len = value->length;
+        vv->data = s.start;
+        vv->len = s.length;
 
         v->set_handler(r, vv, v->data);
 
@@ -1747,14 +1763,14 @@ ngx_http_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data,
     vv->valid = 1;
     vv->not_found = 0;
 
-    vv->data = ngx_pnalloc(r->pool, value->length);
+    vv->data = ngx_pnalloc(r->pool, s.length);
     if (vv->data == NULL) {
         njs_vm_error(vm, "internal error");
         return NJS_ERROR;
     }
 
-    vv->len = value->length;
-    ngx_memcpy(vv->data, value->start, vv->len);
+    vv->len = s.length;
+    ngx_memcpy(vv->data, s.start, vv->len);
 
     return NJS_OK;
 }
@@ -1820,8 +1836,8 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     static const njs_str_t detached_key = njs_str("detached");
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(r == NULL)) {
-        njs_vm_error(vm, "this is not an external");
+    if (r == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -2122,7 +2138,7 @@ ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
     }
 
     ret = njs_vm_external_create(ctx->vm, njs_value_arg(&reply),
-                                 jmcf->req_proto, r);
+                                 jmcf->req_proto, r, 0);
     if (ret != NJS_OK) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "js subrequest reply creation failed");
@@ -2137,44 +2153,52 @@ ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
 
 
 static njs_int_t
-ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
     ngx_http_js_ctx_t   *ctx;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
     ctx = r->parent ? ngx_http_get_module_ctx(r->parent, ngx_http_js_module)
                     : NULL;
 
     if (ctx == NULL || ctx->vm != vm) {
-        njs_value_undefined_set(value);
-        return NJS_OK;
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
     }
 
-    njs_value_assign(value, njs_value_arg(&ctx->request));
+    njs_value_assign(retval, njs_value_arg(&ctx->request));
 
     return NJS_OK;
 }
 
 
 static njs_int_t
-ngx_http_js_ext_get_reply_body(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_http_js_ext_get_response_body(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
     size_t               len;
     u_char              *p;
     ngx_buf_t           *b;
     ngx_http_request_t  *r;
 
-    r = (ngx_http_request_t *) obj;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
     b = r->out ? r->out->buf : NULL;
 
     len = b ? b->last - b->pos : 0;
 
-    p = njs_vm_value_string_alloc(vm, value, len);
+    p = njs_vm_value_string_alloc(vm, retval, len);
     if (p == NULL) {
         return NJS_ERROR;
     }
@@ -2313,6 +2337,7 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     njs_vm_opt_t           options;
     ngx_file_info_t        fi;
     ngx_pool_cleanup_t    *cln;
+    njs_external_proto_t   proto;
 
     if (jmcf->vm) {
         return "is duplicate";
@@ -2426,13 +2451,15 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         }
     }
 
-    jmcf->req_proto = njs_vm_external_prototype(jmcf->vm,
-                                                &ngx_http_js_externals[0]);
-    if (jmcf->req_proto == NULL) {
+    proto = njs_vm_external_prototype(jmcf->vm, ngx_http_js_ext_request,
+                                      njs_nitems(ngx_http_js_ext_request));
+    if (proto == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add request proto");
         return NGX_CONF_ERROR;
     }
 
+    jmcf->req_proto = proto;
+
     rc = njs_vm_compile(jmcf->vm, &start, end);
 
     if (rc != NJS_OK) {
index 8a91178009df1710cd7b32ccae5af4bcc3bbd6f4..3b81a1afd289c53b6947064e87d808aaf0864e8c 100644 (file)
@@ -16,7 +16,7 @@
 typedef struct {
     njs_vm_t              *vm;
     ngx_array_t           *paths;
-    const njs_extern_t    *proto;
+    njs_external_proto_t   proto;
 } ngx_stream_js_main_conf_t;
 
 
@@ -72,25 +72,14 @@ static njs_vm_event_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,
-    njs_value_t *value, void *obj, uintptr_t data);
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
 
 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_deny(njs_vm_t *vm, njs_value_t *args,
-     njs_uint_t nargs, njs_index_t unused);
-static njs_int_t ngx_stream_js_ext_decline(njs_vm_t *vm, njs_value_t *args,
-     njs_uint_t nargs, njs_index_t unused);
-static njs_int_t ngx_stream_js_ext_set_status(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, ngx_int_t status);
 
 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_warn(njs_vm_t *vm, njs_value_t *args,
-     njs_uint_t nargs, njs_index_t unused);
-static njs_int_t ngx_stream_js_ext_error(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_core(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, ngx_uint_t level);
 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,
@@ -98,10 +87,9 @@ static njs_int_t ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args,
 static njs_int_t ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args,
      njs_uint_t nargs, njs_index_t unused);
 
-static njs_int_t ngx_stream_js_ext_get_variable(njs_vm_t *vm,
-    njs_value_t *value, void *obj, uintptr_t data);
-static njs_int_t ngx_stream_js_ext_set_variable(njs_vm_t *vm, void *obj,
-    uintptr_t data, njs_str_t *value);
+static njs_int_t ngx_stream_js_ext_variables(njs_vm_t *vm,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
 
 static njs_host_event_t ngx_stream_js_set_timer(njs_external_ptr_t external,
     uint64_t delay, njs_vm_event_t vm_event);
@@ -202,153 +190,150 @@ ngx_module_t  ngx_stream_js_module = {
 
 static njs_external_t  ngx_stream_js_ext_session[] = {
 
-    { njs_str("remoteAddress"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      ngx_stream_js_ext_get_remote_address,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("variables"),
-      NJS_EXTERN_OBJECT,
-      NULL,
-      0,
-      ngx_stream_js_ext_get_variable,
-      ngx_stream_js_ext_set_variable,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("allow"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_done,
-      0 },
-
-    { njs_str("deny"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_deny,
-      0 },
-
-    { njs_str("decline"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_decline,
-      0 },
-
-    { njs_str("done"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_done,
-      0 },
-
-    { njs_str("log"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_log,
-      0 },
-
-    { njs_str("warn"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_warn,
-      0 },
-
-    { njs_str("error"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_error,
-      0 },
-
-    { njs_str("on"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_on,
-      0 },
-
-    { njs_str("off"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_off,
-      0 },
-
-    { njs_str("send"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      ngx_stream_js_ext_send,
-      0 },
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Stream Session",
+        }
+    },
 
-};
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("remoteAddress"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = ngx_stream_js_ext_get_remote_address,
+        }
+    },
 
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("variables"),
+        .u.object = {
+            .writable = 1,
+            .prop_handler = ngx_stream_js_ext_variables,
+        }
+    },
 
-static njs_external_t  ngx_stream_js_externals[] = {
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("allow"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_done,
+            .magic8 = NGX_OK,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("deny"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_done,
+            .magic8 = -NGX_DONE,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("decline"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_done,
+            .magic8 = -NGX_DECLINED,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("done"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_done,
+            .magic8 = NGX_OK,
+
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("log"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_log,
+            .magic8 = NGX_LOG_INFO,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("warn"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_log,
+            .magic8 = NGX_LOG_WARN,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("error"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_log,
+            .magic8 = NGX_LOG_ERR,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("on"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_on,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("off"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_off,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("send"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = ngx_stream_js_ext_send,
+        }
+    },
 
-    { njs_str("stream"),
-      NJS_EXTERN_OBJECT,
-      ngx_stream_js_ext_session,
-      njs_nitems(ngx_stream_js_ext_session),
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
 };
 
 
@@ -736,7 +721,7 @@ ngx_stream_js_init_vm(ngx_stream_session_t *s)
     }
 
     rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->args[0]),
-                                jmcf->proto, s);
+                                jmcf->proto, s, 0);
     if (rc != NJS_OK) {
         return NGX_ERROR;
     }
@@ -874,59 +859,48 @@ 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, njs_value_t *value,
-    void *obj, uintptr_t data)
+ngx_stream_js_ext_get_remote_address(njs_vm_t *vm,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval)
 {
     ngx_connection_t      *c;
     ngx_stream_session_t  *s;
 
-    s = (ngx_stream_session_t *) obj;
+    s = njs_vm_external(vm, value);
+    if (s == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
     c = s->connection;
 
-    return njs_vm_value_string_set(vm, value, c->addr_text.data,
+    return njs_vm_value_string_set(vm, retval, c->addr_text.data,
                                    c->addr_text.len);
 }
 
 
 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)
-{
-    return ngx_stream_js_ext_set_status(vm, args, nargs, NGX_OK);
-}
-
-
-static njs_int_t
-ngx_stream_js_ext_deny(njs_vm_t *vm, njs_value_t *args,
-     njs_uint_t nargs, njs_index_t unused)
-{
-    return ngx_stream_js_ext_set_status(vm, args, njs_min(nargs, 1),
-                                        NGX_STREAM_FORBIDDEN);
-}
-
-
-static njs_int_t
-ngx_stream_js_ext_decline(njs_vm_t *vm, njs_value_t *args,
-     njs_uint_t nargs, njs_index_t unused)
-{
-    return ngx_stream_js_ext_set_status(vm, args, njs_min(nargs, 1),
-                                        NGX_DECLINED);
-}
-
-
-static njs_int_t
-ngx_stream_js_ext_set_status(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    ngx_int_t status)
+ngx_stream_js_ext_done(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t magic)
 {
+    ngx_int_t              status;
     njs_value_t           *code;
     ngx_stream_js_ctx_t   *ctx;
     ngx_stream_session_t  *s;
 
     s = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(s == NULL)) {
+    if (s == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
+    status = (ngx_int_t) magic;
+    status = -status;
+
+    if (status == NGX_DONE) {
+        status = NGX_STREAM_FORBIDDEN;
+    }
+
     code = njs_arg(args, nargs, 1);
 
     if (!njs_value_is_undefined(code)) {
@@ -942,6 +916,7 @@ ngx_stream_js_ext_set_status(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
     }
 
+
     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
 
     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
@@ -965,31 +940,7 @@ ngx_stream_js_ext_set_status(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 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)
-{
-    return ngx_stream_js_ext_log_core(vm, args, nargs, NGX_LOG_INFO);
-}
-
-
-static njs_int_t
-ngx_stream_js_ext_warn(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    return ngx_stream_js_ext_log_core(vm, args, nargs, NGX_LOG_WARN);
-}
-
-
-static njs_int_t
-ngx_stream_js_ext_error(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    return ngx_stream_js_ext_log_core(vm, args, nargs, NGX_LOG_ERR);
-}
-
-
-static njs_int_t
-ngx_stream_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    ngx_uint_t level)
+    njs_index_t level)
 {
     njs_str_t              msg;
     ngx_connection_t      *c;
@@ -997,7 +948,8 @@ ngx_stream_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_stream_session_t  *s;
 
     s = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(s == NULL)) {
+    if (s == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1030,7 +982,8 @@ ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_stream_session_t  *s;
 
     s = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(s == NULL)) {
+    if (s == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1076,7 +1029,8 @@ ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_stream_session_t  *s;
 
     s = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(s == NULL)) {
+    if (s == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1117,7 +1071,8 @@ ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     static const njs_str_t flush_key = njs_str("flush");
 
     s = njs_vm_external(vm, njs_arg(args, nargs, 0));
-    if (njs_slow_path(s == NULL)) {
+    if (s == NULL) {
+        njs_vm_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -1180,50 +1135,44 @@ ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 
 static njs_int_t
-ngx_stream_js_ext_get_variable(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+ngx_stream_js_ext_variables(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
-    njs_str_t                    *v;
+    njs_int_t                     rc;
+    njs_str_t                     val;
     ngx_str_t                     name;
     ngx_uint_t                    key;
+    ngx_stream_variable_t        *v;
     ngx_stream_session_t         *s;
+    ngx_stream_core_main_conf_t  *cmcf;
     ngx_stream_variable_value_t  *vv;
 
-    s = (ngx_stream_session_t *) obj;
-    v = (njs_str_t *) data;
-
-    name.data = v->start;
-    name.len = v->length;
-
-    key = ngx_hash_strlow(name.data, name.data, name.len);
+    s = njs_vm_external(vm, value);
+    if (s == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
-    vv = ngx_stream_get_variable(s, &name, key);
-    if (vv == NULL || vv->not_found) {
-        njs_value_undefined_set(value);
-        return NJS_OK;
+    rc = njs_vm_prop_name(vm, prop, &val);
+    if (rc != NJS_OK) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
     }
 
-    return njs_vm_value_string_set(vm, value, vv->data, vv->len);
-}
+    name.data = val.start;
+    name.len = val.length;
 
+    if (setval == NULL) {
+        key = ngx_hash_strlow(name.data, name.data, name.len);
 
-static njs_int_t
-ngx_stream_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_str_t *value)
-{
-    njs_str_t                    *val;
-    ngx_str_t                     name;
-    ngx_uint_t                    key;
-    ngx_stream_variable_t        *v;
-    ngx_stream_session_t         *s;
-    ngx_stream_core_main_conf_t  *cmcf;
-    ngx_stream_variable_value_t  *vv;
-
-    s = (ngx_stream_session_t *) obj;
-    val = (njs_str_t *) data;
+        vv = ngx_stream_get_variable(s, &name, key);
+        if (vv == NULL || vv->not_found) {
+            njs_value_undefined_set(retval);
+            return NJS_DECLINED;
+        }
 
-    name.data = val->start;
-    name.len = val->length;
+        return njs_vm_value_string_set(vm, retval, vv->data, vv->len);
+    }
 
     cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
 
@@ -1236,6 +1185,11 @@ ngx_stream_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data,
         return NJS_ERROR;
     }
 
+    rc = ngx_stream_js_string(vm, setval, &val);
+    if (rc != NJS_OK) {
+        return NJS_ERROR;
+    }
+
     if (v->set_handler != NULL) {
         vv = ngx_pcalloc(s->connection->pool,
                          sizeof(ngx_stream_variable_value_t));
@@ -1245,8 +1199,8 @@ ngx_stream_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data,
 
         vv->valid = 1;
         vv->not_found = 0;
-        vv->data = value->start;
-        vv->len = value->length;
+        vv->data = val.start;
+        vv->len = val.length;
 
         v->set_handler(s, vv, v->data);
 
@@ -1263,13 +1217,13 @@ ngx_stream_js_ext_set_variable(njs_vm_t *vm, void *obj, uintptr_t data,
     vv->valid = 1;
     vv->not_found = 0;
 
-    vv->data = ngx_pnalloc(s->connection->pool, value->length);
+    vv->data = ngx_pnalloc(s->connection->pool, val.length);
     if (vv->data == NULL) {
         return NJS_ERROR;
     }
 
-    vv->len = value->length;
-    ngx_memcpy(vv->data, value->start, vv->len);
+    vv->len = val.length;
+    ngx_memcpy(vv->data, val.start, vv->len);
 
     return NJS_OK;
 }
@@ -1396,6 +1350,7 @@ ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     njs_vm_opt_t           options;
     ngx_file_info_t        fi;
     ngx_pool_cleanup_t    *cln;
+    njs_external_proto_t   proto;
 
     if (jmcf->vm) {
         return "is duplicate";
@@ -1509,14 +1464,16 @@ ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         }
     }
 
-    jmcf->proto = njs_vm_external_prototype(jmcf->vm,
-                                            &ngx_stream_js_externals[0]);
+    proto = njs_vm_external_prototype(jmcf->vm, ngx_stream_js_ext_session,
+                                      njs_nitems(ngx_stream_js_ext_session));
 
-    if (jmcf->proto == NULL) {
+    if (proto == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add stream proto");
         return NGX_CONF_ERROR;
     }
 
+    jmcf->proto = proto;
+
     rc = njs_vm_compile(jmcf->vm, &start, end);
 
     if (rc != NJS_OK) {
index d19e532ab67f8e27adfda9a285060039e9469945..7953d2f3173a4b090e950d56ba20d9eb9cf7b169 100644 (file)
--- a/src/njs.h
+++ b/src/njs.h
@@ -11,7 +11,7 @@
 
 #include <njs_auto_config.h>
 
-#define NJS_VERSION                 "0.3.10"
+#define NJS_VERSION                 "0.4.0"
 
 
 #include <unistd.h>                 /* STDOUT_FILENO, STDERR_FILENO */
 typedef uintptr_t                   njs_index_t;
 typedef struct njs_vm_s             njs_vm_t;
 typedef union  njs_value_s          njs_value_t;
-typedef struct njs_extern_s         njs_extern_t;
 typedef struct njs_function_s       njs_function_t;
 typedef struct njs_vm_shared_s      njs_vm_shared_t;
+typedef struct njs_object_prop_s    njs_object_prop_t;
+typedef struct njs_external_s       njs_external_t;
+typedef void *                      njs_external_proto_t;
 
 /*
  * njs_opaque_value_t is the external storage type for native njs_value_t type.
@@ -63,48 +65,91 @@ extern const njs_value_t            njs_value_undefined;
     njs_vm_value_error_set(vm, njs_vm_retval(vm), fmt, ##__VA_ARGS__)
 
 
-typedef njs_int_t (*njs_extern_get_t)(njs_vm_t *vm, njs_value_t *value,
-    void *obj, uintptr_t data);
-typedef njs_int_t (*njs_extern_set_t)(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_str_t *value);
-typedef njs_int_t (*njs_extern_find_t)(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_bool_t delete);
-typedef njs_int_t (*njs_extern_keys_t)(njs_vm_t *vm, void *obj,
-    njs_value_t *keys);
-typedef njs_int_t (*njs_extern_method_t)(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
-
+/*
+ * njs_prop_handler_t operates as a property getter/setter or delete handler.
+ * - retval != NULL && setval == NULL - GET context.
+ * - retval != NULL && setval != NULL - SET context.
+ * - retval == NULL - DELETE context.
+ *
+ * njs_prop_handler_t is expected to return:
+ *   NJS_OK - handler executed successfully;
+ *   NJS_ERROR - some error, vm->retval contains appropriate exception;
+ *   NJS_DECLINED - handler was applied to inappropriate object, vm->retval
+ *   contains undefined value.
+ */
+typedef njs_int_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+typedef njs_int_t (*njs_exotic_keys_t)(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *retval);
+typedef njs_int_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t magic8);
+
+
+typedef enum {
+    NJS_SYMBOL_INVALID,
+    NJS_SYMBOL_ASYNC_ITERATOR,
+    NJS_SYMBOL_HAS_INSTANCE,
+    NJS_SYMBOL_IS_CONCAT_SPREADABLE,
+    NJS_SYMBOL_ITERATOR,
+    NJS_SYMBOL_MATCH,
+    NJS_SYMBOL_MATCH_ALL,
+    NJS_SYMBOL_REPLACE,
+    NJS_SYMBOL_SEARCH,
+    NJS_SYMBOL_SPECIES,
+    NJS_SYMBOL_SPLIT,
+    NJS_SYMBOL_TO_PRIMITIVE,
+    NJS_SYMBOL_TO_STRING_TAG,
+    NJS_SYMBOL_UNSCOPABLES,
+    NJS_SYMBOL_KNOWN_MAX,
+} njs_wellknown_symbol_t;
+
+
+typedef enum {
+    NJS_EXTERN_PROPERTY = 0,
+    NJS_EXTERN_METHOD = 1,
+    NJS_EXTERN_OBJECT = 2,
+    NJS_EXTERN_SYMBOL = 4,
+} njs_extern_flag_t;
 
-typedef struct njs_external_s       njs_external_t;
 
 struct njs_external_s {
-    njs_str_t                       name;
-
-#define NJS_EXTERN_PROPERTY         0x00
-#define NJS_EXTERN_METHOD           0x01
-#define NJS_EXTERN_OBJECT           0x80
-#define NJS_EXTERN_CASELESS_OBJECT  0x81
-
-    uintptr_t                       type;
-
-    njs_external_t                  *properties;
-    njs_uint_t                      nproperties;
-
-    njs_extern_get_t                get;
-    njs_extern_set_t                set;
-    njs_extern_find_t               find;
-
-    njs_extern_keys_t               keys;
-
-    njs_extern_method_t             method;
-
-    uintptr_t                       data;
+    njs_extern_flag_t               flags;
+
+    union {
+        njs_str_t                   string;
+        uint32_t                    symbol;
+    } name;
+
+    unsigned                        writable;
+    unsigned                        configurable;
+    unsigned                        enumerable;
+
+    union {
+        struct {
+            const char              value[15]; /* NJS_STRING_SHORT + 1. */
+            njs_prop_handler_t      handler;
+            uint32_t                magic32;
+        } property;
+
+        struct {
+            njs_function_native_t   native;
+            uint8_t                 magic8;
+        } method;
+
+        struct {
+            njs_external_t          *properties;
+            njs_uint_t              nproperties;
+
+            unsigned                writable;
+            unsigned                configurable;
+            unsigned                enumerable;
+            njs_prop_handler_t      prop_handler;
+            njs_exotic_keys_t       keys;
+        } object;
+    } u;
 };
 
 
-typedef njs_int_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t retval);
-
 /*
  * NJS and event loops.
  *
@@ -234,10 +279,10 @@ NJS_EXPORT njs_int_t njs_vm_start(njs_vm_t *vm);
 
 NJS_EXPORT njs_int_t njs_vm_add_path(njs_vm_t *vm, const njs_str_t *path);
 
-NJS_EXPORT const njs_extern_t *njs_vm_external_prototype(njs_vm_t *vm,
-    njs_external_t *external);
-NJS_EXPORT njs_int_t njs_vm_external_create(njs_vm_t *vm,
-    njs_value_t *value, const njs_extern_t *proto, njs_external_ptr_t object);
+NJS_EXPORT njs_external_proto_t njs_vm_external_prototype(njs_vm_t *vm,
+    const njs_external_t *definition, njs_uint_t n);
+NJS_EXPORT njs_int_t njs_vm_external_create(njs_vm_t *vm, njs_value_t *value,
+    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);
 
@@ -298,6 +343,11 @@ NJS_EXPORT double njs_value_number(const njs_value_t *value);
 NJS_EXPORT void *njs_value_data(const njs_value_t *value);
 NJS_EXPORT njs_function_t *njs_value_function(const njs_value_t *value);
 
+NJS_EXPORT uint16_t njs_vm_prop_magic16(njs_object_prop_t *prop);
+NJS_EXPORT uint32_t njs_vm_prop_magic32(njs_object_prop_t *prop);
+NJS_EXPORT njs_int_t njs_vm_prop_name(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_str_t *dst);
+
 NJS_EXPORT njs_int_t njs_value_is_null(const njs_value_t *value);
 NJS_EXPORT njs_int_t njs_value_is_undefined(const njs_value_t *value);
 NJS_EXPORT njs_int_t njs_value_is_null_or_undefined(const njs_value_t *value);
index a7c03239dfcdd0c981ca869dd785741568e514df..7e0b215a6991ee80c2a7e36de9d724377095fbfe 100644 (file)
@@ -65,6 +65,7 @@ njs_array_alloc(njs_vm_t *vm, njs_bool_t flat, uint64_t length, uint32_t spare)
     njs_lvlhsh_init(&array->object.hash);
     array->object.shared_hash = vm->shared->array_instance_hash;
     array->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_ARRAY].object;
+    array->object.slots = NULL;
     array->object.type = NJS_ARRAY;
     array->object.shared = 0;
     array->object.extensible = 1;
@@ -1557,7 +1558,7 @@ njs_array_indices_handler(const void *first, const void *second)
 
 
 njs_array_t *
-njs_array_keys(njs_vm_t *vm, const njs_value_t *object, njs_bool_t all)
+njs_array_keys(njs_vm_t *vm, njs_value_t *object, njs_bool_t all)
 {
     njs_array_t  *keys;
 
@@ -1575,7 +1576,7 @@ njs_array_keys(njs_vm_t *vm, const njs_value_t *object, njs_bool_t all)
 
 
 njs_array_t *
-njs_array_indices(njs_vm_t *vm, const njs_value_t *object)
+njs_array_indices(njs_vm_t *vm, njs_value_t *object)
 {
     double       idx;
     uint32_t     i;
index 0dae0129ebdffbeae03f075b3aa0c83f7eba063b..a0daf2c11c8fb97c63a0ed7a73239c2079ac8805 100644 (file)
@@ -25,9 +25,8 @@ njs_int_t njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value,
     uint32_t length);
 njs_int_t njs_array_length_set(njs_vm_t *vm, njs_value_t *value,
     njs_object_prop_t *prev, njs_value_t *setval);
-njs_array_t *njs_array_keys(njs_vm_t *vm, const njs_value_t *array,
-    njs_bool_t all);
-njs_array_t *njs_array_indices(njs_vm_t *vm, const njs_value_t *object);
+njs_array_t *njs_array_keys(njs_vm_t *vm, njs_value_t *array, njs_bool_t all);
+njs_array_t *njs_array_indices(njs_vm_t *vm, njs_value_t *object);
 njs_int_t njs_array_string_add(njs_vm_t *vm, njs_array_t *array,
     const u_char *start, size_t size, size_t length);
 njs_int_t njs_array_expand(njs_vm_t *vm, njs_array_t *array, uint32_t prepend,
index 6394e674ec24c4224531ad96d4389a22abc5b60f..bc1051e68ffa162a45c928a0c731032e3d15395a 100644 (file)
@@ -34,6 +34,7 @@ njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size)
     njs_lvlhsh_init(&array->object.hash);
     njs_lvlhsh_init(&array->object.shared_hash);
     array->object.__proto__ = proto;
+    array->object.slots = NULL;
     array->object.type = NJS_ARRAY_BUFFER;
     array->object.shared = 0;
     array->object.extensible = 1;
index 434251fd8f1ba3eae484f6c7a6a79241b8b56c55..9989ef105d636c51d47435726be2f8d52d1b6f11 100644 (file)
@@ -138,6 +138,7 @@ njs_crypto_object_value_alloc(njs_vm_t *vm, njs_object_type_t type)
         ov->object.fast_array = 0;
 
         ov->object.__proto__ = &vm->prototypes[type].object;
+        ov->object.slots = NULL;
         return ov;
     }
 
index 01e102335d5dc9ba9700a87702d609879e727054..58ee967e03bfd28c22d8b50154436e3caa976a0f 100644 (file)
@@ -393,6 +393,7 @@ done:
     date->object.error_data = 0;
     date->object.fast_array = 0;
     date->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_DATE].object;
+    date->object.slots = NULL;
 
     date->time = time;
 
index 97fe51a83471bcebeecfc0e4ce81b67b2e7096d8..ba53a90b3f01fd53df8cb52a7d0ee791f6e07d70 100644 (file)
@@ -203,6 +203,7 @@ njs_error_alloc(njs_vm_t *vm, njs_object_type_t type, const njs_value_t *name,
     error->fast_array = 0;
     error->error_data = 1;
     error->__proto__ = &vm->prototypes[type].object;
+    error->slots = NULL;
 
     lhq.replace = 0;
     lhq.pool = vm->mem_pool;
@@ -539,6 +540,7 @@ njs_memory_error_set(njs_vm_t *vm, njs_value_t *value)
     njs_lvlhsh_init(&object->hash);
     njs_lvlhsh_init(&object->shared_hash);
     object->__proto__ = &prototypes[NJS_OBJ_TYPE_INTERNAL_ERROR].object;
+    object->slots = NULL;
     object->type = NJS_OBJECT;
     object->shared = 1;
 
index 96e14fd4ab8d313f7d6adf66668489a1f7359c32..3df7604cf5cc3907ec894d6bb486f28e0df67564 100644 (file)
 #include <njs_main.h>
 
 
-typedef struct njs_extern_part_s  njs_extern_part_t;
-
-struct njs_extern_part_s {
-    njs_extern_part_t  *next;
-    njs_str_t          str;
-};
+static njs_int_t njs_external_prop_handler(njs_vm_t *vm,
+    njs_object_prop_t *self, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
 
 
 static njs_int_t
-njs_extern_hash_test(njs_lvlhsh_query_t *lhq, void *data)
+njs_external_add(njs_vm_t *vm, njs_arr_t *protos,
+    const njs_external_t *external, njs_uint_t n)
 {
-    njs_extern_t  *ext;
+    size_t              size;
+    ssize_t             length;
+    njs_int_t           ret;
+    njs_lvlhsh_t        *hash;
+    const u_char        *start;
+    njs_function_t      *function;
+    njs_object_prop_t   *prop;
+    njs_lvlhsh_query_t  lhq;
+    njs_exotic_slots_t  *slot, *next;
 
-    ext = (njs_extern_t *) data;
+    slot = njs_arr_add(protos);
+    njs_memzero(slot, sizeof(njs_exotic_slots_t));
 
-    if (njs_strstr_eq(&lhq->key, &ext->name)) {
-        return NJS_OK;
-    }
+    hash = &slot->external_shared_hash;
+    njs_lvlhsh_init(hash);
 
-    return NJS_DECLINED;
-}
+    lhq.replace = 0;
+    lhq.proto = &njs_object_hash_proto;
+    lhq.pool = vm->mem_pool;
 
+    while (n != 0) {
+        prop = njs_object_prop_alloc(vm, &njs_string_empty,
+                                     &njs_value_invalid, 1);
+        if (njs_slow_path(prop == NULL)) {
+            goto memory_error;
+        }
 
-const njs_lvlhsh_proto_t  njs_extern_hash_proto
-    njs_aligned(64) =
-{
-    NJS_LVLHSH_DEFAULT,
-    njs_extern_hash_test,
-    njs_lvlhsh_alloc,
-    njs_lvlhsh_free,
-};
+        prop->writable = external->writable;
+        prop->configurable = external->configurable;
+        prop->enumerable = external->enumerable;
 
+        if (external->flags & 4) {
+            njs_set_symbol(&prop->name, external->name.symbol);
 
-static njs_extern_t *
-njs_vm_external_add(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_external_t *external,
-    njs_uint_t n)
-{
-    njs_int_t           ret;
-    njs_extern_t        *ext, *child;
-    njs_function_t      *function;
-    njs_lvlhsh_query_t  lhq;
+            lhq.key_hash = external->name.symbol;
 
-    do {
-        ext = njs_mp_alloc(vm->mem_pool, sizeof(njs_extern_t));
-        if (njs_slow_path(ext == NULL)) {
-            goto memory_error;
-        }
-
-        ext->name = external->name;
+        } else {
+            ret = njs_string_set(vm, &prop->name, external->name.string.start,
+                                 external->name.string.length);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_ERROR;
+            }
 
-        njs_lvlhsh_init(&ext->hash);
+            lhq.key = external->name.string;
+            lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
+        }
 
-        ext->type = external->type;
-        ext->get = external->get;
-        ext->set = external->set;
-        ext->find = external->find;
-        ext->keys = external->keys;
-        ext->data = external->data;
+        lhq.value = prop;
 
-        if (external->method != NULL) {
+        switch (external->flags & 3) {
+        case NJS_EXTERN_METHOD:
             function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t));
             if (njs_slow_path(function == NULL)) {
                 goto memory_error;
             }
 
-            /*
-             * njs_mp_zalloc() does also:
-             *   njs_lvlhsh_init(&function->object.hash);
-             *   function->object.__proto__ = NULL;
-             *   function->ctor = 0;
-             */
-
-            function->object.__proto__ =
-                              &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
             function->object.shared_hash = vm->shared->arrow_instance_hash;
             function->object.type = NJS_FUNCTION;
             function->object.shared = 1;
             function->object.extensible = 1;
             function->args_offset = 1;
             function->native = 1;
-            function->u.native = external->method;
+            function->magic8 = external->u.method.magic8;
+            function->u.native = external->u.method.native;
 
-            ext->function = function;
+            njs_set_function(&prop->value, function);
 
-        } else {
-            ext->function = NULL;
-        }
+            break;
 
-        if (external->properties != NULL) {
-            child = njs_vm_external_add(vm, &ext->hash, external->properties,
-                                        external->nproperties);
-            if (njs_slow_path(child == NULL)) {
-                goto memory_error;
+        case NJS_EXTERN_PROPERTY:
+            if (external->u.property.handler != NULL) {
+                prop->type = NJS_PROPERTY_HANDLER;
+                prop->value.type = NJS_INVALID;
+                prop->value.data.truth = 1;
+                prop->value.data.magic16 = 0;
+                prop->value.data.magic32 = external->u.property.magic32;
+                prop->value.data.u.prop_handler = external->u.property.handler;
+
+            } else {
+                start = (u_char *) external->u.property.value;
+                size = njs_strlen(start);
+                length = njs_utf8_length(start, size);
+                if (njs_slow_path(length < 0)) {
+                    length = 0;
+                }
+
+                ret = njs_string_new(vm, &prop->value, start, size, length);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    return NJS_ERROR;
+                }
             }
-        }
 
-        if (hash != NULL) {
-            lhq.key_hash = njs_djb_hash(external->name.start,
-                                        external->name.length);
-            lhq.key = ext->name;
-            lhq.replace = 0;
-            lhq.value = ext;
-            lhq.pool = vm->mem_pool;
-            lhq.proto = &njs_extern_hash_proto;
+            break;
+
+        case NJS_EXTERN_OBJECT:
+        default:
+            next = njs_arr_item(protos, protos->items);
 
-            ret = njs_lvlhsh_insert(hash, &lhq);
+            ret = njs_external_add(vm, protos, external->u.object.properties,
+                                   external->u.object.nproperties);
             if (njs_slow_path(ret != NJS_OK)) {
-                njs_internal_error(vm, "lvlhsh insert failed");
-                return NULL;
+                return ret;
             }
+
+            prop->type = NJS_PROPERTY_HANDLER;
+            prop->value.type = NJS_INVALID;
+            prop->value.data.truth = 1;
+            prop->value.data.magic16 = next - slot;
+            prop->value.data.magic32 = lhq.key_hash;
+            prop->value.data.u.prop_handler = njs_external_prop_handler;
+
+            next->writable = external->u.object.writable;
+            next->configurable = external->u.object.configurable;
+            next->enumerable = external->u.object.enumerable;
+            next->prop_handler = external->u.object.prop_handler;
+            next->keys = external->u.object.keys;
+
+            break;
         }
 
-        external++;
-        n--;
+        ret = njs_lvlhsh_insert(hash, &lhq);
+        if (njs_slow_path(ret != NJS_OK)) {
+            njs_internal_error(vm, "lvlhsh insert failed");
+            return NJS_ERROR;
+        }
 
-    } while (n != 0);
+        n--;
+        external++;
+    }
 
-    return ext;
+    return NJS_OK;
 
 memory_error:
 
     njs_memory_error(vm);
 
-    return NULL;
+    return NJS_ERROR;
 }
 
 
-const njs_extern_t *
-njs_vm_external_prototype(njs_vm_t *vm, njs_external_t *external)
-{
-    return njs_vm_external_add(vm, &vm->external_prototypes_hash, external, 1);
-}
-
-
-njs_int_t
-njs_vm_external_create(njs_vm_t *vm, njs_value_t *ext_val,
-    const njs_extern_t *proto, njs_external_ptr_t object)
+static njs_int_t
+njs_external_prop_handler(njs_vm_t *vm, njs_object_prop_t *self,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
-    void       *obj;
-    uint32_t   n;
-    njs_arr_t  *externals;
+    njs_int_t           ret;
+    njs_object_prop_t   *prop;
+    njs_external_ptr_t  external;
+    njs_object_value_t  *ov;
+    njs_lvlhsh_query_t  lhq;
+    njs_exotic_slots_t  *slots;
 
-    if (njs_slow_path(proto == NULL)) {
-        return NJS_ERROR;
+    if (njs_slow_path(retval == NULL)) {
+        return NJS_DECLINED;
     }
 
-    if (vm->external_objects->mem_pool != vm->mem_pool) {
+    slots = NULL;
 
-        /* Making a local copy of externals in shared VM. */
+    if (njs_slow_path(setval != NULL)) {
+        *retval = *setval;
 
-        n = vm->external_objects->items;
+    } else {
+        external = njs_vm_external(vm, value);
+        if (njs_slow_path(external == NULL)) {
+            njs_value_undefined_set(retval);
+            return NJS_OK;
+        }
 
-        externals = njs_arr_create(vm->mem_pool, n + 4, sizeof(void *));
-        if (njs_slow_path(externals == NULL)) {
+        ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
+        if (njs_slow_path(ov == NULL)) {
+            njs_memory_error(vm);
             return NJS_ERROR;
         }
 
-        if (n > 0) {
-            memcpy(externals->start, vm->external_objects->start,
-                   n * sizeof(void *));
-            externals->items = n;
-        }
+        slots = njs_object(value)->slots + self->value.data.magic16;
 
-        vm->external_objects = externals;
+        njs_lvlhsh_init(&ov->object.hash);
+        ov->object.shared_hash = slots->external_shared_hash;
+        ov->object.type = NJS_OBJECT;
+        ov->object.shared = 0;
+        ov->object.extensible = 1;
+        ov->object.error_data = 0;
+        ov->object.fast_array = 0;
+        ov->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
+        ov->object.slots = slots;
+
+        njs_set_data(&ov->value, external);
+        njs_set_object_value(retval, ov);
     }
 
-    obj = njs_arr_add(vm->external_objects);
-    if (njs_slow_path(obj == NULL)) {
+    prop = njs_object_prop_alloc(vm, &self->name, retval, 1);
+    if (njs_slow_path(prop == NULL)) {
         return NJS_ERROR;
     }
 
-    memcpy(obj, &object, sizeof(void *));
-
-    if (proto->type != NJS_EXTERN_METHOD) {
-        ext_val->type = NJS_EXTERNAL;
-        ext_val->data.truth = 1;
-        ext_val->external.proto = proto;
-        ext_val->external.index = vm->external_objects->items - 1;
-
-    } else {
-        njs_set_function(ext_val, proto->function);
+    if (slots != NULL) {
+        prop->writable = slots->writable;
+        prop->configurable = slots->configurable;
+        prop->enumerable = slots->enumerable;
     }
 
-    return NJS_OK;
-}
-
+    lhq.value = prop;
+    njs_string_get(&self->name, &lhq.key);
+    lhq.key_hash = self->value.data.magic32;
+    lhq.replace = 1;
+    lhq.pool = vm->mem_pool;
+    lhq.proto = &njs_object_hash_proto;
 
-njs_external_ptr_t
-njs_vm_external(njs_vm_t *vm, const njs_value_t *value)
-{
-    if (njs_fast_path(njs_is_external(value))) {
-        return njs_extern_object(vm, value);
+    ret = njs_lvlhsh_insert(njs_object_hash(value), &lhq);
+    if (njs_slow_path(ret != NJS_OK)) {
+        njs_internal_error(vm, "lvlhsh insert/replace failed");
+        return NJS_ERROR;
     }
 
-    njs_type_error(vm, "external value is expected");
-
-    return NULL;
+    return NJS_OK;
 }
 
 
-njs_array_t *
-njs_extern_keys_array(njs_vm_t *vm, const njs_extern_t *external)
+static njs_uint_t
+njs_external_protos(const njs_external_t *external, njs_uint_t size)
 {
-    uint32_t            n, keys_length;
-    njs_int_t           ret;
-    njs_array_t         *keys;
-    const njs_lvlhsh_t  *hash;
-    njs_lvlhsh_each_t   lhe;
-    const njs_extern_t  *ext;
-
-    keys_length = 0;
-
-    njs_lvlhsh_each_init(&lhe, &njs_extern_hash_proto);
-
-    hash = &external->hash;
+    njs_uint_t  n;
 
-    for ( ;; ) {
-        ext = njs_lvlhsh_each(hash, &lhe);
+    n = 1;
 
-        if (ext == NULL) {
-            break;
-        }
-
-        keys_length++;
-    }
-
-    keys = njs_array_alloc(vm, 1, keys_length, NJS_ARRAY_SPARE);
-    if (njs_slow_path(keys == NULL)) {
-        return NULL;
-    }
-
-    n = 0;
-
-    njs_lvlhsh_each_init(&lhe, &njs_extern_hash_proto);
-
-    for ( ;; ) {
-        ext = njs_lvlhsh_each(hash, &lhe);
-
-        if (ext == NULL) {
-            break;
+    while (size != 0) {
+        if ((external->flags & 3) == NJS_EXTERN_OBJECT) {
+            n += njs_external_protos(external->u.object.properties,
+                                     external->u.object.nproperties);
         }
 
-        ret = njs_string_new(vm, &keys->start[n++], ext->name.start,
-                             ext->name.length, 0);
-
-        if (ret != NJS_OK) {
-            return NULL;
-        }
+        size--;
+        external++;
     }
 
-    return keys;
+    return n;
 }
 
 
-static njs_int_t
-njs_external_match(njs_vm_t *vm, njs_function_native_t func, njs_extern_t *ext,
-    njs_str_t *name, njs_extern_part_t *head, njs_extern_part_t *ppart)
+njs_external_proto_t
+njs_vm_external_prototype(njs_vm_t *vm, const njs_external_t *definition,
+    njs_uint_t n)
 {
-    u_char             *buf, *p;
-    size_t             len;
-    njs_int_t          ret;
-    njs_extern_t       *prop;
-    njs_extern_part_t  part, *pr;
-    njs_lvlhsh_each_t  lhe;
-
-    ppart->next = &part;
-
-    njs_lvlhsh_each_init(&lhe, &njs_extern_hash_proto);
-
-    for ( ;; ) {
-        prop = njs_lvlhsh_each(&ext->hash, &lhe);
-        if (prop == NULL) {
-            break;
-        }
+    njs_arr_t   *protos;
+    njs_int_t   ret;
+    njs_uint_t  size;
 
-        part.next = NULL;
-        part.str = prop->name;
+    size = njs_external_protos(definition, n) + 1;
 
-        if (prop->function && prop->function->u.native == func) {
-            goto found;
-        }
+    protos = njs_arr_create(vm->mem_pool, size, sizeof(njs_exotic_slots_t));
+    if (njs_slow_path(protos == NULL)) {
+        njs_memory_error(vm);
+        return NULL;
+    }
 
-        ret = njs_external_match(vm, func, prop, name, head, &part);
-        if (ret != NJS_DECLINED) {
-            return ret;
-        }
+    ret = njs_external_add(vm, protos, definition, n);
+    if (njs_slow_path(ret != NJS_OK)) {
+        njs_internal_error(vm, "njs_vm_external_add() failed");
+        return NULL;
     }
 
-    return NJS_DECLINED;
+    return protos;
+}
 
-found:
 
-    len = 0;
+njs_int_t
+njs_vm_external_create(njs_vm_t *vm, njs_value_t *value,
+    njs_external_proto_t proto, njs_external_ptr_t external, njs_bool_t shared)
+{
+    njs_arr_t           *protos;
+    njs_object_value_t  *ov;
+    njs_exotic_slots_t  *slots;
 
-    for (pr = head; pr != NULL; pr = pr->next) {
-        len += pr->str.length + njs_length(".");
+    if (njs_slow_path(proto == NULL)) {
+        return NJS_ERROR;
     }
 
-    buf = njs_mp_zalloc(vm->mem_pool, len);
-    if (buf == NULL) {
+    ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
+    if (njs_slow_path(ov == NULL)) {
+        njs_memory_error(vm);
         return NJS_ERROR;
     }
 
-    p = buf;
+    protos = proto;
+    slots = protos->start;
 
-    for (pr = head; pr != NULL; pr = pr->next) {
-        p = njs_sprintf(p, buf + len, "%V.", &pr->str);
-    }
+    njs_lvlhsh_init(&ov->object.hash);
+    ov->object.shared_hash = slots->external_shared_hash;
+    ov->object.type = NJS_OBJECT;
+    ov->object.shared = shared;
+    ov->object.extensible = 1;
+    ov->object.error_data = 0;
+    ov->object.fast_array = 0;
+    ov->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
+    ov->object.slots = slots;
 
-    name->start = (u_char *) buf;
-    name->length = len - 1;
+    njs_set_object_value(value, ov);
+    njs_set_data(&ov->value, external);
 
     return NJS_OK;
 }
 
 
-njs_int_t
-njs_external_match_native_function(njs_vm_t *vm, njs_function_native_t func,
-    njs_str_t *name)
+njs_external_ptr_t
+njs_vm_external(njs_vm_t *vm, const njs_value_t *value)
 {
-    njs_int_t          ret;
-    njs_extern_t       *ext;
-    njs_extern_part_t  part;
-    njs_lvlhsh_each_t  lhe;
-
-    njs_lvlhsh_each_init(&lhe, &njs_extern_hash_proto);
-
-    for ( ;; ) {
-        ext = njs_lvlhsh_each(&vm->external_prototypes_hash, &lhe);
-        if (ext == NULL) {
-            break;
-        }
-
-        part.next = NULL;
-        part.str = ext->name;
-
-        ret = njs_external_match(vm, func, ext, name, &part, &part);
-        if (ret != NJS_DECLINED) {
-            return ret;
+    if (njs_fast_path(njs_is_object_value(value))) {
+        value = njs_object_value(value);
+        if (njs_fast_path(njs_is_data(value))) {
+            return njs_value_data(value);
         }
     }
 
-    return NJS_DECLINED;
+    return NULL;
 }
diff --git a/src/njs_extern.h b/src/njs_extern.h
deleted file mode 100644 (file)
index bfdad22..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) NGINX, Inc.
- */
-
-#ifndef _NJS_EXTERN_H_INCLUDED_
-#define _NJS_EXTERN_H_INCLUDED_
-
-
-#define njs_extern_object(vm, ext)                                          \
-    (*(void **) njs_arr_item((vm)->external_objects, (ext)->external.index))
-
-#define njs_extern_index(vm, idx)                                          \
-    (*(void **) njs_arr_item((vm)->external_objects, idx))
-
-
-struct njs_extern_s {
-    /* A hash of inclusive njs_extern_t. */
-    njs_lvlhsh_t                 hash;
-
-    uintptr_t                    type;
-    njs_str_t                    name;
-
-    njs_extern_get_t             get;
-    njs_extern_set_t             set;
-    njs_extern_find_t            find;
-
-    njs_extern_keys_t            keys;
-
-    njs_function_t               *function;
-
-    uintptr_t                    data;
-};
-
-
-njs_array_t *njs_extern_keys_array(njs_vm_t *vm, const njs_extern_t *external);
-njs_int_t njs_external_match_native_function(njs_vm_t *vm,
-    njs_function_native_t func, njs_str_t *name);
-
-
-extern const njs_lvlhsh_proto_t  njs_extern_hash_proto;
-
-
-#endif /* _NJS_EXTERN_H_INCLUDED_ */
index bb612a52d7e08f359d18e37579da445ad9c56729..a5bf5132efe66fe1278341cf7117a8d539241546 100644 (file)
@@ -700,7 +700,7 @@ njs_function_native_call(njs_vm_t *vm)
         call = target->u.native;
     }
 
-    ret = call(vm, native->arguments, native->nargs, function->magic);
+    ret = call(vm, native->arguments, native->nargs, function->magic8);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
index cd3aca32da2dcb5bd26a874745449c59ac200758..7724277fe106683843ca85ac458618b01e1865fa 100644 (file)
@@ -206,7 +206,7 @@ njs_function_apply(njs_vm_t *vm, njs_function_t *function,
 njs_inline njs_bool_t
 njs_native_function_same(const njs_function_t *f1, const njs_function_t *f2)
 {
-    return f1->u.native == f2->u.native && f1->magic == f2->magic;
+    return f1->u.native == f2->u.native && f1->magic8 == f2->magic8;
 }
 
 
index 7369b174fcafd3a33e0920baf94c7bf41cfbb12a..18b016773bd5fa053a0817c046cd6026afd72a05 100644 (file)
@@ -1053,7 +1053,7 @@ njs_json_parse_exception(njs_json_parse_ctx_t *ctx, const char *msg,
 
 static njs_json_state_t *
 njs_json_push_stringify_state(njs_vm_t *vm, njs_json_stringify_t *stringify,
-    const njs_value_t *value)
+    njs_value_t *value)
 {
     njs_int_t         ret;
     njs_json_state_t  *state;
@@ -1134,7 +1134,6 @@ njs_json_is_object(const njs_value_t *value)
     return (((value)->type == NJS_OBJECT)
              || ((value)->type == NJS_ARRAY)
              || ((value)->type == NJS_OBJECT_SYMBOL)
-             || ((value)->type == NJS_EXTERNAL)
              || ((value)->type >= NJS_REGEXP));
 }
 
@@ -1991,24 +1990,12 @@ njs_dump_terminal(njs_json_stringify_t *stringify, njs_chb_t *chain,
 }
 
 
-njs_inline njs_bool_t
-njs_dump_is_external_object(const njs_value_t *value)
-{
-    if (!njs_is_external(value)) {
-        return 0;
-    }
-
-    return value->external.proto->type == NJS_EXTERN_OBJECT;
-}
-
-
 njs_inline njs_bool_t
 njs_dump_is_recursive(const njs_value_t *value)
 {
     return (value->type == NJS_OBJECT && !njs_object(value)->error_data)
            || (value->type == NJS_ARRAY)
-           || (value->type >= NJS_OBJECT_SPECIAL_MAX)
-           || njs_dump_is_external_object(value);
+           || (value->type >= NJS_OBJECT_SPECIAL_MAX);
 }
 
 
index 51e8229d855486b3a10a40a825fbfef4aed8792b..d6af2be70d40c21c2eca567b572abfc80406118b 100644 (file)
@@ -78,7 +78,6 @@
 #include <njs_crypto.h>
 
 #include <njs_event.h>
-#include <njs_extern.h>
 #include <njs_module.h>
 
 
index 3443b19e80cd0a802ecce9e52491b2f8dfbd2406..0cc20f90c8746f63bddc28219f91b65106bcf33e 100644 (file)
@@ -454,6 +454,7 @@ njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t local)
         object = &module->object;
 
         object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
+        object->slots = NULL;
         object->shared = 0;
         object->extensible = 1;
         object->error_data = 0;
index 624db73b808ff9f03b323a311a0c0181842fc57f..8052c3c7c2ff4ca54fa41ec2cba8dd95c6f1fccf 100644 (file)
@@ -40,6 +40,7 @@ njs_object_alloc(njs_vm_t *vm)
         njs_lvlhsh_init(&object->hash);
         njs_lvlhsh_init(&object->shared_hash);
         object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
+        object->slots = NULL;
         object->type = NJS_OBJECT;
         object->shared = 0;
         object->extensible = 1;
@@ -58,6 +59,7 @@ njs_object_alloc(njs_vm_t *vm)
 njs_object_t *
 njs_object_value_copy(njs_vm_t *vm, njs_value_t *value)
 {
+    size_t        size;
     njs_object_t  *object;
 
     object = njs_object(value);
@@ -66,10 +68,12 @@ njs_object_value_copy(njs_vm_t *vm, njs_value_t *value)
         return object;
     }
 
-    object = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_t));
+    size = njs_is_object_value(value) ? sizeof(njs_object_value_t)
+                                      : sizeof(njs_object_t);
+    object = njs_mp_alloc(vm->mem_pool, size);
 
     if (njs_fast_path(object != NULL)) {
-        *object = *njs_object(value);
+        memcpy(object, njs_object(value), size);
         object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
         object->shared = 0;
         value->data.u.object = object;
@@ -108,6 +112,7 @@ njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, njs_uint_t type)
 
         index = njs_primitive_prototype_index(type);
         ov->object.__proto__ = &vm->prototypes[index].object;
+        ov->object.slots = NULL;
 
         ov->value = *value;
 
index 5e2291326e4a33c0ae7cf0545d285105d6e40cb0..4ea537dbff738176d989fa71b8341fbe24483b55 100644 (file)
@@ -477,6 +477,8 @@ njs_int_t
 njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
 {
     njs_int_t          ret;
+    njs_value_t        *value;
+    njs_object_t       *object;
     njs_function_t     *function;
     njs_object_prop_t  *prop, *shared;
 
@@ -526,16 +528,32 @@ njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
         return NJS_OK;
     }
 
-    if (!njs_is_function(&prop->value)) {
+    value = &prop->value;
+
+    switch (value->type) {
+    case NJS_OBJECT:
+    case NJS_OBJECT_VALUE:
+        object = njs_object_value_copy(vm, value);
+        if (njs_slow_path(object == NULL)) {
+            return NJS_ERROR;
+        }
+
+        value->data.u.object = object;
         return NJS_OK;
-    }
 
-    function = njs_function_value_copy(vm, &prop->value);
-    if (njs_slow_path(function == NULL)) {
-        return NJS_ERROR;
+    case NJS_FUNCTION:
+        function = njs_function_value_copy(vm, &prop->value);
+        if (njs_slow_path(function == NULL)) {
+            return NJS_ERROR;
+        }
+
+        return njs_function_name_set(vm, function, &prop->name, 0);
+
+    default:
+        break;
     }
 
-    return njs_function_name_set(vm, function, &prop->name, 0);
+    return NJS_OK;
 }
 
 
index 6eb4b02bfe867562bfc202b01eddcc588a3c30c3..cf0a879e5904f337c61534f9b874379c9f7a7001 100644 (file)
@@ -90,6 +90,7 @@ njs_promise_alloc(njs_vm_t *vm)
     promise->object.error_data = 0;
     promise->object.fast_array = 0;
     promise->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_PROMISE].object;
+    promise->object.slots = NULL;
 
     data = (njs_promise_data_t *) ((uint8_t *) promise + sizeof(njs_promise_t));
 
index 1a05d81409cab44afdcaef3617bbdd979b338331..3826c8a6f1ceda63cf4ead4feb5e5f33977223ca 100644 (file)
@@ -710,6 +710,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern)
         njs_lvlhsh_init(&regexp->object.hash);
         regexp->object.shared_hash = vm->shared->regexp_instance_hash;
         regexp->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_REGEXP].object;
+        regexp->object.slots = NULL;
         regexp->object.type = NJS_REGEXP;
         regexp->object.shared = 0;
         regexp->object.extensible = 1;
index 7736b47210575de0945b0622db5e29ae5a037f29..2df2c82d395ba8fa4826ad5a36626eb1a4b55b0e 100644 (file)
@@ -99,9 +99,7 @@ static char *njs_completion_generator(const char *text, int state);
 #endif
 
 static njs_int_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
-static njs_int_t njs_ext_console_dump(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
+    njs_uint_t nargs, njs_index_t indent);
 static njs_int_t njs_ext_console_time(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 static njs_int_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args,
@@ -122,63 +120,59 @@ static void lvlhsh_pool_free(void *pool, void *p, size_t size);
 
 static njs_external_t  njs_ext_console[] = {
 
-    { njs_str("log"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      njs_ext_console_log,
-      0 },
-
-    { njs_str("dump"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      njs_ext_console_dump,
-      0 },
-
-    { njs_str("time"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      njs_ext_console_time,
-      0 },
-
-    { njs_str("timeEnd"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      njs_ext_console_time_end,
-      0 },
-};
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("dump"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_ext_console_log,
+            .magic8 = 1,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("log"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_ext_console_log,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Console",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("time"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_ext_console_time,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("timeEnd"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_ext_console_time_end,
+        }
+    },
 
-static njs_external_t  njs_externals[] = {
-
-    { njs_str("console"),
-      NJS_EXTERN_OBJECT,
-      njs_ext_console,
-      njs_nitems(njs_ext_console),
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
 };
 
 
@@ -620,48 +614,61 @@ njs_console_init(njs_vm_t *vm, njs_console_t *console)
 }
 
 
-static njs_int_t
-njs_externals_add(njs_vm_t *vm, njs_external_t *definition,
-    const njs_str_t *name, njs_external_ptr_t object)
+static njs_value_t *
+njs_external_add(njs_vm_t *vm, njs_external_t *definition,
+    njs_uint_t n, const njs_str_t *name, njs_external_ptr_t external)
 {
-    njs_int_t           ret;
-    njs_value_t         *value;
-    const njs_extern_t  *proto;
+    njs_int_t             ret;
+    njs_value_t           *value;
+    njs_external_proto_t  proto;
 
-    proto = njs_vm_external_prototype(vm, definition);
+    proto = njs_vm_external_prototype(vm, definition, n);
     if (njs_slow_path(proto == NULL)) {
         njs_stderror("failed to add \"%V\" proto\n", name);
-        return NJS_ERROR;
+        return NULL;
     }
 
     value = njs_mp_zalloc(vm->mem_pool, sizeof(njs_opaque_value_t));
     if (njs_slow_path(value == NULL)) {
-        return NJS_ERROR;
+        return NULL;
     }
 
-    ret = njs_vm_external_create(vm, value, proto, object);
+    ret = njs_vm_external_create(vm, value, proto, external, 0);
     if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
+        return NULL;
+    }
+
+    ret = njs_vm_bind(vm, name, value, 0);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NULL;
     }
 
-    return njs_vm_bind(vm, name, value, 1);
+    return value;
 }
 
 
 static njs_int_t
 njs_externals_init(njs_vm_t *vm, njs_console_t *console)
 {
-    njs_int_t  ret;
+    njs_int_t    ret;
+    njs_value_t  *value, method;
 
     static const njs_str_t  console_name = njs_str("console");
     static const njs_str_t  print_name = njs_str("print");
+    static const njs_value_t  string_log = njs_string("log");
 
-    ret = njs_externals_add(vm, &njs_externals[0], &console_name, console);
+    value = njs_external_add(vm, njs_ext_console, njs_nitems(njs_ext_console),
+                            &console_name, console);
+    if (njs_slow_path(value == NULL)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_value_property(vm, value, njs_value_arg(&string_log), &method);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
-    ret = njs_externals_add(vm, &njs_ext_console[0], &print_name, console);
+    ret = njs_vm_bind(vm, &print_name, &method, 0);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -1074,39 +1081,7 @@ next:
 
 static njs_int_t
 njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    njs_str_t   msg;
-    njs_uint_t  n;
-
-    n = 1;
-
-    while (n < nargs) {
-        if (njs_vm_value_dump(vm, &msg, njs_argument(args, n), 1, 0)
-            == NJS_ERROR)
-        {
-            return NJS_ERROR;
-        }
-
-        njs_printf("%s", (n != 1) ? " " : "");
-        njs_print(msg.start, msg.length);
-
-        n++;
-    }
-
-    if (nargs > 1) {
-        njs_printf("\n");
-    }
-
-    njs_set_undefined(&vm->retval);
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_ext_console_dump(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
+    njs_index_t indent)
 {
     njs_str_t   msg;
     njs_uint_t  n;
@@ -1114,7 +1089,7 @@ njs_ext_console_dump(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     n = 1;
 
     while (n < nargs) {
-        if (njs_vm_value_dump(vm, &msg, njs_argument(args, n), 1, 1)
+        if (njs_vm_value_dump(vm, &msg, njs_argument(args, n), 1, indent)
             == NJS_ERROR)
         {
             return NJS_ERROR;
@@ -1152,6 +1127,7 @@ njs_ext_console_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     console = njs_vm_external(vm, njs_arg(args, nargs, 0));
     if (njs_slow_path(console == NULL)) {
+        njs_type_error(vm, "external value is expected");
         return NJS_ERROR;
     }
 
@@ -1228,6 +1204,7 @@ njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     console = njs_vm_external(vm, njs_arg(args, nargs, 0));
     if (njs_slow_path(console == NULL)) {
+        njs_type_error(vm, "external value is expected");
         return NJS_ERROR;
     }
 
index 22f60d295b2cea9bb09546df53389580ae96a022..8e5325494aa721e2f8bd48383048898f532b4d5f 100644 (file)
@@ -7,25 +7,6 @@
 #ifndef _NJS_SYMBOL_H_INCLUDED_
 #define _NJS_SYMBOL_H_INCLUDED_
 
-typedef enum {
-    NJS_SYMBOL_INVALID                  = 0,
-    NJS_SYMBOL_ASYNC_ITERATOR           = 1,
-    NJS_SYMBOL_HAS_INSTANCE             = 2,
-    NJS_SYMBOL_IS_CONCAT_SPREADABLE     = 3,
-    NJS_SYMBOL_ITERATOR                 = 4,
-    NJS_SYMBOL_MATCH                    = 5,
-    NJS_SYMBOL_MATCH_ALL                = 6,
-    NJS_SYMBOL_REPLACE                  = 7,
-    NJS_SYMBOL_SEARCH                   = 8,
-    NJS_SYMBOL_SPECIES                  = 9,
-    NJS_SYMBOL_SPLIT                    = 10,
-    NJS_SYMBOL_TO_PRIMITIVE             = 11,
-    NJS_SYMBOL_TO_STRING_TAG            = 12,
-    NJS_SYMBOL_UNSCOPABLES              = 13,
-#define NJS_SYMBOL_KNOWN_MAX (NJS_SYMBOL_UNSCOPABLES + 1)
-} njs_wellknown_symbol_t;
-
-
 njs_int_t njs_symbol_to_string(njs_vm_t *vm, njs_value_t *dst,
     const njs_value_t *value);
 
index 493c5334cd89d8c2c1b879f8672d1918eeac8015..043f5f2b595fe886f32b35f24370b2ece3651015 100644 (file)
@@ -18,13 +18,7 @@ static njs_int_t njs_typed_array_property_query(njs_vm_t *vm,
 static njs_int_t njs_string_property_query(njs_vm_t *vm,
     njs_property_query_t *pq, njs_value_t *object, uint32_t index);
 static njs_int_t njs_external_property_query(njs_vm_t *vm,
-    njs_property_query_t *pq, njs_value_t *object);
-static njs_int_t njs_external_property_set(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 njs_external_property_delete(njs_vm_t *vm,
-    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
-    njs_value_t *retval);
+    njs_property_query_t *pq, njs_value_t *value);
 
 
 const njs_value_t  njs_value_null =         njs_value(NJS_NULL, 0, 0.0);
@@ -194,40 +188,31 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value,
 
 
 njs_array_t *
-njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
+njs_value_enumerate(njs_vm_t *vm, njs_value_t *value,
     njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all)
 {
-    void                *obj;
     njs_int_t           ret;
     njs_value_t         keys;
     njs_object_value_t  obj_val;
-    const njs_extern_t  *ext_proto;
+    njs_exotic_slots_t  *slots;
 
     if (njs_is_object(value)) {
-        return njs_object_enumerate(vm, njs_object(value), kind, type, all);
-    }
-
-    if (value->type != NJS_STRING) {
-        if (kind == NJS_ENUM_KEYS
-            && (type & NJS_ENUM_STRING)
-            && njs_is_external(value))
-        {
-            ext_proto = value->external.proto;
-
-            if (ext_proto->keys != NULL) {
-                obj = njs_extern_object(vm, value);
-
-                ret = ext_proto->keys(vm, obj, &keys);
+        if (kind == NJS_ENUM_KEYS && (type & NJS_ENUM_STRING)) {
+            slots = njs_object_slots(value);
+            if (slots != NULL && slots->keys != NULL) {
+                ret = slots->keys(vm, value, &keys);
                 if (njs_slow_path(ret != NJS_OK)) {
                     return NULL;
                 }
 
                 return njs_array(&keys);
             }
-
-            return njs_extern_keys_array(vm, ext_proto);
         }
 
+        return njs_object_enumerate(vm, njs_object(value), kind, type, all);
+    }
+
+    if (value->type != NJS_STRING) {
         return njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
     }
 
@@ -239,40 +224,31 @@ njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
 
 
 njs_array_t *
-njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
+njs_value_own_enumerate(njs_vm_t *vm, njs_value_t *value,
     njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all)
 {
-    void                *obj;
     njs_int_t           ret;
     njs_value_t         keys;
     njs_object_value_t  obj_val;
-    const njs_extern_t  *ext_proto;
+    njs_exotic_slots_t  *slots;
 
     if (njs_is_object(value)) {
-        return njs_object_own_enumerate(vm, njs_object(value), kind, type, all);
-    }
-
-    if (value->type != NJS_STRING) {
-        if (kind == NJS_ENUM_KEYS
-            && (type & NJS_ENUM_STRING)
-            && njs_is_external(value))
-        {
-            ext_proto = value->external.proto;
-
-            if (ext_proto->keys != NULL) {
-                obj = njs_extern_object(vm, value);
-
-                ret = ext_proto->keys(vm, obj, &keys);
+        if (kind == NJS_ENUM_KEYS && (type & NJS_ENUM_STRING)) {
+            slots = njs_object_slots(value);
+            if (slots != NULL && slots->keys != NULL) {
+                ret = slots->keys(vm, value, &keys);
                 if (njs_slow_path(ret != NJS_OK)) {
                     return NULL;
                 }
 
                 return njs_array(&keys);
             }
-
-            return njs_extern_keys_array(vm, ext_proto);
         }
 
+        return njs_object_own_enumerate(vm, njs_object(value), kind, type, all);
+    }
+
+    if (value->type != NJS_STRING) {
         return njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
     }
 
@@ -328,13 +304,11 @@ njs_type_string(njs_value_type_t type)
     case NJS_STRING:
         return "string";
 
-    case NJS_EXTERNAL:
-        return "external";
-
     case NJS_INVALID:
         return "invalid";
 
     case NJS_OBJECT:
+    case NJS_OBJECT_VALUE:
         return "object";
 
     case NJS_ARRAY:
@@ -581,10 +555,6 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
         obj = &function->object;
         break;
 
-    case NJS_EXTERNAL:
-        obj = NULL;
-        break;
-
     case NJS_UNDEFINED:
     case NJS_NULL:
     default:
@@ -615,12 +585,11 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
                                             pq->lhq.key.length);
         }
 
-        if (obj == NULL) {
-            pq->own = 1;
+        ret = njs_object_property_query(vm, pq, obj, key);
+
+        if (njs_slow_path(ret == NJS_DECLINED && obj->slots != NULL)) {
             return njs_external_property_query(vm, pq, value);
         }
-
-        return njs_object_property_query(vm, pq, obj, key);
     }
 
     return ret;
@@ -913,13 +882,16 @@ njs_string_property_query(njs_vm_t *vm, njs_property_query_t *pq,
 
 static njs_int_t
 njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq,
-    njs_value_t *object)
+    njs_value_t *value)
 {
-    void                *obj;
-    njs_int_t           ret;
-    uintptr_t           data;
     njs_object_prop_t   *prop;
-    const njs_extern_t  *ext_proto;
+    njs_exotic_slots_t  *slots;
+
+    slots = njs_object_slots(value);
+
+    if (njs_slow_path(slots->prop_handler == NULL)) {
+        return NJS_DECLINED;
+    }
 
     prop = &pq->scratch;
 
@@ -934,129 +906,38 @@ njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq,
      *   njs_set_null(&prop->setter);
      */
 
-    prop->enumerable = 1;
-
-    ext_proto = object->external.proto;
-
-    pq->lhq.proto = &njs_extern_hash_proto;
-    ret = njs_lvlhsh_find(&ext_proto->hash, &pq->lhq);
-
-    if (ret == NJS_OK) {
-        ext_proto = pq->lhq.value;
+    prop->name = pq->key;
 
-        prop->value.type = NJS_EXTERNAL;
-        prop->value.data.truth = 1;
-        prop->value.external.proto = ext_proto;
-        prop->value.external.index = object->external.index;
-
-        if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) {
-            goto done;
-        }
-
-        data = ext_proto->data;
-
-    } else {
-
-        if (pq->lhq.key.start == NULL) {
-            /* Symbol.toStringTag is not supported yet. */
-            goto done;
-        }
+    pq->lhq.value = prop;
 
-        data = (uintptr_t) &pq->lhq.key;
-    }
+    prop->writable = slots->writable;
+    prop->configurable = slots->configurable;
+    prop->enumerable = slots->enumerable;
 
     switch (pq->query) {
 
     case NJS_PROPERTY_QUERY_GET:
-        if (ext_proto->get != NULL) {
-            obj = njs_extern_object(vm, object);
-            ret = ext_proto->get(vm, &prop->value, obj, data);
-            if (njs_slow_path(ret != NJS_OK)) {
-                return ret;
-            }
-        }
-
-        break;
+        return slots->prop_handler(vm, prop, value, NULL, &prop->value);
 
     case NJS_PROPERTY_QUERY_SET:
-    case NJS_PROPERTY_QUERY_DELETE:
-
-        prop->type = NJS_PROPERTY_HANDLER;
-        prop->name = *object;
-
-        if (pq->query == NJS_PROPERTY_QUERY_SET) {
-            prop->writable = (ext_proto->set != NULL);
-            prop->value.data.u.prop_handler = njs_external_property_set;
-
-        } else {
-            prop->configurable = (ext_proto->find != NULL);
-            prop->value.data.u.prop_handler = njs_external_property_delete;
+        if (slots->writable == 0) {
+            return NJS_OK;
         }
 
-        pq->ext_data = data;
-        pq->ext_proto = ext_proto;
-        pq->ext_index = object->external.index;
-
-        pq->lhq.value = prop;
-
-        vm->stash = (uintptr_t) pq;
-
-        return NJS_OK;
-    }
-
-done:
-
-    if (ext_proto->type == NJS_EXTERN_METHOD) {
-        njs_set_function(&prop->value, ext_proto->function);
-    }
-
-    pq->lhq.value = prop;
-
-    return ret;
-}
-
-
-static njs_int_t
-njs_external_property_set(njs_vm_t *vm, njs_object_prop_t *prop,
-    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
-{
-    void                  *obj;
-    njs_int_t             ret;
-    njs_str_t             s;
-    njs_property_query_t  *pq;
-
-    pq = (njs_property_query_t *) vm->stash;
+        break;
 
-    if (!njs_is_null_or_undefined(setval)) {
-        ret = njs_vm_value_to_string(vm, &s, setval);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
+    case NJS_PROPERTY_QUERY_DELETE:
+        if (slots->configurable == 0) {
+            return NJS_OK;
         }
 
-    } else {
-        s = njs_str_value("");
+        break;
     }
 
-    *retval = *setval;
-
-    obj = njs_extern_index(vm, pq->ext_index);
-
-    return pq->ext_proto->set(vm, obj, pq->ext_data, &s);
-}
-
+    prop->type = NJS_PROPERTY_HANDLER;
+    prop->value.data.u.prop_handler = slots->prop_handler;
 
-static njs_int_t
-njs_external_property_delete(njs_vm_t *vm, njs_object_prop_t *prop,
-    njs_value_t *value, njs_value_t *unused, njs_value_t *unused2)
-{
-    void                  *obj;
-    njs_property_query_t  *pq;
-
-    pq = (njs_property_query_t *) vm->stash;
-
-    obj = njs_extern_index(vm, pq->ext_index);
-
-    return pq->ext_proto->find(vm, obj, pq->ext_data, 1);
+    return NJS_OK;
 }
 
 
@@ -1407,13 +1288,11 @@ njs_value_property_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
 
     switch (prop->type) {
     case NJS_PROPERTY_HANDLER:
-        if (njs_is_external(value)) {
+        if (njs_is_object(value) && njs_object_slots(value) != NULL) {
             ret = prop->value.data.u.prop_handler(vm, prop, value, NULL, NULL);
-            if (njs_slow_path(ret != NJS_OK)) {
-                return NJS_ERROR;
+            if (njs_slow_path(ret != NJS_DECLINED)) {
+                return ret;
             }
-
-            return NJS_OK;
         }
 
         /* Fall through. */
index a38a18c1ada51e03102f61fd448e36ca833a7088..622b581cb3a8a5d81083bf3c1e116ec531bf7d9c 100644 (file)
@@ -40,9 +40,6 @@ typedef enum {
 
     NJS_DATA,
 
-    /* The type is external code. */
-    NJS_EXTERNAL,
-
     /*
      * The invalid value type is used:
      *   for uninitialized array members,
@@ -79,7 +76,6 @@ typedef enum {
 } njs_value_type_t;
 
 
-typedef struct njs_object_prop_s      njs_object_prop_t;
 typedef struct njs_string_s           njs_string_t;
 typedef struct njs_object_s           njs_object_t;
 typedef struct njs_object_value_s     njs_object_value_t;
@@ -95,20 +91,6 @@ typedef struct njs_property_next_s    njs_property_next_t;
 typedef struct njs_object_init_s      njs_object_init_t;
 
 
-/*
- * njs_prop_handler_t operates as a property getter and/or setter.
- * The handler receives NULL setval if it is invoked in GET context and
- * non-null otherwise.
- *
- * njs_prop_handler_t is expected to return:
- *   NJS_OK - handler executed successfully;
- *   NJS_ERROR - some error, vm->retval contains appropriate exception;
- *   NJS_DECLINED - handler was applied to inappropriate object, vm->retval
- *   contains undefined value.
- */
-typedef njs_int_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_object_prop_t *prop,
-    njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
-
 #if (!NJS_HAVE_GCC_ATTRIBUTE_ALIGNED)
 #error "aligned attribute is required"
 #endif
@@ -185,18 +167,22 @@ union njs_value_s {
         njs_string_t                  *data;
     } long_string;
 
-    struct {
-        njs_value_type_t              type:8;  /* 6 bits */
-        uint8_t                       truth;
+    njs_value_type_t                  type:8;  /* 6 bits */
+};
 
-        uint16_t                      _spare;
 
-        uint32_t                      index;
-        const njs_extern_t            *proto;
-    } external;
+typedef struct {
+    /* Get, also Set if writable, also Delete if configurable. */
+    njs_prop_handler_t  prop_handler;
+    unsigned            writable:1;
+    unsigned            configurable:1;
+    unsigned            enumerable:1;
 
-    njs_value_type_t                  type:8;  /* 6 bits */
-};
+    njs_exotic_keys_t   keys;
+
+    /* A shared hash of njs_object_prop_t for externals. */
+    njs_lvlhsh_t        external_shared_hash;
+} njs_exotic_slots_t;
 
 
 struct njs_object_s {
@@ -206,8 +192,8 @@ struct njs_object_s {
     /* A shared hash of njs_object_prop_t. */
     njs_lvlhsh_t                      shared_hash;
 
-    /* An object __proto__. */
     njs_object_t                      *__proto__;
+    njs_exotic_slots_t                *slots;
 
     /* The type is used in constructor prototypes. */
     njs_value_type_t                  type:8;
@@ -294,7 +280,7 @@ struct njs_function_s {
     uint8_t                           ctor:1;
     uint8_t                           global_this:1;
 
-    uint8_t                           magic;
+    uint8_t                           magic8;
 
     union {
         njs_function_lambda_t         *lambda;
@@ -403,11 +389,6 @@ typedef struct {
     /* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */
     njs_object_prop_t           scratch;
 
-    /* These three fields are used for NJS_EXTERNAL setters. */
-    uintptr_t                   ext_data;
-    const njs_extern_t          *ext_proto;
-    uint32_t                    ext_index;
-
     njs_value_t                 key;
     njs_object_t                *prototype;
     njs_object_prop_t           *own_whiteout;
@@ -463,7 +444,7 @@ typedef struct {
 
 #define _njs_function(_function, _args_count, _ctor, _magic) {                \
     .native = 1,                                                              \
-    .magic = _magic,                                                          \
+    .magic8 = _magic,                                                         \
     .args_count = _args_count,                                                \
     .ctor = _ctor,                                                            \
     .args_offset = 1,                                                         \
@@ -683,10 +664,6 @@ typedef struct {
     ((value)->type == NJS_OBJECT && njs_object(value)->error_data)
 
 
-#define njs_is_external(value)                                                \
-    ((value)->type == NJS_EXTERNAL)
-
-
 #define njs_is_valid(value)                                                   \
     ((value)->type != NJS_INVALID)
 
@@ -719,6 +696,10 @@ typedef struct {
     (&(value)->data.u.object->hash)
 
 
+#define njs_object_slots(value)                                               \
+    ((value)->data.u.object->slots)
+
+
 #define njs_object_hash_is_empty(value)                                       \
     (njs_lvlhsh_is_empty(njs_object_hash(value)))
 
@@ -861,6 +842,15 @@ njs_set_uint32(njs_value_t *value, uint32_t num)
 }
 
 
+njs_inline void
+njs_set_symbol(njs_value_t *value, uint32_t symbol)
+{
+    value->data.magic32 = symbol;
+    value->type = NJS_SYMBOL;
+    value->data.truth = 1;
+}
+
+
 njs_inline void
 njs_set_data(njs_value_t *value, void *data)
 {
@@ -1008,9 +998,9 @@ void njs_value_retain(njs_value_t *value);
 void njs_value_release(njs_vm_t *vm, njs_value_t *value);
 njs_int_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst,
     njs_value_t *value, njs_uint_t hint);
-njs_array_t *njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
+njs_array_t *njs_value_enumerate(njs_vm_t *vm, njs_value_t *value,
     njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
-njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
+njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, njs_value_t *value,
     njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
 njs_int_t njs_value_length(njs_vm_t *vm, njs_value_t *value, uint64_t *dst);
 const char *njs_type_string(njs_value_type_t type);
index e6ec424c68bbaea1593a7da26af776122100a879..9857ffdd01fb219464130d1ba757fa080dfb03e4 100644 (file)
@@ -60,13 +60,6 @@ njs_vm_create(njs_vm_opt_t *options)
 
     vm->external = options->external;
 
-    vm->external_objects = njs_arr_create(vm->mem_pool, 4, sizeof(void *));
-    if (njs_slow_path(vm->external_objects == NULL)) {
-        return NULL;
-    }
-
-    njs_lvlhsh_init(&vm->external_prototypes_hash);
-
     vm->trace.level = NJS_LEVEL_TRACE;
     vm->trace.size = 2048;
     vm->trace.handler = njs_parser_trace_handler;
@@ -641,6 +634,34 @@ njs_vm_value_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size)
 }
 
 
+uint16_t
+njs_vm_prop_magic16(njs_object_prop_t *prop)
+{
+    return prop->value.data.magic16;
+}
+
+
+uint32_t
+njs_vm_prop_magic32(njs_object_prop_t *prop)
+{
+    return prop->value.data.magic32;
+}
+
+
+njs_int_t
+njs_vm_prop_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_str_t *dst)
+{
+    if (njs_slow_path(!njs_is_string(&prop->name))) {
+        njs_type_error(vm, "property name is not a string");
+        return NJS_ERROR;
+    }
+
+    njs_string_get(&prop->name, dst);
+
+    return NJS_OK;
+}
+
+
 njs_noinline void
 njs_vm_value_error_set(njs_vm_t *vm, njs_value_t *value, const char *fmt, ...)
 {
@@ -998,12 +1019,6 @@ njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
             return NJS_OK;
         }
 
-        ret = njs_external_match_native_function(vm, function->u.native,
-                                                 &be->name);
-        if (ret == NJS_OK) {
-            return NJS_OK;
-        }
-
         be->name = njs_entry_native;
 
         return NJS_OK;
index 8e8b8d41fd097863e74c248d769f51dd0b59c3ce..f97f863a64e1d21b198206cdc4d98a3eb9db7b9e 100644 (file)
@@ -193,10 +193,6 @@ struct njs_vm_s {
     njs_native_frame_t       *top_frame;
     njs_frame_t              *active_frame;
 
-    njs_arr_t                *external_objects; /* of njs_external_ptr_t */
-
-    njs_lvlhsh_t             external_prototypes_hash;
-
     njs_rbtree_t             *variables_hash;
     njs_lvlhsh_t             values_hash;
 
@@ -246,13 +242,6 @@ struct njs_vm_s {
 
     njs_arr_t                *debug;
 
-    /*
-     * njs_property_query() uses it to store reference to a temporary
-     * PROPERTY_HANDLERs for NJS_EXTERNAL values in NJS_PROPERTY_QUERY_SET
-     * and NJS_PROPERTY_QUERY_DELETE modes.
-     */
-    uintptr_t                stash; /* njs_property_query_t * */
-
     uint64_t                 symbol_generator;
 };
 
index 452bf9ab69e53a81b8c7cc80b08db38678df7560..f4697ddda898f5c3cc4a20be87ac46043eb25c07 100644 (file)
@@ -33,15 +33,16 @@ static njs_int_t
 njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report,
     njs_benchmark_test_t *test)
 {
-    u_char        *start;
-    njs_vm_t      *vm, *nvm;
-    uint64_t      us;
-    njs_int_t     ret;
-    njs_str_t     s, *expected;
-    njs_uint_t    i, n;
-    njs_bool_t    success;
-    njs_value_t   *result, name, usec, times;
-    njs_vm_opt_t  options;
+    u_char                *start;
+    njs_vm_t              *vm, *nvm;
+    uint64_t              us;
+    njs_int_t             ret;
+    njs_str_t             s, *expected;
+    njs_uint_t            i, n;
+    njs_bool_t            success;
+    njs_value_t           *result, name, usec, times;
+    njs_vm_opt_t          options;
+    njs_external_proto_t  proto;
 
     static const njs_value_t  name_key = njs_string("name");
     static const njs_value_t  usec_key = njs_string("usec");
@@ -67,8 +68,8 @@ njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report,
         goto done;
     }
 
-    ret = njs_externals_init(vm);
-    if (ret != NJS_OK) {
+    proto = njs_externals_shared_init(vm);
+    if (proto == NULL) {
         goto done;
     }
 
@@ -258,24 +259,24 @@ static njs_benchmark_test_t  njs_test[] =
       njs_str("20000000"),
       1 },
 
-    { "external property ($r.uri)",
-      njs_str("$r.uri"),
-      njs_str("АБВ"),
+    { "external property ($shared.uri)",
+      njs_str("$shared.uri"),
+      njs_str("shared"),
       1000 },
 
-    { "external object property ($r.props.a)",
-      njs_str("$r.props.a"),
-      njs_str("1"),
+    { "external object property ($shared.props.a)",
+      njs_str("$shared.props.a"),
+      njs_str("4294967295"),
       1000 },
 
-    { "external dump (JSON.stringify($r.header))",
-      njs_str("JSON.stringify($r.header)"),
+    { "external dump (JSON.stringify($shared.header))",
+      njs_str("JSON.stringify($shared.header)"),
       njs_str("{\"01\":\"01|АБВ\",\"02\":\"02|АБВ\",\"03\":\"03|АБВ\"}"),
       1000 },
 
-    { "external method ($r.some_method('YES'))",
-      njs_str("$r.some_method('YES')"),
-      njs_str("АБВ"),
+    { "external method ($shared.method('YES'))",
+      njs_str("$shared.method('YES')"),
+      njs_str("shared"),
       1000 },
 };
 
index 850d04417f134c058517df7f1f46b4bcefce3672..fb2fcffb56e2dc710be02d114989a71b71268ffd 100644 (file)
 
 typedef struct {
     njs_lvlhsh_t          hash;
-    const njs_extern_t    *proto;
-    njs_mp_t              *pool;
+    njs_external_proto_t  proto;
 
     uint32_t              a;
+    uint32_t              d;
     njs_str_t             uri;
 
     njs_opaque_value_t    value;
@@ -89,7 +89,8 @@ lvlhsh_unit_test_alloc(njs_mp_t *pool, const njs_value_t *name,
 
 
 static njs_int_t
-lvlhsh_unit_test_add(njs_unit_test_req_t *r, njs_unit_test_prop_t *prop)
+lvlhsh_unit_test_add(njs_mp_t *pool, njs_unit_test_req_t *r,
+    njs_unit_test_prop_t *prop)
 {
     njs_lvlhsh_query_t  lhq;
 
@@ -99,7 +100,7 @@ lvlhsh_unit_test_add(njs_unit_test_req_t *r, njs_unit_test_prop_t *prop)
     lhq.replace = 1;
     lhq.value = (void *) prop;
     lhq.proto = &lvlhsh_proto;
-    lhq.pool = r->pool;
+    lhq.pool = pool;
 
     switch (njs_lvlhsh_insert(&r->hash, &lhq)) {
 
@@ -114,158 +115,141 @@ lvlhsh_unit_test_add(njs_unit_test_req_t *r, njs_unit_test_prop_t *prop)
 
 
 static njs_int_t
-njs_unit_test_r_get_uri_external(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+njs_unit_test_r_uri(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
     char       *p;
     njs_str_t  *field;
 
-    p = obj;
-    field = (njs_str_t *) (p + data);
-
-    return njs_vm_value_string_set(vm, value, field->start, field->length);
-}
-
-
-static njs_int_t
-njs_unit_test_r_set_uri_external(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_str_t *value)
-{
-    char       *p;
-    njs_str_t  *field;
+    p = njs_vm_external(vm, value);
+    if (p == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
-    p = obj;
-    field = (njs_str_t *) (p + data);
+    field = (njs_str_t *) (p + njs_vm_prop_magic32(prop));
 
-    *field = *value;
+    if (setval != NULL) {
+        return njs_vm_value_to_string(vm, field, setval);
+    }
 
-    return NJS_OK;
+    return njs_vm_value_string_set(vm, retval, field->start, field->length);
 }
 
 
 static njs_int_t
-njs_unit_test_r_get_a_external(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+njs_unit_test_r_a(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
 {
     u_char               *p;
     njs_unit_test_req_t  *r;
     u_char               buf[16];
 
-    r = (njs_unit_test_req_t *) obj;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
 
     p = njs_sprintf(buf, buf + njs_length(buf), "%uD", r->a);
 
-    return njs_vm_value_string_set(vm, value, buf, p - buf);
+    return njs_vm_value_string_set(vm, retval, buf, p - buf);
 }
 
 
 static njs_int_t
-njs_unit_test_r_get_b_external(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+njs_unit_test_r_b(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
 {
-    njs_value_number_set(value, data);
+    njs_value_number_set(retval, njs_vm_prop_magic32(prop));
 
     return NJS_OK;
 }
 
 
 static njs_int_t
-njs_unit_test_host_external(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+njs_unit_test_r_d(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
 {
-    return njs_vm_value_string_set(vm, value, (u_char *) "АБВГДЕЁЖЗИЙ", 22);
-}
-
-
-static njs_int_t
-njs_unit_test_r_get_vars(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
-{
-    njs_int_t             ret;
-    njs_str_t             *key;
-    njs_lvlhsh_query_t    lhq;
-    njs_unit_test_req_t   *r;
-    njs_unit_test_prop_t  *prop;
-
-    r = (njs_unit_test_req_t *) obj;
-    key = (njs_str_t *) data;
-
-    lhq.key = *key;
-    lhq.key_hash = njs_djb_hash(key->start, key->length);
-    lhq.proto = &lvlhsh_proto;
-
-    ret = njs_lvlhsh_find(&r->hash, &lhq);
-
-    prop = lhq.value;
+    njs_unit_test_req_t  *r;
 
-    if (ret == NJS_OK && njs_is_valid(&prop->value)) {
-        *value = prop->value;
-        return NJS_OK;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
     }
 
-    njs_value_undefined_set(value);
+    njs_value_number_set(retval, r->d);
 
     return NJS_OK;
 }
 
 
 static njs_int_t
-njs_unit_test_r_set_vars(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_str_t *value)
+njs_unit_test_r_host(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+    return njs_vm_value_string_set(vm, retval, (u_char *) "АБВГДЕЁЖЗИЙ", 22);
+}
+
+
+static njs_int_t
+njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
     njs_int_t             ret;
-    njs_str_t             *key;
-    njs_value_t           name, val;
+    njs_value_t           name;
+    njs_lvlhsh_query_t    lhq;
     njs_unit_test_req_t   *r;
     njs_unit_test_prop_t  *prop;
 
-    r = (njs_unit_test_req_t *) obj;
-    key = (njs_str_t *) data;
-
-    if (key->length == 5 && memcmp(key->start, "error", 5) == 0) {
-        njs_vm_error(vm, "cannot set \"error\" prop");
-        return NJS_ERROR;
+    r = njs_vm_external(vm, value);
+    if (r == NULL) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
     }
 
-    njs_vm_value_string_set(vm, &name, key->start, key->length);
-    njs_vm_value_string_set(vm, &val, value->start, value->length);
+    ret = njs_vm_prop_name(vm, self, &lhq.key);
+    if (ret != NJS_OK) {
+        if (setval == NULL && retval != NULL) {
+            /* Get. */
+            njs_value_undefined_set(retval);
+            return NJS_DECLINED;
+        }
 
-    prop = lvlhsh_unit_test_alloc(vm->mem_pool, &name, &val);
-    if (prop == NULL) {
-        njs_memory_error(vm);
         return NJS_ERROR;
     }
 
-    ret = lvlhsh_unit_test_add(r, prop);
-    if (ret != NJS_OK) {
-        njs_vm_error(vm, "lvlhsh_unit_test_add() failed");
-        return NJS_ERROR;
+    if (setval != NULL || retval == NULL) {
+        /* Set or Delete. */
+        if (lhq.key.length == 5 && memcmp(lhq.key.start, "error", 5) == 0) {
+            njs_vm_error(vm, "cannot %s \"error\" prop",
+                         retval != NULL ? "set" : "delete");
+            return NJS_ERROR;
+        }
     }
 
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_unit_test_r_del_vars(njs_vm_t *vm, void *obj, uintptr_t data,
-    njs_bool_t delete)
-{
-    njs_int_t             ret;
-    njs_str_t             *key;
-    njs_lvlhsh_query_t    lhq;
-    njs_unit_test_req_t   *r;
-    njs_unit_test_prop_t  *prop;
+    if (setval != NULL) {
+        /* Set. */
+        njs_vm_value_string_set(vm, &name, lhq.key.start, lhq.key.length);
+        prop = lvlhsh_unit_test_alloc(vm->mem_pool, &name, setval);
+        if (prop == NULL) {
+            njs_memory_error(vm);
+            return NJS_ERROR;
+        }
 
-    r = (njs_unit_test_req_t *) obj;
-    key = (njs_str_t *) data;
+        ret = lvlhsh_unit_test_add(vm->mem_pool, r, prop);
+        if (ret != NJS_OK) {
+            njs_vm_error(vm, "lvlhsh_unit_test_add() failed");
+            return NJS_ERROR;
+        }
 
-    if (key->length == 5 && memcmp(key->start, "error", 5) == 0) {
-        njs_vm_error(vm, "cannot delete \"error\" prop");
-        return NJS_ERROR;
+        return NJS_OK;
     }
 
-    lhq.key = *key;
-    lhq.key_hash = njs_djb_hash(key->start, key->length);
+    /* Get or Delete. */
+
+    lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
     lhq.proto = &lvlhsh_proto;
 
     ret = njs_lvlhsh_find(&r->hash, &lhq);
@@ -273,47 +257,64 @@ njs_unit_test_r_del_vars(njs_vm_t *vm, void *obj, uintptr_t data,
     prop = lhq.value;
 
     if (ret == NJS_OK) {
-        njs_set_invalid(&prop->value);
+        if (retval == NULL) {
+            njs_set_invalid(&prop->value);
+            return NJS_OK;
+        }
+
+        if (njs_is_valid(&prop->value)) {
+            *retval = prop->value;
+            return NJS_OK;
+        }
     }
 
-    return NJS_OK;
+    if (retval != NULL) {
+        njs_value_undefined_set(retval);
+    }
+
+    return NJS_DECLINED;
 }
 
 
 static njs_int_t
-njs_unit_test_header_external(njs_vm_t *vm, njs_value_t *value, void *obj,
-    uintptr_t data)
+njs_unit_test_r_header(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
 {
     u_char     *p;
     uint32_t   size;
-    njs_str_t  *h;
+    njs_int_t  ret;
+    njs_str_t  h;
 
-    h = (njs_str_t *) data;
+    ret = njs_vm_prop_name(vm, prop, &h);
+    if (ret == NJS_OK) {
+        size = 7 + h.length;
 
-    size = 7 + h->length;
+        p = njs_vm_value_string_alloc(vm, retval, size);
+        if (p == NULL) {
+            return NJS_ERROR;
+        }
 
-    p = njs_vm_value_string_alloc(vm, value, size);
-    if (p == NULL) {
-        return NJS_ERROR;
+        p = njs_cpymem(p, h.start, h.length);
+        *p++ = '|';
+        memcpy(p, "АБВ", njs_length("АБВ"));
+        return NJS_OK;
     }
 
-    p = njs_cpymem(p, h->start, h->length);
-    *p++ = '|';
-    memcpy(p, "АБВ", 6);
+    njs_value_undefined_set(retval);
 
-    return NJS_OK;
+    return NJS_DECLINED;
 }
 
 
 static njs_int_t
-njs_unit_test_header_keys_external(njs_vm_t *vm, void *obj, njs_value_t *keys)
+njs_unit_test_r_header_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys)
 {
-    njs_int_t    rc, i;
-    njs_value_t  *value;
+    njs_int_t    ret, i;
+    njs_value_t  *push;
     u_char       k[2];
 
-    rc = njs_vm_array_alloc(vm, keys, 4);
-    if (rc != NJS_OK) {
+    ret = njs_vm_array_alloc(vm, keys, 4);
+    if (ret != NJS_OK) {
         return NJS_ERROR;
     }
 
@@ -321,12 +322,12 @@ njs_unit_test_header_keys_external(njs_vm_t *vm, void *obj, njs_value_t *keys)
     k[1] = '1';
 
     for (i = 0; i < 3; i++) {
-        value = njs_vm_array_push(vm, keys);
-        if (value == NULL) {
+        push = njs_vm_array_push(vm, keys);
+        if (push == NULL) {
             return NJS_ERROR;
         }
 
-        (void) njs_vm_value_string_set(vm, value, k, 2);
+        (void) njs_vm_value_string_set(vm, push, k, 2);
 
         k[1]++;
     }
@@ -336,7 +337,7 @@ njs_unit_test_header_keys_external(njs_vm_t *vm, void *obj, njs_value_t *keys)
 
 
 static njs_int_t
-njs_unit_test_method_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+njs_unit_test_r_method(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
     njs_int_t            ret;
@@ -345,6 +346,7 @@ njs_unit_test_method_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
     if (r == NULL) {
+        njs_type_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -361,44 +363,36 @@ njs_unit_test_method_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 
 static njs_int_t
-njs_unit_test_create_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+njs_unit_test_r_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
     njs_int_t            ret;
-    njs_str_t            uri;
-    njs_value_t          *value;
     njs_unit_test_req_t  *r, *sr;
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
     if (r == NULL) {
+        njs_type_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
-    if (njs_vm_value_to_string(vm, &uri, njs_arg(args, nargs, 1)) != NJS_OK) {
-        return NJS_ERROR;
-    }
-
-    value = njs_mp_zalloc(r->pool, sizeof(njs_opaque_value_t));
-    if (value == NULL) {
+    sr = njs_mp_zalloc(vm->mem_pool, sizeof(njs_unit_test_req_t));
+    if (sr == NULL) {
         goto memory_error;
     }
 
-    sr = njs_mp_zalloc(r->pool, sizeof(njs_unit_test_req_t));
-    if (sr == NULL) {
-        goto memory_error;
+    if (njs_vm_value_to_string(vm, &sr->uri, njs_arg(args, nargs, 1))
+        != NJS_OK)
+    {
+        return NJS_ERROR;
     }
 
-    sr->uri = uri;
-    sr->pool = r->pool;
     sr->proto = r->proto;
 
-    ret = njs_vm_external_create(vm, value, sr->proto, sr);
+    ret = njs_vm_external_create(vm, &vm->retval, sr->proto, sr, 0);
     if (ret != NJS_OK) {
         return NJS_ERROR;
     }
 
-    njs_vm_retval_set(vm, value);
-
     return NJS_OK;
 
 memory_error:
@@ -410,7 +404,7 @@ memory_error:
 
 
 static njs_int_t
-njs_unit_test_bind_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+njs_unit_test_r_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
     njs_str_t            name;
@@ -418,6 +412,7 @@ njs_unit_test_bind_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     r = njs_vm_external(vm, njs_arg(args, nargs, 0));
     if (r == NULL) {
+        njs_type_error(vm, "\"this\" is not an external");
         return NJS_ERROR;
     }
 
@@ -429,148 +424,180 @@ njs_unit_test_bind_external(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
+static njs_external_t  njs_unit_test_r_c[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("d"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_unit_test_r_d,
+        }
+    },
+
+};
+
+
 static njs_external_t  njs_unit_test_r_props[] = {
 
-    { njs_str("a"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      njs_unit_test_r_get_a_external,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("b"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      njs_unit_test_r_get_b_external,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      42 },
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("a"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_unit_test_r_a,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("b"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_unit_test_r_b,
+            .magic32 = 42,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("c"),
+        .enumerable = 1,
+        .u.object = {
+            .properties = njs_unit_test_r_c,
+            .nproperties = njs_nitems(njs_unit_test_r_c),
+        }
+    },
+
 };
 
 
-static njs_external_t  njs_unit_test_r_external[] = {
+static njs_external_t  njs_unit_test_r_header_props[] = {
 
-    { njs_str("uri"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      njs_unit_test_r_get_uri_external,
-      njs_unit_test_r_set_uri_external,
-      NULL,
-      NULL,
-      NULL,
-      offsetof(njs_unit_test_req_t, uri) },
-
-    { njs_str("host"),
-      NJS_EXTERN_PROPERTY,
-      NULL,
-      0,
-      njs_unit_test_host_external,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("props"),
-      NJS_EXTERN_OBJECT,
-      njs_unit_test_r_props,
-      njs_nitems(njs_unit_test_r_props),
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("vars"),
-      NJS_EXTERN_OBJECT,
-      NULL,
-      0,
-      njs_unit_test_r_get_vars,
-      njs_unit_test_r_set_vars,
-      njs_unit_test_r_del_vars,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("consts"),
-      NJS_EXTERN_OBJECT,
-      NULL,
-      0,
-      njs_unit_test_r_get_vars,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
-
-    { njs_str("header"),
-      NJS_EXTERN_OBJECT,
-      NULL,
-      0,
-      njs_unit_test_header_external,
-      NULL,
-      NULL,
-      njs_unit_test_header_keys_external,
-      NULL,
-      0 },
-
-    { njs_str("some_method"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      njs_unit_test_method_external,
-      0 },
-
-    { njs_str("create"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      njs_unit_test_create_external,
-      0 },
-
-    { njs_str("bind"),
-      NJS_EXTERN_METHOD,
-      NULL,
-      0,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      njs_unit_test_bind_external,
-      0 },
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Header",
+        }
+    },
 
 };
 
 
-static njs_external_t  njs_test_external[] = {
+static njs_external_t  njs_unit_test_r_external[] = {
 
-    { njs_str("request.proto"),
-      NJS_EXTERN_OBJECT,
-      njs_unit_test_r_external,
-      njs_nitems(njs_unit_test_r_external),
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      NULL,
-      0 },
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "External",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("bind"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_unit_test_r_bind,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("consts"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.object = {
+            .enumerable = 1,
+            .prop_handler = njs_unit_test_r_vars,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("create"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_unit_test_r_create,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("header"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.object = {
+            .properties = njs_unit_test_r_header_props,
+            .nproperties = njs_nitems(njs_unit_test_r_header_props),
+            .enumerable = 1,
+            .prop_handler = njs_unit_test_r_header,
+            .keys = njs_unit_test_r_header_keys,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("host"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_unit_test_r_host,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("method"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_unit_test_r_method,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("props"),
+        .enumerable = 1,
+        .writable = 1,
+        .u.object = {
+            .enumerable = 1,
+            .properties = njs_unit_test_r_props,
+            .nproperties = njs_nitems(njs_unit_test_r_props),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("uri"),
+        .writable = 1,
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_unit_test_r_uri,
+            .magic32 = offsetof(njs_unit_test_req_t, uri),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("vars"),
+        .enumerable = 1,
+        .u.object = {
+            .writable = 1,
+            .configurable = 1,
+            .enumerable = 1,
+            .prop_handler = njs_unit_test_r_vars,
+        }
+    },
 
 };
 
@@ -579,104 +606,138 @@ typedef struct {
     njs_str_t             name;
     njs_unit_test_req_t   request;
     njs_unit_test_prop_t  props[2];
-} njs_unit_test_req_t_init_t;
+} njs_unit_test_req_init_t;
 
 
-static const njs_unit_test_req_t_init_t njs_test_requests[] = {
+static njs_unit_test_req_init_t njs_test_requests[] = {
+
+    { njs_str("$shared"),
+        {
+            .uri = njs_str("shared"),
+            .a = -1,
+            .d = -2,
+        },
+        {
+            { njs_string("r"), njs_string("rval") },
+            { njs_string("r2"), njs_string("r2val") },
+        }
+    },
 
     { njs_str("$r"),
-     {
-         .uri = njs_str("АБВ"),
-         .a = 1
-     },
-     {
-         { njs_string("p"), njs_string("pval") },
-         { njs_string("p2"), njs_string("p2val") },
-     }
+        {
+            .uri = njs_str("АБВ"),
+            .a = 1,
+            .d = 1024,
+        },
+        {
+            { njs_string("p"), njs_string("pval") },
+            { njs_string("p2"), njs_string("p2val") },
+        }
     },
 
     { njs_str("$r2"),
-     {
-         .uri = njs_str("αβγ"),
-         .a = 2
-     },
-     {
-         { njs_string("q"), njs_string("qval") },
-         { njs_string("q2"), njs_string("q2val") },
-     }
+        {
+            .uri = njs_str("αβγ"),
+            .a = 2,
+            .d = 1025,
+        },
+        {
+            { njs_string("q"), njs_string("qval") },
+            { njs_string("q2"), njs_string("q2val") },
+        }
     },
 
     { njs_str("$r3"),
-     {
-         .uri = njs_str("abc"),
-         .a = 3
-     },
-     {
-         { njs_string("k"), njs_string("kval") },
-         { njs_string("k2"), njs_string("k2val") },
-     }
+        {
+            .uri = njs_str("abc"),
+            .a = 3,
+            .d = 1026,
+        },
+        {
+            { njs_string("k"), njs_string("kval") },
+            { njs_string("k2"), njs_string("k2val") },
+        }
     },
 };
 
 
-njs_int_t
-njs_externals_init(njs_vm_t *vm)
+static njs_external_proto_t
+njs_externals_init_internal(njs_vm_t *vm, njs_external_proto_t proto,
+    njs_unit_test_req_init_t *init, njs_uint_t n, njs_bool_t shared)
 {
     njs_int_t             ret;
     njs_uint_t            i, j;
-    const njs_extern_t    *proto;
     njs_unit_test_req_t   *requests;
     njs_unit_test_prop_t  *prop;
 
-    proto = njs_vm_external_prototype(vm, &njs_test_external[0]);
-    if (njs_slow_path(proto == NULL)) {
-        njs_printf("njs_vm_external_prototype() failed\n");
-        return NJS_ERROR;
+    if (proto == NULL) {
+        proto = njs_vm_external_prototype(vm, njs_unit_test_r_external,
+                                          njs_nitems(njs_unit_test_r_external));
+        if (njs_slow_path(proto == NULL)) {
+            njs_printf("njs_vm_external_prototype() failed\n");
+            return NULL;
+        }
     }
 
-    requests = njs_mp_zalloc(vm->mem_pool, njs_nitems(njs_test_requests)
-                             * sizeof(njs_unit_test_req_t));
+    requests = njs_mp_zalloc(vm->mem_pool, n * sizeof(njs_unit_test_req_t));
     if (njs_slow_path(requests == NULL)) {
-        return NJS_ERROR;
+        return NULL;
     }
 
-    for (i = 0; i < njs_nitems(njs_test_requests); i++) {
+    for (i = 0; i < n; i++) {
 
-        requests[i] = njs_test_requests[i].request;
-        requests[i].pool = vm->mem_pool;
+        requests[i] = init[i].request;
         requests[i].proto = proto;
 
         ret = njs_vm_external_create(vm, njs_value_arg(&requests[i].value),
-                                     proto, &requests[i]);
+                                     proto, &requests[i], shared);
         if (njs_slow_path(ret != NJS_OK)) {
             njs_printf("njs_vm_external_create() failed\n");
-            return NJS_ERROR;
+            return NULL;
         }
 
-        ret = njs_vm_bind(vm, &njs_test_requests[i].name,
-                          njs_value_arg(&requests[i].value), 1);
+        ret = njs_vm_bind(vm, &init[i].name, njs_value_arg(&requests[i].value),
+                          shared);
         if (njs_slow_path(ret != NJS_OK)) {
             njs_printf("njs_vm_bind() failed\n");
-            return NJS_ERROR;
+            return NULL;
         }
 
-        for (j = 0; j < njs_nitems(njs_test_requests[i].props); j++) {
-            prop = lvlhsh_unit_test_alloc(vm->mem_pool,
-                                          &njs_test_requests[i].props[j].name,
-                                          &njs_test_requests[i].props[j].value);
+        for (j = 0; j < njs_nitems(init[i].props); j++) {
+            prop = lvlhsh_unit_test_alloc(vm->mem_pool, &init[i].props[j].name,
+                                          &init[i].props[j].value);
 
             if (njs_slow_path(prop == NULL)) {
                 njs_printf("lvlhsh_unit_test_alloc() failed\n");
-                return NJS_ERROR;
+                return NULL;
             }
 
-            ret = lvlhsh_unit_test_add(&requests[i], prop);
+            ret = lvlhsh_unit_test_add(vm->mem_pool, &requests[i], prop);
             if (njs_slow_path(ret != NJS_OK)) {
                 njs_printf("lvlhsh_unit_test_add() failed\n");
-                return NJS_ERROR;
+                return NULL;
             }
         }
     }
 
+    return proto;
+}
+
+
+njs_external_proto_t
+njs_externals_shared_init(njs_vm_t *vm)
+{
+    return njs_externals_init_internal(vm, NULL, njs_test_requests, 1, 1);
+}
+
+
+njs_int_t
+njs_externals_init(njs_vm_t *vm, njs_external_proto_t proto)
+{
+    proto = njs_externals_init_internal(vm, proto, &njs_test_requests[1], 3, 0);
+    if (proto == NULL) {
+        return NJS_ERROR;
+    }
+
     return NJS_OK;
 }
index ab728ca915801e139e85a21d17c42738217d6f69..97553b5facbcac295d594fe5b9d7de6829b2eb0f 100644 (file)
@@ -8,7 +8,8 @@
 #define _NJS_EXTERNALS_TEST_H_INCLUDED_
 
 
-njs_int_t njs_externals_init(njs_vm_t *vm);
+njs_external_proto_t njs_externals_shared_init(njs_vm_t *vm);
+njs_int_t njs_externals_init(njs_vm_t *vm, njs_external_proto_t proto);
 
 
 #endif /* _NJS_EXTERNALS_TEST_H_INCLUDED_ */
index 352fa1037349750f7fd0bb9a88cfdf2915b91b2a..de7140fb3e037361882ff47331a8a4c223aae52f 100644 (file)
@@ -185,9 +185,9 @@ static njs_interactive_test_t  njs_test[] =
                  "    at eval (native)\n"
                  "    at main (native)\n") },
 
-    { njs_str("$r.some_method({}.a.a)" ENTER),
+    { njs_str("$r.method({}.a.a)" ENTER),
       njs_str("TypeError: cannot get property \"a\" of undefined\n"
-                 "    at request.proto.some_method (native)\n"
+                 "    at $r3.method (native)\n"
                  "    at main (native)\n") },
 
     { njs_str("new Function(\n\n@)" ENTER),
@@ -328,7 +328,7 @@ njs_interactive_test(njs_bool_t verbose)
             goto done;
         }
 
-        ret = njs_externals_init(vm);
+        ret = njs_externals_init(vm, NULL);
         if (ret != NJS_OK) {
             goto done;
         }
index e4c3fe2626c63016d974c4a471b8fc739d2bbcc5..492bcbb70d3788af2858ed128ec1a38dc5b41902 100644 (file)
@@ -2753,16 +2753,6 @@ static njs_unit_test_t  njs_test[] =
                  "} a"),
       njs_str("A123DT") },
 
-    { njs_str("var t; "
-                 "switch ($r3.uri) {"
-                 "case 'abc': "
-                 "  t='A'; "
-                 "  break; "
-                 "default: "
-                 "  t='F'; "
-                 "}; t"),
-      njs_str("A") },
-
     { njs_str("[isNaN, undefined, isFinite]."
               "map((v)=>{switch(v) { case isNaN: return 1; default: return 0;}})"),
       njs_str("1,0,0") },
@@ -6777,173 +6767,6 @@ static njs_unit_test_t  njs_test[] =
                           " valueOf:  function() { return 0 } };   '12'[n]"),
       njs_str("2") },
 
-    /* Externals. */
-
-    { njs_str("typeof $r"),
-      njs_str("external") },
-
-    { njs_str("var a = $r.uri, s = a.fromUTF8(); s.length +' '+ s"),
-      njs_str("3 АБВ") },
-
-    { njs_str("var a = $r.uri, b = $r2.uri, c = $r3.uri; a+b+c"),
-      njs_str("АБВαβγabc") },
-
-    { njs_str("var a = $r.uri; $r.uri = $r2.uri; $r2.uri = a; $r2.uri+$r.uri"),
-      njs_str("АБВαβγ") },
-
-    { njs_str("var a = $r.uri, s = a.fromUTF8(2); s.length +' '+ s"),
-      njs_str("2 БВ") },
-
-    { njs_str("var a = $r.uri, s = a.fromUTF8(2, 4); s.length +' '+ s"),
-      njs_str("1 Б") },
-
-    { njs_str("var a = $r.uri; a +' '+ a.length +' '+ a"),
-      njs_str("АБВ 6 АБВ") },
-
-    { njs_str("$r.uri = 'αβγ'; var a = $r.uri; a.length +' '+ a"),
-      njs_str("6 αβγ") },
-
-    { njs_str("$r.uri.length +' '+ $r.uri"),
-      njs_str("6 АБВ") },
-
-    { njs_str("$r.uri = $r.uri.substr(2); $r.uri.length +' '+ $r.uri"),
-      njs_str("4 БВ") },
-
-    { njs_str("'' + $r.props.a + $r2.props.a + $r.props.a"),
-      njs_str("121") },
-
-    { njs_str("var p1 = $r.props, p2 = $r2.props; '' + p2.a + p1.a"),
-      njs_str("21") },
-
-    { njs_str("var p1 = $r.props, p2 = $r2.props; '' + p1.a + p2.a"),
-      njs_str("12") },
-
-    { njs_str("var p = $r3.props; p.a = 1"),
-      njs_str("TypeError: Cannot assign to read-only property \"a\" of external") },
-    { njs_str("var p = $r3.props; delete p.a"),
-      njs_str("TypeError: Cannot delete property \"a\" of external") },
-
-    { njs_str("$r.vars.p + $r2.vars.q + $r3.vars.k"),
-      njs_str("pvalqvalkval") },
-
-    { njs_str("$r.vars.unset"),
-      njs_str("undefined") },
-
-    { njs_str("var v = $r3.vars; v.k"),
-      njs_str("kval") },
-
-    { njs_str("var v = $r3.vars; v.unset = 1; v.unset"),
-      njs_str("1") },
-
-    { njs_str("$r.vars.unset = 'a'; $r2.vars.unset = 'b';"
-                 "$r.vars.unset + $r2.vars.unset"),
-      njs_str("ab") },
-
-    { njs_str("$r.vars.unset = 1; $r2.vars.unset = 2;"
-                 "$r.vars.unset + $r2.vars.unset"),
-      njs_str("12") },
-
-    { njs_str("$r3.vars.p = 'a'; $r3.vars.p2 = 'b';"
-                 "$r3.vars.p + $r3.vars.p2"),
-      njs_str("ab") },
-
-    { njs_str("$r3.vars.p = 'a'; delete $r3.vars.p; $r3.vars.p"),
-      njs_str("undefined") },
-
-    { njs_str("$r3.vars.p = 'a'; delete $r3.vars.p; $r3.vars.p = 'b'; $r3.vars.p"),
-      njs_str("b") },
-
-    { njs_str("$r3.vars.error = 1"),
-      njs_str("Error: cannot set \"error\" prop") },
-
-    { njs_str("delete $r3.vars.error"),
-      njs_str("Error: cannot delete \"error\" prop") },
-
-    { njs_str("delete $r3.vars.e"),
-      njs_str("true") },
-
-    { njs_str("$r3.consts.k"),
-      njs_str("kval") },
-
-    { njs_str("$r3.consts.k = 1"),
-      njs_str("TypeError: Cannot assign to read-only property \"k\" of external") },
-
-    { njs_str("delete $r3.consts.k"),
-      njs_str("TypeError: Cannot delete property \"k\" of external") },
-
-    { njs_str("delete $r3.vars.p; $r3.vars.p"),
-      njs_str("undefined") },
-
-    { njs_str("var a = $r.host; a +' '+ a.length +' '+ a"),
-      njs_str("АБВГДЕЁЖЗИЙ 22 АБВГДЕЁЖЗИЙ") },
-
-    { njs_str("var a = $r.host; a.substr(2, 2)"),
-      njs_str("Б") },
-
-    { njs_str("var a = $r.header['User-Agent']; a +' '+ a.length +' '+ a"),
-      njs_str("User-Agent|АБВ 17 User-Agent|АБВ") },
-
-    { njs_str("var a='', p;"
-                 "for (p in $r.header) { a += p +':'+ $r.header[p] +',' }"
-                 "a"),
-      njs_str("01:01|АБВ,02:02|АБВ,03:03|АБВ,") },
-
-    { njs_str("$r.some_method('YES')"),
-      njs_str("АБВ") },
-
-    { njs_str("$r.create('XXX').uri"),
-      njs_str("XXX") },
-
-    { njs_str("var sr = $r.create('XXX'); sr.uri = 'YYY'; sr.uri"),
-      njs_str("YYY") },
-
-    { njs_str("var sr = $r.create('XXX'), sr2 = $r.create('YYY');"
-                 "sr.uri = 'ZZZ'; "
-                 "sr.uri + sr2.uri"),
-      njs_str("ZZZYYY") },
-
-    { njs_str("var sr = $r.create('XXX'); sr.vars.p = 'a'; sr.vars.p"),
-      njs_str("a") },
-
-    { njs_str("var p; for (p in $r.some_method);"),
-      njs_str("undefined") },
-
-    { njs_str("'uri' in $r"),
-      njs_str("true") },
-
-    { njs_str("'one' in $r"),
-      njs_str("false") },
-
-    { njs_str("'a' in $r.props"),
-      njs_str("true") },
-
-    { njs_str("delete $r.uri"),
-      njs_str("TypeError: Cannot delete property \"uri\" of external") },
-
-    { njs_str("delete $r.one"),
-      njs_str("TypeError: Cannot delete property \"one\" of external") },
-
-    { njs_str("$r.some_method.call($r, 'YES')"),
-      njs_str("АБВ") },
-
-    { njs_str("var f = $r.some_method.bind($r); f('YES')"),
-      njs_str("АБВ") },
-
-    { njs_str("function f(fn, arg) {return fn(arg);}; f($r.some_method.bind($r), 'YES')"),
-      njs_str("АБВ") },
-
-    { njs_str("$r.some_method.apply($r, ['YES'])"),
-      njs_str("АБВ") },
-
-    { njs_str("$r.some_method.call([], 'YES')"),
-      njs_str("TypeError: external value is expected") },
-
-    { njs_str("$r.nonexistent"),
-      njs_str("undefined") },
-
-    { njs_str("$r.error = 'OK'"),
-      njs_str("TypeError: Cannot assign to read-only property \"error\" of external") },
-
     { njs_str("var a = { toString: function() { return 1 } }; a"),
       njs_str("1") },
 
@@ -6966,12 +6789,6 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("({})[{}] = 'test'"),
       njs_str("test") },
 
-    { njs_str("var o = {b:$r.props.b}; o.b"),
-      njs_str("42") },
-
-    { njs_str("$r2.uri == 'αβγ' && $r2.uri === 'αβγ'"),
-      njs_str("true") },
-
     /**/
 
     { njs_str("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(5)"),
@@ -10339,13 +10156,6 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("Object.getOwnPropertyNames(this).includes('NaN')"),
       njs_str("true") },
 
-    { njs_str("Object.keys(this).sort()"),
-      njs_str("$r,$r2,$r3,global,njs,process") },
-
-    { njs_str("[this, global, globalThis]"
-              ".every(v=> { var r = njs.dump(v); return ['$r', 'global', njs.version].every(v=>r.includes(v))})"),
-      njs_str("true") },
-
     { njs_str("this.a = 1; this.a"),
       njs_str("1") },
 
@@ -15602,10 +15412,6 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("var e = URIError('e'); e.foo = 'E'; JSON.stringify(e)"),
       njs_str("{\"foo\":\"E\"}") },
 
-    { njs_str("var r = JSON.parse(JSON.stringify($r));"
-              "[r.uri, r.host, r.props.a, njs.dump(r.vars), njs.dump(r.consts), r.header['02']]"),
-      njs_str("АБВ,АБВГДЕЁЖЗИЙ,1,{},{},02|АБВ") },
-
     { njs_str("JSON.stringify({get key() {throw new Error('Oops')}})"),
       njs_str("Error: Oops") },
 
@@ -15912,12 +15718,6 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("var o = Object.defineProperty({}, 'a', { set(){}, enumerable: true }); njs.dump(o)"),
       njs_str("{a:'[Setter]'}") },
 
-    { njs_str("njs.dump($r.props)"),
-      njs_str("{a:'1',b:42}") },
-
-    { njs_str("njs.dump($r.header)"),
-      njs_str("{01:'01|АБВ',02:'02|АБВ',03:'03|АБВ'}") },
-
     { njs_str("njs.dump(njs) == `njs {version:'${njs.version}'}`"),
       njs_str("true") },
 
@@ -16756,6 +16556,253 @@ static njs_unit_test_t  njs_module_test[] =
 };
 
 
+static njs_unit_test_t  njs_externals_test[] =
+{
+    { njs_str("typeof $r"),
+      njs_str("object") },
+
+    { njs_str("var a = $r.uri, s = a.fromUTF8(); s.length +' '+ s"),
+      njs_str("3 АБВ") },
+
+    { njs_str("var a = $r.uri, b = $r2.uri, c = $r3.uri; a+b+c"),
+      njs_str("АБВαβγabc") },
+
+    { njs_str("var a = $r.uri; $r.uri = $r2.uri; $r2.uri = a; $r2.uri+$r.uri"),
+      njs_str("АБВαβγ") },
+
+    { njs_str("var a = $r.uri, s = a.fromUTF8(2); s.length +' '+ s"),
+      njs_str("2 БВ") },
+
+    { njs_str("var a = $r.uri, s = a.fromUTF8(2, 4); s.length +' '+ s"),
+      njs_str("1 Б") },
+
+    { njs_str("var a = $r.uri; a +' '+ a.length +' '+ a"),
+      njs_str("АБВ 6 АБВ") },
+
+    { njs_str("$r.uri = 'αβγ'; var a = $r.uri; a.length +' '+ a"),
+      njs_str("6 αβγ") },
+
+    { njs_str("$r.uri.length +' '+ $r.uri"),
+      njs_str("6 АБВ") },
+
+    { njs_str("var t; "
+              "switch ($r3.uri) {"
+              "case 'abc': "
+              "  t='A'; "
+              "  break; "
+              "default: "
+              "  t='F'; "
+              "}; t"),
+      njs_str("A") },
+
+    { njs_str("$r.uri = $r.uri.substr(2); $r.uri.length +' '+ $r.uri"),
+      njs_str("4 БВ") },
+
+    { njs_str("'' + $r.props.a + $r2.props.a + $r.props.a"),
+      njs_str("121") },
+
+    { njs_str("var p1 = $r.props, p2 = $r2.props; '' + p2.a + p1.a"),
+      njs_str("21") },
+
+    { njs_str("$r.props = $r2.props; $r.props.a"),
+      njs_str("2") },
+
+    { njs_str("var p1 = $r.props, p2 = $r2.props; '' + p1.a + p2.a"),
+      njs_str("12") },
+
+    { njs_str("var p = $r3.props; p.a = 1"),
+      njs_str("TypeError: Cannot assign to read-only property \"a\" of object") },
+
+    { njs_str("var p = $r3.props; delete p.a"),
+      njs_str("TypeError: Cannot delete property \"a\" of object") },
+
+    { njs_str("$r.vars.p + $r2.vars.q + $r3.vars.k"),
+      njs_str("pvalqvalkval") },
+
+    { njs_str("$r.vars.unset"),
+      njs_str("undefined") },
+
+    { njs_str("['k', 'unknown'].map(v=>v in $r3.consts)"),
+      njs_str("true,false") },
+
+    { njs_str("['a', 'unknown'].map(v=>v in $r3.props)"),
+      njs_str("true,false") },
+
+    { njs_str("var v = $r3.vars; v.k"),
+      njs_str("kval") },
+
+    { njs_str("var v = $r3.vars; v.unset = 1; v.unset"),
+      njs_str("1") },
+
+    { njs_str("$r3.a = 1; Object.getOwnPropertyDescriptors($r3).a.value"),
+      njs_str("1") },
+
+    { njs_str("Object.defineProperty($r3.vars, 'a', {value:1}); $r3.vars.a"),
+      njs_str("1") },
+
+    { njs_str("$r3.vars.p = 'a'; delete $r3.vars.p; $r3.vars.p"),
+      njs_str("undefined") },
+
+    { njs_str("$r3.vars.p = 'a'; delete $r3.vars.p; $r3.vars.p = 'b'; $r3.vars.p"),
+      njs_str("b") },
+
+    { njs_str("$r3.vars.error = 1"),
+      njs_str("Error: cannot set \"error\" prop") },
+
+    { njs_str("delete $r3.vars.error"),
+      njs_str("Error: cannot delete \"error\" prop") },
+
+    { njs_str("delete $r3.vars.e"),
+      njs_str("true") },
+
+    { njs_str("delete $r.consts"),
+      njs_str("true") },
+
+    { njs_str("$r3.consts.k"),
+      njs_str("kval") },
+
+    { njs_str("$r3.consts.k = 1"),
+      njs_str("TypeError: Cannot assign to read-only property \"k\" of object") },
+
+    { njs_str("delete $r3.consts.k"),
+      njs_str("TypeError: Cannot delete property \"k\" of object") },
+
+    { njs_str("delete $r3.vars.p; $r3.vars.p"),
+      njs_str("undefined") },
+
+    { njs_str("var a = $r.host; a +' '+ a.length +' '+ a"),
+      njs_str("АБВГДЕЁЖЗИЙ 22 АБВГДЕЁЖЗИЙ") },
+
+    { njs_str("var a = $r.host; a.substr(2, 2)"),
+      njs_str("Б") },
+
+    { njs_str("var a = $r.header['User-Agent']; a +' '+ a.length +' '+ a"),
+      njs_str("User-Agent|АБВ 17 User-Agent|АБВ") },
+
+    { njs_str("var a='', p;"
+                 "for (p in $r.header) { a += p +':'+ $r.header[p] +',' }"
+                 "a"),
+      njs_str("01:01|АБВ,02:02|АБВ,03:03|АБВ,") },
+
+    { njs_str("$r.method('YES')"),
+      njs_str("АБВ") },
+
+    { njs_str("$r.create('XXX').uri"),
+      njs_str("XXX") },
+
+    { njs_str("var sr = $r.create('XXX'); sr.uri = 'YYY'; sr.uri"),
+      njs_str("YYY") },
+
+    { njs_str("var sr = $r.create('XXX'), sr2 = $r.create('YYY');"
+                 "sr.uri = 'ZZZ'; "
+                 "sr.uri + sr2.uri"),
+      njs_str("ZZZYYY") },
+
+    { njs_str("var sr = $r.create('XXX'); sr.vars.p = 'a'; sr.vars.p"),
+      njs_str("a") },
+
+    { njs_str("var p; for (p in $r.method);"),
+      njs_str("undefined") },
+
+    { njs_str("'uri' in $r"),
+      njs_str("true") },
+
+    { njs_str("'one' in $r"),
+      njs_str("false") },
+
+    { njs_str("'a' in $r.props"),
+      njs_str("true") },
+
+    { njs_str("delete $r.uri"),
+      njs_str("TypeError: Cannot delete property \"uri\" of object") },
+
+    { njs_str("delete $shared.uri"),
+      njs_str("TypeError: Cannot delete property \"uri\" of object") },
+
+    { njs_str("delete $r.one"),
+      njs_str("true") },
+
+    { njs_str("delete $r.vars"),
+      njs_str("TypeError: Cannot delete property \"vars\" of object") },
+
+    { njs_str("delete $r.header; $r.header"),
+      njs_str("undefined") },
+
+    { njs_str("$r.header = 1; $r.header"),
+      njs_str("1") },
+
+    { njs_str("$r.method.call($r, 'YES')"),
+      njs_str("АБВ") },
+
+    { njs_str("var f = $r.method.bind($r); f('YES')"),
+      njs_str("АБВ") },
+
+    { njs_str("function f(fn, arg) {return fn(arg);}; f($r.method.bind($r), 'YES')"),
+      njs_str("АБВ") },
+
+    { njs_str("$r.method.apply($r, ['YES'])"),
+      njs_str("АБВ") },
+
+    { njs_str("$shared.method.apply($r, ['YES'])"),
+      njs_str("АБВ") },
+
+    { njs_str("$r.method.call([], 'YES')"),
+      njs_str("TypeError: \"this\" is not an external") },
+
+    { njs_str("$r.nonexistent"),
+      njs_str("undefined") },
+
+    { njs_str("$shared.nonexistent"),
+      njs_str("undefined") },
+
+    { njs_str("njs.dump($r).startsWith('External')"),
+      njs_str("true") },
+
+    { njs_str("njs.dump($r.header)"),
+      njs_str("Header {01:'01|АБВ',02:'02|АБВ',03:'03|АБВ'}") },
+
+    { njs_str("var o = {b:$r.props.b}; o.b"),
+      njs_str("42") },
+
+    { njs_str("$r2.uri == 'αβγ' && $r2.uri === 'αβγ'"),
+      njs_str("true") },
+
+    { njs_str("Object.keys(this).sort()"),
+      njs_str("$r,$r2,$r3,$shared,global,njs,process") },
+
+    { njs_str("Object.getOwnPropertySymbols($r2)[0] == Symbol.toStringTag"),
+      njs_str("true") },
+
+    { njs_str("Object.getOwnPropertyDescriptors($r2)[Symbol.toStringTag].value"),
+      njs_str("External") },
+
+    { njs_str("Object.getPrototypeOf($r3) === Object.prototype"),
+      njs_str("true") },
+
+    { njs_str("Object.isExtensible($r3)"),
+      njs_str("true") },
+
+    { njs_str("$r3[0] = 0; $r3[1] = 1; $r3.length = 2;"
+              "Array.prototype.join.call($r3, '|')"),
+      njs_str("0|1") },
+
+    { njs_str("$r3.toJSON = ()=> 'R3';"
+              "JSON.stringify($r3)"),
+      njs_str("\"R3\"") },
+
+    { njs_str("$r3[0] = 0; $r3[1] = 1; $r3.length = 2;"
+              "$r3.__proto__ = Array.prototype; $r3.join('|')"),
+      njs_str("0|1") },
+
+    { njs_str("[this, global, globalThis]"
+              ".every(v=> { var r = njs.dump(v); return ['$r', 'global', njs.version].every(v=>r.includes(v))})"),
+      njs_str("true") },
+
+    { njs_str("var r = JSON.parse(JSON.stringify($r));"
+              "[r.uri, r.host, r.props.a, njs.dump(r.vars), njs.dump(r.consts), r.header['02']]"),
+      njs_str("АБВ,АБВГДЕЁЖЗИЙ,1,{},{},02|АБВ") },
+};
+
 static njs_unit_test_t  njs_shared_test[] =
 {
     { njs_str("var cr = require('crypto'); cr.createHash"),
@@ -16788,9 +16835,49 @@ static njs_unit_test_t  njs_shared_test[] =
     { njs_str("isNaN(function(){})"),
       njs_str("true") },
 
+    { njs_str("var a = $r.uri; $r.uri = $r2.uri; $r2.uri = a; $r2.uri + $r.uri"),
+      njs_str("АБВαβγ") },
+
+    { njs_str("njs.dump($r.props)"),
+      njs_str("{a:'1',b:42,c:{d:1024}}") },
+
+    { njs_str("njs.dump($shared.props)"),
+      njs_str("{a:'4294967295',b:42,c:{d:4294967294}}") },
+
+    { njs_str("var r = JSON.parse(JSON.stringify($shared));"
+              "[r.uri, r.host, r.props.a, njs.dump(r.vars), njs.dump(r.consts), r.header['02']]"),
+      njs_str("shared,АБВГДЕЁЖЗИЙ,4294967295,{},{},02|АБВ") },
+
+    { njs_str("$shared.toString()"),
+      njs_str("[object External]") },
+
+    { njs_str("$shared.toString().length"),
+      njs_str("17") },
+
+    { njs_str("delete $shared.method; $shared.method"),
+      njs_str("undefined") },
+
+    { njs_str("$shared.method = () => 1; $shared.method()"),
+      njs_str("1") },
+
+    { njs_str("$shared.method = function() {return this.props.a;}; $shared.method()"),
+      njs_str("4294967295") },
+
     { njs_str("var r; for (var i = 0; i < 2**10; i++) {r = $r.create('XXX').uri;}"),
       njs_str("undefined") },
 
+    { njs_str("$r.vars.unset = 'a'; $r2.vars.unset = 'b';"
+              "$r.vars.unset + $r2.vars.unset"),
+      njs_str("ab") },
+
+    { njs_str("$r.vars.unset = 1; $r2.vars.unset = 2;"
+              "$r.vars.unset + $r2.vars.unset"),
+      njs_str("3") },
+
+    { njs_str("$r3.vars.p = 'a'; $r3.vars.p2 = 'b';"
+              "$r3.vars.p + $r3.vars.p2"),
+      njs_str("ab") },
+
     { njs_str("delete $r3.vars.p; $r3.vars.p"),
       njs_str("undefined") },
 
@@ -16966,6 +17053,7 @@ typedef struct {
     njs_bool_t  unsafe;
     njs_bool_t  module;
     njs_uint_t  repeat;
+    njs_uint_t  externals;
 } njs_opts_t;
 
 
@@ -16992,17 +17080,19 @@ static njs_int_t
 njs_unit_test(njs_unit_test_t tests[], size_t num, const char *name,
     njs_opts_t *opts, njs_stat_t *stat)
 {
-    u_char        *start;
-    njs_vm_t      *vm, *nvm;
-    njs_int_t     ret;
-    njs_str_t     s;
-    njs_uint_t    i, repeat;
-    njs_stat_t    prev;
-    njs_bool_t    success;
-    njs_vm_opt_t  options;
+    u_char                *start;
+    njs_vm_t              *vm, *nvm;
+    njs_int_t             ret;
+    njs_str_t             s;
+    njs_uint_t            i, repeat;
+    njs_stat_t            prev;
+    njs_bool_t            success;
+    njs_vm_opt_t          options;
+    njs_external_proto_t  proto;
 
     vm = NULL;
     nvm = NULL;
+    proto = NULL;
 
     prev = *stat;
 
@@ -17025,9 +17115,11 @@ njs_unit_test(njs_unit_test_t tests[], size_t num, const char *name,
             goto done;
         }
 
-        ret = njs_externals_init(vm);
-        if (ret != NJS_OK) {
-            goto done;
+        if (opts->externals) {
+            proto = njs_externals_shared_init(vm);
+            if (proto == NULL) {
+                goto done;
+            }
         }
 
         start = tests[i].script.start;
@@ -17052,6 +17144,13 @@ njs_unit_test(njs_unit_test_t tests[], size_t num, const char *name,
                     goto done;
                 }
 
+                if (opts->externals) {
+                    ret = njs_externals_init(nvm, proto);
+                    if (ret != NJS_OK) {
+                        goto done;
+                    }
+                }
+
                 ret = njs_vm_start(nvm);
             } while (--repeat != 0);
 
@@ -17792,12 +17891,6 @@ main(int argc, char **argv)
 
     njs_mm_denormals(0);
 
-    ret = njs_unit_test(njs_test, njs_nitems(njs_test),
-                        "script tests (disabled denormals)", &opts, &stat);
-    if (ret != NJS_OK) {
-        return ret;
-    }
-
     ret = njs_unit_test(njs_disabled_denormals_test,
                         njs_nitems(njs_disabled_denormals_test),
                         "disabled denormals tests", &opts, &stat);
@@ -17842,6 +17935,14 @@ main(int argc, char **argv)
     }
 
     opts.module = 0;
+    opts.externals = 1;
+
+    ret = njs_unit_test(njs_externals_test, njs_nitems(njs_externals_test),
+                        "externals tests", &opts, &stat);
+    if (ret != NJS_OK) {
+        return ret;
+    }
+
     opts.repeat = 128;
 
     ret = njs_unit_test(njs_shared_test, njs_nitems(njs_shared_test),
index fe167da35d1ab003926091588837e2c3d52ecd76..a6b970d0c2c3d1af49adbc674fd24811417a04f0 100644 (file)
@@ -227,6 +227,26 @@ njs_test {
 
 # console object
 njs_test {
+    {"console[Symbol.toStringTag]\r\n"
+     "console\\\[Symbol.toStringTag]\r\n'Console'\r\n>> "}
+    {"Object.prototype.toString.call(console)\r\n"
+     "Object.prototype.toString.call(console)\r\n'\\\[object Console]'\r\n>> "}
+    {"console.toString()\r\n"
+     "console.toString()\r\n'\\\[object Console]'\r\n>> "}
+    {"console\r\n"
+     "console\r\nConsole *>> "}
+    {"delete console.log\r\n"
+     "delete console.log\r\ntrue\r\n>>"}
+    {"console\r\n"
+     "console\r\nConsole *>> "}
+}
+
+# console log functions
+njs_test {
+    {"console[Symbol.toStringTag]\r\n"
+     "console\\\[Symbol.toStringTag]\r\n'Console'\r\n>> "}
+    {"console\r\n"
+     "console\r\nConsole *>> "}
     {"console.log()\r\n"
      "console.log()\r\nundefined\r\n>> "}
     {"console.log('')\r\n"
@@ -293,15 +313,15 @@ njs_test {
     {"var print = console.dump.bind(console); print(1, 'a', [1, 2])\r\n"
      "1 a \\\[\r\n 1,\r\n 2\r\n]\r\nundefined\r\n>> "}
     {"var print = console.log.bind(console); print(console.a.a)\r\n"
-     "TypeError: cannot get property \"a\" of undefined*at print"}
+     "TypeError: cannot get property \"a\" of undefined*at console.log"}
     {"print(console.a.a)\r\n"
-     "TypeError: cannot get property \"a\" of undefined*at print"}
+     "TypeError: cannot get property \"a\" of undefined*at console.log"}
 }
 
 # Backtraces for external objects
 njs_test {
     {"console.log(console.a.a)\r\n"
-     "console.log(console.a.a)\r\nThrown:\r\nTypeError:*at print (native)"}
+     "console.log(console.a.a)\r\nThrown:\r\nTypeError:*at console.log (native)"}
 }
 
 # dumper