From: Dmitry Volyntsev Date: Fri, 20 Mar 2020 14:33:06 +0000 (+0300) Subject: Refactored externals. X-Git-Tag: 0.4.0~22 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=fe69071ab7b162ef289d0a5e292695f24fcb7d34;p=njs.git Refactored externals. This closes #16 issue on Github. --- diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 5af7319f..a286be9c 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -13,34 +13,34 @@ 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) { diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index 8a911780..3b81a1af 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -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) { diff --git a/src/njs.h b/src/njs.h index d19e532a..7953d2f3 100644 --- a/src/njs.h +++ b/src/njs.h @@ -11,7 +11,7 @@ #include -#define NJS_VERSION "0.3.10" +#define NJS_VERSION "0.4.0" #include /* STDOUT_FILENO, STDERR_FILENO */ @@ -25,9 +25,11 @@ 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); diff --git a/src/njs_array.c b/src/njs_array.c index a7c03239..7e0b215a 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -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; diff --git a/src/njs_array.h b/src/njs_array.h index 0dae0129..a0daf2c1 100644 --- a/src/njs_array.h +++ b/src/njs_array.h @@ -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, diff --git a/src/njs_array_buffer.c b/src/njs_array_buffer.c index 6394e674..bc1051e6 100644 --- a/src/njs_array_buffer.c +++ b/src/njs_array_buffer.c @@ -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; diff --git a/src/njs_crypto.c b/src/njs_crypto.c index 434251fd..9989ef10 100644 --- a/src/njs_crypto.c +++ b/src/njs_crypto.c @@ -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; } diff --git a/src/njs_date.c b/src/njs_date.c index 01e10233..58ee967e 100644 --- a/src/njs_date.c +++ b/src/njs_date.c @@ -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; diff --git a/src/njs_error.c b/src/njs_error.c index 97fe51a8..ba53a90b 100644 --- a/src/njs_error.c +++ b/src/njs_error.c @@ -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; diff --git a/src/njs_extern.c b/src/njs_extern.c index 96e14fd4..3df7604c 100644 --- a/src/njs_extern.c +++ b/src/njs_extern.c @@ -8,347 +8,326 @@ #include -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 = ∂ - - 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 index bfdad22d..00000000 --- a/src/njs_extern.h +++ /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_ */ diff --git a/src/njs_function.c b/src/njs_function.c index bb612a52..a5bf5132 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -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; } diff --git a/src/njs_function.h b/src/njs_function.h index cd3aca32..7724277f 100644 --- a/src/njs_function.h +++ b/src/njs_function.h @@ -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; } diff --git a/src/njs_json.c b/src/njs_json.c index 7369b174..18b01677 100644 --- a/src/njs_json.c +++ b/src/njs_json.c @@ -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); } diff --git a/src/njs_main.h b/src/njs_main.h index 51e8229d..d6af2be7 100644 --- a/src/njs_main.h +++ b/src/njs_main.h @@ -78,7 +78,6 @@ #include #include -#include #include diff --git a/src/njs_module.c b/src/njs_module.c index 3443b19e..0cc20f90 100644 --- a/src/njs_module.c +++ b/src/njs_module.c @@ -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; diff --git a/src/njs_object.c b/src/njs_object.c index 624db73b..8052c3c7 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -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; diff --git a/src/njs_object_prop.c b/src/njs_object_prop.c index 5e229132..4ea537db 100644 --- a/src/njs_object_prop.c +++ b/src/njs_object_prop.c @@ -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; } diff --git a/src/njs_promise.c b/src/njs_promise.c index 6eb4b02b..cf0a879e 100644 --- a/src/njs_promise.c +++ b/src/njs_promise.c @@ -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)); diff --git a/src/njs_regexp.c b/src/njs_regexp.c index 1a05d814..3826c8a6 100644 --- a/src/njs_regexp.c +++ b/src/njs_regexp.c @@ -710,6 +710,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern) njs_lvlhsh_init(®exp->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; diff --git a/src/njs_shell.c b/src/njs_shell.c index 7736b472..2df2c82d 100644 --- a/src/njs_shell.c +++ b/src/njs_shell.c @@ -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; } diff --git a/src/njs_symbol.h b/src/njs_symbol.h index 22f60d29..8e532549 100644 --- a/src/njs_symbol.h +++ b/src/njs_symbol.h @@ -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); diff --git a/src/njs_value.c b/src/njs_value.c index 493c5334..043f5f2b 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -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. */ diff --git a/src/njs_value.h b/src/njs_value.h index a38a18c1..622b581c 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -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); diff --git a/src/njs_vm.c b/src/njs_vm.c index e6ec424c..9857ffdd 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -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; diff --git a/src/njs_vm.h b/src/njs_vm.h index 8e8b8d41..f97f863a 100644 --- a/src/njs_vm.h +++ b/src/njs_vm.h @@ -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; }; diff --git a/src/test/njs_benchmark.c b/src/test/njs_benchmark.c index 452bf9ab..f4697ddd 100644 --- a/src/test/njs_benchmark.c +++ b/src/test/njs_benchmark.c @@ -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 }, }; diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c index 850d0441..fb2fcffb 100644 --- a/src/test/njs_externals_test.c +++ b/src/test/njs_externals_test.c @@ -11,10 +11,10 @@ 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; } diff --git a/src/test/njs_externals_test.h b/src/test/njs_externals_test.h index ab728ca9..97553b5f 100644 --- a/src/test/njs_externals_test.h +++ b/src/test/njs_externals_test.h @@ -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_ */ diff --git a/src/test/njs_interactive_test.c b/src/test/njs_interactive_test.c index 352fa103..de7140fb 100644 --- a/src/test/njs_interactive_test.c +++ b/src/test/njs_interactive_test.c @@ -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; } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index e4c3fe26..492bcbb7 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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), diff --git a/test/njs_expect_test.exp b/test/njs_expect_test.exp index fe167da3..a6b970d0 100644 --- a/test/njs_expect_test.exp +++ b/test/njs_expect_test.exp @@ -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