aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES55
-rw-r--r--README.md16
-rw-r--r--external/njs_regex.c34
-rw-r--r--external/njs_webcrypto_module.c9
-rw-r--r--nginx/ngx_http_js_module.c57
-rw-r--r--nginx/ngx_js.c37
-rw-r--r--nginx/ngx_js.h9
-rw-r--r--nginx/ngx_js_fetch.c21
-rw-r--r--nginx/ngx_js_shared_dict.c1374
-rw-r--r--nginx/ngx_js_shared_dict.h1
-rw-r--r--nginx/ngx_qjs_fetch.c21
-rw-r--r--nginx/ngx_stream_js_module.c53
-rw-r--r--nginx/t/js_fetch.t31
-rw-r--r--nginx/t/js_fetch_https.t10
-rw-r--r--nginx/t/js_fetch_objects.t10
-rw-r--r--nginx/t/js_fetch_resolver.t10
-rw-r--r--nginx/t/js_fetch_timeout.t10
-rw-r--r--nginx/t/js_fetch_verify.t10
-rw-r--r--nginx/t/js_internal_redirect.t29
-rw-r--r--nginx/t/js_periodic.t2
-rw-r--r--nginx/t/js_periodic_fetch.t19
-rw-r--r--nginx/t/js_shared_dict.t10
-rw-r--r--nginx/t/js_shared_dict_state.t256
-rw-r--r--nginx/t/stream_js_fetch.t10
-rw-r--r--nginx/t/stream_js_fetch_https.t10
-rw-r--r--nginx/t/stream_js_fetch_init.t10
-rw-r--r--nginx/t/stream_js_periodic_fetch.t10
-rw-r--r--nginx/t/stream_js_shared_dict.t10
-rw-r--r--nginx/t/stream_js_shared_dict_state.t137
-rw-r--r--src/njs_array.c6
-rw-r--r--src/njs_atom.c78
-rw-r--r--src/njs_buffer.c8
-rw-r--r--src/njs_builtin.c160
-rw-r--r--src/njs_chb.c6
-rw-r--r--src/njs_clang.h2
-rw-r--r--src/njs_disassembler.c3
-rw-r--r--src/njs_dtoa.h2
-rw-r--r--src/njs_error.c47
-rw-r--r--src/njs_extern.c49
-rw-r--r--src/njs_flathsh.c43
-rw-r--r--src/njs_flathsh.h32
-rw-r--r--src/njs_function.c204
-rw-r--r--src/njs_function.h29
-rw-r--r--src/njs_generator.c90
-rw-r--r--src/njs_json.c28
-rw-r--r--src/njs_lexer.h10
-rw-r--r--src/njs_module.c25
-rw-r--r--src/njs_object.c125
-rw-r--r--src/njs_object.h29
-rw-r--r--src/njs_object_prop.c335
-rw-r--r--src/njs_object_prop_declare.h8
-rw-r--r--src/njs_parser.c29
-rw-r--r--src/njs_parser.h4
-rw-r--r--src/njs_regexp.c86
-rw-r--r--src/njs_scope.c27
-rw-r--r--src/njs_scope.h1
-rw-r--r--src/njs_string.c148
-rw-r--r--src/njs_typed_array.c41
-rw-r--r--src/njs_value.c55
-rw-r--r--src/njs_value.h40
-rw-r--r--src/njs_variable.c18
-rw-r--r--src/njs_variable.h3
-rw-r--r--src/njs_vm.c61
-rw-r--r--src/njs_vmcode.c117
-rw-r--r--src/njs_vmcode.h8
-rw-r--r--src/qjs.c10
-rw-r--r--src/test/lvlhsh_unit_test.c6
-rw-r--r--src/test/njs_externals_test.c8
-rw-r--r--src/test/njs_unit_test.c126
-rw-r--r--test/js/async_closure.t.js24
-rw-r--r--test/js/async_closure_arg.t.js24
-rw-r--r--test/js/async_closure_share.t.js28
-rw-r--r--ts/ngx_core.d.ts9
-rw-r--r--ts/ngx_http_js_module.d.ts2
-rw-r--r--ts/ngx_stream_js_module.d.ts2
-rw-r--r--ts/njs_core.d.ts1
-rw-r--r--ts/njs_webcrypto.d.ts15
77 files changed, 3216 insertions, 1267 deletions
diff --git a/CHANGES b/CHANGES
index af7e4b61..719e25b4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,58 @@
+Changes with njs 0.9.1 10 Jul 2025
+
+ nginx modules:
+
+ *) Feature: added Fetch API for QuickJS engine.
+
+ *) Feature: added state file for a shared dictionary.
+
+ *) Bugfix: fixed handling of Content-Length header when
+ a body is provided for Fetch API.
+
+ *) Bugfix: fixed qjs engine after bellard/quickjs@458c34d2.
+
+ *) Bugfix: fixed NULL pointer dereference when processing
+ If-* headers.
+
+ Core:
+
+ *) Feature: added ECDH support for WebCrypto.
+
+ *) Improvement: reduced memory consumption by the object hash.
+ The new hash uses 42% less memory per element.
+
+ *) Improvement: reduced memory consumption for concatenation of
+ numbers and strings.
+
+ *) Improvement: reduced memory consumption of
+ String.prototype.concat() with scalar values.
+
+ *) Bugfix: fixed segfault in njs_property_query().
+ The issue was introduced in b28e50b1 (0.9.0).
+
+ *) Bugfix: fixed Function constructor template injection.
+
+ *) Bugfix: fixed GCC compilation with O3 optimization level.
+
+ *) Bugfix: fixed constant is too large for 'long' warning
+ on MIPS -mabi=n32.
+
+ *) Bugfix: fixed compilation with GCC 4.1.
+
+ *) Bugfix: fixed %TypedArray%.from() with the buffer is detached
+ by the mapper.
+
+ *) Bugfix: fixed %TypedArray%.prototype.slice() with overlapping
+ buffers.
+
+ *) Bugfix: fixed handling of detached buffers for typed arrays.
+
+ *) Bugfix: fixed frame saving for async functions with
+ closures.
+
+ *) Bugfix: fixed RegExp compilation of patterns with
+ escaped '[' characters.
+
Changes with njs 0.9.0 06 May 2025
Core:
diff --git a/README.md b/README.md
index 10ef0ba7..d196510c 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ NGINX JavaScript, also known as [NJS](https://nginx.org/en/docs/njs/), is a dyna
- [Enabling the NGINX JavaScript modules](#enabling-the-nginx-javascipt-modules)
- [Basics of writing .js script files](#basics-of-writing-js-script-files)
- [Reference of custom objects, methods, and properties](#reference-of-custom-objects-methods-and-properties)
- - [Example: Hello World](#hello-world)
+ - [Example: Hello World](#example-hello-world)
- [The NJS command line interface (CLI)](#the-njs-command-line-interface-cli)
- [Building from source](#building-from-source)
- [Installing dependencies](#installing-dependencies)
@@ -310,20 +310,30 @@ https://github.com/nginx/nginx.git
## Building NGINX JavaScript as a module of NGINX
To build NGINX JavaScript as a dynamic module, execute the following commands from the NGINX source code repository's root directory:
+> [!NOTE]
+> Replace `<NJS_SRC_ROOT_DIR>` with the actual path to your NJS source directory.
+
```bash
auto/configure --add-dynamic-module=<NJS_SRC_ROOT_DIR>/nginx
```
To build with [QuickJS](https://nginx.org/en/docs/njs/engine.html) support, provide include and library path using `--with-cc-opt=` and `--with-ld-opt=` options:
+
+> [!NOTE]
+> Replace `<NJS_SRC_ROOT_DIR>` with the actual path to your NJS source directory and `<QUICKJS_SRC_ROOT_DIR>` with the actual path to your QuickJS source directory.
+
```bash
auto/configure --add-dynamic-module=<NJS_SRC_ROOT_DIR>/nginx \
- --with-cc-opt="-I<QUICKJS_SRC_ROOT_DIR>" --with-ld-opt="-L<QUICKJS_SRC_ROOT_DIR>"
+ --with-cc-opt="-I<QUICKJS_SRC_ROOT_DIR>" \
+ --with-ld-opt="-L<QUICKJS_SRC_ROOT_DIR>"
```
> [!WARNING]
> By default, this method will only build the `ngx_http_js_module` module. To use NJS with the NGINX Stream module, you'll need to enable it during the `configure` step so it builds with the NGINX binary. Doing so will automatically compile the `ngx_stream_js_module` module when NJS is added to the build. One way of accomplishing this is to alter the `configure` step to:
+>
> ```bash
-> auto/configure --with-stream --add-dynamic-module=<NJS_SRC_ROOT_DIR>/nginx
+> auto/configure --with-stream \
+> --add-dynamic-module=<NJS_SRC_ROOT_DIR>/nginx
> ```
Compile the module
diff --git a/external/njs_regex.c b/external/njs_regex.c
index a118666b..cd45afc0 100644
--- a/external/njs_regex.c
+++ b/external/njs_regex.c
@@ -114,6 +114,11 @@ njs_regex_escape(njs_mp_t *mp, njs_str_t *text)
for (p = start; p < end; p++) {
switch (*p) {
+ case '\\':
+ p += 1;
+
+ break;
+
case '[':
if (p + 1 < end && p[1] == ']') {
p += 1;
@@ -122,6 +127,11 @@ njs_regex_escape(njs_mp_t *mp, njs_str_t *text)
} else if (p + 2 < end && p[1] == '^' && p[2] == ']') {
p += 2;
anychars += 1;
+
+ } else {
+ while (p < end && *p != ']') {
+ p += 1;
+ }
}
break;
@@ -146,6 +156,15 @@ njs_regex_escape(njs_mp_t *mp, njs_str_t *text)
for (p = start; p < end; p++) {
switch (*p) {
+ case '\\':
+ *dst++ = *p;
+ if (p + 1 < end) {
+ p += 1;
+ *dst++ = *p;
+ }
+
+ continue;
+
case '[':
if (p + 1 < end && p[1] == ']') {
p += 1;
@@ -156,12 +175,27 @@ njs_regex_escape(njs_mp_t *mp, njs_str_t *text)
p += 2;
dst = njs_cpymem(dst, "[\\s\\S]", 6);
continue;
+
+ } else {
+ *dst++ = *p++; /* Copy '['. */
+
+ while (p < end && *p != ']') {
+ *dst++ = *p++;
+ }
+
+ if (p < end) {
+ *dst++ = *p; /* Copy ']'. */
+ }
+
+ continue;
}
}
*dst++ = *p;
}
+ njs_assert(dst == text->start + text->length);
+
return NJS_OK;
#else
diff --git a/external/njs_webcrypto_module.c b/external/njs_webcrypto_module.c
index d9b05d09..b9a74353 100644
--- a/external/njs_webcrypto_module.c
+++ b/external/njs_webcrypto_module.c
@@ -2593,7 +2593,6 @@ static njs_int_t
njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused, njs_value_t *retval)
{
- int nid;
unsigned usage;
njs_int_t ret;
njs_bool_t extractable;
@@ -2730,8 +2729,7 @@ njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
case NJS_ALGORITHM_ECDSA:
case NJS_ALGORITHM_ECDH:
- nid = 0;
- ret = njs_algorithm_curve(vm, aobject, &nid);
+ ret = njs_algorithm_curve(vm, aobject, &key->u.a.curve);
if (njs_slow_path(ret == NJS_ERROR)) {
goto fail;
}
@@ -2747,7 +2745,7 @@ njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
goto fail;
}
- if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0) {
+ if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, key->u.a.curve) <= 0) {
njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_ec_paramgen_curve_nid() "
"failed");
goto fail;
@@ -4854,7 +4852,8 @@ njs_algorithm_hash(njs_vm_t *vm, njs_value_t *options,
if (njs_value_is_object(options)) {
val = njs_vm_object_prop(vm, options, &string_hash, &value);
if (val == NULL) {
- return NJS_HASH_SHA256;
+ *hash = NJS_HASH_SHA256;
+ return NJS_OK;
}
} else {
diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c
index 40bb83a5..45ddf17e 100644
--- a/nginx/ngx_http_js_module.c
+++ b/nginx/ngx_http_js_module.c
@@ -433,6 +433,13 @@ static ngx_command_t ngx_http_js_commands[] = {
offsetof(ngx_http_js_loc_conf_t, reuse),
NULL },
+ { ngx_string("js_context_reuse_max_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_js_loc_conf_t, reuse_max_size),
+ NULL },
+
{ ngx_string("js_import"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13,
ngx_js_import,
@@ -2448,6 +2455,8 @@ ngx_http_js_ext_send_header(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
return NJS_ERROR;
}
+ r->disable_not_modified = 1;
+
if (ngx_http_send_header(r) == NGX_ERROR) {
return NJS_ERROR;
}
@@ -2731,6 +2740,8 @@ ngx_http_js_ext_return(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
cv.value.data = text.start;
cv.value.len = text.length;
+ r->disable_not_modified = 1;
+
ctx->status = ngx_http_send_response(r, status, NULL, &cv);
if (ctx->status == NGX_ERROR) {
@@ -5438,6 +5449,8 @@ ngx_http_qjs_ext_return(JSContext *cx, JSValueConst this_val,
cv.value.data = body.data;
cv.value.len = body.len;
+ r->disable_not_modified = 1;
+
ctx->status = ngx_http_send_response(r, status, NULL, &cv);
if (ctx->status == NGX_ERROR) {
@@ -5663,6 +5676,8 @@ ngx_http_qjs_ext_send_header(JSContext *cx, JSValueConst this_val,
return JS_ThrowInternalError(cx, "failed to set content type");
}
+ r->disable_not_modified = 1;
+
if (ngx_http_send_header(r) == NGX_ERROR) {
return JS_ThrowInternalError(cx, "failed to send header");
}
@@ -7684,21 +7699,12 @@ ngx_http_js_init(ngx_conf_t *cf)
static ngx_int_t
-ngx_http_js_init_worker(ngx_cycle_t *cycle)
+ngx_http_js_init_worker_periodics(ngx_js_main_conf_t *jmcf)
{
ngx_uint_t i;
ngx_js_periodic_t *periodics;
- ngx_js_main_conf_t *jmcf;
-
- if ((ngx_process != NGX_PROCESS_WORKER)
- && ngx_process != NGX_PROCESS_SINGLE)
- {
- return NGX_OK;
- }
-
- jmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_js_module);
- if (jmcf == NULL || jmcf->periodics == NULL) {
+ if (jmcf->periodics == NULL) {
return NGX_OK;
}
@@ -7726,6 +7732,35 @@ ngx_http_js_init_worker(ngx_cycle_t *cycle)
}
+static ngx_int_t
+ngx_http_js_init_worker(ngx_cycle_t *cycle)
+{
+ ngx_js_main_conf_t *jmcf;
+
+ if ((ngx_process != NGX_PROCESS_WORKER)
+ && ngx_process != NGX_PROCESS_SINGLE)
+ {
+ return NGX_OK;
+ }
+
+ jmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_js_module);
+
+ if (jmcf == NULL) {
+ return NGX_OK;
+ }
+
+ if (ngx_http_js_init_worker_periodics(jmcf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_js_dict_init_worker(jmcf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
static char *
ngx_http_js_periodic(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c
index e4bae32a..01d4bb2a 100644
--- a/nginx/ngx_js.c
+++ b/nginx/ngx_js.c
@@ -31,12 +31,6 @@ typedef struct {
} ngx_js_rejected_promise_t;
-#if defined(PATH_MAX)
-#define NGX_MAX_PATH PATH_MAX
-#else
-#define NGX_MAX_PATH 4096
-#endif
-
typedef struct {
int fd;
njs_str_t name;
@@ -984,6 +978,11 @@ ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external)
"js load module exception: %V", &exception);
goto destroy;
}
+
+ if (i != length - 1) {
+ /* JS_EvalFunction() does JS_FreeValue(cx, rv) for the last rv. */
+ JS_FreeValue(cx, rv);
+ }
}
if (JS_ResolveModule(cx, rv) < 0) {
@@ -1133,6 +1132,7 @@ ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
JSRuntime *rt;
JSContext *cx;
JSClassID class_id;
+ JSMemoryUsage stats;
ngx_qjs_event_t *event;
ngx_js_opaque_t *opaque;
njs_rbtree_node_t *node;
@@ -1198,6 +1198,28 @@ ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx,
cln->data = conf->reuse_queue;
}
+ /*
+ * After the request object is freed, the runtime's memory usage should
+ * be low. It can only remain high if the global scope was
+ * modified.
+ *
+ * To prevent unlimited memory consumption growth, check whether memory
+ * usage exceeds the configured limit. The check is performed rarely to
+ * avoid performance impact of JS_ComputeMemoryUsage() which is slow.
+ */
+
+ if ((ngx_random() & 0xff) == 1) {
+ JS_ComputeMemoryUsage(JS_GetRuntime(cx), &stats);
+
+ if ((size_t) stats.malloc_size > conf->reuse_max_size) {
+ ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
+ "js remaining memory usage of the context "
+ "exceeds \"js_context_reuse_max_size\" limit: %L"
+ ", not reusing it", stats.malloc_size);
+ goto free_ctx;
+ }
+ }
+
if (ngx_js_queue_push(conf->reuse_queue, cx) != NGX_OK) {
goto free_ctx;
}
@@ -3950,6 +3972,7 @@ ngx_js_create_conf(ngx_conf_t *cf, size_t size)
conf->preload_objects = NGX_CONF_UNSET_PTR;
conf->reuse = NGX_CONF_UNSET_SIZE;
+ conf->reuse_max_size = NGX_CONF_UNSET_SIZE;
conf->buffer_size = NGX_CONF_UNSET_SIZE;
conf->max_response_body_size = NGX_CONF_UNSET_SIZE;
conf->timeout = NGX_CONF_UNSET_MSEC;
@@ -4059,6 +4082,8 @@ ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child,
ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
ngx_conf_merge_size_value(conf->reuse, prev->reuse, 128);
+ ngx_conf_merge_size_value(conf->reuse_max_size, prev->reuse_max_size,
+ 4 * 1024 * 1024);
ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 16384);
ngx_conf_merge_size_value(conf->max_response_body_size,
prev->max_response_body_size, 1048576);
diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h
index bb7c1d26..99330f88 100644
--- a/nginx/ngx_js.h
+++ b/nginx/ngx_js.h
@@ -16,8 +16,6 @@
#include <njs.h>
#include <njs_rbtree.h>
#include <njs_arr.h>
-#include "ngx_js_fetch.h"
-#include "ngx_js_shared_dict.h"
#if (NJS_HAVE_QUICKJS)
#include <qjs.h>
@@ -124,6 +122,7 @@ typedef struct {
ngx_uint_t type; \
ngx_engine_t *engine; \
ngx_uint_t reuse; \
+ size_t reuse_max_size; \
ngx_js_queue_t *reuse_queue; \
ngx_str_t cwd; \
ngx_array_t *imports; \
@@ -317,6 +316,9 @@ ngx_int_t ngx_js_exception(njs_vm_t *vm, ngx_str_t *s);
ngx_engine_t *ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf,
void *external);
+#define NGX_CHB_CTX_INIT(chain, pool) \
+ njs_chb_init(chain, pool, (njs_chb_alloc_t) ngx_palloc, NULL)
+
#if (NJS_HAVE_QUICKJS)
typedef struct ngx_qjs_event_s ngx_qjs_event_t;
@@ -437,4 +439,7 @@ extern njs_module_t njs_xml_module;
extern njs_module_t njs_zlib_module;
+#include "ngx_js_fetch.h"
+#include "ngx_js_shared_dict.h"
+
#endif /* _NGX_JS_H_INCLUDED_ */
diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c
index 45f2dc10..faa38aab 100644
--- a/nginx/ngx_js_fetch.c
+++ b/nginx/ngx_js_fetch.c
@@ -514,6 +514,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
ngx_url_t u;
ngx_uint_t i;
njs_bool_t has_host;
+ ngx_str_t method;
ngx_pool_t *pool;
njs_value_t *init, *value;
ngx_js_http_t *http;
@@ -674,6 +675,13 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
continue;
}
+ if (h[i].key.len == 14
+ && ngx_strncasecmp(h[i].key.data, (u_char *) "Content-Length", 14)
+ == 0)
+ {
+ continue;
+ }
+
njs_chb_append(&http->chain, h[i].key.data, h[i].key.len);
njs_chb_append_literal(&http->chain, ": ");
njs_chb_append(&http->chain, h[i].value.data, h[i].value.len);
@@ -693,7 +701,18 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_chb_append(&http->chain, request.body.data, request.body.len);
} else {
- njs_chb_append_literal(&http->chain, CRLF);
+ method = request.method;
+
+ if ((method.len == 4
+ && (ngx_strncasecmp(method.data, (u_char *) "POST", 4) == 0))
+ || (method.len == 3
+ && ngx_strncasecmp(method.data, (u_char *) "PUT", 3) == 0))
+ {
+ njs_chb_append_literal(&http->chain, "Content-Length: 0" CRLF CRLF);
+
+ } else {
+ njs_chb_append_literal(&http->chain, CRLF);
+ }
}
if (u.addrs == NULL) {
diff --git a/nginx/ngx_js_shared_dict.c b/nginx/ngx_js_shared_dict.c
index ccca530d..5445b4f2 100644
--- a/nginx/ngx_js_shared_dict.c
+++ b/nginx/ngx_js_shared_dict.c
@@ -18,17 +18,10 @@ typedef struct {
ngx_rbtree_t rbtree_expire;
ngx_rbtree_node_t sentinel_expire;
-} ngx_js_dict_sh_t;
-
-typedef struct {
- ngx_str_node_t sn;
- ngx_rbtree_node_t expire;
- union {
- ngx_str_t value;
- double number;
- } u;
-} ngx_js_dict_node_t;
+ unsigned dirty:1;
+ unsigned writing:1;
+} ngx_js_dict_sh_t;
struct ngx_js_dict_s {
@@ -36,16 +29,47 @@ struct ngx_js_dict_s {
ngx_js_dict_sh_t *sh;
ngx_slab_pool_t *shpool;
+ /**
+ * in order for ngx_js_dict_t to be used as a ngx_event_t data,
+ * fd is used for event debug and should be at the same position
+ * as in ngx_connection_t. see ngx_event_ident() for details.
+ */
+ ngx_socket_t fd;
+
ngx_msec_t timeout;
ngx_flag_t evict;
#define NGX_JS_DICT_TYPE_STRING 0
#define NGX_JS_DICT_TYPE_NUMBER 1
ngx_uint_t type;
+ ngx_event_t save_event;
+ ngx_str_t state_file;
+ ngx_str_t state_temp_file;
+
ngx_js_dict_t *next;
};
+typedef union {
+ ngx_str_t str; /* NGX_JS_DICT_TYPE_STRING */
+ double number; /* NGX_JS_DICT_TYPE_NUMBER */
+} ngx_js_dict_value_t;
+
+
+typedef struct {
+ ngx_str_node_t sn;
+ ngx_rbtree_node_t expire;
+ ngx_js_dict_value_t value;
+} ngx_js_dict_node_t;
+
+
+typedef struct {
+ ngx_str_t key;
+ ngx_js_dict_value_t value;
+ ngx_msec_t expire;
+} ngx_js_dict_entry_t;
+
+
static njs_int_t njs_js_ext_shared_dict_capacity(njs_vm_t *vm,
njs_object_prop_t *prop, uint32_t unused, njs_value_t *value,
njs_value_t *setval, njs_value_t *retval);
@@ -79,25 +103,25 @@ static njs_int_t njs_js_ext_shared_dict_type(njs_vm_t *vm,
njs_object_prop_t *prop, uint32_t unused, njs_value_t *value,
njs_value_t *setval, njs_value_t *retval);
static ngx_js_dict_node_t *ngx_js_dict_lookup(ngx_js_dict_t *dict,
- njs_str_t *key);
+ ngx_str_t *key);
#define NGX_JS_DICT_FLAG_MUST_EXIST 1
#define NGX_JS_DICT_FLAG_MUST_NOT_EXIST 2
static ngx_int_t ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict,
- njs_str_t *key, njs_value_t *value, ngx_msec_t timeout, unsigned flags);
+ ngx_str_t *key, njs_value_t *value, ngx_msec_t timeout, unsigned flags);
static ngx_int_t ngx_js_dict_add(njs_vm_t *vm, ngx_js_dict_t *dict,
- njs_str_t *key, njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now);
+ ngx_str_t *key, njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now);
static ngx_int_t ngx_js_dict_update(njs_vm_t *vm, ngx_js_dict_t *dict,
ngx_js_dict_node_t *node, njs_value_t *value, ngx_msec_t timeout,
ngx_msec_t now);
static ngx_int_t ngx_js_dict_get(njs_vm_t *vm, ngx_js_dict_t *dict,
- njs_str_t *key, njs_value_t *retval);
+ ngx_str_t *key, njs_value_t *retval);
static ngx_int_t ngx_js_dict_incr(njs_vm_t *vm, ngx_js_dict_t *dict,
- njs_str_t *key, njs_value_t *delta, njs_value_t *init, double *value,
+ ngx_str_t *key, njs_value_t *delta, njs_value_t *init, double *value,
ngx_msec_t timeout);
static ngx_int_t ngx_js_dict_delete(njs_vm_t *vm, ngx_js_dict_t *dict,
- njs_str_t *key, njs_value_t *retval);
+ ngx_str_t *key, njs_value_t *retval);
static ngx_int_t ngx_js_dict_copy_value_locked(njs_vm_t *vm,
ngx_js_dict_t *dict, ngx_js_dict_node_t *node, njs_value_t *retval);
@@ -622,8 +646,14 @@ njs_js_ext_shared_dict_clear(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
done:
+ dict->sh->dirty = 1;
+
ngx_rwlock_unlock(&dict->sh->rwlock);
+ if (dict->state_file.data && !dict->save_event.timer_set) {
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
njs_value_undefined_set(retval);
return NJS_OK;
@@ -662,7 +692,7 @@ njs_js_ext_shared_dict_delete(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused, njs_value_t *retval)
{
ngx_int_t rc;
- njs_str_t key;
+ ngx_str_t key;
ngx_shm_zone_t *shm_zone;
shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id,
@@ -672,7 +702,7 @@ njs_js_ext_shared_dict_delete(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
return NJS_ERROR;
}
- if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
+ if (ngx_js_ngx_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
return NJS_ERROR;
}
@@ -689,7 +719,7 @@ njs_js_ext_shared_dict_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused, njs_value_t *retval)
{
ngx_int_t rc;
- njs_str_t key;
+ ngx_str_t key;
ngx_shm_zone_t *shm_zone;
shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id,
@@ -699,7 +729,7 @@ njs_js_ext_shared_dict_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
return NJS_ERROR;
}
- if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
+ if (ngx_js_ngx_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
return NJS_ERROR;
}
@@ -717,7 +747,7 @@ static njs_int_t
njs_js_ext_shared_dict_has(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused, njs_value_t *retval)
{
- njs_str_t key;
+ ngx_str_t key;
ngx_msec_t now;
ngx_time_t *tp;
ngx_js_dict_t *dict;
@@ -731,7 +761,7 @@ njs_js_ext_shared_dict_has(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
return NJS_ERROR;
}
- if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
+ if (ngx_js_ngx_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
return NJS_ERROR;
}
@@ -851,7 +881,7 @@ njs_js_ext_shared_dict_incr(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
{
double value;
ngx_int_t rc;
- njs_str_t key;
+ ngx_str_t key;
ngx_msec_t timeout;
njs_value_t *delta, *init, *timeo;
ngx_js_dict_t *dict;
@@ -872,7 +902,7 @@ njs_js_ext_shared_dict_incr(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
return NJS_ERROR;
}
- if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
+ if (ngx_js_ngx_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
return NJS_ERROR;
}
@@ -1058,7 +1088,7 @@ njs_js_ext_shared_dict_pop(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused, njs_value_t *retval)
{
ngx_int_t rc;
- njs_str_t key;
+ ngx_str_t key;
ngx_shm_zone_t *shm_zone;
shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id,
@@ -1068,7 +1098,7 @@ njs_js_ext_shared_dict_pop(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
return NJS_ERROR;
}
- if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
+ if (ngx_js_ngx_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
return NJS_ERROR;
}
@@ -1086,7 +1116,7 @@ static njs_int_t
njs_js_ext_shared_dict_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t flags, njs_value_t *retval)
{
- njs_str_t key;
+ ngx_str_t key;
ngx_int_t rc;
ngx_msec_t timeout;
njs_value_t *value, *timeo;
@@ -1100,7 +1130,7 @@ njs_js_ext_shared_dict_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
return NJS_ERROR;
}
- if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
+ if (ngx_js_ngx_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) {
return NJS_ERROR;
}
@@ -1218,7 +1248,7 @@ njs_js_ext_shared_dict_type(njs_vm_t *vm, njs_object_prop_t *prop,
uint32_t unused, njs_value_t *value, njs_value_t *setval,
njs_value_t *retval)
{
- njs_str_t type;
+ ngx_str_t type;
ngx_js_dict_t *dict;
ngx_shm_zone_t *shm_zone;
@@ -1229,36 +1259,41 @@ njs_js_ext_shared_dict_type(njs_vm_t *vm, njs_object_prop_t *prop,
}
dict = shm_zone->data;
-
switch (dict->type) {
case NGX_JS_DICT_TYPE_STRING:
- type = njs_str_value("string");
+ ngx_str_set(&type, "string");
break;
default:
- type = njs_str_value("number");
+ ngx_str_set(&type, "number");
break;
}
- return njs_vm_value_string_create(vm, retval, type.start, type.length);
+ return njs_vm_value_string_create(vm, retval, type.data, type.len);
+}
+
+
+static njs_int_t
+ngx_js_dict_shared_error_name(njs_vm_t *vm, njs_object_prop_t *prop,
+ uint32_t unused, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval)
+{
+ return njs_vm_value_string_create(vm, retval,
+ (u_char *) "SharedMemoryError", 17);
}
static ngx_js_dict_node_t *
-ngx_js_dict_lookup(ngx_js_dict_t *dict, njs_str_t *key)
+ngx_js_dict_lookup(ngx_js_dict_t *dict, ngx_str_t *key)
{
uint32_t hash;
- ngx_str_t k;
ngx_rbtree_t *rbtree;
rbtree = &dict->sh->rbtree;
- hash = ngx_crc32_long(key->start, key->length);
-
- k.data = key->start;
- k.len = key->length;
+ hash = ngx_crc32_long(key->data, key->len);
- return (ngx_js_dict_node_t *) ngx_str_rbtree_lookup(rbtree, &k, hash);
+ return (ngx_js_dict_node_t *) ngx_str_rbtree_lookup(rbtree, key, hash);
}
@@ -1286,7 +1321,7 @@ ngx_js_dict_node_free(ngx_js_dict_t *dict, ngx_js_dict_node_t *node)
shpool = dict->shpool;
if (dict->type == NGX_JS_DICT_TYPE_STRING) {
- ngx_slab_free_locked(shpool, node->u.value.data);
+ ngx_slab_free_locked(shpool, node->value.str.data);
}
ngx_slab_free_locked(shpool, node);
@@ -1294,7 +1329,7 @@ ngx_js_dict_node_free(ngx_js_dict_t *dict, ngx_js_dict_node_t *node)
static ngx_int_t
-ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
+ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_str_t *key,
njs_value_t *value, ngx_msec_t timeout, unsigned flags)
{
ngx_msec_t now;
@@ -1331,8 +1366,14 @@ ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
}
}
+ dict->sh->dirty = 1;
+
ngx_rwlock_unlock(&dict->sh->rwlock);
+ if (dict->state_file.data && !dict->save_event.timer_set) {
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
return NGX_OK;
memory_error:
@@ -1346,20 +1387,19 @@ memory_error:
static ngx_int_t
-ngx_js_dict_add(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
- njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now)
+ngx_js_dict_add_value(ngx_js_dict_t *dict, ngx_str_t *key,
+ ngx_js_dict_value_t *value, ngx_msec_t timeout, ngx_msec_t now)
{
size_t n;
uint32_t hash;
- njs_str_t string;
ngx_js_dict_node_t *node;
if (dict->timeout) {
ngx_js_dict_expire(dict, now);
}
- n = sizeof(ngx_js_dict_node_t) + key->length;
- hash = ngx_crc32_long(key->start, key->length);
+ n = sizeof(ngx_js_dict_node_t) + key->len;
+ hash = ngx_crc32_long(key->data, key->len);
node = ngx_js_dict_alloc(dict, n);
if (node == NULL) {
@@ -1369,24 +1409,23 @@ ngx_js_dict_add(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
node->sn.str.data = (u_char *) node + sizeof(ngx_js_dict_node_t);
if (dict->type == NGX_JS_DICT_TYPE_STRING) {
- njs_value_string_get(vm, value, &string);
- node->u.value.data = ngx_js_dict_alloc(dict, string.length);
- if (node->u.value.data == NULL) {
+ node->value.str.data = ngx_js_dict_alloc(dict, value->str.len);
+ if (node->value.str.data == NULL) {
ngx_slab_free_locked(dict->shpool, node);
return NGX_ERROR;
}
- ngx_memcpy(node->u.value.data, string.start, string.length);
- node->u.value.len = string.length;
+ ngx_memcpy(node->value.str.data, value->str.data, value->str.len);
+ node->value.str.len = value->str.len;
} else {
- node->u.number = njs_value_number(value);
+ node->value.number = value->number;
}
node->sn.node.key = hash;
- ngx_memcpy(node->sn.str.data, key->start, key->length);
- node->sn.str.len = key->length;
+ ngx_memcpy(node->sn.str.data, key->data, key->len);
+ node->sn.str.len = key->len;
ngx_rbtree_insert(&dict->sh->rbtree, &node->sn.node);
@@ -1400,6 +1439,29 @@ ngx_js_dict_add(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
static ngx_int_t
+ngx_js_dict_add(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_str_t *key,
+ njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now)
+{
+ njs_str_t string;
+ ngx_js_dict_value_t entry;
+
+ if (dict->type == NGX_JS_DICT_TYPE_STRING) {
+ njs_value_string_get(vm, value, &string);
+
+ entry.str.data = string.start;
+ entry.str.len = string.length;
+
+ } else {
+ /* GCC complains about uninitialized entry.str.data. */
+ entry.str.data = NULL;
+ entry.number = njs_value_number(value);
+ }
+
+ return ngx_js_dict_add_value(dict, key, &entry, timeout, now);
+}
+
+
+static ngx_int_t
ngx_js_dict_update(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_js_dict_node_t *node,
njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now)
{
@@ -1414,14 +1476,14 @@ ngx_js_dict_update(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_js_dict_node_t *node,
return NGX_ERROR;
}
- ngx_slab_free_locked(dict->shpool, node->u.value.data);
+ ngx_slab_free_locked(dict->shpool, node->value.str.data);
ngx_memcpy(p, string.start, string.length);
- node->u.value.data = p;
- node->u.value.len = string.length;
+ node->value.str.data = p;
+ node->value.str.len = string.length;
} else {
- node->u.number = njs_value_number(value);
+ node->value.number = njs_value_number(value);
}
if (dict->timeout) {
@@ -1435,7 +1497,7 @@ ngx_js_dict_update(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_js_dict_node_t *node,
static ngx_int_t
-ngx_js_dict_delete(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
+ngx_js_dict_delete(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_str_t *key,
njs_value_t *retval)
{
ngx_int_t rc;
@@ -1475,14 +1537,20 @@ ngx_js_dict_delete(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
ngx_js_dict_node_free(dict, node);
+ dict->sh->dirty = 1;
+
ngx_rwlock_unlock(&dict->sh->rwlock);
+ if (dict->state_file.data && !dict->save_event.timer_set) {
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
return rc;
}
static ngx_int_t
-ngx_js_dict_incr(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
+ngx_js_dict_incr(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_str_t *key,
njs_value_t *delta, njs_value_t *init, double *value, ngx_msec_t timeout)
{
ngx_msec_t now;
@@ -1507,8 +1575,8 @@ ngx_js_dict_incr(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
*value = njs_value_number(init);
} else {
- node->u.number += njs_value_number(delta);
- *value = node->u.number;
+ node->value.number += njs_value_number(delta);
+ *value = node->value.number;
if (dict->timeout) {
ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire);
@@ -1517,14 +1585,20 @@ ngx_js_dict_incr(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
}
}
+ dict->sh->dirty = 1;
+
ngx_rwlock_unlock(&dict->sh->rwlock);
+ if (dict->state_file.data && !dict->save_event.timer_set) {
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
return NGX_OK;
}
static ngx_int_t
-ngx_js_dict_get(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
+ngx_js_dict_get(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_str_t *key,
njs_value_t *retval)
{
ngx_int_t rc;
@@ -1573,14 +1647,14 @@ ngx_js_dict_copy_value_locked(njs_vm_t *vm, ngx_js_dict_t *dict,
type = dict->type;
if (type == NGX_JS_DICT_TYPE_STRING) {
- ret = njs_vm_value_string_create(vm, retval, node->u.value.data,
- node->u.value.len);
+ ret = njs_vm_value_string_create(vm, retval, node->value.str.data,
+ node->value.str.len);
if (ret != NJS_OK) {
return NGX_ERROR;
}
} else {
- njs_value_number_set(retval, node->u.number);
+ njs_value_number_set(retval, node->value.number);
}
return NGX_OK;
@@ -1657,13 +1731,1013 @@ ngx_js_dict_evict(ngx_js_dict_t *dict, ngx_int_t count)
}
-static njs_int_t
-ngx_js_dict_shared_error_name(njs_vm_t *vm, njs_object_prop_t *prop,
- uint32_t unused, njs_value_t *value, njs_value_t *setval,
- njs_value_t *retval)
+static ngx_int_t
+ngx_js_render_string(njs_chb_t *chain, ngx_str_t *str)
{
- return njs_vm_value_string_create(vm, retval,
- (u_char *) "SharedMemoryError", 17);
+ size_t size;
+ u_char c, *dst, *dst_end;
+ const u_char *p, *end;
+
+ static char hex2char[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ p = str->data;
+ end = p + str->len;
+ size = str->len + 2;
+
+ dst = njs_chb_reserve(chain, size);
+ if (dst == NULL) {
+ return NGX_ERROR;
+ }
+
+ dst_end = dst + size;
+
+ *dst++ = '\"';
+ njs_chb_written(chain, 1);
+
+ while (p < end) {
+ if (dst_end <= dst + sizeof("\\uXXXX")) {
+ size = ngx_max(end - p + 1, 6);
+ dst = njs_chb_reserve(chain, size);
+ if (dst == NULL) {
+ return NGX_ERROR;
+ }
+
+ dst_end = dst + size;
+ }
+
+ if (*p < ' ' || *p == '\\' || *p == '\"') {
+ c = (u_char) *p++;
+ *dst++ = '\\';
+ njs_chb_written(chain, 2);
+
+ switch (c) {
+ case '\\':
+ *dst++ = '\\';
+ break;
+ case '"':
+ *dst++ = '\"';
+ break;
+ case '\r':
+ *dst++ = 'r';
+ break;
+ case '\n':
+ *dst++ = 'n';
+ break;
+ case '\t':
+ *dst++ = 't';
+ break;
+ case '\b':
+ *dst++ = 'b';
+ break;
+ case '\f':
+ *dst++ = 'f';
+ break;
+ default:
+ *dst++ = 'u';
+ *dst++ = '0';
+ *dst++ = '0';
+ *dst++ = hex2char[(c & 0xf0) >> 4];
+ *dst++ = hex2char[c & 0x0f];
+ njs_chb_written(chain, 4);
+ }
+
+ continue;
+ }
+
+ dst = njs_utf8_copy(dst, &p, end);
+
+ njs_chb_written(chain, dst - chain->last->pos);
+ }
+
+ njs_chb_append_literal(chain, "\"");
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_js_dict_render_json(ngx_js_dict_t *dict, njs_chb_t *chain)
+{
+ u_char *p, *dst;
+ size_t len;
+ ngx_msec_t now;
+ ngx_time_t *tp;
+ ngx_rbtree_t *rbtree;
+ ngx_rbtree_node_t *rn, *next;
+ ngx_js_dict_node_t *node;
+
+ tp = ngx_timeofday();
+ now = tp->sec * 1000 + tp->msec;
+
+ rbtree = &dict->sh->rbtree;
+
+ njs_chb_append_literal(chain,"{");
+
+ if (rbtree->root == rbtree->sentinel) {
+ njs_chb_append_literal(chain, "}");
+ return NGX_OK;
+ }
+
+ for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel);
+ rn != NULL;
+ rn = next)
+ {
+ node = (ngx_js_dict_node_t *) rn;
+
+ next = ngx_rbtree_next(rbtree, rn);
+
+ if (dict->timeout && now >= node->expire.key) {
+ continue;
+ }
+
+ if (ngx_js_render_string(chain, &node->sn.str) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ njs_chb_append_literal(chain,":{");
+
+ if (dict->type == NGX_JS_DICT_TYPE_STRING) {
+ njs_chb_append_literal(chain,"\"value\":");
+
+ if (ngx_js_render_string(chain, &node->value.str) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ len = sizeof("\"value\":.") + 18 + 6;
+ dst = njs_chb_reserve(chain, len);
+ if (dst == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = njs_sprintf(dst, dst + len, "\"value\":%.6f",
+ node->value.number);
+ njs_chb_written(chain, p - dst);
+ }
+
+ if (dict->timeout) {
+ len = sizeof(",\"expire\":1000000000");
+ dst = njs_chb_reserve(chain, len);
+ if (dst == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = njs_sprintf(dst, dst + len, ",\"expire\":%ui",
+ node->expire.key);
+ njs_chb_written(chain, p - dst);
+ }
+
+ njs_chb_append_literal(chain, "}");
+
+ if (next != NULL) {
+ njs_chb_append_literal(chain, ",");
+ }
+ }
+
+ njs_chb_append_literal(chain, "}");
+
+ return NGX_OK;
+}
+
+
+static u_char *
+ngx_js_skip_space(u_char *start, u_char *end)
+{
+ u_char *p;
+
+ for (p = start; p != end; p++) {
+
+ switch (*p) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ continue;
+ }
+
+ break;
+ }
+
+ return p;
+}
+
+
+static uint32_t
+ngx_js_unicode(const u_char *p)
+{
+ u_char c;
+ uint32_t utf;
+ njs_uint_t i;
+
+ utf = 0;
+
+ for (i = 0; i < 4; i++) {
+ utf <<= 4;
+ c = p[i] | 0x20;
+ c -= '0';
+ if (c > 9) {
+ c += '0' - 'a' + 10;
+ }
+
+ utf |= c;
+ }
+
+ return utf;
+}
+
+
+static u_char *
+ngx_js_dict_parse_string(ngx_pool_t *pool, u_char *p, u_char *end,
+ ngx_str_t *str, const char **err, u_char **at)
+{
+ u_char ch, *s, *dst, *start, *last;
+ size_t size, surplus;
+ uint32_t utf, utf_low;
+
+ enum {
+ sw_usual = 0,
+ sw_escape,
+ sw_encoded1,
+ sw_encoded2,
+ sw_encoded3,
+ sw_encoded4,
+ } state;
+
+ if (*p != '"') {
+ *err = "unexpected character, expected '\"'";
+ goto error;
+ }
+
+ start = p + 1;
+
+ dst = NULL;
+ state = 0;
+ surplus = 0;
+
+ for (p = start; p < end; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ case sw_usual:
+
+ if (ch == '"') {
+ break;
+ }
+
+ if (ch == '\\') {
+ state = sw_escape;
+ continue;
+ }
+
+ if (ch >= ' ') {
+ continue;
+ }
+
+ *err = "Invalid source char";
+ goto error;
+
+ case sw_escape:
+
+ switch (ch) {
+ case '"':
+ case '\\':
+ case '/':
+ case 'n':
+ case 'r':
+ case 't':
+ case 'b':
+ case 'f':
+ surplus++;
+ state = sw_usual;
+ continue;
+
+ case 'u':
+ /*
+ * Basic unicode 6 bytes "\uXXXX" in JSON
+ * and up to 3 bytes in UTF-8.
+ *
+ * Surrogate pair: 12 bytes "\uXXXX\uXXXX" in JSON
+ * and 3 or 4 bytes in UTF-8.
+ */
+ surplus += 3;
+ state = sw_encoded1;
+ continue;
+ }
+
+ *err = "Invalid escape char";
+ goto error;
+
+ case sw_encoded1:
+ case sw_encoded2:
+ case sw_encoded3:
+ case sw_encoded4:
+
+ if ((ch >= '0' && ch <= '9')
+ || (ch >= 'A' && ch <= 'F')
+ || (ch >= 'a' && ch <= 'f'))
+ {
+ state = (state == sw_encoded4) ? sw_usual : state + 1;
+ continue;
+ }
+
+ *err = "Invalid Unicode escape sequence";
+ goto error;
+ }
+
+ break;
+ }
+
+ if (p == end) {
+ *err = "unexpected end of input";
+ goto error;
+ }
+
+ /* Points to the ending quote mark. */
+ last = p;
+
+ size = last - start - surplus;
+
+ if (surplus != 0) {
+ p = start;
+
+ dst = ngx_palloc(pool, size);
+ if (dst == NULL) {
+ *err = "out of memory";
+ goto error;
+ }
+
+ s = dst;
+
+ do {
+ ch = *p++;
+
+ if (ch != '\\') {
+ *s++ = ch;
+ continue;
+ }
+
+ ch = *p++;
+
+ switch (ch) {
+ case '"':
+ case '\\':
+ case '/':
+ *s++ = ch;
+ continue;
+
+ case 'n':
+ *s++ = '\n';
+ continue;
+
+ case 'r':
+ *s++ = '\r';
+ continue;
+
+ case 't':
+ *s++ = '\t';
+ continue;
+
+ case 'b':
+ *s++ = '\b';
+ continue;
+
+ case 'f':
+ *s++ = '\f';
+ continue;
+ }
+
+ /* "\uXXXX": Unicode escape sequence. */
+
+ utf = ngx_js_unicode(p);
+ p += 4;
+
+ if (njs_surrogate_any(utf)) {
+
+ if (utf > 0xdbff || p[0] != '\\' || p[1] != 'u') {
+ s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT);
+ continue;
+ }
+
+ p += 2;
+
+ utf_low = ngx_js_unicode(p);
+ p += 4;
+
+ if (njs_fast_path(njs_surrogate_trailing(utf_low))) {
+ utf = njs_surrogate_pair(utf, utf_low);
+
+ } else if (njs_surrogate_leading(utf_low)) {
+ utf = NJS_UNICODE_REPLACEMENT;
+ s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT);
+
+ } else {
+ utf = utf_low;
+ s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT);
+ }
+ }
+
+ s = njs_utf8_encode(s, utf);
+
+ } while (p != last);
+
+ size = s - dst;
+ start = dst;
+ }
+
+ str->data = start;
+ str->len = size;
+
+ return p + 1;
+
+error:
+
+ *at = p;
+
+ return NULL;
+}
+
+
+static u_char *
+ngx_js_dict_parse_entry(ngx_js_dict_t *dict, ngx_pool_t *pool,
+ ngx_js_dict_entry_t *entry, u_char *buf, u_char *end, const char **err,
+ u_char **at)
+{
+ int see_value;
+ u_char *p, *pp;
+ double number;
+ ngx_str_t key, str;
+
+ p = buf;
+
+ if (*p++ != '{') {
+ *err = "unexpected character, expected '{'";
+ goto error;
+ }
+
+ see_value = 0;
+
+ while (1) {
+ p = ngx_js_skip_space(p, end);
+ if (p == end) {
+ *err = "unexpected end of json";
+ goto error;
+ }
+
+ if (*p == '}') {
+ break;
+ }
+
+ p = ngx_js_dict_parse_string(pool, p, end, &key, err, at);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ p = ngx_js_skip_space(p, end);
+ if (p == end) {
+ *err = "unexpected end of json";
+ goto error;
+ }
+
+ if (*p++ != ':') {
+ *err = "unexpected character, expected ':'";
+ goto error;
+ }
+
+ p = ngx_js_skip_space(p, end);
+ if (p == end) {
+ *err = "unexpected end of json";
+ goto error;
+ }
+
+ if (*p == '\"') {
+ p = ngx_js_dict_parse_string(pool, p, end, &str, err, at);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ if (key.len == 5 && ngx_strncmp(key.data, "value", 5) == 0) {
+ if (dict->type != NGX_JS_DICT_TYPE_STRING) {
+ *err = "expected string value";
+ goto error;
+ }
+
+ entry->value.str = str;
+ see_value = 1;
+ }
+
+ } else {
+ pp = p;
+ number = strtod((char *) p, (char **) &p);
+ if (pp == p) {
+ *err = "invalid number value";
+ goto error;
+ }
+
+ if (key.len == 5 && ngx_strncmp(key.data, "value", 5) == 0) {
+ if (dict->type == NGX_JS_DICT_TYPE_STRING) {
+ *err = "expected number value";
+ goto error;
+ }
+
+ entry->value.number = number;
+ see_value = 1;
+
+ } else if (key.len == 6
+ && ngx_strncmp(key.data, "expire", 6) == 0)
+ {
+ entry->expire = number;
+ }
+ }
+
+ p = ngx_js_skip_space(p, end);
+ if (p == end) {
+ *err = "unexpected end of json";
+ goto error;
+ }
+
+ if (*p == ',') {
+ p++;
+ }
+ }
+
+ if (!see_value) {
+ *err = "missing value";
+ goto error;
+ }
+
+ return p + 1;
+
+error:
+
+ *at = p;
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_js_dict_parse_state(ngx_js_dict_t *dict, ngx_pool_t *pool,
+ ngx_array_t *entries, u_char *buf, u_char *end)
+{
+ u_char *p, *at;
+ const char *err;
+ ngx_js_dict_entry_t *e;
+
+ /* GCC complains about uninitialized err, at. */
+
+ err = "";
+ at = NULL;
+
+ p = ngx_js_skip_space(buf, end);
+ if (p == end) {
+ err = "empty json";
+ goto error;
+ }
+
+ if (*p++ != '{') {
+ err = "json must start with '{'";
+ goto error;
+ }
+
+ while (1) {
+ p = ngx_js_skip_space(p, end);
+ if (p == end) {
+ err = "unexpected end of json";
+ goto error;
+ }
+
+ if (*p == '}') {
+ p++;
+ break;
+ }
+
+ e = ngx_array_push(entries);
+ if (e == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_js_dict_parse_string(pool, p, end, &e->key, &err, &at);
+ if (p == NULL) {
+ p = at;
+ goto error;
+ }
+
+ p = ngx_js_skip_space(p, end);
+ if (p == end) {
+ err = "unexpected end of json";
+ goto error;
+ }
+
+ if (*p++ != ':') {
+ err = "unexpected character, expected ':'";
+ goto error;
+ }
+
+ p = ngx_js_skip_space(p, end);
+ if (p == end) {
+ err = "unexpected end of json";
+ goto error;
+ }
+
+ p = ngx_js_dict_parse_entry(dict, pool, e, p, end, &err, &at);
+ if (p == NULL) {
+ p = at;
+ goto error;
+ }
+
+ p = ngx_js_skip_space(p, end);
+ if (p == end) {
+ err = "unexpected end of json";
+ goto error;
+ }
+
+ if (*p == ',') {
+ p++;
+ }
+ }
+
+ p = ngx_js_skip_space(p, end);
+
+ if (p != end) {
+ err = "unexpected character, expected end of json";
+ goto error;
+ }
+
+ return NGX_OK;
+
+error:
+
+ ngx_log_error(NGX_LOG_EMERG, dict->shm_zone->shm.log, 0,
+ "invalid format while loading js_shared_dict_zone \"%V\""
+ " from state file \"%s\": %s at offset %z",
+ &dict->shm_zone->shm.name, dict->state_file.data, err,
+ p - buf);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_js_dict_save(ngx_js_dict_t *dict)
+{
+
+ u_char *name;
+ ngx_int_t rc;
+ ngx_log_t *log;
+ njs_chb_t chain;
+ ngx_file_t file;
+ ngx_pool_t *pool;
+ ngx_chain_t *out, *cl, **ll;
+ njs_chb_node_t *node;
+ ngx_ext_rename_file_t ext;
+
+ log = dict->shm_zone->shm.log;
+
+ pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, log);
+ if (pool == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_rwlock_wlock(&dict->sh->rwlock);
+
+ if (!dict->sh->dirty) {
+ ngx_rwlock_unlock(&dict->sh->rwlock);
+ ngx_destroy_pool(pool);
+ return NGX_OK;
+ }
+
+ if (dict->sh->writing) {
+ ngx_rwlock_unlock(&dict->sh->rwlock);
+ ngx_destroy_pool(pool);
+ return NGX_AGAIN;
+ }
+
+ ngx_rwlock_downgrade(&dict->sh->rwlock);
+
+ NGX_CHB_CTX_INIT(&chain, pool);
+
+ rc = ngx_js_dict_render_json(dict, &chain);
+
+ if (rc != NGX_OK) {
+ ngx_rwlock_unlock(&dict->sh->rwlock);
+ ngx_destroy_pool(pool);
+ return rc;
+ }
+
+ dict->sh->writing = 1;
+ dict->sh->dirty = 0;
+
+ ngx_rwlock_unlock(&dict->sh->rwlock);
+
+ name = dict->state_temp_file.data;
+
+ out = NULL;
+ ll = &out;
+
+ for (node = chain.nodes; node != NULL; node = node->next) {
+ cl = ngx_alloc_chain_link(pool);
+ if (cl == NULL) {
+ goto error;
+ }
+
+ cl->buf = ngx_calloc_buf(pool);
+ if (cl->buf == NULL) {
+ goto error;
+ }
+
+ cl->buf->pos = node->start;
+ cl->buf->last = node->pos;
+ cl->buf->memory = 1;
+ cl->buf->last_buf = (node->next == NULL) ? 1 : 0;
+
+ *ll = cl;
+ ll = &cl->next;
+ }
+
+ *ll = NULL;
+
+ ngx_memzero(&file, sizeof(ngx_file_t));
+ file.name = dict->state_temp_file;
+ file.log = log;
+
+ file.fd = ngx_open_file(file.name.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE,
+ NGX_FILE_DEFAULT_ACCESS);
+
+ if (file.fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", name);
+ goto error;
+ }
+
+ rc = ngx_write_chain_to_file(&file, out, 0, pool);
+
+ if (rc == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_write_fd_n " \"%s\" failed", file.name.data);
+ goto error;
+ }
+
+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file.name.data);
+ }
+
+ file.fd = NGX_INVALID_FILE;
+
+ ext.access = 0;
+ ext.time = -1;
+ ext.create_path = 0;
+ ext.delete_file = 0;
+ ext.log = log;
+
+ if (ngx_ext_rename_file(&dict->state_temp_file, &dict->state_file, &ext)
+ != NGX_OK)
+ {
+ goto error;
+ }
+
+ /* no lock required */
+ dict->sh->writing = 0;
+ ngx_destroy_pool(pool);
+
+ return NGX_OK;
+
+error:
+
+ if (file.fd != NGX_INVALID_FILE
+ && ngx_close_file(file.fd) == NGX_FILE_ERROR)
+ {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name);
+ }
+
+ ngx_destroy_pool(pool);
+
+ /* no lock required */
+ dict->sh->writing = 0;
+ dict->sh->dirty = 1;
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_js_dict_load(ngx_js_dict_t *dict)
+{
+ off_t size;
+ u_char *name, *buf;
+ size_t len;
+ ssize_t n;
+ ngx_fd_t fd;
+ ngx_err_t err;
+ ngx_int_t rc;
+ ngx_log_t *log;
+ ngx_uint_t i;
+ ngx_msec_t now, expire;
+ ngx_time_t *tp;
+ ngx_pool_t *pool;
+ ngx_array_t data;
+ ngx_file_info_t fi;
+ ngx_js_dict_entry_t *entries;
+
+ if (dict->state_file.data == NULL) {
+ return NGX_OK;
+ }
+
+ log = dict->shm_zone->shm.log;
+
+ name = dict->state_file.data;
+
+ fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+ if (fd == NGX_INVALID_FILE) {
+ err = ngx_errno;
+
+ if (err == NGX_ENOENT || err == NGX_ENOPATH) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, log, err,
+ ngx_open_file_n " \"%s\" failed", name);
+ return NGX_ERROR;
+ }
+
+ if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", name);
+ pool = NULL;
+ goto failed;
+ }
+
+ size = ngx_file_size(&fi);
+
+ if (size == 0) {
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name);
+ }
+
+ return NGX_OK;
+ }
+
+ pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, log);
+ if (pool == NULL) {
+ goto failed;
+ }
+
+ len = size;
+
+ buf = ngx_pnalloc(pool, len);
+ if (buf == NULL) {
+ goto failed;
+ }
+
+ n = ngx_read_fd(fd, buf, len);
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ ngx_read_fd_n " \"%s\" failed", name);
+ goto failed;
+ }
+
+ if ((size_t) n != len) {
+ ngx_log_error(NGX_LOG_EMERG, log, 0,
+ ngx_read_fd_n " has read only %z of %uz from %s",
+ n, len, name);
+ goto failed;
+ }
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name);
+ fd = NGX_INVALID_FILE;
+ goto failed;
+ }
+
+ fd = NGX_INVALID_FILE;
+
+ if (ngx_array_init(&data, pool, 4, sizeof(ngx_js_dict_entry_t))
+ != NGX_OK)
+ {
+ goto failed;
+ }
+
+ rc = ngx_js_dict_parse_state(dict, pool, &data, buf, buf + len);
+
+ if (rc != NGX_OK) {
+ goto failed;
+ }
+
+ entries = data.elts;
+
+ tp = ngx_timeofday();
+ now = tp->sec * 1000 + tp->msec;
+
+ for (i = 0; i < data.nelts; i++) {
+
+ if (dict->timeout) {
+ expire = entries[i].expire;
+
+ if (expire && now >= expire) {
+ dict->sh->dirty = 1;
+ continue;
+ }
+
+ if (expire == 0) {
+ /* treat state without expire as new */
+ expire = now + dict->timeout;
+ dict->sh->dirty = 1;
+ }
+
+ } else {
+ expire = 0;
+ }
+
+ if (ngx_js_dict_lookup(dict, &entries[i].key) != NULL) {
+ goto failed;
+ }
+
+ if (ngx_js_dict_add_value(dict, &entries[i].key, &entries[i].value,
+ expire, 1)
+ != NGX_OK)
+ {
+ goto failed;
+ }
+ }
+
+ ngx_destroy_pool(pool);
+
+ return NGX_OK;
+
+failed:
+
+ if (fd != NGX_INVALID_FILE && ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name);
+ }
+
+ if (pool) {
+ ngx_destroy_pool(pool);
+ }
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_js_dict_save_handler(ngx_event_t *ev)
+{
+ ngx_int_t rc;
+ ngx_js_dict_t *dict;
+
+ dict = ev->data;
+
+ rc = ngx_js_dict_save(dict);
+
+ if (rc == NGX_OK) {
+ return;
+ }
+
+ if (rc == NGX_ERROR && (ngx_terminate || ngx_exiting)) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "failed to save the state of shared dict zone \"%V\"",
+ &dict->shm_zone->shm.name);
+ return;
+ }
+
+ /* NGX_ERROR, NGX_AGAIN */
+
+ ngx_add_timer(ev, 1000);
+}
+
+
+ngx_int_t
+ngx_js_dict_init_worker(ngx_js_main_conf_t *jmcf)
+{
+ ngx_js_dict_t *dict;
+
+ if ((ngx_process != NGX_PROCESS_WORKER || ngx_worker != 0)
+ && ngx_process != NGX_PROCESS_SINGLE)
+ {
+ return NGX_OK;
+ }
+
+ if (jmcf->dicts == NULL) {
+ return NGX_OK;
+ }
+
+ for (dict = jmcf->dicts; dict != NULL; dict = dict->next) {
+
+ if (!dict->sh->dirty || !dict->state_file.data) {
+ continue;
+ }
+
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
+ return NGX_OK;
}
@@ -1733,6 +2807,10 @@ ngx_js_dict_init_zone(ngx_shm_zone_t *shm_zone, void *data)
ngx_sprintf(dict->shpool->log_ctx, " in js shared zone \"%V\"%Z",
&shm_zone->shm.name);
+ if (ngx_js_dict_load(dict) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
return NGX_OK;
}
@@ -1745,7 +2823,7 @@ ngx_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf,
u_char *p;
ssize_t size;
- ngx_str_t *value, name, s;
+ ngx_str_t *value, name, file, s;
ngx_flag_t evict;
ngx_msec_t timeout;
ngx_uint_t i, type;
@@ -1756,6 +2834,7 @@ ngx_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf,
evict = 0;
timeout = 0;
name.len = 0;
+ ngx_str_null(&file);
type = NGX_JS_DICT_TYPE_STRING;
value = cf->args->elts;
@@ -1807,6 +2886,17 @@ ngx_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf,
continue;
}
+ if (ngx_strncmp(value[i].data, "state=", 6) == 0) {
+ file.data = value[i].data + 6;
+ file.len = value[i].len - 6;
+
+ if (ngx_conf_full_name(cf->cycle, &file, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
if (ngx_strncmp(value[i].data, "timeout=", 8) == 0) {
s.data = value[i].data + 8;
@@ -1880,6 +2970,23 @@ ngx_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf,
dict->timeout = timeout;
dict->type = type;
+ dict->save_event.handler = ngx_js_dict_save_handler;
+ dict->save_event.data = dict;
+ dict->save_event.log = &cf->cycle->new_log;
+ dict->fd = -1;
+
+ if (file.data) {
+ dict->state_file = file;
+
+ p = ngx_pnalloc(cf->pool, file.len + sizeof(".tmp"));
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ dict->state_temp_file.data = p;
+ dict->state_temp_file.len = ngx_sprintf(p, "%V.tmp%Z", &file) - p - 1;
+ }
+
return NGX_CONF_OK;
}
@@ -2079,8 +3186,14 @@ ngx_qjs_ext_shared_dict_clear(JSContext *cx, JSValueConst this_val,
done:
+ dict->sh->dirty = 1;
+
ngx_rwlock_unlock(&dict->sh->rwlock);
+ if (dict->state_file.data && !dict->save_event.timer_set) {
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
return JS_UNDEFINED;
}
@@ -2630,13 +3743,13 @@ ngx_qjs_dict_copy_value_locked(JSContext *cx, ngx_js_dict_t *dict,
ngx_js_dict_node_t *node)
{
if (dict->type == NGX_JS_DICT_TYPE_STRING) {
- return JS_NewStringLen(cx, (const char *) node->u.value.data,
- node->u.value.len);
+ return JS_NewStringLen(cx, (const char *) node->value.str.data,
+ node->value.str.len);
}
/* NGX_JS_DICT_TYPE_NUMBER */
- return JS_NewFloat64(cx, node->u.number);
+ return JS_NewFloat64(cx, node->value.number);
}
@@ -2658,64 +3771,31 @@ static ngx_int_t
ngx_qjs_dict_add(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key,
JSValue value, ngx_msec_t timeout, ngx_msec_t now)
{
- size_t n;
- uint32_t hash;
- ngx_str_t string;
- ngx_js_dict_node_t *node;
-
- if (dict->timeout) {
- ngx_js_dict_expire(dict, now);
- }
-
- n = sizeof(ngx_js_dict_node_t) + key->len;
- hash = ngx_crc32_long(key->data, key->len);
-
- node = ngx_js_dict_alloc(dict, n);
- if (node == NULL) {
- return NGX_ERROR;
- }
-
- node->sn.str.data = (u_char *) node + sizeof(ngx_js_dict_node_t);
+ ngx_int_t rc;
+ ngx_js_dict_value_t entry;
if (dict->type == NGX_JS_DICT_TYPE_STRING) {
- string.data = (u_char *) JS_ToCStringLen(cx, &string.len, value);
- if (string.data == NULL) {
- ngx_slab_free_locked(dict->shpool, node);
+ entry.str.data = (u_char *) JS_ToCStringLen(cx, &entry.str.len, value);
+ if (entry.str.data == NULL) {
return NGX_ERROR;
}
- node->u.value.data = ngx_js_dict_alloc(dict, string.len);
- if (node->u.value.data == NULL) {
- ngx_slab_free_locked(dict->shpool, node);
- JS_FreeCString(cx, (char *) string.data);
- return NGX_ERROR;
- }
-
- ngx_memcpy(node->u.value.data, string.data, string.len);
- node->u.value.len = string.len;
-
- JS_FreeCString(cx, (char *) string.data);
-
} else {
- if (JS_ToFloat64(cx, &node->u.number, value) < 0) {
- ngx_slab_free_locked(dict->shpool, node);
+ /* GCC complains about uninitialized entry.str.data. */
+ entry.str.data = NULL;
+
+ if (JS_ToFloat64(cx, &entry.number, value) < 0) {
return NGX_ERROR;
}
}
- node->sn.node.key = hash;
-
- ngx_memcpy(node->sn.str.data, key->data, key->len);
- node->sn.str.len = key->len;
+ rc = ngx_js_dict_add_value(dict, key, &entry, timeout, now);
- ngx_rbtree_insert(&dict->sh->rbtree, &node->sn.node);
-
- if (dict->timeout) {
- node->expire.key = now + timeout;
- ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire);
+ if (dict->type == NGX_JS_DICT_TYPE_STRING) {
+ JS_FreeCString(cx, (char *) entry.str.data);
}
- return NGX_OK;
+ return rc;
}
@@ -2760,8 +3840,14 @@ ngx_qjs_dict_delete(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key,
ngx_js_dict_node_free(dict, node);
+ dict->sh->dirty = 1;
+
ngx_rwlock_unlock(&dict->sh->rwlock);
+ if (dict->state_file.data && !dict->save_event.timer_set) {
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
return ret;
}
@@ -2829,8 +3915,8 @@ ngx_qjs_dict_incr(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key,
}
} else {
- node->u.number += delta;
- value = JS_NewFloat64(cx, node->u.number);
+ node->value.number += delta;
+ value = JS_NewFloat64(cx, node->value.number);
if (dict->timeout) {
ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire);
@@ -2839,8 +3925,14 @@ ngx_qjs_dict_incr(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key,
}
}
+ dict->sh->dirty = 1;
+
ngx_rwlock_unlock(&dict->sh->rwlock);
+ if (dict->state_file.data && !dict->save_event.timer_set) {
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
return value;
}
@@ -2870,24 +3962,30 @@ ngx_qjs_dict_set(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key,
goto memory_error;
}
- ngx_rwlock_unlock(&dict->sh->rwlock);
+ } else {
- return JS_TRUE;
- }
+ if (flags & NGX_JS_DICT_FLAG_MUST_NOT_EXIST) {
+ if (!dict->timeout || now < node->expire.key) {
+ ngx_rwlock_unlock(&dict->sh->rwlock);
+ return JS_FALSE;
+ }
+ }
- if (flags & NGX_JS_DICT_FLAG_MUST_NOT_EXIST) {
- if (!dict->timeout || now < node->expire.key) {
- ngx_rwlock_unlock(&dict->sh->rwlock);
- return JS_FALSE;
+ if (ngx_qjs_dict_update(cx, dict, node, value, timeout, now)
+ != NGX_OK)
+ {
+ goto memory_error;
}
}
- if (ngx_qjs_dict_update(cx, dict, node, value, timeout, now) != NGX_OK) {
- goto memory_error;
- }
+ dict->sh->dirty = 1;
ngx_rwlock_unlock(&dict->sh->rwlock);
+ if (dict->state_file.data && !dict->save_event.timer_set) {
+ ngx_add_timer(&dict->save_event, 1000);
+ }
+
return JS_TRUE;
memory_error:
@@ -2917,16 +4015,16 @@ ngx_qjs_dict_update(JSContext *cx, ngx_js_dict_t *dict,
return NGX_ERROR;
}
- ngx_slab_free_locked(dict->shpool, node->u.value.data);
+ ngx_slab_free_locked(dict->shpool, node->value.str.data);
ngx_memcpy(p, string.data, string.len);
- node->u.value.data = p;
- node->u.value.len = string.len;
+ node->value.str.data = p;
+ node->value.str.len = string.len;
JS_FreeCString(cx, (char *) string.data);
} else {
- if (JS_ToFloat64(cx, &node->u.number, value) < 0) {
+ if (JS_ToFloat64(cx, &node->value.number, value) < 0) {
return NGX_ERROR;
}
}
diff --git a/nginx/ngx_js_shared_dict.h b/nginx/ngx_js_shared_dict.h
index b9c7f967..b082962c 100644
--- a/nginx/ngx_js_shared_dict.h
+++ b/nginx/ngx_js_shared_dict.h
@@ -13,6 +13,7 @@ njs_int_t njs_js_ext_global_shared_prop(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *retval);
njs_int_t njs_js_ext_global_shared_keys(njs_vm_t *vm, njs_value_t *value,
njs_value_t *keys);
+ngx_int_t ngx_js_dict_init_worker(ngx_js_main_conf_t *jmcf);
extern njs_module_t ngx_js_shared_dict_module;
diff --git a/nginx/ngx_qjs_fetch.c b/nginx/ngx_qjs_fetch.c
index 084162ba..5ed8fc30 100644
--- a/nginx/ngx_qjs_fetch.c
+++ b/nginx/ngx_qjs_fetch.c
@@ -241,6 +241,7 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
JSValue init, value, promise;
ngx_int_t rc;
ngx_url_t u;
+ ngx_str_t method;
ngx_uint_t i;
ngx_pool_t *pool;
ngx_js_ctx_t *ctx;
@@ -410,6 +411,13 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
continue;
}
+ if (h[i].key.len == 14
+ && ngx_strncasecmp(h[i].key.data, (u_char *) "Content-Length", 14)
+ == 0)
+ {
+ continue;
+ }
+
njs_chb_append(&http->chain, h[i].key.data, h[i].key.len);
njs_chb_append_literal(&http->chain, ": ");
njs_chb_append(&http->chain, h[i].value.data, h[i].value.len);
@@ -429,7 +437,18 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
njs_chb_append(&http->chain, request.body.data, request.body.len);
} else {
- njs_chb_append_literal(&http->chain, CRLF);
+ method = request.method;
+
+ if ((method.len == 4
+ && (ngx_strncasecmp(method.data, (u_char *) "POST", 4) == 0))
+ || (method.len == 3
+ && ngx_strncasecmp(method.data, (u_char *) "PUT", 3) == 0))
+ {
+ njs_chb_append_literal(&http->chain, "Content-Length: 0" CRLF CRLF);
+
+ } else {
+ njs_chb_append_literal(&http->chain, CRLF);
+ }
}
if (u.addrs == NULL) {
diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c
index 0e022eb0..328ce581 100644
--- a/nginx/ngx_stream_js_module.c
+++ b/nginx/ngx_stream_js_module.c
@@ -264,6 +264,13 @@ static ngx_command_t ngx_stream_js_commands[] = {
offsetof(ngx_stream_js_srv_conf_t, reuse),
NULL },
+ { ngx_string("js_context_reuse_max_size"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_js_srv_conf_t, reuse_max_size),
+ NULL },
+
{ ngx_string("js_import"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE13,
ngx_js_import,
@@ -3192,21 +3199,12 @@ ngx_stream_js_periodic_init(ngx_js_periodic_t *periodic)
static ngx_int_t
-ngx_stream_js_init_worker(ngx_cycle_t *cycle)
+ngx_stream_js_init_worker_periodics(ngx_js_main_conf_t *jmcf)
{
- ngx_uint_t i;
- ngx_js_periodic_t *periodics;
- ngx_js_main_conf_t *jmcf;
-
- if ((ngx_process != NGX_PROCESS_WORKER)
- && ngx_process != NGX_PROCESS_SINGLE)
- {
- return NGX_OK;
- }
+ ngx_uint_t i;
+ ngx_js_periodic_t *periodics;
- jmcf = ngx_stream_cycle_get_module_main_conf(cycle, ngx_stream_js_module);
-
- if (jmcf == NULL || jmcf->periodics == NULL) {
+ if (jmcf->periodics == NULL) {
return NGX_OK;
}
@@ -3234,6 +3232,35 @@ ngx_stream_js_init_worker(ngx_cycle_t *cycle)
}
+static ngx_int_t
+ngx_stream_js_init_worker(ngx_cycle_t *cycle)
+{
+ ngx_js_main_conf_t *jmcf;
+
+ if ((ngx_process != NGX_PROCESS_WORKER)
+ && ngx_process != NGX_PROCESS_SINGLE)
+ {
+ return NGX_OK;
+ }
+
+ jmcf = ngx_stream_cycle_get_module_main_conf(cycle, ngx_stream_js_module);
+
+ if (jmcf == NULL) {
+ return NGX_OK;
+ }
+
+ if (ngx_stream_js_init_worker_periodics(jmcf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_js_dict_init_worker(jmcf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
static char *
ngx_stream_js_periodic(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
diff --git a/nginx/t/js_fetch.t b/nginx/t/js_fetch.t
index 7ee1a602..76d9238d 100644
--- a/nginx/t/js_fetch.t
+++ b/nginx/t/js_fetch.t
@@ -52,10 +52,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /broken {
js_content test.broken;
}
@@ -68,6 +64,10 @@ http {
js_content test.body;
}
+ location /body_content_length {
+ js_content test.body_content_length;
+ }
+
location /body_special {
js_content test.body_special;
}
@@ -138,10 +138,6 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
function body(r) {
var loc = r.args.loc;
var getter = r.args.getter;
@@ -164,6 +160,13 @@ $t->write_file('test.js', <<EOF);
.catch(e => r.return(501, e.message))
}
+ async function body_content_length(r) {
+ let resp = await ngx.fetch(`http://127.0.0.1:$p0/loc`,
+ {headers: {'Content-Length': '100'},
+ body: "CONTENT-BODY"});
+ r.return(resp.status);
+ }
+
function property(r) {
var opts = {headers:{}};
@@ -408,12 +411,12 @@ $t->write_file('test.js', <<EOF);
export default {njs: test_njs, body, broken, broken_response, body_special,
chain, chunked_ok, chunked_fail, header, header_iter,
- host_header, multi, loc, property, engine};
+ host_header, multi, loc, property, body_content_length };
EOF
$t->try_run('no njs.fetch');
-$t->plan(37);
+$t->plan(38);
$t->run_daemon(\&http_daemon, port(8082));
$t->waitforsocket('127.0.0.1:' . port(8082));
@@ -516,6 +519,14 @@ like(http_get('/body_special?loc=head/large&method=HEAD'),
qr/200 OK.*<empty>$/s, 'fetch head method large content-length');
}
+TODO: {
+local $TODO = 'not yet' unless has_version('0.9.1');
+
+like(http_get('/body_content_length'), qr/200 OK/s,
+ 'fetch body content-length');
+
+}
+
###############################################################################
sub has_version {
diff --git a/nginx/t/js_fetch_https.t b/nginx/t/js_fetch_https.t
index 8ede1048..42b5acbb 100644
--- a/nginx/t/js_fetch_https.t
+++ b/nginx/t/js_fetch_https.t
@@ -48,10 +48,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /https {
js_content test.https;
}
@@ -106,10 +102,6 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
function https(r) {
var url = `https://\${r.args.domain}:$p1/loc`;
var opt = {};
@@ -124,7 +116,7 @@ $t->write_file('test.js', <<EOF);
.catch(e => r.return(501, e.message))
}
- export default {njs: test_njs, https, engine};
+ export default {njs: test_njs, https};
EOF
my $d = $t->testdir();
diff --git a/nginx/t/js_fetch_objects.t b/nginx/t/js_fetch_objects.t
index bc5cc7ed..c9d04c49 100644
--- a/nginx/t/js_fetch_objects.t
+++ b/nginx/t/js_fetch_objects.t
@@ -45,10 +45,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /headers {
js_content test.headers;
}
@@ -92,10 +88,6 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
function header(r) {
r.return(200, r.headersIn.a);
}
@@ -528,7 +520,7 @@ $t->write_file('test.js', <<EOF);
run(r, tests);
}
- export default {njs: test_njs, engine, body, headers, request, response,
+ export default {njs: test_njs, body, headers, request, response,
fetch, fetch_multi_header};
EOF
diff --git a/nginx/t/js_fetch_resolver.t b/nginx/t/js_fetch_resolver.t
index 031ff43c..67680283 100644
--- a/nginx/t/js_fetch_resolver.t
+++ b/nginx/t/js_fetch_resolver.t
@@ -50,10 +50,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /dns {
js_content test.dns;
@@ -108,10 +104,6 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
const p0 = $p0;
const p1 = $p1;
@@ -141,7 +133,7 @@ $t->write_file('test.js', <<EOF);
r.return(c, `\${v.host}:\${v.request_method}:\${foo}:\${bar}:\${body}`);
}
- export default {njs: test_njs, dns, loc, engine};
+ export default {njs: test_njs, dns, loc};
EOF
$t->try_run('no njs.fetch');
diff --git a/nginx/t/js_fetch_timeout.t b/nginx/t/js_fetch_timeout.t
index ab1ba24a..2ca1510f 100644
--- a/nginx/t/js_fetch_timeout.t
+++ b/nginx/t/js_fetch_timeout.t
@@ -47,10 +47,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /normal_timeout {
js_content test.timeout_test;
}
@@ -84,10 +80,6 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
async function timeout_test(r) {
let rs = await Promise.allSettled([
'http://127.0.0.1:$p1/normal_reply',
@@ -110,7 +102,7 @@ $t->write_file('test.js', <<EOF);
setTimeout((r) => { r.return(200); }, 250, r, 0);
}
- export default {njs: test_njs, engine, timeout_test, normal_reply,
+ export default {njs: test_njs, timeout_test, normal_reply,
delayed_reply};
EOF
diff --git a/nginx/t/js_fetch_verify.t b/nginx/t/js_fetch_verify.t
index f98b4d8c..8b691a74 100644
--- a/nginx/t/js_fetch_verify.t
+++ b/nginx/t/js_fetch_verify.t
@@ -48,10 +48,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /https {
js_content test.https;
}
@@ -80,10 +76,6 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
function https(r) {
ngx.fetch(`https://example.com:$p1/loc`)
.then(reply => reply.text())
@@ -91,7 +83,7 @@ $t->write_file('test.js', <<EOF);
.catch(e => r.return(501, e.message));
}
- export default {njs: test_njs, engine, https};
+ export default {njs: test_njs, https};
EOF
$t->write_file('openssl.conf', <<EOF);
diff --git a/nginx/t/js_internal_redirect.t b/nginx/t/js_internal_redirect.t
index abfe79f9..721113bb 100644
--- a/nginx/t/js_internal_redirect.t
+++ b/nginx/t/js_internal_redirect.t
@@ -11,6 +11,7 @@ use warnings;
use strict;
use Test::More;
+use Socket qw/ CRLF /;
BEGIN { use FindBin; chdir($FindBin::Bin); }
@@ -54,6 +55,10 @@ http {
return 200 redirect$arg_b;
}
+ location /destroyed_ctx {
+ js_content test.destroyed_ctx;
+ }
+
location @named {
return 200 named;
}
@@ -87,7 +92,16 @@ $t->write_file('test.js', <<EOF);
}
}
- export default {njs:test_njs, redirect};
+ function destroyed_ctx(r) {
+ try {
+ r.return(200);
+
+ } catch (e) {
+ r.internalRedirect("\@sub");
+ }
+ }
+
+ export default {njs:test_njs, redirect, destroyed_ctx};
EOF
@@ -103,5 +117,18 @@ like(http_get('/test?unsafe=1'), qr/500 Internal Server/s,
'unsafe redirect');
like(http_get('/test?quoted=1'), qr/200 .*redirect/s,
'quoted redirect');
+get('/destroyed_ctx', 'If-Match: tt');
###############################################################################
+
+sub get {
+ my ($url, @headers) = @_;
+ return http(
+ "GET $url HTTP/1.1" . CRLF .
+ 'Host: localhost' . CRLF .
+ 'Connection: close' . CRLF .
+ join(CRLF, @headers) . CRLF . CRLF
+ );
+}
+
+################################################################################
diff --git a/nginx/t/js_periodic.t b/nginx/t/js_periodic.t
index d6868935..7e134588 100644
--- a/nginx/t/js_periodic.t
+++ b/nginx/t/js_periodic.t
@@ -56,7 +56,7 @@ http {
server_name localhost;
location @periodic {
- js_periodic test.tick interval=30ms jitter=1ms;
+ js_periodic test.tick interval=20ms jitter=1ms;
js_periodic test.timer interval=1s worker_affinity=all;
js_periodic test.overrun interval=30ms;
js_periodic test.affinity interval=50ms worker_affinity=0101;
diff --git a/nginx/t/js_periodic_fetch.t b/nginx/t/js_periodic_fetch.t
index 0231b662..39385132 100644
--- a/nginx/t/js_periodic_fetch.t
+++ b/nginx/t/js_periodic_fetch.t
@@ -54,10 +54,6 @@ http {
js_periodic test.fetch_exception interval=1s;
}
- location /engine {
- js_content test.engine;
- }
-
location /fetch_ok {
return 200 'ok';
}
@@ -81,10 +77,6 @@ EOF
my $p0 = port(8080);
$t->write_file('test.js', <<EOF);
- function engine(r) {
- r.return(200, njs.engine);
- }
-
async function fetch() {
let reply = await ngx.fetch('http://127.0.0.1:$p0/fetch_ok');
let body = await reply.text();
@@ -107,16 +99,15 @@ $t->write_file('test.js', <<EOF);
}
function test_fetch(r) {
- r.return(200, ngx.shared.strings.get('fetch').startsWith('okok'));
+ r.return(200, ngx.shared.strings.get('fetch'));
}
function test_multiple_fetches(r) {
- r.return(200, ngx.shared.strings.get('multiple_fetches')
- .startsWith('ok\@foo'));
+ r.return(200, ngx.shared.strings.get('multiple_fetches'));
}
export default { fetch, fetch_exception, multiple_fetches, test_fetch,
- test_multiple_fetches, engine };
+ test_multiple_fetches };
EOF
$t->try_run('no js_periodic with fetch');
@@ -127,8 +118,8 @@ $t->plan(3);
select undef, undef, undef, 0.1;
-like(http_get('/test_fetch'), qr/true/, 'periodic fetch test');
-like(http_get('/test_multiple_fetches'), qr/true/, 'multiple fetch test');
+like(http_get('/test_fetch'), qr/(ok)+/, 'periodic fetch test');
+like(http_get('/test_multiple_fetches'), qr/ok\@foo/, 'multiple fetch test');
$t->stop();
diff --git a/nginx/t/js_shared_dict.t b/nginx/t/js_shared_dict.t
index 8be2831f..b27a33ef 100644
--- a/nginx/t/js_shared_dict.t
+++ b/nginx/t/js_shared_dict.t
@@ -52,10 +52,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /add {
js_content test.add;
}
@@ -141,10 +137,6 @@ $t->write_file('test.js', <<'EOF');
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
function convertToValue(dict, v) {
if (dict.type == 'number') {
return parseInt(v);
@@ -337,7 +329,7 @@ $t->write_file('test.js', <<'EOF');
export default { add, capacity, chain, clear, del, free_space, get, has,
incr, items, keys, name, njs: test_njs, pop, replace, set,
- set_clear, size, zones, engine, overflow };
+ set_clear, size, zones, overflow };
EOF
$t->try_run('no js_shared_dict_zone');
diff --git a/nginx/t/js_shared_dict_state.t b/nginx/t/js_shared_dict_state.t
new file mode 100644
index 00000000..32eef948
--- /dev/null
+++ b/nginx/t/js_shared_dict_state.t
@@ -0,0 +1,256 @@
+#!/usr/bin/perl
+
+# (C) Dmitry Volyntsev
+# (C) Nginx, Inc.
+
+# Tests for js_shared_dict_zone directive, state= parameter.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+eval { require JSON::PP; };
+plan(skip_all => "JSON::PP not installed") if $@;
+
+my $t = Test::Nginx->new()->has(qw/http/)
+ ->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+ %%TEST_GLOBALS_HTTP%%
+
+ js_import test.js;
+
+ js_shared_dict_zone zone=bar:64k type=string state=bar.json;
+ js_shared_dict_zone zone=waka:32k timeout=1000s type=number state=waka.json;
+
+ server {
+ listen 127.0.0.1:8080;
+ server_name localhost;
+
+ location /add {
+ js_content test.add;
+ }
+
+ location /clear {
+ js_content test.clear;
+ }
+
+ location /delete {
+ js_content test.del;
+ }
+
+ location /get {
+ js_content test.get;
+ }
+
+ location /incr {
+ js_content test.incr;
+ }
+
+ location /pop {
+ js_content test.pop;
+ }
+
+ location /set {
+ js_content test.set;
+ }
+ }
+}
+
+EOF
+
+$t->write_file('bar.json', <<EOF);
+{"waka":{"value":"foo","expire":0},
+ "bar": { "value" :"\\u0061\\u0062\\u0063"},
+"FOO \\n": { "value" : "BAZ", "unexpected_str": "u\\r" },
+ "X": { "valu\\u0065" : "\\n" , "unexpected_num": 23.1 } ,
+ "\\u0061\\u0062\\u0063": { "value" : "def" } ,
+}
+EOF
+
+$t->write_file('test.js', <<'EOF');
+ function convertToValue(dict, v) {
+ if (dict.type == 'number') {
+ return parseInt(v);
+
+ } else if (v == 'empty') {
+ v = '';
+ }
+
+ return v;
+ }
+
+ function add(r) {
+ var dict = ngx.shared[r.args.dict];
+ var value = convertToValue(dict, r.args.value);
+
+ if (r.args.timeout) {
+ var timeout = Number(r.args.timeout);
+ r.return(200, dict.add(r.args.key, value, timeout));
+
+ } else {
+ r.return(200, dict.add(r.args.key, value));
+ }
+ }
+
+ function clear(r) {
+ var dict = ngx.shared[r.args.dict];
+ var result = dict.clear();
+ r.return(200, result === undefined ? 'undefined' : result);
+ }
+
+ function del(r) {
+ var dict = ngx.shared[r.args.dict];
+ r.return(200, dict.delete(r.args.key));
+ }
+
+ function get(r) {
+ var dict = ngx.shared[r.args.dict];
+ var val = dict.get(r.args.key);
+
+ if (val == '') {
+ val = 'empty';
+
+ } else if (val === undefined) {
+ val = 'undefined';
+ }
+
+ r.return(200, val);
+ }
+
+ function incr(r) {
+ var dict = ngx.shared[r.args.dict];
+ var def = r.args.def ? parseInt(r.args.def) : 0;
+
+ if (r.args.timeout) {
+ var timeout = Number(r.args.timeout);
+ var val = dict.incr(r.args.key, parseInt(r.args.by), def, timeout);
+ r.return(200, val);
+
+ } else {
+ var val = dict.incr(r.args.key, parseInt(r.args.by), def);
+ r.return(200, val);
+ }
+ }
+
+ function pop(r) {
+ var dict = ngx.shared[r.args.dict];
+ var val = dict.pop(r.args.key);
+ if (val == '') {
+ val = 'empty';
+
+ } else if (val === undefined) {
+ val = 'undefined';
+ }
+
+ r.return(200, val);
+ }
+
+ function set(r) {
+ var dict = ngx.shared[r.args.dict];
+ var value = convertToValue(dict, r.args.value);
+
+ if (r.args.timeout) {
+ var timeout = Number(r.args.timeout);
+ r.return(200, dict.set(r.args.key, value, timeout) === dict);
+
+ } else {
+ r.return(200, dict.set(r.args.key, value) === dict);
+ }
+ }
+
+ export default { add, clear, del, get, incr, pop, set };
+EOF
+
+$t->try_run('no js_shared_dict_zone with state=')->plan(11);
+
+###############################################################################
+
+like(http_get('/get?dict=bar&key=waka'), qr/foo/, 'get bar.waka');
+like(http_get('/get?dict=bar&key=bar'), qr/abc/, 'get bar.bar');
+like(http_get('/get?dict=bar&key=FOO%20%0A'), qr/BAZ/, 'get bar["FOO \\n"]');
+like(http_get('/get?dict=bar&key=abc'), qr/def/, 'get bar.abc');
+
+http_get('/set?dict=bar&key=waka&value=foo2');
+http_get('/delete?dict=bar&key=bar');
+
+http_get('/set?dict=waka&key=foo&value=42');
+
+select undef, undef, undef, 1.1;
+
+$t->reload();
+
+my $bar_state = read_state($t, 'bar.json');
+my $waka_state = read_state($t, 'waka.json');
+
+is($bar_state->{waka}->{value}, 'foo2', 'get bar.waka from state');
+is($bar_state->{bar}, undef, 'no bar.bar in state');
+is($waka_state->{foo}->{value}, '42', 'get waka.foo from state');
+like($waka_state->{foo}->{expire}, qr/^\d+$/, 'waka.foo expire');
+
+http_get('/pop?dict=bar&key=FOO%20%0A');
+
+http_get('/incr?dict=waka&key=foo&by=1');
+
+select undef, undef, undef, 1.1;
+
+$bar_state = read_state($t, 'bar.json');
+$waka_state = read_state($t, 'waka.json');
+
+is($bar_state->{'FOO \\n'}, undef, 'no bar.FOO \\n in state');
+is($waka_state->{foo}->{value}, '43', 'get waka.foo from state');
+
+http_get('/clear?dict=bar');
+
+select undef, undef, undef, 1.1;
+
+$bar_state = read_state($t, 'bar.json');
+
+is($bar_state->{waka}, undef, 'no bar.waka in state');
+
+###############################################################################
+
+sub decode_json {
+ my $json;
+ eval { $json = JSON::PP::decode_json(shift) };
+
+ if ($@) {
+ return "<failed to parse JSON>";
+ }
+
+ return $json;
+}
+
+sub read_state {
+ my ($self, $file) = @_;
+ my $json = $self->read_file($file);
+
+ if ($json) {
+ $json = decode_json($json);
+ }
+
+ return $json;
+}
+
+###############################################################################
diff --git a/nginx/t/stream_js_fetch.t b/nginx/t/stream_js_fetch.t
index 9a42ae29..cb87eec7 100644
--- a/nginx/t/stream_js_fetch.t
+++ b/nginx/t/stream_js_fetch.t
@@ -46,10 +46,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /validate {
js_content test.validate;
}
@@ -103,10 +99,6 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
function validate(r) {
r.return((r.requestText == 'QZ') ? 200 : 403);
}
@@ -166,7 +158,7 @@ $t->write_file('test.js', <<EOF);
}
export default {njs: test_njs, validate, preread_verify, filter_verify,
- access_ok, access_nok, engine};
+ access_ok, access_nok};
EOF
$t->try_run('no stream njs available');
diff --git a/nginx/t/stream_js_fetch_https.t b/nginx/t/stream_js_fetch_https.t
index 987a896a..f397ea70 100644
--- a/nginx/t/stream_js_fetch_https.t
+++ b/nginx/t/stream_js_fetch_https.t
@@ -47,10 +47,6 @@ http {
location /njs {
js_content test.njs;
}
-
- location /engine {
- js_content test.engine;
- }
}
server {
@@ -163,10 +159,6 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
function preread(s) {
s.on('upload', function (data, flags) {
if (data.startsWith('GO')) {
@@ -201,7 +193,7 @@ $t->write_file('test.js', <<EOF);
(r.status == 200) ? s.allow(): s.deny();
}
- export default {njs: test_njs, engine, preread, access_ok, access_nok};
+ export default {njs: test_njs, preread, access_ok, access_nok};
EOF
my $d = $t->testdir();
diff --git a/nginx/t/stream_js_fetch_init.t b/nginx/t/stream_js_fetch_init.t
index f48b9d5e..3980a9ee 100644
--- a/nginx/t/stream_js_fetch_init.t
+++ b/nginx/t/stream_js_fetch_init.t
@@ -58,10 +58,6 @@ http {
js_content test.njs;
}
- location /engine {
- js_content test.engine;
- }
-
location /success {
return 200;
}
@@ -77,17 +73,13 @@ $t->write_file('test.js', <<EOF);
r.return(200, njs.version);
}
- function engine(r) {
- r.return(200, njs.engine);
- }
-
async function access_ok(s) {
let reply = await ngx.fetch('http://127.0.0.1:$p/success');
(reply.status == 200) ? s.allow(): s.deny();
}
- export default {njs: test_njs, engine, access_ok};
+ export default {njs: test_njs, access_ok};
EOF
$t->try_run('no stream njs available');
diff --git a/nginx/t/stream_js_periodic_fetch.t b/nginx/t/stream_js_periodic_fetch.t
index 60599423..4ebec96e 100644
--- a/nginx/t/stream_js_periodic_fetch.t
+++ b/nginx/t/stream_js_periodic_fetch.t
@@ -67,10 +67,6 @@ http {
listen 127.0.0.1:8080;
server_name localhost;
- location /engine {
- js_content test.engine;
- }
-
location /fetch_ok {
return 200 'ok';
}
@@ -86,10 +82,6 @@ EOF
my $p1 = port(8080);
$t->write_file('test.js', <<EOF);
- function engine(r) {
- r.return(200, njs.engine);
- }
-
async function fetch() {
let reply = await ngx.fetch('http://127.0.0.1:$p1/fetch_ok');
let body = await reply.text();
@@ -142,7 +134,7 @@ $t->write_file('test.js', <<EOF);
});
}
- export default { engine, fetch, fetch_exception, test, multiple_fetches };
+ export default { fetch, fetch_exception, test, multiple_fetches };
EOF
$t->run_daemon(\&stream_daemon, port(8090));
diff --git a/nginx/t/stream_js_shared_dict.t b/nginx/t/stream_js_shared_dict.t
index 915cc40b..0435033d 100644
--- a/nginx/t/stream_js_shared_dict.t
+++ b/nginx/t/stream_js_shared_dict.t
@@ -43,10 +43,6 @@ http {
location / {
return 200;
}
-
- location /engine {
- js_content test.engine;
- }
}
}
@@ -75,10 +71,6 @@ EOF
$t->write_file('test.js', <<EOF);
import qs from 'querystring';
- function engine(r) {
- r.return(200, 'engine');
- }
-
function preread_verify(s) {
var collect = Buffer.from([]);
@@ -121,7 +113,7 @@ $t->write_file('test.js', <<EOF);
});
}
- export default { engine, preread_verify, control_access };
+ export default { preread_verify, control_access };
EOF
diff --git a/nginx/t/stream_js_shared_dict_state.t b/nginx/t/stream_js_shared_dict_state.t
new file mode 100644
index 00000000..c2edb63e
--- /dev/null
+++ b/nginx/t/stream_js_shared_dict_state.t
@@ -0,0 +1,137 @@
+#!/usr/bin/perl
+
+# (C) Dmitry Volyntsev
+# (C) Nginx, Inc.
+
+# Tests for js_shared_dict_zone directive, state= parameter.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::Stream qw/ stream /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/stream/)
+ ->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+stream {
+ %%TEST_GLOBALS_STREAM%%
+
+ js_import test.js;
+
+ js_shared_dict_zone zone=foo:32k state=foo.json;
+
+ server {
+ listen 127.0.0.1:8081;
+ js_preread test.preread_verify;
+ proxy_pass 127.0.0.1:8090;
+ }
+}
+
+EOF
+
+$t->write_file('foo.json', <<EOF);
+{"QZ":{"value":"1"},"QQ":{"value":"1"}}
+EOF
+
+$t->write_file('test.js', <<EOF);
+ function preread_verify(s) {
+ var collect = Buffer.from([]);
+
+ s.on('upstream', function (data, flags) {
+ collect = Buffer.concat([collect, data]);
+
+ if (collect.length >= 4 && collect.readUInt16BE(0) == 0xabcd) {
+ let id = collect.slice(2,4);
+
+ ngx.shared.foo.get(id) ? s.done(): s.deny();
+
+ } else if (collect.length) {
+ s.deny();
+ }
+ });
+ }
+
+ export default { preread_verify };
+
+EOF
+
+$t->try_run('no js_shared_dict_zone with state=');
+
+$t->plan(2);
+
+$t->run_daemon(\&stream_daemon, port(8090));
+$t->waitforsocket('127.0.0.1:' . port(8090));
+
+###############################################################################
+
+is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQY##"), "",
+ 'access failed, QY is not in the shared dict');
+is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQZ##"), "\xAB\xCDQZ##",
+ 'access granted');
+
+###############################################################################
+
+sub stream_daemon {
+ my $server = IO::Socket::INET->new(
+ Proto => 'tcp',
+ LocalAddr => '127.0.0.1:' . port(8090),
+ Listen => 5,
+ Reuse => 1
+ )
+ or die "Can't create listening socket: $!\n";
+
+ local $SIG{PIPE} = 'IGNORE';
+
+ while (my $client = $server->accept()) {
+ $client->autoflush(1);
+
+ log2c("(new connection $client)");
+
+ $client->sysread(my $buffer, 65536) or next;
+
+ log2i("$client $buffer");
+
+ log2o("$client $buffer");
+
+ $client->syswrite($buffer);
+
+ close $client;
+ }
+}
+
+sub log2i { Test::Nginx::log_core('|| <<', @_); }
+sub log2o { Test::Nginx::log_core('|| >>', @_); }
+sub log2c { Test::Nginx::log_core('||', @_); }
+
+sub get {
+ my ($url, %extra) = @_;
+
+ my $s = IO::Socket::INET->new(
+ Proto => 'tcp',
+ PeerAddr => '127.0.0.1:' . port(8082)
+ ) or die "Can't connect to nginx: $!\n";
+
+ return http_get($url, socket => $s);
+}
+
+###############################################################################
diff --git a/src/njs_array.c b/src/njs_array.c
index e6f8ed83..3f424bc4 100644
--- a/src/njs_array.c
+++ b/src/njs_array.c
@@ -702,8 +702,10 @@ njs_array_length(njs_vm_t *vm,njs_object_prop_t *prop, uint32_t unused,
}
}
- prop->type = NJS_PROPERTY;
- njs_set_number(njs_prop_value(prop), length);
+ ret = njs_array_length_redefine(vm, value, length, 1);
+ if (ret != NJS_OK) {
+ return ret;
+ }
njs_value_assign(retval, setval);
diff --git a/src/njs_atom.c b/src/njs_atom.c
index 24e6dc17..dc66f886 100644
--- a/src/njs_atom.c
+++ b/src/njs_atom.c
@@ -82,7 +82,7 @@ njs_atom_find_or_add(njs_vm_t *vm, u_char *key, size_t size, size_t length,
uint32_t hash)
{
njs_int_t ret;
- njs_value_t *entry;
+ njs_object_prop_t *prop;
njs_lvlhsh_query_t lhq;
lhq.key.start = key;
@@ -92,33 +92,65 @@ njs_atom_find_or_add(njs_vm_t *vm, u_char *key, size_t size, size_t length,
ret = njs_lvlhsh_find(vm->atom_hash_current, &lhq);
if (ret == NJS_OK) {
- return lhq.value;
+ return njs_prop_value(lhq.value);
}
ret = njs_lvlhsh_find(&vm->atom_hash_shared, &lhq);
if (ret == NJS_OK) {
- return lhq.value;
+ return njs_prop_value(lhq.value);
}
- entry = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
- if (njs_slow_path(entry == NULL)) {
+ lhq.pool = vm->mem_pool;
+
+ ret = njs_lvlhsh_insert(vm->atom_hash_current, &lhq);
+ if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
- ret = njs_string_create(vm, entry, key, size);
+ prop = lhq.value;
+
+ ret = njs_string_create(vm, &prop->u.value, key, size);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
- entry->string.atom_id = vm->atom_id_generator++;
- if (njs_atom_is_number(entry->string.atom_id)) {
+ prop->u.value.string.atom_id = vm->atom_id_generator++;
+ if (njs_atom_is_number(prop->u.value.string.atom_id)) {
njs_internal_error(vm, "too many atoms");
return NULL;
}
- entry->string.token_type = NJS_KEYWORD_TYPE_UNDEF;
+ prop->u.value.string.token_type = NJS_KEYWORD_TYPE_UNDEF;
+
+ return &prop->u.value;
+}
+
+
+static njs_value_t *
+njs_atom_find_or_add_string(njs_vm_t *vm, njs_value_t *value,
+ uint32_t hash)
+{
+ njs_int_t ret;
+ njs_object_prop_t *prop;
+ njs_lvlhsh_query_t lhq;
+
+ njs_assert(njs_is_string(value));
+
+ lhq.key.start = value->string.data->start;
+ lhq.key.length = value->string.data->size;
+ lhq.key_hash = hash;
+ lhq.proto = &njs_lexer_hash_proto;
+
+ ret = njs_lvlhsh_find(vm->atom_hash_current, &lhq);
+ if (ret == NJS_OK) {
+ return njs_prop_value(lhq.value);
+ }
+
+ ret = njs_lvlhsh_find(&vm->atom_hash_shared, &lhq);
+ if (ret == NJS_OK) {
+ return njs_prop_value(lhq.value);
+ }
- lhq.value = entry;
lhq.pool = vm->mem_pool;
ret = njs_lvlhsh_insert(vm->atom_hash_current, &lhq);
@@ -126,7 +158,19 @@ njs_atom_find_or_add(njs_vm_t *vm, u_char *key, size_t size, size_t length,
return NULL;
}
- return entry;
+ prop = lhq.value;
+
+ prop->u.value = *value;
+
+ prop->u.value.string.atom_id = vm->atom_id_generator++;
+ if (njs_atom_is_number(prop->u.value.string.atom_id)) {
+ njs_internal_error(vm, "too many atoms");
+ return NULL;
+ }
+
+ prop->u.value.string.token_type = NJS_KEYWORD_TYPE_UNDEF;
+
+ return &prop->u.value;
}
@@ -190,7 +234,6 @@ njs_atom_hash_init(njs_vm_t *vm)
if (value->type == NJS_SYMBOL) {
lhq.key_hash = value->string.atom_id;
- lhq.value = (void *) value;
ret = njs_flathsh_insert(&vm->atom_hash_shared, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
@@ -206,7 +249,6 @@ njs_atom_hash_init(njs_vm_t *vm)
lhq.key_hash = njs_djb_hash(start, len);
lhq.key.length = len;
lhq.key.start = start;
- lhq.value = (void *) value;
ret = njs_flathsh_insert(&vm->atom_hash_shared, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
@@ -214,6 +256,8 @@ njs_atom_hash_init(njs_vm_t *vm)
return 0xffffffff;
}
}
+
+ *njs_prop_value(lhq.value) = *value;
}
vm->atom_hash_current = &vm->atom_hash_shared;
@@ -247,10 +291,7 @@ njs_atom_atomize_key(njs_vm_t *vm, njs_value_t *value)
hash_id = njs_djb_hash(value->string.data->start,
value->string.data->size);
- entry = njs_atom_find_or_add(vm, value->string.data->start,
- value->string.data->size,
- value->string.data->length,
- hash_id);
+ entry = njs_atom_find_or_add_string(vm, value, hash_id);
if (njs_slow_path(entry == NULL)) {
return NJS_ERROR;
}
@@ -320,13 +361,14 @@ njs_atom_symbol_add(njs_vm_t *vm, njs_value_t *value)
if (value->type == NJS_SYMBOL) {
lhq.key_hash = value->atom_id;
- lhq.value = (void *) value;
ret = njs_flathsh_insert(vm->atom_hash_current, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "flathsh insert/replace failed");
return NJS_ERROR;
}
+
+ *njs_prop_value(lhq.value) = *value;
}
return NJS_OK;
diff --git a/src/njs_buffer.c b/src/njs_buffer.c
index 3f0469e7..dbe9447a 100644
--- a/src/njs_buffer.c
+++ b/src/njs_buffer.c
@@ -2732,8 +2732,8 @@ static njs_int_t
njs_buffer(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t atom_id,
njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
{
- return njs_object_prop_init(vm, &njs_buffer_constructor_init, prop, atom_id,
- value, retval);
+ return njs_object_props_init(vm, &njs_buffer_constructor_init, prop,
+ atom_id, value, retval);
}
@@ -2741,8 +2741,8 @@ static njs_int_t
njs_buffer_constants(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t atom_id,
njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
{
- return njs_object_prop_init(vm, &njs_buffer_constants_init, prop, atom_id,
- value, retval);
+ return njs_object_props_init(vm, &njs_buffer_constants_init, prop, atom_id,
+ value, retval);
}
diff --git a/src/njs_builtin.c b/src/njs_builtin.c
index 230b4c1c..5ce2ec7e 100644
--- a/src/njs_builtin.c
+++ b/src/njs_builtin.c
@@ -433,17 +433,11 @@ njs_builtin_traverse(njs_vm_t *vm, njs_traverse_t *traverse, void *data)
/* NJS_BUILTIN_TRAVERSE_KEYS. */
- prop = njs_object_prop_alloc(vm, &njs_value_null, 0);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
ret = njs_atom_string_create(vm, &prop_name, buf, p - buf);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- lhq.value = prop;
lhq.key_hash = prop_name.atom_id;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -455,6 +449,14 @@ njs_builtin_traverse(njs_vm_t *vm, njs_traverse_t *traverse, void *data)
return NJS_ERROR;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 0;
+ prop->writable = 0;
+ prop->u.value = njs_value_null;
+
return NJS_OK;
}
@@ -477,6 +479,7 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
njs_uint_t i, n;
njs_value_t value, tag;
njs_object_t object;
+ njs_object_prop_t *prop;
njs_lvlhsh_each_t lhe;
njs_exotic_slots_t *slots;
njs_function_name_t *fn;
@@ -538,12 +541,13 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
njs_lvlhsh_each_init(&lhe, &njs_modules_hash_proto);
for ( ;; ) {
- module = njs_lvlhsh_each(&vm->modules_hash, &lhe);
-
- if (module == NULL) {
+ prop = (njs_object_prop_t *) njs_flathsh_each(&vm->modules_hash, &lhe);
+ if (prop == NULL) {
break;
}
+ module = prop->u.mod;
+
if (njs_is_object(&module->value)
&& !njs_object(&module->value)->shared)
{
@@ -759,7 +763,6 @@ njs_global_this_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop,
{
njs_value_t *value;
njs_variable_t *var;
- njs_function_t *function;
njs_rbtree_node_t *rb_node;
njs_variable_node_t *node, var_node;
@@ -788,15 +791,6 @@ njs_global_this_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop,
value = njs_scope_valid_value(vm, var->index);
- if (var->type == NJS_VARIABLE_FUNCTION && njs_is_undefined(value)) {
- njs_value_assign(value, &var->value);
-
- function = njs_function_value_copy(vm, value);
- if (njs_slow_path(function == NULL)) {
- return NJS_ERROR;
- }
- }
-
if (setval != NULL) {
njs_value_assign(value, setval);
}
@@ -825,15 +819,6 @@ njs_global_this_object(njs_vm_t *vm, njs_object_prop_t *self, uint32_t atom_id,
njs_value_assign(retval, setval);
}
- prop = njs_object_prop_alloc(vm, retval, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
- njs_value_assign(njs_prop_value(prop), retval);
- prop->enumerable = self->enumerable;
-
- lhq.value = prop;
lhq.key_hash = atom_id;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -845,6 +830,14 @@ njs_global_this_object(njs_vm_t *vm, njs_object_prop_t *self, uint32_t atom_id,
return NJS_ERROR;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = self->enumerable;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = *retval;
+
return NJS_OK;
}
@@ -876,15 +869,6 @@ njs_top_level_object(njs_vm_t *vm, njs_object_prop_t *self, uint32_t atom_id,
object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT);
}
- prop = njs_object_prop_alloc(vm, retval, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
- njs_value_assign(njs_prop_value(prop), retval);
- prop->enumerable = self->enumerable;
-
- lhq.value = prop;
lhq.key_hash = atom_id;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -896,6 +880,14 @@ njs_top_level_object(njs_vm_t *vm, njs_object_prop_t *self, uint32_t atom_id,
return NJS_ERROR;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = self->enumerable;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = *retval;
+
return NJS_OK;
}
@@ -925,15 +917,6 @@ njs_top_level_constructor(njs_vm_t *vm, njs_object_prop_t *self,
return NJS_OK;
}
- prop = njs_object_prop_alloc(vm, retval, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
- njs_value_assign(njs_prop_value(prop), retval);
- prop->enumerable = 0;
-
- lhq.value = prop;
lhq.key_hash = atom_id;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -945,6 +928,14 @@ njs_top_level_constructor(njs_vm_t *vm, njs_object_prop_t *self,
return NJS_ERROR;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = *retval;
+
return NJS_OK;
}
@@ -1215,28 +1206,29 @@ njs_process_object_argv(njs_vm_t *vm, njs_object_prop_t *pr, uint32_t unused,
}
}
- prop = njs_object_prop_alloc(vm, &njs_value_undefined, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
- njs_set_array(njs_prop_value(prop), argv);
-
- lhq.value = prop;
lhq.key_hash = NJS_ATOM_STRING_argv;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
lhq.proto = &njs_object_hash_proto;
ret = njs_flathsh_unique_insert(njs_object_hash(process), &lhq);
- if (njs_fast_path(ret == NJS_OK)) {
- njs_value_assign(retval, njs_prop_value(prop));
- return NJS_OK;
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NJS_ERROR;
}
- njs_internal_error(vm, "lvlhsh insert failed");
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+
+ njs_set_array(njs_prop_value(prop), argv);
+
+ njs_value_assign(retval, njs_prop_value(prop));
+ return NJS_OK;
- return NJS_ERROR;
}
@@ -1261,10 +1253,6 @@ njs_env_hash_init(njs_vm_t *vm, njs_flathsh_t *hash, char **environment)
ep = environment;
while (*ep != NULL) {
- prop = njs_object_prop_alloc(vm, &njs_value_undefined, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
entry = (u_char *) *ep++;
@@ -1293,17 +1281,11 @@ njs_env_hash_init(njs_vm_t *vm, njs_flathsh_t *hash, char **environment)
val++;
- ret = njs_string_create(vm, njs_prop_value(prop), val, njs_strlen(val));
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
-
ret = njs_atom_atomize_key(vm, &prop_name);
if (ret != NJS_OK) {
return ret;
}
- lhq.value = prop;
lhq.key_hash = prop_name.atom_id;
ret = njs_flathsh_unique_insert(hash, &lhq);
@@ -1319,6 +1301,19 @@ njs_env_hash_init(njs_vm_t *vm, njs_flathsh_t *hash, char **environment)
* Always using the first element among the duplicates
* and ignoring the rest.
*/
+ continue;
+ }
+
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+
+ ret = njs_string_create(vm, njs_prop_value(prop), val, njs_strlen(val));
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
}
}
@@ -1342,28 +1337,29 @@ njs_process_object_env(njs_vm_t *vm, njs_object_prop_t *pr, uint32_t unused,
env->shared_hash = vm->shared->env_hash;
- prop = njs_object_prop_alloc(vm, &njs_value_undefined, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
- njs_set_object(njs_prop_value(prop), env);
-
lhq.replace = 1;
lhq.pool = vm->mem_pool;
lhq.proto = &njs_object_hash_proto;
- lhq.value = prop;
lhq.key_hash = NJS_ATOM_STRING_env;
ret = njs_flathsh_unique_insert(njs_object_hash(process), &lhq);
- if (njs_fast_path(ret == NJS_OK)) {
- njs_value_assign(retval, njs_prop_value(prop));
- return NJS_OK;
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NJS_ERROR;
}
- njs_internal_error(vm, "lvlhsh insert failed");
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
- return NJS_ERROR;
+ njs_set_object(njs_prop_value(prop), env);
+
+ njs_value_assign(retval, njs_prop_value(prop));
+
+ return NJS_OK;
}
diff --git a/src/njs_chb.c b/src/njs_chb.c
index 3ee28009..ac88c0bd 100644
--- a/src/njs_chb.c
+++ b/src/njs_chb.c
@@ -251,7 +251,11 @@ njs_chb_destroy(njs_chb_t *chain)
while (n != NULL) {
next = n->next;
- chain->free(chain->pool, n);
+
+ if (chain->free != NULL) {
+ chain->free(chain->pool, n);
+ }
+
n = next;
}
}
diff --git a/src/njs_clang.h b/src/njs_clang.h
index 2bd45a74..af9f3350 100644
--- a/src/njs_clang.h
+++ b/src/njs_clang.h
@@ -111,7 +111,7 @@ njs_leading_zeros64(uint64_t x)
n = 0;
- while ((x & 0x8000000000000000) == 0) {
+ while ((x & 0x8000000000000000ULL) == 0) {
n++;
x <<= 1;
}
diff --git a/src/njs_disassembler.c b/src/njs_disassembler.c
index c7927bf5..72ea63b9 100644
--- a/src/njs_disassembler.c
+++ b/src/njs_disassembler.c
@@ -30,9 +30,6 @@ static njs_code_name_t code_names[] = {
{ NJS_VMCODE_TEMPLATE_LITERAL, sizeof(njs_vmcode_template_literal_t),
njs_str("TEMPLATE LITERAL") },
- { NJS_VMCODE_FUNCTION_COPY, sizeof(njs_vmcode_function_copy_t),
- njs_str("FUNCTION COPY ") },
-
{ NJS_VMCODE_PROPERTY_GET, sizeof(njs_vmcode_prop_get_t),
njs_str("PROP GET ") },
{ NJS_VMCODE_PROPERTY_ATOM_GET, sizeof(njs_vmcode_prop_get_t),
diff --git a/src/njs_dtoa.h b/src/njs_dtoa.h
index 35ff09a5..4dcfc098 100644
--- a/src/njs_dtoa.h
+++ b/src/njs_dtoa.h
@@ -7,6 +7,8 @@
#ifndef _NJS_DTOA_H_INCLUDED_
#define _NJS_DTOA_H_INCLUDED_
+#define NJS_DTOA_MAX_LEN njs_length("-1.7976931348623157e+308")
+
NJS_EXPORT size_t njs_dtoa(double value, char *start);
NJS_EXPORT size_t njs_dtoa_precision(double value, char *start, size_t prec);
NJS_EXPORT size_t njs_dtoa_exponential(double value, char *start,
diff --git a/src/njs_error.c b/src/njs_error.c
index 6e12aa61..d19e0e22 100644
--- a/src/njs_error.c
+++ b/src/njs_error.c
@@ -212,12 +212,6 @@ njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name,
lhq.proto = &njs_object_hash_proto;
if (name != NULL) {
- prop = njs_object_prop_alloc(vm, name, 1);
- if (njs_slow_path(prop == NULL)) {
- goto memory_error;
- }
-
- lhq.value = prop;
lhq.key_hash = NJS_ATOM_STRING_name;
ret = njs_flathsh_unique_insert(&error->hash, &lhq);
@@ -225,17 +219,18 @@ njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name,
njs_internal_error(vm, "lvlhsh insert failed");
return NULL;
}
- }
- if (message!= NULL) {
- prop = njs_object_prop_alloc(vm, message, 1);
- if (njs_slow_path(prop == NULL)) {
- goto memory_error;
- }
+ prop = lhq.value;
- prop->enumerable = 0;
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+
+ prop->u.value = *name;
+ }
- lhq.value = prop;
+ if (message!= NULL) {
lhq.key_hash = NJS_ATOM_STRING_message;
ret = njs_flathsh_unique_insert(&error->hash, &lhq);
@@ -243,17 +238,18 @@ njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name,
njs_internal_error(vm, "lvlhsh insert failed");
return NULL;
}
- }
- if (errors != NULL) {
- prop = njs_object_prop_alloc(vm, errors, 1);
- if (njs_slow_path(prop == NULL)) {
- goto memory_error;
- }
+ prop = lhq.value;
+ prop->type = NJS_PROPERTY;
prop->enumerable = 0;
+ prop->configurable = 1;
+ prop->writable = 1;
- lhq.value = prop;
+ prop->u.value = *message;
+ }
+
+ if (errors != NULL) {
lhq.key_hash = NJS_ATOM_STRING_errors;
ret = njs_flathsh_unique_insert(&error->hash, &lhq);
@@ -261,6 +257,15 @@ njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name,
njs_internal_error(vm, "lvlhsh insert failed");
return NULL;
}
+
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 1;
+ prop->writable = 1;
+
+ prop->u.value = *errors;
}
return error;
diff --git a/src/njs_extern.c b/src/njs_extern.c
index 077d2a5f..adc8cd67 100644
--- a/src/njs_extern.c
+++ b/src/njs_extern.c
@@ -58,15 +58,6 @@ njs_external_add(njs_vm_t *vm, njs_arr_t *protos,
continue;
}
- prop = njs_object_prop_alloc(vm, &njs_value_invalid, 1);
- if (njs_slow_path(prop == NULL)) {
- goto memory_error;
- }
-
- prop->writable = external->writable;
- prop->configurable = external->configurable;
- prop->enumerable = external->enumerable;
-
if (external->flags & NJS_EXTERN_SYMBOL) {
lhq.key_hash = external->name.symbol;
@@ -81,7 +72,20 @@ njs_external_add(njs_vm_t *vm, njs_arr_t *protos,
lhq.key_hash = prop_name.atom_id;
}
- lhq.value = prop;
+ ret = njs_flathsh_unique_insert(hash, &lhq);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NJS_ERROR;
+ }
+
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = external->enumerable;
+ prop->configurable = external->configurable;
+ prop->writable = external->writable;
+ prop->u.value = njs_value_invalid;
+
switch (external->flags & NJS_EXTERN_TYPE_MASK) {
case NJS_EXTERN_METHOD:
@@ -167,12 +171,6 @@ njs_external_add(njs_vm_t *vm, njs_arr_t *protos,
break;
}
- ret = njs_flathsh_unique_insert(hash, &lhq);
- if (njs_slow_path(ret != NJS_OK)) {
- njs_internal_error(vm, "lvlhsh insert failed");
- return NJS_ERROR;
- }
-
external++;
}
@@ -224,16 +222,6 @@ njs_external_prop_handler(njs_vm_t *vm, njs_object_prop_t *self,
njs_set_object_value(retval, ov);
}
- prop = njs_object_prop_alloc(vm, retval, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
- prop->writable = self->writable;
- prop->configurable = self->configurable;
- prop->enumerable = self->enumerable;
-
- lhq.value = prop;
lhq.key_hash = atom_id;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -245,6 +233,15 @@ njs_external_prop_handler(njs_vm_t *vm, njs_object_prop_t *self,
return NJS_ERROR;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = self->enumerable;
+ prop->configurable = self->configurable;
+ prop->writable = self->writable;
+
+ prop->u.value = *retval;
+
return NJS_OK;
}
diff --git a/src/njs_flathsh.c b/src/njs_flathsh.c
index a9ef6e69..7e374c62 100644
--- a/src/njs_flathsh.c
+++ b/src/njs_flathsh.c
@@ -210,13 +210,14 @@ njs_flathsh_add_elt(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
elts = njs_hash_elts(h);
elt = &elts[h->elts_count++];
- elt->value = fhq->value;
elt->key_hash = fhq->key_hash;
cell_num = fhq->key_hash & h->hash_mask;
elt->next_elt = njs_hash_cells_end(h)[-cell_num - 1];
njs_hash_cells_end(h)[-cell_num - 1] = h->elts_count;
+ elt->type = NJS_PROPERTY;
+
return elt;
}
@@ -269,7 +270,7 @@ njs_expand_elts(njs_flathsh_query_t *fhq, njs_flathsh_descr_t *h)
njs_memzero(chunk, sizeof(uint32_t) * new_hash_size);
for (i = 0, elt = njs_hash_elts(h); i < h->elts_count; i++, elt++) {
- if (elt->value != NULL) {
+ if (elt->type != NJS_FREE_FLATHSH_ELEMENT) {
cell_num = elt->key_hash & new_hash_mask;
elt->next_elt = njs_hash_cells_end(h)[-cell_num - 1];
njs_hash_cells_end(h)[-cell_num - 1] = i + 1;
@@ -331,7 +332,7 @@ njs_flathsh_find(const njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
if (e->key_hash == fhq->key_hash &&
fhq->proto->test(fhq, e->value) == NJS_OK)
{
- fhq->value = e->value;
+ fhq->value = e;
return NJS_OK;
}
@@ -362,7 +363,7 @@ njs_flathsh_unique_find(const njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
e = &elts[elt_num - 1];
if (e->key_hash == fhq->key_hash) {
- fhq->value = e->value;
+ fhq->value = e;
return NJS_OK;
}
@@ -376,7 +377,6 @@ njs_flathsh_unique_find(const njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
njs_int_t
njs_flathsh_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
{
- void *tmp;
njs_int_t cell_num, elt_num;
njs_flathsh_elt_t *elt, *elts;
njs_flathsh_descr_t *h;
@@ -403,15 +403,10 @@ njs_flathsh_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
fhq->proto->test(fhq, elt->value) == NJS_OK)
{
if (fhq->replace) {
- tmp = fhq->value;
- fhq->value = elt->value;
- elt->value = tmp;
-
+ fhq->value = elt;
return NJS_OK;
} else {
- fhq->value = elt->value;
-
return NJS_DECLINED;
}
}
@@ -424,7 +419,7 @@ njs_flathsh_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
return NJS_ERROR;
}
- elt->value = fhq->value;
+ fhq->value = elt;
return NJS_OK;
}
@@ -433,7 +428,6 @@ njs_flathsh_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
njs_int_t
njs_flathsh_unique_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
{
- void *tmp;
njs_int_t cell_num, elt_num;
njs_flathsh_elt_t *elt, *elts;
njs_flathsh_descr_t *h;
@@ -458,15 +452,10 @@ njs_flathsh_unique_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
if (elt->key_hash == fhq->key_hash) {
if (fhq->replace) {
- tmp = fhq->value;
- fhq->value = elt->value;
- elt->value = tmp;
-
+ fhq->value = elt;
return NJS_OK;
} else {
- fhq->value = elt->value;
-
return NJS_DECLINED;
}
}
@@ -479,7 +468,7 @@ njs_flathsh_unique_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
return NJS_ERROR;
}
- elt->value = fhq->value;
+ fhq->value = elt;
return NJS_OK;
}
@@ -521,8 +510,8 @@ njs_shrink_elts(njs_flathsh_query_t *fhq, njs_flathsh_descr_t *h)
elt_src = njs_hash_elts(h_src);
for (i = 0, j = 0, elt = njs_hash_elts(h); i < h->elts_count; i++) {
- if (elt_src->value != NULL) {
- elt->value = elt_src->value;
+ if (elt_src->type != NJS_FREE_FLATHSH_ELEMENT) {
+ *elt = *elt_src;
elt->key_hash = elt_src->key_hash;
cell_num = elt_src->key_hash & new_hash_mask;
@@ -572,7 +561,7 @@ njs_flathsh_delete(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
if (elt->key_hash == fhq->key_hash &&
fhq->proto->test(fhq, elt->value) == NJS_OK)
{
- fhq->value = elt->value;
+ fhq->value = elt;
if (elt_prev != NULL) {
elt_prev->next_elt = elt->next_elt;
@@ -583,7 +572,7 @@ njs_flathsh_delete(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
h->elts_deleted_count++;
- elt->value = NULL;
+ elt->type = NJS_FREE_FLATHSH_ELEMENT;
/* Shrink elts if elts_deleted_count is eligible. */
@@ -637,7 +626,7 @@ njs_flathsh_unique_delete(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
elt = &elts[elt_num - 1];
if (elt->key_hash == fhq->key_hash) {
- fhq->value = elt->value;
+ fhq->value = elt;
if (elt_prev != NULL) {
elt_prev->next_elt = elt->next_elt;
@@ -648,7 +637,7 @@ njs_flathsh_unique_delete(njs_flathsh_t *fh, njs_flathsh_query_t *fhq)
h->elts_deleted_count++;
- elt->value = NULL;
+ elt->type = NJS_FREE_FLATHSH_ELEMENT;
/* Shrink elts if elts_deleted_count is eligible. */
@@ -695,7 +684,7 @@ njs_flathsh_each(const njs_flathsh_t *fh, njs_flathsh_each_t *fhe)
while (fhe->cp < h->elts_count) {
e = &elt[fhe->cp++];
- if (e->value != NULL) {
+ if (e->type != NJS_FREE_FLATHSH_ELEMENT) {
return e;
}
}
diff --git a/src/njs_flathsh.h b/src/njs_flathsh.h
index 985bdab1..06fd00c0 100644
--- a/src/njs_flathsh.h
+++ b/src/njs_flathsh.h
@@ -13,9 +13,18 @@ typedef struct {
typedef struct {
- uint32_t next_elt;
+ /* next_elt + property descriptor : 32 bits */
+
+ uint32_t next_elt:26;
+
+ uint32_t type:3;
+ uint32_t writable:1;
+ uint32_t enumerable:1;
+ uint32_t configurable:1;
+
uint32_t key_hash;
- void *value;
+
+ void *value[16 / sizeof(void *)];
} njs_flathsh_elt_t;
@@ -72,7 +81,7 @@ struct njs_flathsh_query_s {
#define njs_hash_elts(h) \
- ((njs_flathsh_elt_t *) ((char *) (h) + 16 /* njs_flathsh_descr_t size */))
+ ((njs_flathsh_elt_t *) ((char *) (h) + sizeof(njs_flathsh_descr_t)))
/*
@@ -91,14 +100,13 @@ NJS_EXPORT njs_int_t njs_flathsh_unique_find(const njs_flathsh_t *fh,
njs_flathsh_query_t *fhq);
/*
- * njs_flathsh_insert() adds a hash element. If the element is already
- * present in flathsh and the fhq->replace flag is zero, then fhq->value
- * is updated with the old element and NJS_DECLINED is returned.
- * If the element is already present in flathsh and the fhq->replace flag
- * is non-zero, then the old element is replaced with the new element.
- * fhq->value is updated with the old element, and NJS_OK is returned.
+ * njs_flathsh_insert() adds a hash element. If the element is already present
+ * in flathsh and the fhq->replace flag is zero, then NJS_DECLINED is returned.
+ * If the element is already present in flathsh and the fhq->replace flag is
+ * non-zero, then the old element is replaced with the new element and NJS_OK is
+ * returned.
* If the element is not present in flathsh, then it is inserted and
- * NJS_OK is returned. The fhq->value is not changed.
+ * NJS_OK is returned.
* On memory allocation failure NJS_ERROR is returned.
*
* The required njs_flathsh_query_t fields: key_hash, key, proto, replace,
@@ -175,7 +183,7 @@ typedef struct njs_flathsh_proto_s njs_lvlhsh_proto_t;
#define njs_lvlhsh_delete(lh, lhq) njs_flathsh_delete(lh, lhq)
#define njs_lvlhsh_each_init(lhe, _proto) njs_flathsh_each_init(lhe, _proto)
-njs_inline void *
+njs_inline njs_flathsh_elt_t *
njs_lvlhsh_each(const njs_flathsh_t *lh, njs_flathsh_each_t *lhe)
{
njs_flathsh_elt_t *e;
@@ -185,7 +193,7 @@ njs_lvlhsh_each(const njs_flathsh_t *lh, njs_flathsh_each_t *lhe)
return NULL;
}
- return e->value;
+ return e;
}
diff --git a/src/njs_function.c b/src/njs_function.c
index 0840d437..09bc2ebb 100644
--- a/src/njs_function.c
+++ b/src/njs_function.c
@@ -126,11 +126,25 @@ njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
- prop = njs_object_prop_alloc(vm, name, 0);
- if (njs_slow_path(prop == NULL)) {
+ lhq.key_hash = NJS_ATOM_STRING_name;
+ lhq.replace = 0;
+ lhq.pool = vm->mem_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = njs_flathsh_unique_insert(&function->object.hash, &lhq);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
return NJS_ERROR;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 1;
+ prop->writable = 0;
+ prop->u.value = *name;
+
symbol = 0;
if (njs_is_symbol(njs_prop_value(prop))) {
@@ -172,20 +186,6 @@ njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
}
}
- prop->configurable = 1;
-
- lhq.value = prop;
- lhq.key_hash = NJS_ATOM_STRING_name;
- lhq.replace = 0;
- lhq.pool = vm->mem_pool;
- lhq.proto = &njs_object_hash_proto;
-
- ret = njs_flathsh_unique_insert(&function->object.hash, &lhq);
- if (njs_slow_path(ret != NJS_OK)) {
- njs_internal_error(vm, "lvlhsh insert failed");
- return NJS_ERROR;
- }
-
return NJS_OK;
}
@@ -327,12 +327,12 @@ njs_function_prototype_thrower(njs_vm_t *vm, njs_value_t *args,
const njs_object_prop_init_t njs_arguments_object_instance_properties[] =
{
{
- .atom_id = NJS_ATOM_STRING_callee,
.desc = {
+ .atom_id = NJS_ATOM_STRING_callee,
.type = NJS_ACCESSOR,
.u.accessor = njs_accessor(njs_function_prototype_thrower, 0,
njs_function_prototype_thrower, 0),
- .writable = NJS_ATTRIBUTE_UNSET,
+ .writable = 0,
},
},
};
@@ -394,6 +394,19 @@ njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function,
lambda = function->u.lambda;
+ /*
+ * Lambda frame has the following layout:
+ * njs_frame_t | p0 , p2, ..., pn | v0, v1, ..., vn
+ * where:
+ * p0, p1, ..., pn - pointers to arguments and locals,
+ * v0, v1, ..., vn - values of arguments and locals.
+ * n - number of arguments + locals.
+ *
+ * Normally, the pointers point to the values directly after them,
+ * but if a value was captured as a closure by an inner function,
+ * pn points to a value allocated from the heap.
+ */
+
args_count = njs_max(nargs, lambda->nargs);
value_count = args_count + lambda->nlocal;
@@ -523,7 +536,6 @@ njs_function_lambda_call(njs_vm_t *vm, njs_value_t *retval, void *promise_cap)
njs_value_t *args, **local, *value;
njs_value_t **cur_local, **cur_closures;
njs_function_t *function;
- njs_declaration_t *declr;
njs_function_lambda_t *lambda;
frame = (njs_frame_t *) vm->top_frame;
@@ -582,29 +594,6 @@ njs_function_lambda_call(njs_vm_t *vm, njs_value_t *retval, void *promise_cap)
vm->active_frame = frame;
- /* Closures */
-
- n = lambda->ndeclarations;
-
- while (n != 0) {
- n--;
-
- declr = &lambda->declarations[n];
- value = njs_scope_value(vm, declr->index);
-
- *value = *declr->value;
-
- function = njs_function_value_copy(vm, value);
- if (njs_slow_path(function == NULL)) {
- return NJS_ERROR;
- }
-
- ret = njs_function_capture_closure(vm, function, function->u.lambda);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
- }
-
ret = njs_vmcode_interpreter(vm, lambda->start, retval, promise_cap, NULL);
/* Restore current level. */
@@ -699,11 +688,11 @@ njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *native)
njs_int_t
njs_function_frame_save(njs_vm_t *vm, njs_frame_t *frame, u_char *pc)
{
- size_t args_count, value_count, n;
- njs_value_t *start, *end, *p, **new, *value, **local;
- njs_function_t *function;
+ size_t args_count, value_count, n;
+ njs_value_t **map, *value, **current_map;
+ njs_function_t *function;
+ njs_native_frame_t *active, *native;
njs_function_lambda_t *lambda;
- njs_native_frame_t *active, *native;
*frame = *vm->active_frame;
@@ -721,34 +710,37 @@ njs_function_frame_save(njs_vm_t *vm, njs_frame_t *frame, u_char *pc)
args_count = njs_max(native->nargs, lambda->nargs);
value_count = args_count + lambda->nlocal;
- new = (njs_value_t **) ((u_char *) native + NJS_FRAME_SIZE);
- value = (njs_value_t *) (new + value_count);
-
- native->arguments = value;
- native->local = new + njs_function_frame_args_count(active);
- native->pc = pc;
+ /*
+ * We need to save the current frame state because it will be freed
+ * when the function returns.
+ *
+ * To detect whether a value is captured as a closure,
+ * we check whether the pointer is within the frame. In this case
+ * the pointer is copied as is because the value it points to
+ * is already allocated in the heap and will not be freed.
+ * See njs_function_capture_closure() and njs_function_lambda_frame()
+ * for details.
+ */
- start = njs_function_frame_values(active, &end);
- p = native->arguments;
+ map = (njs_value_t **) ((u_char *) native + NJS_FRAME_SIZE);
+ value = (njs_value_t *) (map + value_count);
- while (start < end) {
- njs_value_assign(p, start++);
- *new++ = p++;
- }
+ current_map = (njs_value_t **) ((u_char *) active + NJS_FRAME_SIZE);
- /* Move all arguments. */
+ for (n = 0; n < value_count; n++) {
+ if (njs_is_value_allocated_on_frame(active, current_map[n])) {
+ map[n] = &value[n];
+ njs_value_assign(&value[n], current_map[n]);
- p = native->arguments;
- local = native->local + 1 /* this */;
-
- for (n = 0; n < function->args_count; n++) {
- if (!njs_is_valid(p)) {
- njs_set_undefined(p);
+ } else {
+ map[n] = current_map[n];
}
-
- *local++ = p++;
}
+ native->arguments = value;
+ native->local = map + args_count;
+ native->pc = pc;
+
return NJS_OK;
}
@@ -770,7 +762,6 @@ njs_int_t
njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
njs_function_lambda_t *lambda)
{
- void *start, *end;
uint32_t n;
njs_value_t *value, **closure;
njs_native_frame_t *frame;
@@ -785,9 +776,6 @@ njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
frame = frame->previous;
}
- start = frame;
- end = frame->free;
-
closure = njs_function_closures(function);
n = lambda->nclosures;
@@ -796,7 +784,7 @@ njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
value = njs_scope_value(vm, lambda->closures[n]);
- if (start <= (void *) value && (void *) value < end) {
+ if (njs_is_value_allocated_on_frame(frame, value)) {
value = njs_scope_value_clone(vm, lambda->closures[n], value);
if (njs_slow_path(value == NULL)) {
return NJS_ERROR;
@@ -812,14 +800,14 @@ njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
njs_inline njs_value_t *
-njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index,
- void *start, void *end)
+njs_function_closure_value(njs_vm_t *vm, njs_native_frame_t *frame,
+ njs_value_t **scope, njs_index_t index)
{
njs_value_t *value, *newval;
value = scope[njs_scope_index_value(index)];
- if (start <= (void *) value && end > (void *) value) {
+ if (njs_is_value_allocated_on_frame(frame, value)) {
newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
if (njs_slow_path(newval == NULL)) {
njs_memory_error(vm);
@@ -839,7 +827,6 @@ njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index,
njs_int_t
njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
{
- void *start, *end;
uint32_t n;
njs_value_t *value, **refs, **global;
njs_index_t *indexes, index;
@@ -858,9 +845,6 @@ njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
native = native->previous;
}
- start = native;
- end = native->free;
-
indexes = lambda->closures;
refs = njs_function_closures(function);
@@ -875,12 +859,12 @@ njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
switch (njs_scope_index_type(index)) {
case NJS_LEVEL_LOCAL:
- value = njs_function_closure_value(vm, native->local, index,
- start, end);
+ value = njs_function_closure_value(vm, native, native->local,
+ index);
break;
case NJS_LEVEL_GLOBAL:
- value = njs_function_closure_value(vm, global, index, start, end);
+ value = njs_function_closure_value(vm, native, global, index);
break;
default:
@@ -910,14 +894,6 @@ njs_function_property_prototype_set(njs_vm_t *vm, njs_flathsh_t *hash,
njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
- prop = njs_object_prop_alloc(vm, prototype, 0);
- if (njs_slow_path(prop == NULL)) {
- return NULL;
- }
-
- prop->writable = 1;
-
- lhq.value = prop;
lhq.key_hash = NJS_ATOM_STRING_prototype;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -925,13 +901,21 @@ njs_function_property_prototype_set(njs_vm_t *vm, njs_flathsh_t *hash,
ret = njs_flathsh_unique_insert(hash, &lhq);
- if (njs_fast_path(ret == NJS_OK)) {
- return njs_prop_value(prop);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NULL;
}
- njs_internal_error(vm, "lvlhsh insert failed");
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 0;
+ prop->writable = 1;
+ prop->u.value = *prototype;
+
+ return njs_prop_value(prop);
- return NULL;
}
@@ -950,9 +934,8 @@ njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
uint32_t unused, njs_value_t *value, njs_value_t *setval,
njs_value_t *retval)
{
- njs_value_t *proto, proto_value, *cons;
- njs_object_t *prototype;
- njs_function_t *function;
+ njs_value_t *proto, proto_value, *cons;
+ njs_object_t *prototype;
if (setval == NULL) {
prototype = njs_object_alloc(vm);
@@ -965,11 +948,6 @@ njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
setval = &proto_value;
}
- function = njs_function_value_copy(vm, value);
- if (njs_slow_path(function == NULL)) {
- return NJS_ERROR;
- }
-
proto = njs_function_property_prototype_set(vm, njs_object_hash(value),
setval);
if (njs_slow_path(proto == NULL)) {
@@ -1049,7 +1027,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
}
}
- njs_chb_append_literal(&chain, "){");
+ njs_chb_append_literal(&chain, "\n){\n");
if (nargs > 1) {
ret = njs_value_to_chain(vm, &chain, njs_argument(args, nargs - 1));
@@ -1058,7 +1036,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
}
}
- njs_chb_append_literal(&chain, "})");
+ njs_chb_append_literal(&chain, "\n})");
ret = njs_chb_join(&chain, &str);
if (njs_slow_path(ret != NJS_OK)) {
@@ -1125,7 +1103,15 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_chb_destroy(&chain);
- lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
+ if ((code->end - code->start)
+ != (sizeof(njs_vmcode_function_t) + sizeof(njs_vmcode_return_t))
+ || ((njs_vmcode_generic_t *) code->start)->code != NJS_VMCODE_FUNCTION)
+ {
+ njs_syntax_error(vm, "single function literal required");
+ return NJS_ERROR;
+ }
+
+ lambda = ((njs_vmcode_function_t *) code->start)->lambda;
function = njs_function_alloc(vm, lambda, (njs_bool_t) async);
if (njs_slow_path(function == NULL)) {
@@ -1446,23 +1432,23 @@ static const njs_object_prop_init_t njs_function_prototype_properties[] =
NJS_DECLARE_PROP_NATIVE(STRING_bind, njs_function_prototype_bind, 1, 0),
{
- .atom_id = NJS_ATOM_STRING_caller,
.desc = {
+ .atom_id = NJS_ATOM_STRING_caller,
.type = NJS_ACCESSOR,
.u.accessor = njs_accessor(njs_function_prototype_thrower, 0,
njs_function_prototype_thrower, 0),
- .writable = NJS_ATTRIBUTE_UNSET,
+ .writable = 0,
.configurable = 1,
},
},
{
- .atom_id = NJS_ATOM_STRING_arguments,
.desc = {
+ .atom_id = NJS_ATOM_STRING_arguments,
.type = NJS_ACCESSOR,
.u.accessor = njs_accessor(njs_function_prototype_thrower, 0,
njs_function_prototype_thrower, 0),
- .writable = NJS_ATTRIBUTE_UNSET,
+ .writable = 0,
.configurable = 1,
},
},
diff --git a/src/njs_function.h b/src/njs_function.h
index 6516ff78..1b5de544 100644
--- a/src/njs_function.h
+++ b/src/njs_function.h
@@ -13,9 +13,6 @@ struct njs_function_lambda_s {
uint32_t nclosures;
uint32_t nlocal;
- njs_declaration_t *declarations;
- uint32_t ndeclarations;
-
njs_index_t self;
uint32_t nargs;
@@ -205,29 +202,15 @@ njs_function_frame_size(njs_native_frame_t *frame)
}
-njs_inline size_t
-njs_function_frame_args_count(njs_native_frame_t *frame)
-{
- uintptr_t start;
-
- start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE);
-
- return ((uintptr_t) frame->local - start) / sizeof(njs_value_t *);
-}
-
-
-njs_inline njs_value_t *
-njs_function_frame_values(njs_native_frame_t *frame, njs_value_t **end)
+njs_inline njs_bool_t
+njs_is_value_allocated_on_frame(njs_native_frame_t *frame, njs_value_t *value)
{
- size_t count;
- uintptr_t start;
-
- start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE);
- count = ((uintptr_t) frame->arguments - start) / sizeof(njs_value_t *);
+ void *start, *end;
- *end = frame->arguments + count;
+ start = frame;
+ end = frame->free;
- return frame->arguments;
+ return start <= (void *) value && (void *) value < end;
}
diff --git a/src/njs_generator.c b/src/njs_generator.c
index a6209cce..bb443017 100644
--- a/src/njs_generator.c
+++ b/src/njs_generator.c
@@ -890,11 +890,10 @@ static njs_int_t
njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_variable_t *var;
- njs_parser_scope_t *scope;
- njs_vmcode_variable_t *variable;
- njs_vmcode_function_copy_t *copy;
+ njs_int_t ret;
+ njs_variable_t *var;
+ njs_parser_scope_t *scope;
+ njs_vmcode_variable_t *variable;
var = njs_variable_reference(vm, node);
if (njs_slow_path(var == NULL)) {
@@ -906,13 +905,6 @@ njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
return njs_generator_stack_pop(vm, generator, NULL);
}
- if (var->function && var->type == NJS_VARIABLE_FUNCTION) {
- njs_generate_code(generator, njs_vmcode_function_copy_t, copy,
- NJS_VMCODE_FUNCTION_COPY, node);
- copy->function = &var->value;
- copy->retval = node->index;
- }
-
if (var->init) {
return njs_generator_stack_pop(vm, generator, NULL);
}
@@ -935,10 +927,9 @@ static njs_int_t
njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
{
- njs_variable_t *var;
- njs_parser_scope_t *scope;
- njs_vmcode_variable_t *variable;
- njs_vmcode_function_copy_t *copy;
+ njs_variable_t *var;
+ njs_parser_scope_t *scope;
+ njs_vmcode_variable_t *variable;
var = njs_variable_reference(vm, node);
@@ -958,13 +949,6 @@ njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
}
}
- if (var->function && var->type == NJS_VARIABLE_FUNCTION) {
- njs_generate_code(generator, njs_vmcode_function_copy_t, copy,
- NJS_VMCODE_FUNCTION_COPY, node);
- copy->function = &var->value;
- copy->retval = node->index;
- }
-
if (var->init) {
return NJS_OK;
}
@@ -4293,7 +4277,6 @@ njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev,
const njs_str_t *name)
{
njs_int_t ret;
- njs_arr_t *arr;
njs_uint_t depth;
njs_vm_code_t *code;
njs_generator_t generator;
@@ -4327,10 +4310,6 @@ njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev,
lambda->nclosures = generator.closures->items;
lambda->nlocal = node->scope->items;
- arr = node->scope->declarations;
- lambda->declarations = (arr != NULL) ? arr->start : NULL;
- lambda->ndeclarations = (arr != NULL) ? arr->items : 0;
-
return NJS_OK;
}
@@ -4339,11 +4318,15 @@ njs_vm_code_t *
njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_scope_t *scope, const njs_str_t *name)
{
- u_char *p;
- int64_t nargs;
- njs_int_t ret;
- njs_uint_t index;
- njs_vm_code_t *code;
+ u_char *p, *code_start;
+ size_t code_size, prelude;
+ int64_t nargs;
+ njs_int_t ret;
+ njs_arr_t *arr;
+ njs_uint_t index, n;
+ njs_vm_code_t *code;
+ njs_declaration_t *declr;
+ njs_vmcode_function_t *fun;
generator->code_size = 128;
@@ -4414,6 +4397,45 @@ njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator,
} while (generator->state != NULL);
+ arr = scope->declarations;
+ scope->declarations = NULL;
+
+ if (arr != NULL && arr->items > 0) {
+ declr = arr->start;
+ prelude = sizeof(njs_vmcode_function_t) * arr->items;
+ code_size = generator->code_end - generator->code_start;
+
+ for (n = 0; n < arr->items; n++) {
+ fun = (njs_vmcode_function_t *) njs_generate_reserve(vm,
+ generator, sizeof(njs_vmcode_function_t));
+ if (njs_slow_path(fun == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ generator->code_end += sizeof(njs_vmcode_function_t);
+ fun->code = NJS_VMCODE_FUNCTION;
+
+ fun->lambda = declr[n].lambda;
+ fun->async = declr[n].async;
+ fun->retval = declr[n].index;
+ }
+
+ code_start = njs_mp_alloc(vm->mem_pool, code_size + prelude);
+ if (njs_slow_path(code_start == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ memcpy(code_start, &generator->code_start[code_size], prelude);
+ memcpy(&code_start[prelude], generator->code_start, code_size);
+
+ njs_mp_free(vm->mem_pool, generator->code_start);
+
+ generator->code_start = code_start;
+ generator->code_end = code_start + code_size + prelude;
+ }
+
code = njs_arr_item(vm->codes, index);
code->start = generator->code_start;
code->end = generator->code_end;
@@ -5491,7 +5513,7 @@ njs_generate_reference_error(njs_vm_t *vm, njs_generator_t *generator,
ref_err->type = NJS_OBJ_TYPE_REF_ERROR;
- njs_lexer_entry(vm, node->u.reference.atom_id, &entry);
+ njs_atom_string_get(vm, node->u.reference.atom_id, &entry);
return njs_name_copy(vm, &ref_err->u.name, &entry);
}
diff --git a/src/njs_json.c b/src/njs_json.c
index e79b1eb4..49c2b7e7 100644
--- a/src/njs_json.c
+++ b/src/njs_json.c
@@ -394,12 +394,6 @@ njs_json_parse_object(njs_json_parse_ctx_t *ctx, njs_value_t *value,
return NULL;
}
- prop = njs_object_prop_alloc(ctx->vm, &prop_value, 1);
- if (njs_slow_path(prop == NULL)) {
- goto memory_error;
- }
-
- lhq.value = prop;
lhq.key_hash = prop_name.atom_id;
lhq.replace = 1;
lhq.pool = ctx->pool;
@@ -411,6 +405,14 @@ njs_json_parse_object(njs_json_parse_ctx_t *ctx, njs_value_t *value,
return NULL;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = prop_value;
+
p = njs_json_skip_space(p, ctx->end);
if (njs_slow_path(p == ctx->end)) {
goto error_end;
@@ -1609,12 +1611,6 @@ njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper,
wrapper->type = NJS_OBJECT;
wrapper->data.truth = 1;
- prop = njs_object_prop_alloc(vm, value, 1);
- if (njs_slow_path(prop == NULL)) {
- return NULL;
- }
-
- lhq.value = prop;
lhq.key_hash = NJS_ATOM_STRING_empty;
lhq.replace = 0;
lhq.pool = vm->mem_pool;
@@ -1625,6 +1621,14 @@ njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper,
return NULL;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = *value;
+
return wrapper->data.u.object;
}
diff --git a/src/njs_lexer.h b/src/njs_lexer.h
index 42f612a6..3728e253 100644
--- a/src/njs_lexer.h
+++ b/src/njs_lexer.h
@@ -288,16 +288,6 @@ const njs_lexer_keyword_entry_t *njs_lexer_keyword(const u_char *key,
njs_int_t njs_lexer_keywords(njs_arr_t *array);
-njs_inline void
-njs_lexer_entry(njs_vm_t *vm, uintptr_t atom_id, njs_str_t *entry)
-{
- njs_value_t value;
-
- njs_atom_to_value(vm, &value, atom_id);
- njs_string_get(vm, &value, entry);
-}
-
-
njs_inline njs_bool_t
njs_lexer_token_is_keyword(njs_lexer_token_t *token)
{
diff --git a/src/njs_module.c b/src/njs_module.c
index 5af174f3..7d58972c 100644
--- a/src/njs_module.c
+++ b/src/njs_module.c
@@ -13,7 +13,7 @@ njs_module_hash_test(njs_lvlhsh_query_t *lhq, void *data)
{
njs_mod_t *module;
- module = data;
+ module = *(njs_mod_t **) data;
if (njs_strstr_eq(&lhq->key, &module->name)) {
return NJS_OK;
@@ -39,6 +39,7 @@ njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared)
njs_int_t ret;
njs_mod_t *shrd, *module;
njs_object_t *object;
+ njs_object_prop_t *prop;
njs_lvlhsh_query_t lhq;
lhq.key = *name;
@@ -46,11 +47,11 @@ njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared)
lhq.proto = &njs_modules_hash_proto;
if (njs_lvlhsh_find(&vm->modules_hash, &lhq) == NJS_OK) {
- return lhq.value;
+ return njs_prop_module(lhq.value);
}
if (njs_lvlhsh_find(&vm->shared->modules_hash, &lhq) == NJS_OK) {
- shrd = lhq.value;
+ shrd = njs_prop_module(lhq.value);
if (shared) {
return shrd;
@@ -70,13 +71,19 @@ njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared)
}
lhq.replace = 0;
- lhq.value = module;
lhq.pool = vm->mem_pool;
ret = njs_lvlhsh_insert(&vm->modules_hash, &lhq);
- if (njs_fast_path(ret == NJS_OK)) {
- return module;
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NULL;
}
+
+ prop = lhq.value;
+
+ prop->u.mod = module;
+
+ return module;
+
}
return NULL;
@@ -88,6 +95,7 @@ njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_value_t *value)
{
njs_int_t ret;
njs_mod_t *module;
+ njs_object_prop_t *prop;
njs_lvlhsh_query_t lhq;
module = njs_mp_zalloc(vm->mem_pool, sizeof(njs_mod_t));
@@ -105,7 +113,6 @@ njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_value_t *value)
lhq.replace = 0;
lhq.key = *name;
lhq.key_hash = njs_djb_hash(name->start, name->length);
- lhq.value = module;
lhq.pool = vm->mem_pool;
lhq.proto = &njs_modules_hash_proto;
@@ -115,6 +122,10 @@ njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_value_t *value)
return NULL;
}
+ prop = lhq.value;
+
+ prop->u.mod = module;
+
if (value != NULL) {
njs_value_assign(&module->value, value);
module->function.native = 1;
diff --git a/src/njs_object.c b/src/njs_object.c
index 63273628..0c592176 100644
--- a/src/njs_object.c
+++ b/src/njs_object.c
@@ -157,6 +157,7 @@ njs_object_hash_create(njs_vm_t *vm, njs_flathsh_t *hash,
const njs_object_prop_init_t *prop, njs_uint_t n)
{
njs_int_t ret;
+ njs_object_prop_t *obj_prop;
njs_flathsh_query_t lhq;
lhq.replace = 0;
@@ -164,7 +165,7 @@ njs_object_hash_create(njs_vm_t *vm, njs_flathsh_t *hash,
lhq.pool = vm->mem_pool;
while (n != 0) {
- lhq.key_hash = prop->atom_id;
+ lhq.key_hash = prop->desc.atom_id;
lhq.value = (void *) prop;
ret = njs_flathsh_unique_insert(hash, &lhq);
@@ -173,6 +174,14 @@ njs_object_hash_create(njs_vm_t *vm, njs_flathsh_t *hash,
return NJS_ERROR;
}
+ obj_prop = lhq.value;
+
+ obj_prop->type = prop->desc.type;
+ obj_prop->enumerable = prop->desc.enumerable;
+ obj_prop->configurable = prop->desc.configurable;
+ obj_prop->writable = prop->desc.writable;
+ obj_prop->u.value = prop->desc.u.value;
+
prop++;
n--;
}
@@ -939,7 +948,7 @@ njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object,
break;
}
- prop = elt->value;
+ prop = (njs_object_prop_t *) elt;
ret = njs_atom_to_value(vm, &prop_name, elt->key_hash);
if (ret != NJS_OK) {
@@ -980,7 +989,7 @@ njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object,
} else {
- if (!(((njs_object_prop_t *)(lhq.value))->enumerable
+ if (!(((njs_object_prop_t *) (lhq.value))->enumerable
|| !(flags & NJS_ENUM_ENUMERABLE_ONLY)))
{
continue;
@@ -993,13 +1002,7 @@ njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object,
njs_object_prop_t *hash_prop = lhq.value;
- /* select names of prop which are not deleted and
- * not deleted and created again i.e.,
- * they are replaced shared hash props
- */
- if (hash_prop->type != NJS_WHITEOUT &&
- !(hash_prop->enum_in_object_hash))
- {
+ if (hash_prop->type != NJS_WHITEOUT) {
njs_process_prop(vm, &prop_name, flags, items_string,
items_symbol);
}
@@ -1018,7 +1021,7 @@ local_hash:
break;
}
- prop = elt->value;
+ prop = (njs_object_prop_t *) elt;
ret = njs_atom_to_value(vm, &prop_name, elt->key_hash);
if (ret != NJS_OK) {
@@ -1060,8 +1063,7 @@ local_hash:
} else {
/* prop is: in_hash && in_shared_hash */
- /* select names of not deleted and created again */
- if (prop->enum_in_object_hash) {
+ if (prop->type == NJS_WHITEOUT) {
njs_process_prop(vm, &prop_name, flags, items_string,
items_symbol);
}
@@ -1222,7 +1224,7 @@ njs_object_copy_shared_hash(njs_vm_t *vm, njs_object_t *object)
njs_int_t ret;
njs_value_t prop_name;
njs_flathsh_t new_hash, *shared_hash;
- njs_object_prop_t *prop;
+ njs_object_prop_t *prop, *obj_prop;
njs_flathsh_elt_t *elt;
njs_flathsh_each_t fhe;
njs_flathsh_query_t fhq;
@@ -1242,7 +1244,7 @@ njs_object_copy_shared_hash(njs_vm_t *vm, njs_object_t *object)
break;
}
- prop = elt->value;
+ prop = (njs_object_prop_t *) elt;
ret = njs_atom_to_value(vm, &prop_name, elt->key_hash);
if (ret != NJS_OK) {
@@ -1258,13 +1260,19 @@ njs_object_copy_shared_hash(njs_vm_t *vm, njs_object_t *object)
fhq.key_hash = elt->key_hash;
}
- fhq.value = prop;
-
ret = njs_flathsh_unique_insert(&new_hash, &fhq);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "flathsh insert failed");
return NJS_ERROR;
}
+
+ obj_prop = fhq.value;
+
+ obj_prop->type = prop->type;
+ obj_prop->enumerable = prop->enumerable;
+ obj_prop->configurable = prop->configurable;
+ obj_prop->writable = prop->writable;
+ obj_prop->u.value = prop->u.value;
}
object->shared_hash = new_hash;
@@ -1281,7 +1289,7 @@ njs_object_make_shared(njs_vm_t *vm, njs_object_t *object)
njs_object_t **start;
njs_value_t value, *key;
njs_traverse_t *s;
- njs_object_prop_t *prop;
+ njs_object_prop_t *prop, *obj_prop;
njs_property_query_t pq;
njs_traverse_t state[NJS_TRAVERSE_MAX_DEPTH];
@@ -1360,6 +1368,14 @@ njs_object_make_shared(njs_vm_t *vm, njs_object_t *object)
return NJS_ERROR;
}
+ obj_prop = pq.lhq.value;
+
+ obj_prop->type = prop->type;
+ obj_prop->enumerable = prop->enumerable;
+ obj_prop->configurable = prop->configurable;
+ obj_prop->writable = prop->writable;
+ obj_prop->u.value = prop->u.value;
+
njs_value_assign(&value, njs_prop_value(prop));
if (njs_is_object(&value)
@@ -1659,7 +1675,7 @@ njs_object_get_own_property_descriptors(njs_vm_t *vm, njs_value_t *args,
njs_array_t *names;
njs_value_t descriptor, *value, *key;
njs_object_t *descriptors;
- njs_object_prop_t *pr;
+ njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
value = njs_arg(args, nargs, 1);
@@ -1697,20 +1713,22 @@ njs_object_get_own_property_descriptors(njs_vm_t *vm, njs_value_t *args,
goto done;
}
- pr = njs_object_prop_alloc(vm, &descriptor, 1);
- if (njs_slow_path(pr == NULL)) {
- ret = NJS_ERROR;
- goto done;
- }
-
lhq.key_hash = key->atom_id;
- lhq.value = pr;
ret = njs_flathsh_unique_insert(&descriptors->hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "lvlhsh insert failed");
goto done;
}
+
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+
+ prop->u.value = descriptor;
}
ret = NJS_OK;
@@ -1889,7 +1907,7 @@ njs_object_set_integrity_level(njs_vm_t *vm, njs_value_t *args,
break;
}
- prop = elt->value;
+ prop = (njs_object_prop_t *) elt;
if (level == NJS_OBJECT_INTEGRITY_FROZEN
&& !njs_is_accessor_descriptor(prop))
@@ -1949,7 +1967,7 @@ njs_object_test_integrity_level(njs_vm_t *vm, njs_value_t *args,
break;
}
- prop = elt->value;
+ prop = (njs_object_prop_t *) elt;
if (prop->configurable) {
goto done;
@@ -2172,15 +2190,6 @@ njs_property_prototype_create(njs_vm_t *vm, njs_flathsh_t *hash,
njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
- prop = njs_object_prop_alloc(vm, &njs_value_undefined, 0);
- if (njs_slow_path(prop == NULL)) {
- return NULL;
- }
-
- lhq.value = prop;
-
- njs_set_type_object(njs_prop_value(prop), prototype, prototype->type);
-
lhq.key_hash = NJS_ATOM_STRING_prototype;
lhq.replace = 1;
@@ -2189,13 +2198,21 @@ njs_property_prototype_create(njs_vm_t *vm, njs_flathsh_t *hash,
ret = njs_flathsh_unique_insert(hash, &lhq);
- if (njs_fast_path(ret == NJS_OK)) {
- return njs_prop_value(prop);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NULL;
}
- njs_internal_error(vm, "lvlhsh insert failed");
+ prop = lhq.value;
- return NULL;
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 0;
+ prop->writable = 0;
+
+ njs_set_type_object(njs_prop_value(prop), prototype, prototype->type);
+
+ return njs_prop_value(prop);
}
@@ -2433,16 +2450,6 @@ njs_property_constructor_set(njs_vm_t *vm, njs_flathsh_t *hash,
njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
- prop = njs_object_prop_alloc(vm, constructor, 1);
- if (njs_slow_path(prop == NULL)) {
- return NULL;
- }
-
- njs_value_assign(njs_prop_value(prop), constructor);
- prop->enumerable = 0;
-
- lhq.value = prop;
-
lhq.key_hash = NJS_ATOM_STRING_constructor;
lhq.replace = 1;
@@ -2450,13 +2457,21 @@ njs_property_constructor_set(njs_vm_t *vm, njs_flathsh_t *hash,
lhq.proto = &njs_object_hash_proto;
ret = njs_flathsh_unique_insert(hash, &lhq);
- if (njs_fast_path(ret == NJS_OK)) {
- return njs_prop_value(prop);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert/replace failed");
+ return NULL;
}
- njs_internal_error(vm, "lvlhsh insert/replace failed");
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 0;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = *constructor;
+
+ return njs_prop_value(prop);
- return NULL;
}
diff --git a/src/njs_object.h b/src/njs_object.h
index 6b2438df..dd5334d2 100644
--- a/src/njs_object.h
+++ b/src/njs_object.h
@@ -101,8 +101,8 @@ njs_int_t njs_object_length(njs_vm_t *vm, njs_value_t *value, int64_t *dst);
njs_int_t njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq,
njs_object_t *proto);
-njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm,
- const njs_value_t *value, uint8_t attributes);
+void njs_object_prop_init(njs_object_prop_t *prop, njs_object_prop_type_t type,
+ uint8_t attributes);
njs_int_t njs_object_property(njs_vm_t *vm, njs_object_t *object,
njs_flathsh_query_t *lhq, njs_value_t *retval);
njs_object_prop_t *njs_object_property_add(njs_vm_t *vm, njs_value_t *object,
@@ -114,7 +114,7 @@ njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
njs_int_t njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
const char *njs_prop_type_string(njs_object_prop_type_t type);
-njs_int_t njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init,
+njs_int_t njs_object_props_init(njs_vm_t *vm, const njs_object_init_t* init,
njs_object_prop_t *base, uint32_t atom_id, njs_value_t *value,
njs_value_t *retval);
@@ -122,10 +122,11 @@ njs_int_t njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init,
njs_inline njs_bool_t
njs_is_data_descriptor(njs_object_prop_t *prop)
{
- return prop->writable != NJS_ATTRIBUTE_UNSET
- || (prop->type != NJS_ACCESSOR && njs_is_valid(njs_prop_value(prop)))
- || prop->type == NJS_PROPERTY_HANDLER;
-
+ return (prop->type == NJS_PROPERTY && njs_is_valid(njs_prop_value(prop)))
+ || prop->type == NJS_PROPERTY_HANDLER
+ || prop->type == NJS_PROPERTY_REF
+ || prop->type == NJS_PROPERTY_PLACE_REF
+ || prop->type == NJS_PROPERTY_TYPED_ARRAY_REF;
}
@@ -136,13 +137,6 @@ njs_is_accessor_descriptor(njs_object_prop_t *prop)
}
-njs_inline njs_bool_t
-njs_is_generic_descriptor(njs_object_prop_t *prop)
-{
- return !njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop);
-}
-
-
njs_inline njs_int_t
njs_primitive_value_to_key(njs_vm_t *vm, njs_value_t *dst,
const njs_value_t *src)
@@ -241,18 +235,17 @@ njs_key_string_get(njs_vm_t *vm, njs_value_t *key, njs_str_t *str)
}
-njs_inline njs_int_t
+njs_inline void
njs_atom_string_get(njs_vm_t *vm, uint32_t atom_id, njs_str_t *str)
{
njs_value_t value;
if (njs_atom_to_value(vm, &value, atom_id) != NJS_OK) {
- return NJS_ERROR;
+ str->start = (u_char *) "unknown";
+ str->length = njs_length("unknown");
}
njs_key_string_get(vm, &value, str);
-
- return NJS_OK;
}
diff --git a/src/njs_object_prop.c b/src/njs_object_prop.c
index d7c55d3a..6b93960c 100644
--- a/src/njs_object_prop.c
+++ b/src/njs_object_prop.c
@@ -8,57 +8,20 @@
#include <njs_main.h>
-static njs_object_prop_t *njs_object_prop_alloc2(njs_vm_t *vm,
- njs_object_prop_type_t type, unsigned flags);
static njs_object_prop_t *njs_descriptor_prop(njs_vm_t *vm,
- const njs_value_t *desc);
+ const njs_value_t *desc, njs_object_prop_t *prop,
+ uint32_t *unset_enumerable, uint32_t *unset_configuarble,
+ uint32_t *enum_writable);
-njs_object_prop_t *
-njs_object_prop_alloc(njs_vm_t *vm,
- const njs_value_t *value, uint8_t attributes)
-{
- unsigned flags;
- njs_object_prop_t *prop;
-
- switch (attributes) {
- case NJS_ATTRIBUTE_FALSE:
- case NJS_ATTRIBUTE_TRUE:
- flags = attributes ? NJS_OBJECT_PROP_VALUE_ECW : 0;
- break;
-
- case NJS_ATTRIBUTE_UNSET:
- default:
- flags = NJS_OBJECT_PROP_UNSET;
- break;
- }
-
- prop = njs_object_prop_alloc2(vm, NJS_PROPERTY, flags);
- if (njs_slow_path(prop == NULL)) {
- return NULL;
- }
-
- njs_value_assign(njs_prop_value(prop), value);
-
- return prop;
-}
-
-
-static njs_object_prop_t *
-njs_object_prop_alloc2(njs_vm_t *vm,
- njs_object_prop_type_t type, unsigned flags)
+void
+njs_object_prop_init(njs_object_prop_t *prop, njs_object_prop_type_t type,
+ uint8_t flags)
{
- njs_object_prop_t *prop;
-
- prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
- sizeof(njs_object_prop_t));
- if (njs_slow_path(prop == NULL)) {
- njs_memory_error(vm);
- return NULL;
- }
+ prop->next_elt = 0;
+ prop->atom_id = 0;
prop->type = type;
- prop->enum_in_object_hash = 0;
if (flags != NJS_OBJECT_PROP_UNSET) {
prop->enumerable = !!(flags & NJS_OBJECT_PROP_ENUMERABLE);
@@ -68,16 +31,14 @@ njs_object_prop_alloc2(njs_vm_t *vm,
prop->writable = !!(flags & NJS_OBJECT_PROP_WRITABLE);
} else {
- prop->writable = NJS_ATTRIBUTE_UNSET;
+ prop->writable = 0;
}
} else {
- prop->enumerable = NJS_ATTRIBUTE_UNSET;
- prop->configurable = NJS_ATTRIBUTE_UNSET;
- prop->writable = NJS_ATTRIBUTE_UNSET;
+ prop->enumerable = 0;
+ prop->configurable = 0;
+ prop->writable = 0;
}
-
- return prop;
}
@@ -142,12 +103,6 @@ njs_object_property_add(njs_vm_t *vm, njs_value_t *object, unsigned atom_id,
njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
- prop = njs_object_prop_alloc(vm, &njs_value_invalid, 1);
- if (njs_slow_path(prop == NULL)) {
- return NULL;
- }
-
- lhq.value = prop;
lhq.key_hash = atom_id;
lhq.replace = replace;
lhq.pool = vm->mem_pool;
@@ -159,6 +114,14 @@ njs_object_property_add(njs_vm_t *vm, njs_value_t *object, unsigned atom_id,
return NULL;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = njs_value_invalid;
+
return prop;
}
@@ -170,15 +133,21 @@ njs_int_t
njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, unsigned atom_id,
njs_value_t *value, unsigned flags)
{
- uint32_t length, index;
+ uint32_t length, index, set_enumerable, set_configurable,
+ set_writable;
njs_int_t ret;
njs_array_t *array;
njs_value_t key, retval;
- njs_object_prop_t *prop, *prev;
+ njs_object_prop_t _prop;
+ njs_object_prop_t *prop = &_prop, *prev, *obj_prop;
njs_property_query_t pq;
again:
+ set_enumerable = 1;
+ set_configurable = 1;
+ set_writable = 1;
+
njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
ret = (flags & NJS_OBJECT_PROP_CREATE)
@@ -191,7 +160,8 @@ again:
switch (njs_prop_type(flags)) {
case NJS_OBJECT_PROP_DESCRIPTOR:
- prop = njs_descriptor_prop(vm, value);
+ prop = njs_descriptor_prop(vm, value, prop, &set_enumerable,
+ &set_configurable, &set_writable);
if (njs_slow_path(prop == NULL)) {
return NJS_ERROR;
}
@@ -212,12 +182,8 @@ again:
}
}
- prop = njs_object_prop_alloc2(vm, NJS_PROPERTY,
- flags & NJS_OBJECT_PROP_VALUE_ECW);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
+ njs_object_prop_init(prop, NJS_PROPERTY,
+ flags & NJS_OBJECT_PROP_VALUE_ECW);
njs_value_assign(njs_prop_value(prop), value);
break;
@@ -226,11 +192,9 @@ again:
default:
njs_assert(njs_is_function(value));
- prop = njs_object_prop_alloc2(vm, NJS_ACCESSOR,
- NJS_OBJECT_PROP_VALUE_EC);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
+ njs_object_prop_init(prop, NJS_ACCESSOR, NJS_OBJECT_PROP_VALUE_EC);
+
+ set_writable = 0;
if (njs_prop_type(flags) == NJS_OBJECT_PROP_GETTER) {
njs_prop_getter(prop) = njs_function(value);
@@ -283,7 +247,7 @@ set_prop:
}
} else {
- if (prop->writable == NJS_ATTRIBUTE_UNSET) {
+ if (!set_writable) {
prop->writable = 0;
}
@@ -292,11 +256,11 @@ set_prop:
}
}
- if (prop->enumerable == NJS_ATTRIBUTE_UNSET) {
+ if (!set_enumerable) {
prop->enumerable = 0;
}
- if (prop->configurable == NJS_ATTRIBUTE_UNSET) {
+ if (!set_configurable) {
prop->configurable = 0;
}
@@ -305,6 +269,8 @@ set_prop:
if (njs_slow_path(prev->type == NJS_WHITEOUT)) {
/* Previously deleted property. */
+ prop->atom_id = prev->atom_id;
+ prop->next_elt = prev->next_elt;
*prev = *prop;
}
@@ -312,7 +278,6 @@ set_prop:
pq.lhq.key_hash = atom_id;
pq.lhq.proto = &njs_object_hash_proto;
- pq.lhq.value = prop;
pq.lhq.replace = 0;
pq.lhq.pool = vm->mem_pool;
@@ -321,6 +286,13 @@ set_prop:
njs_internal_error(vm, "lvlhsh insert failed");
return NJS_ERROR;
}
+
+ obj_prop = pq.lhq.value;
+ obj_prop->enumerable = prop->enumerable;
+ obj_prop->configurable = prop->configurable;
+ obj_prop->writable = prop->writable;
+ obj_prop->type = prop->type;
+ obj_prop->u.value = prop->u.value;
}
return NJS_OK;
@@ -340,9 +312,9 @@ set_prop:
case NJS_PROPERTY_PLACE_REF:
if (prev->type == NJS_PROPERTY_REF
&& !njs_is_accessor_descriptor(prop)
- && prop->configurable != NJS_ATTRIBUTE_FALSE
- && prop->enumerable != NJS_ATTRIBUTE_FALSE
- && prop->writable != NJS_ATTRIBUTE_FALSE)
+ && (!set_configurable || prop->configurable)
+ && (!set_enumerable || prop->enumerable)
+ && (!set_writable || prop->writable))
{
if (njs_is_valid(njs_prop_value(prop))) {
njs_value_assign(njs_prop_ref(prev), njs_prop_value(prop));
@@ -373,9 +345,9 @@ set_prop:
goto exception;
}
- if (prop->configurable == NJS_ATTRIBUTE_TRUE ||
- prop->enumerable == NJS_ATTRIBUTE_FALSE ||
- prop->writable == NJS_ATTRIBUTE_FALSE)
+ if ((set_configurable && prop->configurable)
+ || (set_enumerable && !prop->enumerable)
+ || (set_writable && !prop->writable))
{
goto exception;
}
@@ -401,22 +373,24 @@ set_prop:
if (!prev->configurable) {
- if (prop->configurable == NJS_ATTRIBUTE_TRUE) {
+ if (prop->configurable) {
goto exception;
}
- if (prop->enumerable != NJS_ATTRIBUTE_UNSET
- && prev->enumerable != prop->enumerable)
- {
+ if (set_enumerable && prev->enumerable != prop->enumerable) {
goto exception;
}
}
- if (njs_is_generic_descriptor(prop)) {
+ if (!(set_writable || njs_is_data_descriptor(prop))
+ && !njs_is_accessor_descriptor(prop))
+ {
goto done;
}
- if (njs_is_data_descriptor(prev) != njs_is_data_descriptor(prop)) {
+ if (njs_is_data_descriptor(prev)
+ != (set_writable || njs_is_data_descriptor(prop)))
+ {
if (!prev->configurable) {
goto exception;
}
@@ -435,12 +409,12 @@ set_prop:
}
if (njs_is_data_descriptor(prev)) {
- prev->writable = NJS_ATTRIBUTE_UNSET;
+ set_writable = 0;
njs_prop_getter(prev) = NULL;
njs_prop_setter(prev) = NULL;
} else {
- prev->writable = NJS_ATTRIBUTE_FALSE;
+ prev->writable = 0;
njs_set_undefined(njs_prop_value(prev));
}
@@ -448,10 +422,10 @@ set_prop:
prev->type = prop->type;
} else if (njs_is_data_descriptor(prev)
- && njs_is_data_descriptor(prop))
+ && (set_writable || njs_is_data_descriptor(prop)))
{
if (!prev->configurable && !prev->writable) {
- if (prop->writable == NJS_ATTRIBUTE_TRUE) {
+ if (prop->writable) {
goto exception;
}
@@ -484,7 +458,7 @@ done:
if (njs_slow_path(njs_is_fast_array(object)
&& pq.lhq.key_hash == NJS_ATOM_STRING_length)
- && prop->writable == NJS_ATTRIBUTE_FALSE)
+ && (set_writable && !prop->writable))
{
array = njs_array(object);
length = array->length;
@@ -539,8 +513,8 @@ done:
if (njs_slow_path(njs_is_array(object)
&& pq.lhq.key_hash == NJS_ATOM_STRING_length))
{
- if (prev->configurable != NJS_ATTRIBUTE_TRUE
- && prev->writable != NJS_ATTRIBUTE_TRUE
+ if (!prev->configurable
+ && !prev->writable
&& !njs_values_strict_equal(vm, njs_prop_value(prev),
njs_prop_value(prop)))
{
@@ -548,7 +522,7 @@ done:
return NJS_ERROR;
}
- if (prop->writable != NJS_ATTRIBUTE_UNSET) {
+ if (set_writable) {
prev->writable = prop->writable;
}
@@ -565,15 +539,15 @@ done:
* attribute of the property named P of object O to the value of the field.
*/
- if (prop->writable != NJS_ATTRIBUTE_UNSET) {
+ if (set_writable) {
prev->writable = prop->writable;
}
- if (prop->enumerable != NJS_ATTRIBUTE_UNSET) {
+ if (set_enumerable) {
prev->enumerable = prop->enumerable;
}
- if (prop->configurable != NJS_ATTRIBUTE_UNSET) {
+ if (set_configurable) {
prev->configurable = prop->configurable;
}
@@ -598,18 +572,9 @@ njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq,
njs_function_t *function;
njs_object_prop_t *prop, *shared;
- prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
- sizeof(njs_object_prop_t));
- if (njs_slow_path(prop == NULL)) {
- njs_memory_error(vm);
- return NJS_ERROR;
- }
-
shared = pq->lhq.value;
- *prop = *shared;
pq->lhq.replace = 0;
- pq->lhq.value = prop;
pq->lhq.pool = vm->mem_pool;
ret = njs_flathsh_unique_insert(&proto->hash, &pq->lhq);
@@ -618,6 +583,13 @@ njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq,
return NJS_ERROR;
}
+ prop = pq->lhq.value;
+ prop->enumerable = shared->enumerable;
+ prop->configurable = shared->configurable;
+ prop->writable = shared->writable;
+ prop->type = shared->type;
+ prop->u.value = shared->u.value;
+
if (njs_is_accessor_descriptor(prop)) {
if (njs_prop_getter(prop) != NULL) {
function = njs_function_copy(vm, njs_prop_getter(prop));
@@ -684,14 +656,15 @@ njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq,
static njs_object_prop_t *
-njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *desc)
+njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *desc,
+ njs_object_prop_t *prop, uint32_t *set_enumerable,
+ uint32_t *set_configurable, uint32_t *set_writable)
{
njs_int_t ret;
njs_bool_t data, accessor;
njs_value_t value;
njs_object_t *desc_object;
njs_function_t *getter, *setter;
- njs_object_prop_t *prop;
njs_flathsh_query_t lhq;
if (!njs_is_object(desc)) {
@@ -699,11 +672,12 @@ njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *desc)
return NULL;
}
- prop = njs_object_prop_alloc(vm, &njs_value_invalid,
- NJS_ATTRIBUTE_UNSET);
- if (njs_slow_path(prop == NULL)) {
- return NULL;
- }
+ njs_object_prop_init(prop, NJS_PROPERTY, NJS_OBJECT_PROP_UNSET);
+ *njs_prop_value(prop) = njs_value_invalid;
+
+ *set_enumerable = 0;
+ *set_configurable = 0;
+ *set_writable = 0;
data = 0;
accessor = 0;
@@ -768,6 +742,7 @@ njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *desc)
if (ret == NJS_OK) {
data = 1;
prop->writable = njs_is_true(&value);
+ *set_writable = 1;
}
if (accessor && data) {
@@ -785,6 +760,7 @@ njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *desc)
if (ret == NJS_OK) {
prop->enumerable = njs_is_true(&value);
+ *set_enumerable = 1;
}
lhq.key_hash = NJS_ATOM_STRING_configurable;
@@ -796,6 +772,7 @@ njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *desc)
if (ret == NJS_OK) {
prop->configurable = njs_is_true(&value);
+ *set_configurable = 1;
}
if (accessor) {
@@ -879,29 +856,23 @@ njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
if (njs_is_data_descriptor(prop)) {
lhq.key_hash = NJS_ATOM_STRING_value;
- pr = njs_object_prop_alloc(vm, njs_prop_value(prop), 1);
- if (njs_slow_path(pr == NULL)) {
- return NJS_ERROR;
- }
-
- lhq.value = pr;
-
ret = njs_flathsh_unique_insert(&desc->hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "lvlhsh insert failed");
return NJS_ERROR;
}
- lhq.key_hash = NJS_ATOM_STRING_writable;
+ pr = lhq.value;
- setval = (prop->writable == 1) ? &njs_value_true : &njs_value_false;
+ pr->type = NJS_PROPERTY;
+ pr->enumerable = 1;
+ pr->configurable = 1;
+ pr->writable = 1;
+ pr->u.value = *(njs_prop_value(prop));
- pr = njs_object_prop_alloc(vm, setval, 1);
- if (njs_slow_path(pr == NULL)) {
- return NJS_ERROR;
- }
+ lhq.key_hash = NJS_ATOM_STRING_writable;
- lhq.value = pr;
+ setval = (prop->writable == 1) ? &njs_value_true : &njs_value_false;
ret = njs_flathsh_unique_insert(&desc->hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
@@ -909,20 +880,37 @@ njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
return NJS_ERROR;
}
+ pr = lhq.value;
+
+ pr->type = NJS_PROPERTY;
+ pr->enumerable = 1;
+ pr->configurable = 1;
+ pr->writable = 1;
+ pr->u.value = *setval;
+
} else {
lhq.key_hash = NJS_ATOM_STRING_get;
- pr = njs_object_prop_alloc(vm, &njs_value_undefined, 1);
- if (njs_slow_path(pr == NULL)) {
+ ret = njs_flathsh_unique_insert(&desc->hash, &lhq);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
return NJS_ERROR;
}
+ pr = lhq.value;
+
+ pr->type = NJS_PROPERTY;
+ pr->enumerable = 1;
+ pr->configurable = 1;
+ pr->writable = 1;
+ pr->u.value = njs_value_undefined;
+
if (njs_prop_getter(prop) != NULL) {
njs_set_function(njs_prop_value(pr), njs_prop_getter(prop));
}
- lhq.value = pr;
+ lhq.key_hash = NJS_ATOM_STRING_set;
ret = njs_flathsh_unique_insert(&desc->hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
@@ -930,53 +918,40 @@ njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
return NJS_ERROR;
}
- lhq.key_hash = NJS_ATOM_STRING_set;
+ pr = lhq.value;
- pr = njs_object_prop_alloc(vm, &njs_value_undefined, 1);
- if (njs_slow_path(pr == NULL)) {
- return NJS_ERROR;
- }
+ pr->type = NJS_PROPERTY;
+ pr->enumerable = 1;
+ pr->configurable = 1;
+ pr->writable = 1;
+ pr->u.value = njs_value_undefined;
if (njs_prop_setter(prop) != NULL) {
njs_set_function(njs_prop_value(pr), njs_prop_setter(prop));
}
-
- lhq.value = pr;
-
- ret = njs_flathsh_unique_insert(&desc->hash, &lhq);
- if (njs_slow_path(ret != NJS_OK)) {
- njs_internal_error(vm, "lvlhsh insert failed");
- return NJS_ERROR;
- }
}
lhq.key_hash = NJS_ATOM_STRING_enumerable;
setval = (prop->enumerable == 1) ? &njs_value_true : &njs_value_false;
- pr = njs_object_prop_alloc(vm, setval, 1);
- if (njs_slow_path(pr == NULL)) {
- return NJS_ERROR;
- }
-
- lhq.value = pr;
-
ret = njs_flathsh_unique_insert(&desc->hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "lvlhsh insert failed");
return NJS_ERROR;
}
- lhq.key_hash = NJS_ATOM_STRING_configurable;
+ pr = lhq.value;
- setval = (prop->configurable == 1) ? &njs_value_true : &njs_value_false;
+ pr->type = NJS_PROPERTY;
+ pr->enumerable = 1;
+ pr->configurable = 1;
+ pr->writable = 1;
+ pr->u.value = *setval;
- pr = njs_object_prop_alloc(vm, setval, 1);
- if (njs_slow_path(pr == NULL)) {
- return NJS_ERROR;
- }
+ lhq.key_hash = NJS_ATOM_STRING_configurable;
- lhq.value = pr;
+ setval = (prop->configurable == 1) ? &njs_value_true : &njs_value_false;
ret = njs_flathsh_unique_insert(&desc->hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
@@ -984,6 +959,14 @@ njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
return NJS_ERROR;
}
+ pr = lhq.value;
+
+ pr->type = NJS_PROPERTY;
+ pr->enumerable = 1;
+ pr->configurable = 1;
+ pr->writable = 1;
+ pr->u.value = *setval;
+
njs_set_object(dest, desc);
return NJS_OK;
@@ -1007,6 +990,9 @@ njs_prop_type_string(njs_object_prop_type_t type)
case NJS_PROPERTY:
return "property";
+ case NJS_FREE_FLATHSH_ELEMENT:
+ return "free hash element";
+
default:
return "unknown";
}
@@ -1014,7 +1000,7 @@ njs_prop_type_string(njs_object_prop_type_t type)
njs_int_t
-njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init,
+njs_object_props_init(njs_vm_t *vm, const njs_object_init_t* init,
njs_object_prop_t *base, uint32_t atom_id, njs_value_t *value,
njs_value_t *retval)
{
@@ -1034,31 +1020,26 @@ njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init,
return NJS_ERROR;
}
- prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
- sizeof(njs_object_prop_t));
- if (njs_slow_path(prop == NULL)) {
- njs_memory_error(vm);
- return NJS_ERROR;
- }
-
- *prop = *base;
-
- prop->type = NJS_PROPERTY;
- njs_set_object(njs_prop_value(prop), object);
-
- lhq.value = prop;
lhq.key_hash = atom_id;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
lhq.proto = &njs_object_hash_proto;
ret = njs_flathsh_unique_insert(njs_object_hash(value), &lhq);
- if (njs_fast_path(ret == NJS_OK)) {
- njs_value_assign(retval, njs_prop_value(prop));
- return NJS_OK;
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NJS_ERROR;
}
- njs_internal_error(vm, "lvlhsh insert failed");
+ prop = lhq.value;
- return NJS_ERROR;
+ prop->enumerable = base->enumerable;
+ prop->configurable = base->configurable;
+ prop->writable = base->writable;
+ prop->type = NJS_PROPERTY;
+ njs_set_object(njs_prop_value(prop), object);
+
+ njs_value_assign(retval, njs_prop_value(prop));
+
+ return NJS_OK;
}
diff --git a/src/njs_object_prop_declare.h b/src/njs_object_prop_declare.h
index f2f7a5eb..3c991c50 100644
--- a/src/njs_object_prop_declare.h
+++ b/src/njs_object_prop_declare.h
@@ -9,8 +9,8 @@
#define NJS_DECLARE_PROP_VALUE(_name, _v, _fl) \
{ \
- .atom_id = NJS_ATOM_ ## _name, \
.desc = { \
+ .atom_id = NJS_ATOM_ ## _name, \
.type = NJS_PROPERTY, \
.u.value = _v, \
.enumerable = !!(_fl & NJS_OBJECT_PROP_ENUMERABLE), \
@@ -28,8 +28,8 @@
#define NJS_DECLARE_PROP_HANDLER(_name, _native, _m16, _fl) \
{ \
- .atom_id = NJS_ATOM_ ## _name, \
.desc = { \
+ .atom_id = NJS_ATOM_ ## _name, \
.type = NJS_PROPERTY_HANDLER, \
.u.value = njs_prop_handler2(_native, _m16), \
.enumerable = !!(_fl & NJS_OBJECT_PROP_ENUMERABLE), \
@@ -41,11 +41,11 @@
#define NJS_DECLARE_PROP_GETTER(_name, _native, _magic) \
{ \
- .atom_id = NJS_ATOM_ ## _name, \
.desc = { \
+ .atom_id = NJS_ATOM_ ## _name, \
.type = NJS_ACCESSOR, \
.u.accessor = njs_getter(_native, _magic), \
- .writable = NJS_ATTRIBUTE_UNSET, \
+ .writable = 0, \
.configurable = 1, \
}, \
}
diff --git a/src/njs_parser.c b/src/njs_parser.c
index 50cee672..cfaad740 100644
--- a/src/njs_parser.c
+++ b/src/njs_parser.c
@@ -839,10 +839,9 @@ njs_inline njs_int_t
njs_parser_expect_semicolon(njs_parser_t *parser, njs_lexer_token_t *token)
{
if (token->type != NJS_TOKEN_SEMICOLON) {
- if (parser->strict_semicolon
- || (token->type != NJS_TOKEN_END
- && token->type != NJS_TOKEN_CLOSE_BRACE
- && parser->lexer->prev_type != NJS_TOKEN_LINE_END))
+ if (token->type != NJS_TOKEN_END
+ && token->type != NJS_TOKEN_CLOSE_BRACE
+ && parser->lexer->prev_type != NJS_TOKEN_LINE_END)
{
return NJS_DECLINED;
}
@@ -5408,10 +5407,6 @@ static njs_int_t
njs_parser_do_while_semicolon(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
- if (parser->strict_semicolon) {
- return njs_parser_failed(parser);
- }
-
parser->target->right = parser->node;
parser->node = parser->target;
@@ -6257,10 +6252,9 @@ njs_parser_break_continue(njs_parser_t *parser, njs_lexer_token_t *token,
break;
}
- if (parser->strict_semicolon
- || (token->type != NJS_TOKEN_END
- && token->type != NJS_TOKEN_CLOSE_BRACE
- && parser->lexer->prev_type != NJS_TOKEN_LINE_END))
+ if (token->type != NJS_TOKEN_END
+ && token->type != NJS_TOKEN_CLOSE_BRACE
+ && parser->lexer->prev_type != NJS_TOKEN_LINE_END)
{
return njs_parser_failed(parser);
}
@@ -6314,9 +6308,7 @@ njs_parser_return_statement(njs_parser_t *parser, njs_lexer_token_t *token,
return njs_parser_failed(parser);
default:
- if (!parser->strict_semicolon
- && parser->lexer->prev_type == NJS_TOKEN_LINE_END)
- {
+ if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) {
break;
}
@@ -6702,7 +6694,6 @@ njs_parser_labelled_statement_after(njs_parser_t *parser,
njs_int_t ret;
njs_str_t str;
uintptr_t atom_id;
- njs_value_t entry;
njs_parser_node_t *node;
node = parser->node;
@@ -6719,8 +6710,7 @@ njs_parser_labelled_statement_after(njs_parser_t *parser,
atom_id = (uint32_t) (uintptr_t) parser->target;
- njs_atom_to_value(parser->vm, &entry, atom_id);
- njs_string_get(parser->vm, &entry, &str);
+ njs_atom_string_get(parser->vm, atom_id, &str);
ret = njs_name_copy(parser->vm, &parser->node->name, &str);
if (ret != NJS_OK) {
@@ -7093,8 +7083,7 @@ njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
njs_lexer_consume_token(parser->lexer, 1);
- var = njs_variable_function_add(parser, parser->scope, atom_id,
- NJS_VARIABLE_FUNCTION);
+ var = njs_variable_function_add(parser, parser->scope, atom_id);
if (var == NULL) {
return NJS_ERROR;
}
diff --git a/src/njs_parser.h b/src/njs_parser.h
index 2f7cff79..8a67bdab 100644
--- a/src/njs_parser.h
+++ b/src/njs_parser.h
@@ -84,7 +84,6 @@ struct njs_parser_s {
uint8_t use_lhs;
uint8_t module;
- njs_bool_t strict_semicolon;
njs_str_t file;
uint32_t line;
@@ -109,8 +108,9 @@ typedef struct {
typedef struct {
- njs_value_t *value;
+ njs_function_lambda_t *lambda;
njs_index_t index;
+ njs_bool_t async;
} njs_declaration_t;
diff --git a/src/njs_regexp.c b/src/njs_regexp.c
index ed560b18..d958e2af 100644
--- a/src/njs_regexp.c
+++ b/src/njs_regexp.c
@@ -1038,25 +1038,9 @@ njs_regexp_exec_result(njs_vm_t *vm, njs_value_t *r, njs_utf8_t utf8,
}
/* FIXME: implement fast CreateDataPropertyOrThrow(). */
- prop = njs_object_prop_alloc(vm, &njs_value_undefined, 1);
- if (njs_slow_path(prop == NULL)) {
- goto fail;
- }
-
- c = njs_regex_capture(match_data, 0);
-
- if (utf8 == NJS_STRING_UTF8) {
- index = njs_string_index(string, c);
-
- } else {
- index = c;
- }
-
- njs_set_number(&prop->u.value, index);
lhq.key_hash = NJS_ATOM_STRING_index;
lhq.replace = 0;
- lhq.value = prop;
lhq.pool = vm->mem_pool;
lhq.proto = &njs_object_hash_proto;
@@ -1065,32 +1049,54 @@ njs_regexp_exec_result(njs_vm_t *vm, njs_value_t *r, njs_utf8_t utf8,
goto insert_fail;
}
- prop = njs_object_prop_alloc(vm, &regexp->string, 1);
- if (njs_slow_path(prop == NULL)) {
- goto fail;
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+
+ c = njs_regex_capture(match_data, 0);
+
+ if (utf8 == NJS_STRING_UTF8) {
+ index = njs_string_index(string, c);
+
+ } else {
+ index = c;
}
+ njs_set_number(&prop->u.value, index);
+
lhq.key_hash = NJS_ATOM_STRING_input;
- lhq.value = prop;
ret = njs_flathsh_unique_insert(&array->object.hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
goto insert_fail;
}
- prop = njs_object_prop_alloc(vm, &njs_value_undefined, 1);
- if (njs_slow_path(prop == NULL)) {
- goto fail;
- }
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = regexp->string;
lhq.key_hash = NJS_ATOM_STRING_groups;
- lhq.value = prop;
ret = njs_flathsh_unique_insert(&array->object.hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
goto insert_fail;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = njs_value_undefined;
+
if (pattern->ngroups != 0) {
groups = njs_object_alloc(vm);
if (njs_slow_path(groups == NULL)) {
@@ -1110,19 +1116,21 @@ njs_regexp_exec_result(njs_vm_t *vm, njs_value_t *r, njs_utf8_t utf8,
goto fail;
}
- prop = njs_object_prop_alloc(vm, &array->start[group->capture], 1);
- if (njs_slow_path(prop == NULL)) {
- goto fail;
- }
-
lhq.key_hash = name.atom_id;
- lhq.value = prop;
ret = njs_flathsh_unique_insert(&groups->hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
goto insert_fail;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = array->start[group->capture];
+
i++;
} while (i < pattern->ngroups);
@@ -1149,26 +1157,10 @@ static void
njs_regexp_exec_result_free(njs_vm_t *vm, njs_array_t *result)
{
njs_flathsh_t *hash;
- njs_object_prop_t *prop;
- njs_flathsh_elt_t *elt;
- njs_flathsh_each_t lhe;
njs_flathsh_query_t lhq;
- njs_flathsh_each_init(&lhe, &njs_object_hash_proto);
-
hash = &result->object.hash;
- for ( ;; ) {
- elt = njs_flathsh_each(hash, &lhe);
- if (elt == NULL) {
- break;
- }
-
- prop = elt->value;
-
- njs_mp_free(vm->mem_pool, prop);
- }
-
lhq.pool = vm->mem_pool;
lhq.proto = &njs_object_hash_proto;
diff --git a/src/njs_scope.c b/src/njs_scope.c
index 304e4e33..7959849a 100644
--- a/src/njs_scope.c
+++ b/src/njs_scope.c
@@ -25,22 +25,6 @@ njs_scope_temp_index(njs_parser_scope_t *scope)
}
-njs_value_t *
-njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index)
-{
- njs_value_t *value;
-
- value = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
- if (njs_slow_path(value == NULL)) {
- return NULL;
- }
-
- njs_scope_value_set(vm, index, value);
-
- return value;
-}
-
-
njs_value_t **
njs_scope_make(njs_vm_t *vm, uint32_t count)
{
@@ -124,7 +108,7 @@ njs_scope_values_hash_test(njs_lvlhsh_query_t *lhq, void *data)
njs_str_t string;
njs_value_t *value;
- value = data;
+ value = *(njs_value_t **) data;
if (njs_is_string(value)) {
/* parser strings are always initialized. */
@@ -172,6 +156,7 @@ njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime,
njs_value_t *value;
njs_string_t *string;
njs_lvlhsh_t *values_hash;
+ njs_object_prop_t *pr;
njs_lvlhsh_query_t lhq;
is_string = 0;
@@ -197,12 +182,12 @@ njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime,
lhq.proto = &njs_values_hash_proto;
if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) {
- value = lhq.value;
+ value = ((njs_object_prop_t *) lhq.value)->u.val;
*index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
} else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) {
- value = lhq.value;
+ value = ((njs_object_prop_t *) lhq.value)->u.val;
*index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
@@ -244,7 +229,6 @@ njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime,
**index = NJS_INDEX_ERROR;
lhq.replace = 0;
- lhq.value = value;
lhq.pool = vm->mem_pool;
values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash;
@@ -253,6 +237,9 @@ njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime,
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
+
+ pr = lhq.value;
+ pr->u.val = value;
}
if (start != (u_char *) src) {
diff --git a/src/njs_scope.h b/src/njs_scope.h
index 16a47d98..32e44c6c 100644
--- a/src/njs_scope.h
+++ b/src/njs_scope.h
@@ -27,7 +27,6 @@
njs_index_t njs_scope_temp_index(njs_parser_scope_t *scope);
-njs_value_t *njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index);
njs_value_t **njs_scope_make(njs_vm_t *vm, uint32_t count);
njs_index_t njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src,
njs_uint_t runtime);
diff --git a/src/njs_string.c b/src/njs_string.c
index 6d7c464d..1a2b2333 100644
--- a/src/njs_string.c
+++ b/src/njs_string.c
@@ -601,56 +601,152 @@ njs_int_t
njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused, njs_value_t *retval)
{
- u_char *p, *start;
- uint64_t size, length, mask;
+ char *np, *np_end;
+ double num;
+ u_char *p;
+ uint64_t sz, size, length;
njs_int_t ret;
njs_uint_t i;
njs_string_prop_t string;
+ char buf[512];
+
+#define NJS_SZ_LAST 64
if (njs_is_null_or_undefined(&args[0])) {
njs_type_error(vm, "\"this\" argument is null or undefined");
return NJS_ERROR;
}
+ size = 0;
+ length = 0;
+
+ np = buf;
+ np_end = buf + sizeof(buf);
+ *np = 0;
+
for (i = 0; i < nargs; i++) {
- if (!njs_is_string(&args[i])) {
- ret = njs_value_to_string(vm, &args[i], &args[i]);
- if (ret != NJS_OK) {
- return ret;
+ if (njs_is_number(&args[i])) {
+ num = njs_number(&args[i]);
+
+ if (isnan(num)) {
+ size += njs_length("NaN");
+ length += njs_length("NaN");
+
+ } else if (isinf(num)) {
+ if (num < 0) {
+ size += njs_length("-Infinity");
+ length += njs_length("-Infinity");
+
+ } else {
+ size += njs_length("Infinity");
+ length += njs_length("Infinity");
+ }
+
+ } else {
+ sz = njs_dtoa(num, np + sizeof(uint8_t));
+
+ if (*np == 0) {
+ if (np + sizeof(uint8_t) + sz
+ < np_end - NJS_DTOA_MAX_LEN - sizeof(uint8_t))
+ {
+ *np = (uint8_t) sz;
+ np += sizeof(uint8_t) + sz;
+ *np = 0;
+
+ } else {
+ *np = NJS_SZ_LAST;
+ }
+ }
+
+ size += sz;
+ length += sz;
+ }
+
+ } else if (njs_is_boolean(&args[i])) {
+ if (njs_is_true(&args[i])) {
+ size += njs_length("true");
+ length += njs_length("true");
+ } else {
+ size += njs_length("false");
+ length += njs_length("false");
+ }
+
+ } else if (njs_is_null(&args[i])) {
+ size += njs_length("null");
+ length += njs_length("null");
+
+ } else if (njs_is_undefined(&args[i])) {
+ size += njs_length("undefined");
+ length += njs_length("undefined");
+
+ } else {
+ if (!njs_is_string(&args[i])) {
+ ret = njs_value_to_string(vm, &args[i], &args[i]);
+ if (ret != NJS_OK) {
+ return ret;
+ }
+
}
+
+ njs_string_prop(vm, &string, &args[i]);
+
+ size += string.size;
+ length += string.length;
}
}
- if (nargs == 1) {
- njs_string_copy(retval, &args[0]);
- return NJS_OK;
+ p = njs_string_alloc(vm, retval, size, length);
+ if (njs_slow_path(p == NULL)) {
+ return NJS_ERROR;
}
- size = 0;
- length = 0;
- mask = -1;
+ np = buf;
for (i = 0; i < nargs; i++) {
- (void) njs_string_prop(vm, &string, &args[i]);
+ if (njs_is_number(&args[i])) {
+ num = njs_number(&args[i]);
- size += string.size;
- length += string.length;
- }
+ if (isnan(num)) {
+ p = njs_cpymem(p, "NaN", njs_length("NaN"));
- length &= mask;
+ } else if (isinf(num)) {
+ if (num < 0) {
+ p = njs_cpymem(p, "-Infinity", njs_length("-Infinity"));
- start = njs_string_alloc(vm, retval, size, length);
- if (njs_slow_path(start == NULL)) {
- return NJS_ERROR;
- }
+ } else {
+ p = njs_cpymem(p, "Infinity", njs_length("Infinity"));
+ }
- p = start;
+ } else {
+ if (*np != NJS_SZ_LAST) {
+ sz = *np++;
+ p = njs_cpymem(p, np, sz);
+ np += sz;
- for (i = 0; i < nargs; i++) {
- (void) njs_string_prop(vm, &string, &args[i]);
+ } else {
+ sz = njs_dtoa(num, (char *) p);
+ p += sz;
+ }
+ }
- p = memcpy(p, string.start, string.size);
- p += string.size;
+ } else if (njs_is_boolean(&args[i])) {
+ if (njs_is_true(&args[i])) {
+ p = njs_cpymem(p, "true", njs_length("true"));
+ } else {
+ p = njs_cpymem(p, "false", njs_length("false"));
+ }
+
+ } else if (njs_is_null(&args[i])) {
+ p = njs_cpymem(p, "null", njs_length("null"));
+
+ } else if (njs_is_undefined(&args[i])) {
+ p = njs_cpymem(p, "undefined", njs_length("undefined"));
+
+ } else {
+ njs_string_prop(vm, &string, &args[i]);
+
+ p = njs_cpymem(p, string.start, string.size);
+ }
}
return NJS_OK;
diff --git a/src/njs_typed_array.c b/src/njs_typed_array.c
index 19399e3b..d7ca2e83 100644
--- a/src/njs_typed_array.c
+++ b/src/njs_typed_array.c
@@ -324,13 +324,14 @@ static njs_int_t
njs_typed_array_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused, njs_value_t *retval)
{
- double num;
- int64_t length, i;
- njs_int_t ret;
- njs_value_t *this, *source, *mapfn;
- njs_value_t arguments[3], value;
- njs_function_t *function;
- njs_typed_array_t *array;
+ double num;
+ int64_t length, i;
+ njs_int_t ret;
+ njs_value_t *this, *source, *mapfn;
+ njs_value_t arguments[3], value;
+ njs_function_t *function;
+ njs_typed_array_t *array;
+ njs_array_buffer_t *buffer;
this = njs_argument(args, 0);
@@ -371,6 +372,7 @@ njs_typed_array_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
}
array = njs_typed_array(retval);
+ buffer = njs_typed_array_buffer(array);
arguments[0] = *njs_arg(args, nargs, 3);
for (i = 0; i < length; i++) {
@@ -393,7 +395,9 @@ njs_typed_array_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
return NJS_ERROR;
}
- njs_typed_array_prop_set(vm, array, i, num);
+ if (!njs_is_detached_buffer(buffer)) {
+ njs_typed_array_prop_set(vm, array, i, num);
+ }
}
njs_set_typed_array(retval, array);
@@ -651,6 +655,11 @@ njs_typed_array_set_value(njs_vm_t *vm, njs_typed_array_t *array,
return ret;
}
+ buffer = njs_typed_array_buffer(array);
+ if (njs_slow_path(njs_is_detached_buffer(buffer))) {
+ return NJS_OK;
+ }
+
buffer = njs_typed_array_writable(vm, array);
if (njs_slow_path(buffer == NULL)) {
return NJS_ERROR;
@@ -908,6 +917,20 @@ njs_typed_array_prototype_fill(njs_vm_t *vm, njs_value_t *args,
}
+static void
+njs_slice_memcpy(uint8_t *dst, const uint8_t *src, size_t len)
+{
+ if (dst + len <= src || dst >= src + len) {
+ /* no overlap: can use memcpy */
+ memcpy(dst, src, len);
+
+ } else {
+ while (len-- != 0)
+ *dst++ = *src++;
+ }
+}
+
+
njs_int_t
njs_typed_array_prototype_slice(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t copy, njs_value_t *retval)
@@ -986,7 +1009,7 @@ njs_typed_array_prototype_slice(njs_vm_t *vm, njs_value_t *args,
start = start * element_size;
count = count * element_size;
- memcpy(&new_buffer->u.u8[0], &buffer->u.u8[start], count);
+ njs_slice_memcpy(&new_buffer->u.u8[0], &buffer->u.u8[start], count);
} else {
for (i = 0; i < count; i++) {
diff --git a/src/njs_value.c b/src/njs_value.c
index dcdab5a2..78f03821 100644
--- a/src/njs_value.c
+++ b/src/njs_value.c
@@ -8,8 +8,9 @@
#include <njs_main.h>
-static njs_int_t njs_object_property_query(njs_vm_t *vm,
- njs_property_query_t *pq, njs_object_t *object, uint32_t atom_id);
+njs_inline njs_int_t
+njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+ njs_object_t *object, uint32_t atom_id);
static njs_int_t njs_array_property_query(njs_vm_t *vm,
njs_property_query_t *pq, njs_array_t *array, uint32_t index,
uint32_t atom_id);
@@ -560,9 +561,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
{
uint32_t index;
njs_int_t ret;
- njs_value_t key;
njs_object_t *obj;
- njs_function_t *function;
njs_assert(atom_id != NJS_ATOM_STRING_unknown);
@@ -585,6 +584,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
case NJS_OBJECT:
case NJS_ARRAY:
+ case NJS_FUNCTION:
case NJS_ARRAY_BUFFER:
case NJS_DATA_VIEW:
case NJS_TYPED_ARRAY:
@@ -595,31 +595,12 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
obj = njs_object(value);
break;
- case NJS_FUNCTION:
- function = njs_function_value_copy(vm, value);
- if (njs_slow_path(function == NULL)) {
- return NJS_ERROR;
- }
-
- obj = &function->object;
- break;
-
case NJS_UNDEFINED:
case NJS_NULL:
default:
- ret = njs_atom_to_value(vm, &key, atom_id);
-
- if (njs_fast_path(ret == NJS_OK)) {
- njs_string_get(vm, &key, &pq->lhq.key);
- njs_type_error(vm, "cannot get property \"%V\" of %s",
- &pq->lhq.key, njs_is_null(value) ? "null"
- : "undefined");
- return NJS_ERROR;
- }
-
- njs_type_error(vm, "cannot get property \"unknown\" of %s",
- njs_is_null(value) ? "null" : "undefined");
-
+ njs_atom_string_get(vm, atom_id, &pq->lhq.key);
+ njs_type_error(vm, "cannot get property \"%V\" of %s", &pq->lhq.key,
+ njs_type_string(value->type));
return NJS_ERROR;
}
@@ -979,6 +960,7 @@ njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq,
pq->lhq.value = prop;
+ prop->type = NJS_PROPERTY;
prop->writable = slots->writable;
prop->configurable = slots->configurable;
prop->enumerable = slots->enumerable;
@@ -1029,8 +1011,7 @@ njs_value_property(njs_vm_t *vm, njs_value_t *value, uint32_t atom_id,
tarray = njs_typed_array(value);
if (njs_slow_path(njs_is_detached_buffer(tarray->buffer))) {
- njs_type_error(vm, "detached buffer");
- return NJS_ERROR;
+ goto not_found;
}
if (njs_slow_path(index >= njs_typed_array_length(tarray))) {
@@ -1119,6 +1100,7 @@ slow_path:
break;
case NJS_DECLINED:
+not_found:
njs_set_undefined(retval);
return NJS_DECLINED;
@@ -1298,10 +1280,7 @@ slow_path:
return NJS_ERROR;
}
- elt->value = (&pq.lhq)->value;
-
- prop = (njs_object_prop_t *) elt->value;
-
+ prop = (njs_object_prop_t *) elt;
prop->type = NJS_PROPERTY;
prop->enumerable = 1;
prop->configurable = 1;
@@ -1337,13 +1316,8 @@ slow_path:
goto fail;
}
- prop = njs_object_prop_alloc(vm, &njs_value_undefined, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
pq.lhq.replace = 0;
- pq.lhq.value = prop;
pq.lhq.key_hash = atom_id;
pq.lhq.pool = vm->mem_pool;
@@ -1353,6 +1327,12 @@ slow_path:
return NJS_ERROR;
}
+ prop = pq.lhq.value;
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+
found:
njs_value_assign(njs_prop_value(prop), setval);
@@ -1471,7 +1451,6 @@ slow_path:
}
prop->type = NJS_WHITEOUT;
- prop->enum_in_object_hash = 1;
return NJS_OK;
}
diff --git a/src/njs_value.h b/src/njs_value.h
index 7bc9eff3..3fba344b 100644
--- a/src/njs_value.h
+++ b/src/njs_value.h
@@ -287,7 +287,8 @@ struct njs_object_type_init_s {
typedef enum {
- NJS_PROPERTY = 0,
+ NJS_FREE_FLATHSH_ELEMENT = 0,
+ NJS_PROPERTY,
NJS_ACCESSOR,
NJS_PROPERTY_HANDLER,
@@ -305,22 +306,23 @@ typedef enum {
} njs_prop_query_t;
-/*
- * Attributes are generally used as Boolean values.
- * The UNSET value is can be seen:
- * for newly created property descriptors in njs_define_property(),
- * for writable attribute of accessor descriptors (desc->writable
- * cannot be used as a boolean value).
- */
-typedef enum {
- NJS_ATTRIBUTE_FALSE = 0,
- NJS_ATTRIBUTE_TRUE = 1,
- NJS_ATTRIBUTE_UNSET,
-} njs_object_attribute_t;
-
+/* njs_object_prop_s: same structure and length as njs_flathsh_elt_t. */
struct njs_object_prop_s {
+ /* next_elt + property descriptor : 32 bits */
+
+ uint32_t next_elt:26;
+
+ uint32_t type:3;
+ uint32_t writable:1;
+ uint32_t enumerable:1;
+ uint32_t configurable:1;
+
+ uint32_t atom_id;
+
union {
+ njs_value_t *val;
+ njs_mod_t *mod;
njs_value_t value;
struct {
njs_function_t *getter;
@@ -328,7 +330,8 @@ struct njs_object_prop_s {
} accessor;
} u;
-#define njs_prop_value(_p) (&(_p)->u.value)
+#define njs_prop_value(_p) (&((njs_object_prop_t *) (_p))->u.value)
+#define njs_prop_module(_p) (((njs_object_prop_t *) (_p))->u.mod)
#define njs_prop_handler(_p) (_p)->u.value.data.u.prop_handler
#define njs_prop_ref(_p) (_p)->u.value.data.u.value
#define njs_prop_typed_ref(_p) (_p)->u.value.data.u.typed_array
@@ -338,18 +341,11 @@ struct njs_object_prop_s {
#define njs_prop_getter(_p) (_p)->u.accessor.getter
#define njs_prop_setter(_p) (_p)->u.accessor.setter
- njs_object_prop_type_t type:8; /* 3 bits */
- njs_object_prop_type_t enum_in_object_hash:8; /* 3 bits */
-
- njs_object_attribute_t writable:8; /* 2 bits */
- njs_object_attribute_t enumerable:8; /* 2 bits */
- njs_object_attribute_t configurable:8; /* 2 bits */
};
struct njs_object_prop_init_s {
struct njs_object_prop_s desc;
- uint32_t atom_id;
};
diff --git a/src/njs_variable.c b/src/njs_variable.c
index 78bd2afb..b65e5934 100644
--- a/src/njs_variable.c
+++ b/src/njs_variable.c
@@ -36,7 +36,7 @@ njs_variable_add(njs_parser_t *parser, njs_parser_scope_t *scope,
njs_variable_t *
njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope,
- uintptr_t atom_id, njs_variable_type_t type)
+ uintptr_t atom_id)
{
njs_bool_t ctor;
njs_variable_t *var;
@@ -44,14 +44,15 @@ njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope,
njs_parser_scope_t *root;
njs_function_lambda_t *lambda;
- root = njs_variable_scope_find(parser, scope, atom_id, type);
+ root = njs_variable_scope_find(parser, scope, atom_id,
+ NJS_VARIABLE_FUNCTION);
if (njs_slow_path(root == NULL)) {
njs_parser_ref_error(parser, "scope not found");
return NULL;
}
- var = njs_variable_scope_add(parser, root, scope, atom_id, type,
- NJS_INDEX_ERROR);
+ var = njs_variable_scope_add(parser, root, scope, atom_id,
+ NJS_VARIABLE_FUNCTION, NJS_INDEX_ERROR);
if (njs_slow_path(var == NULL)) {
return NULL;
}
@@ -77,15 +78,15 @@ njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope,
}
var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL,
- type);
+ NJS_VARIABLE_FUNCTION);
- declr->value = &var->value;
+ declr->lambda = lambda;
+ declr->async = !ctor;
declr->index = var->index;
root->items++;
var->type = NJS_VARIABLE_FUNCTION;
- var->function = 1;
return var;
}
@@ -173,7 +174,6 @@ njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope,
if (var != NULL && var->scope == root) {
if (var->self) {
- var->function = 0;
return scope;
}
@@ -239,7 +239,7 @@ njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope,
failed:
- njs_lexer_entry(parser->vm, atom_id, &entry);
+ njs_atom_string_get(parser->vm, atom_id, &entry);
njs_parser_syntax_error(parser, "\"%V\" has already been declared", &entry);
return NULL;
diff --git a/src/njs_variable.h b/src/njs_variable.h
index 2ed5220a..db40de75 100644
--- a/src/njs_variable.h
+++ b/src/njs_variable.h
@@ -26,7 +26,6 @@ typedef struct {
njs_bool_t self;
njs_bool_t init;
njs_bool_t closure;
- njs_bool_t function;
njs_parser_scope_t *scope;
njs_parser_scope_t *original;
@@ -62,7 +61,7 @@ typedef struct {
njs_variable_t *njs_variable_add(njs_parser_t *parser,
njs_parser_scope_t *scope, uintptr_t atom_id, njs_variable_type_t type);
njs_variable_t *njs_variable_function_add(njs_parser_t *parser,
- njs_parser_scope_t *scope, uintptr_t atom_id, njs_variable_type_t type);
+ njs_parser_scope_t *scope, uintptr_t atom_id);
njs_variable_t * njs_label_add(njs_vm_t *vm, njs_parser_scope_t *scope,
uintptr_t atom_id);
njs_variable_t *njs_label_find(njs_vm_t *vm, njs_parser_scope_t *scope,
diff --git a/src/njs_vm.c b/src/njs_vm.c
index 008f9d04..e6aac927 100644
--- a/src/njs_vm.c
+++ b/src/njs_vm.c
@@ -310,7 +310,6 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start,
u_char *end)
{
njs_int_t ret;
- njs_arr_t *arr;
njs_mod_t *module;
njs_parser_t parser;
njs_vm_code_t *code;
@@ -366,10 +365,6 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start,
lambda->start = generator.code_start;
lambda->nlocal = scope->items;
- arr = scope->declarations;
- lambda->declarations = (arr != NULL) ? arr->start : NULL;
- lambda->ndeclarations = (arr != NULL) ? arr->items : 0;
-
module->function.u.lambda = lambda;
return module;
@@ -908,6 +903,7 @@ njs_vm_bind2(njs_vm_t *vm, const njs_str_t *var_name, njs_object_prop_t *prop,
njs_value_t prop_name;
njs_object_t *global;
njs_flathsh_t *hash;
+ njs_object_prop_t *obj_prop;
njs_flathsh_query_t lhq;
ret = njs_atom_string_create(vm, &prop_name, var_name->start,
@@ -916,7 +912,6 @@ njs_vm_bind2(njs_vm_t *vm, const njs_str_t *var_name, njs_object_prop_t *prop,
return NJS_ERROR;
}
- lhq.value = prop;
lhq.key_hash = prop_name.atom_id;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -931,6 +926,14 @@ njs_vm_bind2(njs_vm_t *vm, const njs_str_t *var_name, njs_object_prop_t *prop,
return ret;
}
+ obj_prop = lhq.value;
+
+ obj_prop->type = prop->type;
+ obj_prop->enumerable = prop->enumerable;
+ obj_prop->configurable = prop->configurable;
+ obj_prop->writable = prop->writable;
+ obj_prop->u.value = prop->u.value;
+
return NJS_OK;
}
@@ -939,14 +942,13 @@ njs_int_t
njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name, const njs_value_t *value,
njs_bool_t shared)
{
- njs_object_prop_t *prop;
+ njs_object_prop_t prop;
- prop = njs_object_prop_alloc(vm, value, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
+ njs_object_prop_init(&prop, NJS_PROPERTY,
+ NJS_OBJECT_PROP_VALUE_ECW);
+ *njs_prop_value(&prop) = *value;
- return njs_vm_bind2(vm, var_name, prop, shared);
+ return njs_vm_bind2(vm, var_name, &prop, shared);
}
@@ -955,21 +957,18 @@ njs_vm_bind_handler(njs_vm_t *vm, const njs_str_t *var_name,
njs_prop_handler_t handler, uint16_t magic16, uint32_t magic32,
njs_bool_t shared)
{
- njs_object_prop_t *prop;
+ njs_object_prop_t prop;
- prop = njs_object_prop_alloc(vm, &njs_value_invalid, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
+ njs_object_prop_init(&prop, NJS_PROPERTY_HANDLER,
+ NJS_OBJECT_PROP_VALUE_ECW);
- prop->type = NJS_PROPERTY_HANDLER;
- prop->u.value.type = NJS_INVALID;
- prop->u.value.data.truth = 1;
- njs_prop_magic16(prop) = magic16;
- njs_prop_magic32(prop) = magic32;
- njs_prop_handler(prop) = handler;
+ prop.u.value.type = NJS_INVALID;
+ prop.u.value.data.truth = 1;
+ njs_prop_magic16(&prop) = magic16;
+ njs_prop_magic32(&prop) = magic32;
+ njs_prop_handler(&prop) = handler;
- return njs_vm_bind2(vm, var_name, prop, shared);
+ return njs_vm_bind2(vm, var_name, &prop, shared);
}
@@ -1247,11 +1246,6 @@ njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...)
goto done;
}
- prop = njs_object_prop_alloc(vm, value, 1);
- if (njs_slow_path(prop == NULL)) {
- goto done;
- }
-
if (name->atom_id == NJS_ATOM_STRING_unknown) {
ret = njs_atom_atomize_key(vm, name);
if (ret != NJS_OK) {
@@ -1259,7 +1253,6 @@ njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...)
}
}
- lhq.value = prop;
lhq.key_hash = name->atom_id;
lhq.replace = 0;
lhq.pool = vm->mem_pool;
@@ -1270,6 +1263,14 @@ njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...)
njs_internal_error(vm, NULL);
goto done;
}
+
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = *value;
}
ret = NJS_OK;
diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c
index 3826f9eb..727dc6d0 100644
--- a/src/njs_vmcode.c
+++ b/src/njs_vmcode.c
@@ -16,15 +16,12 @@ struct njs_property_next_s {
static njs_jump_off_t njs_vmcode_object(njs_vm_t *vm, njs_value_t *retval);
static njs_jump_off_t njs_vmcode_array(njs_vm_t *vm, u_char *pc,
njs_value_t *retval);
-static njs_jump_off_t njs_vmcode_function(njs_vm_t *vm, u_char *pc,
- njs_value_t *retval);
+static njs_jump_off_t njs_vmcode_function(njs_vm_t *vm, u_char *pc);
static njs_jump_off_t njs_vmcode_arguments(njs_vm_t *vm, u_char *pc);
static njs_jump_off_t njs_vmcode_regexp(njs_vm_t *vm, u_char *pc,
njs_value_t *retval);
static njs_jump_off_t njs_vmcode_template_literal(njs_vm_t *vm,
njs_value_t *retval);
-static njs_jump_off_t njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value,
- njs_index_t retval);
static njs_jump_off_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value,
njs_value_t *key, njs_value_t *retval);
@@ -113,7 +110,6 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc, njs_value_t *rval,
njs_vmcode_equal_jump_t *equal;
njs_vmcode_try_return_t *try_return;
njs_vmcode_method_frame_t *method_frame;
- njs_vmcode_function_copy_t *fcopy;
njs_vmcode_prop_accessor_t *accessor;
njs_vmcode_try_trampoline_t *try_trampoline;
njs_vmcode_function_frame_t *function_frame;
@@ -157,7 +153,6 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc, njs_value_t *rval,
NJS_GOTO_ROW(NJS_VMCODE_IF_EQUAL_JUMP),
NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_INIT),
NJS_GOTO_ROW(NJS_VMCODE_RETURN),
- NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_COPY),
NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_FRAME),
NJS_GOTO_ROW(NJS_VMCODE_METHOD_FRAME),
NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_CALL),
@@ -672,14 +667,52 @@ NEXT_LBL;
src = value1;
}
- ret = njs_primitive_value_to_string(vm, &dst, src);
- if (njs_slow_path(ret != NJS_OK)) {
- goto error;
- }
+ if (njs_is_number(src)) {
+ size_t size;
+ njs_string_t sp;
+ char buf[64];
- ret = njs_string_concat(vm, s1, s2, &name);
- if (njs_slow_path(ret == NJS_ERROR)) {
- goto error;
+ /* Alloc free path for "str" + int or int + "str" concatenation. */
+
+ num = njs_number(src);
+
+ if (isnan(num)) {
+ njs_atom_to_value(vm, &dst, NJS_ATOM_STRING_NaN);
+
+ } else if (isinf(num)) {
+
+ if (num < 0) {
+ njs_atom_to_value(vm, &dst, NJS_ATOM_STRING__Infinity);
+
+ } else {
+ njs_atom_to_value(vm, &dst, NJS_ATOM_STRING_Infinity);
+ }
+
+ } else {
+ size = njs_dtoa(num, buf);
+
+ sp.start = (u_char *) buf;
+ sp.size = size;
+ sp.length = size;
+
+ dst.string.data = &sp;
+ }
+
+ ret = njs_string_concat(vm, s1, s2, &name);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto error;
+ }
+
+ } else {
+ ret = njs_primitive_value_to_string(vm, &dst, src);
+ if (njs_slow_path(ret != NJS_OK)) {
+ goto error;
+ }
+
+ ret = njs_string_concat(vm, s1, s2, &name);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto error;
+ }
}
njs_value_assign(retval, &name);
@@ -1155,9 +1188,7 @@ NEXT_LBL;
CASE (NJS_VMCODE_FUNCTION):
njs_vmcode_debug_opcode();
- njs_vmcode_operand(vm, vmcode->operand1, retval);
-
- ret = njs_vmcode_function(vm, pc, retval);
+ ret = njs_vmcode_function(vm, pc);
if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) {
goto error;
}
@@ -1422,17 +1453,6 @@ NEXT_LBL;
return NJS_OK;
- CASE (NJS_VMCODE_FUNCTION_COPY):
- njs_vmcode_debug_opcode();
-
- fcopy = (njs_vmcode_function_copy_t *) pc;
- ret = njs_vmcode_function_copy(vm, fcopy->function, fcopy->retval);
- if (njs_slow_path(ret == NJS_ERROR)) {
- goto error;
- }
-
- BREAK;
-
CASE (NJS_VMCODE_FUNCTION_FRAME):
njs_vmcode_debug_opcode();
@@ -1928,7 +1948,7 @@ njs_vmcode_array(njs_vm_t *vm, u_char *pc, njs_value_t *retval)
static njs_jump_off_t
-njs_vmcode_function(njs_vm_t *vm, u_char *pc, njs_value_t *retval)
+njs_vmcode_function(njs_vm_t *vm, u_char *pc)
{
njs_function_t *function;
njs_vmcode_function_t *code;
@@ -1948,7 +1968,7 @@ njs_vmcode_function(njs_vm_t *vm, u_char *pc, njs_value_t *retval)
function->args_count = lambda->nargs - lambda->rest_parameters;
- njs_set_function(retval, function);
+ njs_set_function(njs_scope_value(vm, code->retval), function);
return sizeof(njs_vmcode_function_t);
}
@@ -2028,28 +2048,9 @@ njs_vmcode_template_literal(njs_vm_t *vm, njs_value_t *retval)
return ret;
}
- return sizeof(njs_vmcode_template_literal_t);
-}
-
-
-static njs_jump_off_t
-njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, njs_index_t retidx)
-{
- njs_value_t *retval;
- njs_function_t *function;
-
- retval = njs_scope_value(vm, retidx);
-
- if (!njs_is_valid(retval)) {
- *retval = *value;
-
- function = njs_function_value_copy(vm, retval);
- if (njs_slow_path(function == NULL)) {
- return NJS_ERROR;
- }
- }
+ njs_array_destroy(vm, array);
- return sizeof(njs_vmcode_function_copy_t);
+ return sizeof(njs_vmcode_template_literal_t);
}
@@ -2122,12 +2123,6 @@ njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
}
}
- prop = njs_object_prop_alloc(vm, init, 1);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
- lhq.value = prop;
lhq.key_hash = name.atom_id;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
@@ -2139,6 +2134,14 @@ njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
return NJS_ERROR;
}
+ prop = lhq.value;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->configurable = 1;
+ prop->writable = 1;
+ prop->u.value = *init;
+
break;
default:
@@ -2591,7 +2594,7 @@ njs_function_frame_create(njs_vm_t *vm, njs_value_t *value,
}
-inline njs_object_t *
+njs_object_t *
njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor)
{
njs_value_t proto, bound;
diff --git a/src/njs_vmcode.h b/src/njs_vmcode.h
index 83507f63..5170f29f 100644
--- a/src/njs_vmcode.h
+++ b/src/njs_vmcode.h
@@ -38,7 +38,6 @@ enum {
NJS_VMCODE_IF_EQUAL_JUMP,
NJS_VMCODE_PROPERTY_INIT,
NJS_VMCODE_RETURN,
- NJS_VMCODE_FUNCTION_COPY,
NJS_VMCODE_FUNCTION_FRAME,
NJS_VMCODE_METHOD_FRAME,
NJS_VMCODE_FUNCTION_CALL,
@@ -383,13 +382,6 @@ typedef struct {
typedef struct {
njs_vmcode_t code;
- njs_value_t *function;
- njs_index_t retval;
-} njs_vmcode_function_copy_t;
-
-
-typedef struct {
- njs_vmcode_t code;
njs_index_t retval;
njs_mod_t *module;
} njs_vmcode_import_t;
diff --git a/src/qjs.c b/src/qjs.c
index 7993de1b..9c0fcdb4 100644
--- a/src/qjs.c
+++ b/src/qjs.c
@@ -911,6 +911,11 @@ qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value)
goto string;
}
+ /* GCC complains about uninitialized variables. */
+
+ byte_offset = 0;
+ byte_length = 0;
+
val = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length, NULL);
if (!JS_IsException(val)) {
bytes->start = JS_GetArrayBuffer(ctx, &bytes->length, val);
@@ -967,6 +972,11 @@ qjs_typed_array_data(JSContext *ctx, JSValueConst value, njs_str_t *data)
size_t byte_offset, byte_length;
JSValue ab;
+ /* GCC complains about uninitialized variables. */
+
+ byte_offset = 0;
+ byte_length = 0;
+
/* TODO: DataView. */
ab = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length,
diff --git a/src/test/lvlhsh_unit_test.c b/src/test/lvlhsh_unit_test.c
index 6172c1c7..0e334c5d 100644
--- a/src/test/lvlhsh_unit_test.c
+++ b/src/test/lvlhsh_unit_test.c
@@ -11,7 +11,7 @@
static njs_int_t
lvlhsh_unit_test_key_test(njs_lvlhsh_query_t *lhq, void *data)
{
- if (*(uintptr_t *) lhq->key.start == (uintptr_t) data) {
+ if (*(uintptr_t *) lhq->key.start == *(uintptr_t *) data) {
return NJS_OK;
}
@@ -51,13 +51,13 @@ lvlhsh_unit_test_add(njs_lvlhsh_t *lh, const njs_lvlhsh_proto_t *proto,
lhq.replace = 0;
lhq.key.length = sizeof(uintptr_t);
lhq.key.start = (u_char *) &key;
- lhq.value = (void *) key;
lhq.proto = proto;
lhq.pool = pool;
switch (njs_lvlhsh_insert(lh, &lhq)) {
case NJS_OK:
+ ((njs_flathsh_elt_t *) lhq.value)->value[0] = (void *) key;
return NJS_OK;
case NJS_DECLINED:
@@ -84,7 +84,7 @@ lvlhsh_unit_test_get(njs_lvlhsh_t *lh, const njs_lvlhsh_proto_t *proto,
if (njs_lvlhsh_find(lh, &lhq) == NJS_OK) {
- if (key == (uintptr_t) lhq.value) {
+ if (key == (uintptr_t) ((njs_flathsh_elt_t *) lhq.value)->value[0]) {
return NJS_OK;
}
}
diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c
index 8d71aae7..d3d3f1c1 100644
--- a/src/test/njs_externals_test.c
+++ b/src/test/njs_externals_test.c
@@ -61,7 +61,7 @@ lvlhsh_unit_test_key_test(njs_lvlhsh_query_t *lhq, void *data)
njs_str_t name;
njs_unit_test_prop_t *prop;
- prop = data;
+ prop = *(njs_unit_test_prop_t **) data;
name = prop->name;
if (name.length != lhq->key.length) {
@@ -129,13 +129,13 @@ lvlhsh_unit_test_add(njs_mp_t *pool, njs_unit_test_req_t *r,
lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
lhq.replace = 1;
- lhq.value = (void *) prop;
lhq.proto = &lvlhsh_proto;
lhq.pool = pool;
switch (njs_lvlhsh_insert(&r->hash, &lhq)) {
case NJS_OK:
+ ((njs_flathsh_elt_t *) lhq.value)->value[0] = (void *) prop;
return NJS_OK;
case NJS_DECLINED:
@@ -291,9 +291,9 @@ njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, uint32_t atom_id,
ret = njs_lvlhsh_find(&r->hash, &lhq);
- prop = lhq.value;
-
if (ret == NJS_OK) {
+ prop = ((njs_flathsh_elt_t *) lhq.value)->value[0];
+
if (retval == NULL) {
njs_value_invalid_set(njs_value_arg(&prop->value));
return NJS_OK;
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index 2227c0a6..78e12197 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -3923,6 +3923,9 @@ static njs_unit_test_t njs_test[] =
{ njs_str("delete this !== true"),
njs_str("false") },
+ { njs_str("undefined[Symbol()]"),
+ njs_str("TypeError: cannot get property \"Symbol()\" of undefined") },
+
/* Object shorthand methods. */
{ njs_str("var o = {m(){}}; new o.m();"),
@@ -5906,6 +5909,12 @@ static njs_unit_test_t njs_test[] =
njs_str("true") },
{ njs_str(NJS_TYPED_ARRAY_LIST
+ ".every(v=>{Object.defineProperty(v.prototype, '0', {set(){ throw 'Oops' }});"
+ " var t = new v([0]); var r = Object.create(t);"
+ " r[0] = 1; return true})"),
+ njs_str("true") },
+
+ { njs_str(NJS_TYPED_ARRAY_LIST
".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {get(){return 22}})} "
" catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"),
njs_str("true") },
@@ -9553,6 +9562,9 @@ static njs_unit_test_t njs_test[] =
{ njs_str("/["),
njs_str("SyntaxError: Unterminated RegExp \"/[\" in 1") },
+ { njs_str("/[][a"),
+ njs_str("SyntaxError: Unterminated RegExp \"/[][a\" in 1") },
+
{ njs_str("/[\\"),
njs_str("SyntaxError: Unterminated RegExp \"/[\\\" in 1") },
@@ -9588,11 +9600,24 @@ static njs_unit_test_t njs_test[] =
njs_str("/\\]cd/") },
#endif
+ { njs_str("RegExp('[][a')"),
+ njs_str("SyntaxError: "
+ njs_pcre_var("pcre_compile2(\"(?!)[a\") failed: missing terminating ] for character class at \"\"",
+ "pcre_compile(\"[][a\") failed: missing terminating ] for character class")) },
+
+ { njs_str("RegExp('[][a][a')"),
+ njs_str("SyntaxError: "
+ njs_pcre_var("pcre_compile2(\"(?!)[a][a\") failed: missing terminating ] for character class at \"\"",
+ "pcre_compile(\"[][a][a\") failed: missing terminating ] for character class")) },
+
{ njs_str("RegExp('[\\\\')"),
njs_str("SyntaxError: "
njs_pcre_var("pcre_compile2(\"[\\\") failed: \\ at end of pattern at \"\"",
"pcre_compile(\"[\\\") failed: \\ at end of pattern")) },
+ { njs_str("RegExp('[][a]')"),
+ njs_str(njs_pcre_var("/(?!)[a]/", "/[][a]/")) },
+
{ njs_str("RegExp('\\\\0').source[1]"),
njs_str("0") },
@@ -10984,6 +11009,30 @@ static njs_unit_test_t njs_test[] =
"f.apply(123, {})"),
njs_str("123") },
+ { njs_str("'Hello'.concat(' ', 'World')"),
+ njs_str("Hello World") },
+
+ { njs_str("'Value: '.concat(42, ' and ', 3.14)"),
+ njs_str("Value: 42 and 3.14") },
+
+ { njs_str("'Flags: '.concat(true, ' and ', false)"),
+ njs_str("Flags: true and false") },
+
+ { njs_str("'Values: '.concat(null, ' and ', undefined)"),
+ njs_str("Values: null and undefined") },
+
+ { njs_str("'Mixed: '.concat(123, ' ', true, ' ', null, ' ', undefined)"),
+ njs_str("Mixed: 123 true null undefined") },
+
+ { njs_str("'Special: '.concat(NaN, ' ', Infinity, ' ', -Infinity)"),
+ njs_str("Special: NaN Infinity -Infinity") },
+
+ { njs_str("'Numbers: '.concat(1234567890, ' ', 0.123456789, ' ', 1.23e-10)"),
+ njs_str("Numbers: 1234567890 0.123456789 1.23e-10") },
+
+ { njs_str("'Zero: '.concat(0, ' ', -0)"),
+ njs_str("Zero: 0 0") },
+
{ njs_str("(function(index, ...rest){ return rest[index];})"
".apply({}, [1022].concat(Array(1023).fill(1).map((v,i)=>i.toString(16))))"),
njs_str("3fe") },
@@ -11033,6 +11082,15 @@ static njs_unit_test_t njs_test[] =
{ njs_str("''.concat.apply('a', [ 'b', 'c' ], 'd')"),
njs_str("abc") },
+ { njs_str("''.concat.apply('', Array(128).fill(1.23456789123e14)) == '123456789123000'.repeat(128)"),
+ njs_str("true") },
+
+ { njs_str("''.concat.apply('', Array(128).fill(0).map((v,i)=>Math.log2(i))).startsWith('-Infinity')"),
+ njs_str("true") },
+
+ { njs_str("''.concat.apply('', Array(256).fill(0).map((v,i)=> !(i % 2) ? Math.exp(i) : 'α'.repeat(Math.log2(i)))).endsWith('110ααααααα')"),
+ njs_str("true") },
+
{ njs_str("[].join.call([1,2,3])"),
njs_str("1,2,3") },
@@ -11966,6 +12024,48 @@ static njs_unit_test_t njs_test[] =
{ njs_str("/[]a/.test('a')"),
njs_str("false") },
+ { njs_str("/[#[]/.test('[')"),
+ njs_str("true") },
+
+ { njs_str("/[\\s[]/.test('[')"),
+ njs_str("true") },
+
+ { njs_str("/[#[^]/.test('[')"),
+ njs_str("true") },
+
+ { njs_str("/[#\\[]/.test('[')"),
+ njs_str("true") },
+
+ { njs_str("/[\\[^]/.test('[')"),
+ njs_str("true") },
+
+ { njs_str("/[^]abc]/.test('#abc]')"),
+ njs_str("true") },
+
+ { njs_str("/[[^]abc]/.test('[abc]')"),
+ njs_str("true") },
+
+ { njs_str("/[[^]abc]/.test('^abc]')"),
+ njs_str("true") },
+
+ { njs_str("/[]/.test('[]')"),
+ njs_str("false") },
+
+ { njs_str("/[[]/.test('[')"),
+ njs_str("true") },
+
+ { njs_str("/\\[]/.test('[]')"),
+ njs_str("true") },
+
+ { njs_str("/[]abc]/.test('abc]')"),
+ njs_str("false") },
+
+ { njs_str("/abc]/.test('abc]')"),
+ njs_str("true") },
+
+ { njs_str("/\\\\\\[]/.test('\\\\[]')"),
+ njs_str("true") },
+
#ifdef NJS_HAVE_PCRE2
{ njs_str("/[]*a/.test('a')"),
njs_str("true") },
@@ -14186,22 +14286,22 @@ static njs_unit_test_t njs_test[] =
njs_str("true") },
{ njs_str("new Function('('.repeat(2**13));"),
- njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") },
+ njs_str("SyntaxError: Unexpected token \"}\" in runtime") },
{ njs_str("new Function('{'.repeat(2**13));"),
- njs_str("SyntaxError: Unexpected token \")\" in runtime:1") },
+ njs_str("SyntaxError: Unexpected token \")\" in runtime") },
{ njs_str("new Function('['.repeat(2**13));"),
- njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") },
+ njs_str("SyntaxError: Unexpected token \"}\" in runtime") },
{ njs_str("new Function('`'.repeat(2**13));"),
njs_str("[object Function]") },
{ njs_str("new Function('{['.repeat(2**13));"),
- njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") },
+ njs_str("SyntaxError: Unexpected token \"}\" in runtime") },
{ njs_str("new Function('{;'.repeat(2**13));"),
- njs_str("SyntaxError: Unexpected token \")\" in runtime:1") },
+ njs_str("SyntaxError: Unexpected token \")\" in runtime") },
{ njs_str("(new Function('1;'.repeat(2**13) + 'return 2'))()"),
njs_str("2") },
@@ -14213,7 +14313,7 @@ static njs_unit_test_t njs_test[] =
njs_str("-4") },
{ njs_str("new Function('new '.repeat(2**13));"),
- njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") },
+ njs_str("SyntaxError: Unexpected token \"}\" in runtime") },
{ njs_str("(new Function('return ' + 'typeof '.repeat(2**13) + 'x'))()"),
njs_str("string") },
@@ -14279,7 +14379,13 @@ static njs_unit_test_t njs_test[] =
njs_str("ReferenceError: \"foo\" is not defined") },
{ njs_str("this.NN = {}; var f = Function('eval = 42;'); f()"),
- njs_str("SyntaxError: Identifier \"eval\" is forbidden as left-hand in assignment in runtime:1") },
+ njs_str("SyntaxError: Identifier \"eval\" is forbidden as left-hand in assignment in runtime") },
+
+ { njs_str("new Function('}); let a; a; function o(){}; //')"),
+ njs_str("SyntaxError: Unexpected token \"}\" in runtime") },
+
+ { njs_str("new Function('}); let a; a; function o(){}; ({')"),
+ njs_str("SyntaxError: single function literal required") },
{ njs_str("RegExp()"),
njs_str("/(?:)/") },
@@ -19808,7 +19914,7 @@ static njs_unit_test_t njs_test[] =
njs_str("[object AsyncFunction]") },
{ njs_str("let f = new Function('x', 'await 1; return x'); f(1)"),
- njs_str("SyntaxError: await is only valid in async functions in runtime:1") },
+ njs_str("SyntaxError: await is only valid in async functions in runtime") },
{ njs_str("new AsyncFunction()"),
njs_str("ReferenceError: \"AsyncFunction\" is not defined") },
@@ -21673,7 +21779,9 @@ done:
return NJS_ERROR;
}
- success = njs_strstr_eq(&expected->ret, &s);
+ success = expected->ret.length <= s.length
+ && (memcmp(expected->ret.start, s.start, expected->ret.length)
+ == 0);
if (!success) {
njs_stderror("njs(\"%V\")\nexpected: \"%V\"\n got: \"%V\"\n",
&expected->script, &expected->ret, &s);
diff --git a/test/js/async_closure.t.js b/test/js/async_closure.t.js
new file mode 100644
index 00000000..9a387ff2
--- /dev/null
+++ b/test/js/async_closure.t.js
@@ -0,0 +1,24 @@
+/*---
+includes: []
+flags: [async]
+---*/
+
+async function f() {
+ await 1;
+ var v = 2;
+
+ function g() {
+ return v + 1;
+ }
+
+ function s() {
+ g + 1;
+ }
+
+ return g();
+}
+
+f().then(v => {
+ assert.sameValue(v, 3)
+})
+.then($DONE, $DONE);
diff --git a/test/js/async_closure_arg.t.js b/test/js/async_closure_arg.t.js
new file mode 100644
index 00000000..d6aa8ab6
--- /dev/null
+++ b/test/js/async_closure_arg.t.js
@@ -0,0 +1,24 @@
+/*---
+includes: []
+flags: [async]
+---*/
+
+async function f(v) {
+ await 1;
+ v = 2;
+
+ function g() {
+ return v + 1;
+ }
+
+ function s() {
+ g + 1;
+ }
+
+ return g();
+}
+
+f(42).then(v => {
+ assert.sameValue(v, 3)
+})
+.then($DONE, $DONE);
diff --git a/test/js/async_closure_share.t.js b/test/js/async_closure_share.t.js
new file mode 100644
index 00000000..d78f92c5
--- /dev/null
+++ b/test/js/async_closure_share.t.js
@@ -0,0 +1,28 @@
+/*---
+includes: []
+flags: [async]
+---*/
+
+async function f() {
+ await 1;
+ var v = 'f';
+
+ function g() {
+ v += ':g';
+ return v;
+ }
+
+ function s() {
+ v += ':s';
+ return v;
+ }
+
+ return [g, s];
+}
+
+f().then(pair => {
+ pair[0]();
+ var v = pair[1]();
+ assert.sameValue(v, 'f:g:s');
+})
+.then($DONE, $DONE);
diff --git a/ts/ngx_core.d.ts b/ts/ngx_core.d.ts
index b459c929..d35cb40f 100644
--- a/ts/ngx_core.d.ts
+++ b/ts/ngx_core.d.ts
@@ -276,6 +276,7 @@ interface NgxSharedDict<V extends string | number = string | number> {
*
* @param key The key of the item to add.
* @param value The value of the item to add.
+ * @param timeout Overrides the default timeout for this item in milliseconds.
* @returns `true` if the value has been added successfully, `false`
* if the `key` already exists in this dictionary.
* @throws {SharedMemoryError} if there's not enough free space in this
@@ -283,7 +284,7 @@ interface NgxSharedDict<V extends string | number = string | number> {
* @throws {TypeError} if the `value` is of a different type than expected
* by this dictionary.
*/
- add(key: string, value: V): boolean;
+ add(key: string, value: V, timeout?: number): boolean;
/**
* Removes all items from this dictionary.
*/
@@ -307,13 +308,14 @@ interface NgxSharedDict<V extends string | number = string | number> {
* @param delta The number to increment/decrement the value by.
* @param init The number to initialize the item with if it didn't exist
* (default is `0`).
+ * @param timeout Overrides the default timeout for this item in milliseconds.
* @returns The new value.
* @throws {SharedMemoryError} if there's not enough free space in this
* dictionary.
* @throws {TypeError} if this dictionary does not expect numbers.
*/
incr: V extends number
- ? (key: string, delta: V, init?: number) => number
+ ? (key: string, delta: V, init?: number, timeout?: number) => number
: never;
/**
* @param maxCount The maximum number of pairs to retrieve (default is 1024).
@@ -371,13 +373,14 @@ interface NgxSharedDict<V extends string | number = string | number> {
*
* @param key The key of the item to set.
* @param value The value of the item to set.
+ * @param timeout Overrides the default timeout for this item in milliseconds.
* @returns This dictionary (for method chaining).
* @throws {SharedMemoryError} if there's not enough free space in this
* dictionary.
* @throws {TypeError} if the `value` is of a different type than expected
* by this dictionary.
*/
- set(key: string, value: V): this;
+ set(key: string, value: V, timeout?: number): this;
/**
* @returns The number of items in this shared dictionary.
*/
diff --git a/ts/ngx_http_js_module.d.ts b/ts/ngx_http_js_module.d.ts
index 37932893..d7dd1c9e 100644
--- a/ts/ngx_http_js_module.d.ts
+++ b/ts/ngx_http_js_module.d.ts
@@ -468,7 +468,7 @@ interface NginxHTTPRequest {
/**
* nginx variables as strings.
*
- * **Warning:** Bytes invalid in UTF-8 encoding may be converted into the replacement character.
+ * After 0.8.5 bytes invalid in UTF-8 encoding are converted into the replacement characters.
*
* @see rawVariables
*/
diff --git a/ts/ngx_stream_js_module.d.ts b/ts/ngx_stream_js_module.d.ts
index 58c4d908..c78d008b 100644
--- a/ts/ngx_stream_js_module.d.ts
+++ b/ts/ngx_stream_js_module.d.ts
@@ -200,7 +200,7 @@ interface NginxStreamRequest {
/**
* nginx variables as strings.
*
- * **Warning:** Bytes invalid in UTF-8 encoding may be converted into the replacement character.
+ * After 0.8.5 bytes invalid in UTF-8 encoding are converted into the replacement characters.
*
* @see rawVariables
*/
diff --git a/ts/njs_core.d.ts b/ts/njs_core.d.ts
index 2f1d45d0..f64c9576 100644
--- a/ts/njs_core.d.ts
+++ b/ts/njs_core.d.ts
@@ -581,6 +581,7 @@ interface NjsProcess {
readonly env: NjsEnv;
/**
+ * Send signal to a process by its PID.
* @since 0.8.8
*/
kill(pid: number, signal?: string | number): true;
diff --git a/ts/njs_webcrypto.d.ts b/ts/njs_webcrypto.d.ts
index 058359cb..4e6f2057 100644
--- a/ts/njs_webcrypto.d.ts
+++ b/ts/njs_webcrypto.d.ts
@@ -41,12 +41,12 @@ interface RsaHashedKeyGenParams {
}
interface EcKeyImportParams {
- name: "ECDSA";
+ name: "ECDSA" | "ECDH";
namedCurve: "P-256" | "P-384" | "P-521";
}
interface EcKeyGenParams {
- name: "ECDSA";
+ name: "ECDSA" | "ECDH";
namedCurve: "P-256" | "P-384" | "P-521";
}
@@ -68,7 +68,8 @@ type ImportAlgorithm =
| AesImportParams
| AesVariants
| "PBKDF2"
- | "HKDF";
+ | "HKDF"
+ | "ECDH";
type GenerateAlgorithm =
| RsaHashedKeyGenParams
@@ -99,9 +100,15 @@ interface Pbkdf2Params {
interations: number;
}
+interface EcdhParams {
+ name: "ECDH";
+ public: CryptoKey;
+}
+
type DeriveAlgorithm =
| HkdfParams
- | Pbkdf2Params;
+ | Pbkdf2Params
+ | EcdhParams;
interface HmacKeyGenParams {
name: "HMAC";