From: Dmitry Volyntsev Date: Tue, 20 Feb 2018 16:12:53 +0000 (+0300) Subject: Externals refactored. X-Git-Tag: 0.2.0~35 X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=71c34dc00d6249c9aed6ccedd1b760fe8ff23a50;p=njs.git Externals refactored. Public API is rectified to allow the creation of external objects in runtime. 1) njs_vm_external_add() is replaced with njs_vm_external_prototype(). The later functions returns a pointer to a prototype object which can be used to create a value with such a prototype in runtime. 2) njs_vm_external() is split into njs_vm_external_create() and njs_vm_external_bind(). The former creates a variable with a specified prototype and associates it with an external pointer. The latter binds a variable to a name in the global namespace. --- diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index b717334c..d6d0f5b4 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -15,37 +15,21 @@ #include #include #include -#include -#include -#include #include -#define NGX_HTTP_JS_MCP_CLUSTER_SIZE (2 * ngx_pagesize) -#define NGX_HTTP_JS_MCP_PAGE_ALIGNMENT 128 -#define NGX_HTTP_JS_MCP_PAGE_SIZE 512 -#define NGX_HTTP_JS_MCP_MIN_CHUNK_SIZE 16 - - -#define ngx_http_js_create_mem_cache_pool() \ - nxt_mem_cache_pool_create(&ngx_http_js_mem_cache_pool_proto, NULL, NULL, \ - NGX_HTTP_JS_MCP_CLUSTER_SIZE, \ - NGX_HTTP_JS_MCP_PAGE_ALIGNMENT, \ - NGX_HTTP_JS_MCP_PAGE_SIZE, \ - NGX_HTTP_JS_MCP_MIN_CHUNK_SIZE) - - typedef struct { njs_vm_t *vm; - njs_opaque_value_t args[2]; ngx_str_t content; + const njs_extern_t *req_proto; + const njs_extern_t *res_proto; } ngx_http_js_loc_conf_t; typedef struct { njs_vm_t *vm; - njs_opaque_value_t *args; + njs_opaque_value_t args[2]; } ngx_http_js_ctx_t; @@ -59,12 +43,7 @@ static ngx_int_t ngx_http_js_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_js_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r); -static void ngx_http_js_cleanup_mem_cache_pool(void *data); - -static void *ngx_http_js_alloc(void *mem, size_t size); -static void *ngx_http_js_calloc(void *mem, size_t size); -static void *ngx_http_js_memalign(void *mem, size_t alignment, size_t size); -static void ngx_http_js_free(void *mem, void *p); +static void ngx_http_js_cleanup_vm(void *data); static njs_ret_t ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data); @@ -183,17 +162,6 @@ ngx_module_t ngx_http_js_module = { }; -static const nxt_mem_proto_t ngx_http_js_mem_cache_pool_proto = { - ngx_http_js_alloc, - ngx_http_js_calloc, - ngx_http_js_memalign, - NULL, - ngx_http_js_free, - NULL, - NULL, -}; - - static njs_external_t ngx_http_js_ext_response[] = { { nxt_string("headers"), @@ -284,18 +252,6 @@ static njs_external_t ngx_http_js_ext_response[] = { static njs_external_t ngx_http_js_ext_request[] = { - { nxt_string("response"), - NJS_EXTERN_OBJECT, - ngx_http_js_ext_response, - nxt_nitems(ngx_http_js_ext_response), - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - 0 }, - { nxt_string("log"), NJS_EXTERN_METHOD, NULL, @@ -396,7 +352,7 @@ static njs_external_t ngx_http_js_ext_request[] = { static njs_external_t ngx_http_js_externals[] = { - { nxt_string("$r"), + { nxt_string("request"), NJS_EXTERN_OBJECT, ngx_http_js_ext_request, nxt_nitems(ngx_http_js_ext_request), @@ -407,6 +363,18 @@ static njs_external_t ngx_http_js_externals[] = { NULL, NULL, 0 }, + + { nxt_string("response"), + NJS_EXTERN_OBJECT, + ngx_http_js_ext_response, + nxt_nitems(ngx_http_js_ext_response), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, }; @@ -520,11 +488,10 @@ ngx_http_js_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r) { - void **ext; - ngx_http_js_ctx_t *ctx; - ngx_pool_cleanup_t *cln; - nxt_mem_cache_pool_t *mcp; - ngx_http_js_loc_conf_t *jlcf; + nxt_int_t rc; + ngx_http_js_ctx_t *ctx; + ngx_pool_cleanup_t *cln; + ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); if (jlcf->vm == NULL) { @@ -546,8 +513,8 @@ ngx_http_js_init_vm(ngx_http_request_t *r) return NGX_OK; } - mcp = ngx_http_js_create_mem_cache_pool(); - if (mcp == NULL) { + ctx->vm = njs_vm_clone(jlcf->vm, r); + if (ctx->vm == NULL) { return NGX_ERROR; } @@ -556,65 +523,33 @@ ngx_http_js_init_vm(ngx_http_request_t *r) return NGX_ERROR; } - cln->handler = ngx_http_js_cleanup_mem_cache_pool; - cln->data = mcp; + cln->handler = ngx_http_js_cleanup_vm; + cln->data = ctx->vm; - ext = ngx_palloc(r->pool, sizeof(void *)); - if (ext == NULL) { + if (njs_vm_run(ctx->vm) != NJS_OK) { return NGX_ERROR; } - *ext = r; - - ctx->vm = njs_vm_clone(jlcf->vm, mcp, ext); - if (ctx->vm == NULL) { + rc = njs_vm_external_create(ctx->vm, &ctx->args[0], jlcf->req_proto, r); + if (rc != NXT_OK) { return NGX_ERROR; } - if (njs_vm_run(ctx->vm) != NJS_OK) { + rc = njs_vm_external_create(ctx->vm, &ctx->args[1], jlcf->res_proto, r); + if (rc != NXT_OK) { return NGX_ERROR; } - ctx->args = &jlcf->args[0]; - return NGX_OK; } static void -ngx_http_js_cleanup_mem_cache_pool(void *data) -{ - nxt_mem_cache_pool_t *mcp = data; - - nxt_mem_cache_pool_destroy(mcp); -} - - -static void * -ngx_http_js_alloc(void *mem, size_t size) +ngx_http_js_cleanup_vm(void *data) { - return ngx_alloc(size, ngx_cycle->log); -} - + njs_vm_t *vm = data; -static void * -ngx_http_js_calloc(void *mem, size_t size) -{ - return ngx_calloc(size, ngx_cycle->log); -} - - -static void * -ngx_http_js_memalign(void *mem, size_t alignment, size_t size) -{ - return ngx_memalign(alignment, size, ngx_cycle->log); -} - - -static void -ngx_http_js_free(void *mem, void *p) -{ - ngx_free(p); + njs_vm_destroy(vm); } @@ -1229,12 +1164,10 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_fd_t fd; ngx_str_t *value, file; nxt_int_t rc; - nxt_str_t text, ext; + nxt_str_t text; njs_vm_opt_t options; - nxt_lvlhsh_t externals; ngx_file_info_t fi; ngx_pool_cleanup_t *cln; - nxt_mem_cache_pool_t *mcp; if (jlcf->vm) { return "is duplicate"; @@ -1295,8 +1228,13 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) end = start + size; - mcp = ngx_http_js_create_mem_cache_pool(); - if (mcp == NULL) { + ngx_memzero(&options, sizeof(njs_vm_opt_t)); + + options.backtrace = 1; + + jlcf->vm = njs_vm_create(&options); + if (jlcf->vm == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM"); return NGX_CONF_ERROR; } @@ -1305,28 +1243,21 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - cln->handler = ngx_http_js_cleanup_mem_cache_pool; - cln->data = mcp; - - nxt_lvlhsh_init(&externals); + cln->handler = ngx_http_js_cleanup_vm; + cln->data = jlcf->vm; - if (njs_vm_external_add(&externals, mcp, 0, ngx_http_js_externals, - nxt_nitems(ngx_http_js_externals)) - != NJS_OK) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "could not add js externals"); + jlcf->req_proto = njs_vm_external_prototype(jlcf->vm, + &ngx_http_js_externals[0]); + if (jlcf->req_proto == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add request proto"); return NGX_CONF_ERROR; } - ngx_memzero(&options, sizeof(njs_vm_opt_t)); - - options.mcp = mcp; - options.backtrace = 1; - options.externals_hash = &externals; - - jlcf->vm = njs_vm_create(&options); - if (jlcf->vm == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM"); + jlcf->res_proto = njs_vm_external_prototype(jlcf->vm, + &ngx_http_js_externals[1]); + if (jlcf->res_proto == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "failed to add response proto"); return NGX_CONF_ERROR; } @@ -1348,25 +1279,6 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - ext = nxt_string_value("$r"); - - if (njs_vm_external(jlcf->vm, NULL, &ext, &jlcf->args[0]) != NJS_OK) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "js external \"%*s\" not found", - ext.length, ext.start); - return NGX_CONF_ERROR; - } - - ext = nxt_string_value("response"); - - rc = njs_vm_external(jlcf->vm, &jlcf->args[0], &ext, &jlcf->args[1]); - if (rc != NXT_OK) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "js external \"$r.%*s\" not found", - ext.length, ext.start); - return NGX_CONF_ERROR; - } - return NGX_CONF_OK; } @@ -1443,6 +1355,8 @@ ngx_http_js_create_loc_conf(ngx_conf_t *cf) * set by ngx_pcalloc(): * * conf->vm = NULL; + * conf->req_proto = NULL; + * conf->res_proto = NULL; */ return conf; @@ -1457,8 +1371,8 @@ ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->vm == NULL) { conf->vm = prev->vm; - conf->args[0] = prev->args[0]; - conf->args[1] = prev->args[1]; + conf->req_proto = prev->req_proto; + conf->res_proto = prev->res_proto; } return NGX_CONF_OK; diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index e8380b4a..f11e1459 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -15,39 +15,22 @@ #include #include #include -#include -#include -#include #include -#define NGX_STREAM_JS_MCP_CLUSTER_SIZE (2 * ngx_pagesize) -#define NGX_STREAM_JS_MCP_PAGE_ALIGNMENT 128 -#define NGX_STREAM_JS_MCP_PAGE_SIZE 512 -#define NGX_STREAM_JS_MCP_MIN_CHUNK_SIZE 16 - - -#define ngx_stream_js_create_mem_cache_pool() \ - nxt_mem_cache_pool_create(&ngx_stream_js_mem_cache_pool_proto, NULL, NULL,\ - NGX_STREAM_JS_MCP_CLUSTER_SIZE, \ - NGX_STREAM_JS_MCP_PAGE_ALIGNMENT, \ - NGX_STREAM_JS_MCP_PAGE_SIZE, \ - NGX_STREAM_JS_MCP_MIN_CHUNK_SIZE) - - typedef struct { njs_vm_t *vm; - njs_opaque_value_t arg; ngx_str_t access; ngx_str_t preread; ngx_str_t filter; + const njs_extern_t *proto; } ngx_stream_js_srv_conf_t; typedef struct { njs_vm_t *vm; - njs_opaque_value_t *arg; + njs_opaque_value_t arg; ngx_buf_t *buf; ngx_chain_t *free; ngx_chain_t *busy; @@ -65,13 +48,8 @@ static ngx_int_t ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in, ngx_uint_t from_upstream); static ngx_int_t ngx_stream_js_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); -static void ngx_stream_js_cleanup_mem_cache_pool(void *data); static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s); - -static void *ngx_stream_js_alloc(void *mem, size_t size); -static void *ngx_stream_js_calloc(void *mem, size_t size); -static void *ngx_stream_js_memalign(void *mem, size_t alignment, size_t size); -static void ngx_stream_js_free(void *mem, void *p); +static void ngx_stream_js_cleanup_vm(void *data); static njs_ret_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data); @@ -169,17 +147,6 @@ ngx_module_t ngx_stream_js_module = { }; -static const nxt_mem_proto_t ngx_stream_js_mem_cache_pool_proto = { - ngx_stream_js_alloc, - ngx_stream_js_calloc, - ngx_stream_js_memalign, - NULL, - ngx_stream_js_free, - NULL, - NULL, -}; - - static njs_external_t ngx_stream_js_ext_session[] = { { nxt_string("remoteAddress"), @@ -318,7 +285,7 @@ static njs_external_t ngx_stream_js_ext_session[] = { static njs_external_t ngx_stream_js_externals[] = { - { nxt_string("$s"), + { nxt_string("stream"), NJS_EXTERN_OBJECT, ngx_stream_js_ext_session, nxt_nitems(ngx_stream_js_ext_session), @@ -405,7 +372,7 @@ ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name) return NGX_ERROR; } - if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) { + if (njs_vm_call(ctx->vm, func, &ctx->arg, 1) != NJS_OK) { njs_vm_retval_to_ext_string(ctx->vm, &exception); ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %*s", @@ -492,7 +459,7 @@ ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in, while (in) { ctx->buf = in->buf; - if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) { + if (njs_vm_call(ctx->vm, func, &ctx->arg, 1) != NJS_OK) { njs_vm_retval_to_ext_string(ctx->vm, &exception); ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %*s", @@ -590,7 +557,7 @@ ngx_stream_js_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, return NGX_OK; } - if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) { + if (njs_vm_call(ctx->vm, func, &ctx->arg, 1) != NJS_OK) { njs_vm_retval_to_ext_string(ctx->vm, &exception); ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, @@ -617,11 +584,10 @@ ngx_stream_js_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s) { - void **ext; - ngx_pool_cleanup_t *cln; - nxt_mem_cache_pool_t *mcp; - ngx_stream_js_ctx_t *ctx; - ngx_stream_js_srv_conf_t *jscf; + nxt_int_t rc; + ngx_pool_cleanup_t *cln; + ngx_stream_js_ctx_t *ctx; + ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); if (jscf->vm == NULL) { @@ -643,8 +609,8 @@ ngx_stream_js_init_vm(ngx_stream_session_t *s) return NGX_OK; } - mcp = ngx_stream_js_create_mem_cache_pool(); - if (mcp == NULL) { + ctx->vm = njs_vm_clone(jscf->vm, s); + if (ctx->vm == NULL) { return NGX_ERROR; } @@ -653,65 +619,28 @@ ngx_stream_js_init_vm(ngx_stream_session_t *s) return NGX_ERROR; } - cln->handler = ngx_stream_js_cleanup_mem_cache_pool; - cln->data = mcp; - - ext = ngx_palloc(s->connection->pool, sizeof(void *)); - if (ext == NULL) { - return NGX_ERROR; - } - - *ext = s; + cln->handler = ngx_stream_js_cleanup_vm; + cln->data = ctx->vm; - ctx->vm = njs_vm_clone(jscf->vm, mcp, ext); - if (ctx->vm == NULL) { + if (njs_vm_run(ctx->vm) != NJS_OK) { return NGX_ERROR; } - if (njs_vm_run(ctx->vm) != NJS_OK) { + rc = njs_vm_external_create(ctx->vm, &ctx->arg, jscf->proto, s); + if (rc != NXT_OK) { return NGX_ERROR; } - ctx->arg = &jscf->arg; - return NGX_OK; } static void -ngx_stream_js_cleanup_mem_cache_pool(void *data) -{ - nxt_mem_cache_pool_t *mcp = data; - - nxt_mem_cache_pool_destroy(mcp); -} - - -static void * -ngx_stream_js_alloc(void *mem, size_t size) -{ - return ngx_alloc(size, ngx_cycle->log); -} - - -static void * -ngx_stream_js_calloc(void *mem, size_t size) +ngx_stream_js_cleanup_vm(void *data) { - return ngx_calloc(size, ngx_cycle->log); -} - - -static void * -ngx_stream_js_memalign(void *mem, size_t alignment, size_t size) -{ - return ngx_memalign(alignment, size, ngx_cycle->log); -} + njs_vm_t *vm = data; - -static void -ngx_stream_js_free(void *mem, void *p) -{ - ngx_free(p); + njs_vm_destroy(vm); } @@ -937,12 +866,10 @@ ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_fd_t fd; ngx_str_t *value, file; nxt_int_t rc; - nxt_str_t text, ext; + nxt_str_t text; njs_vm_opt_t options; - nxt_lvlhsh_t externals; ngx_file_info_t fi; ngx_pool_cleanup_t *cln; - nxt_mem_cache_pool_t *mcp; if (jscf->vm) { return "is duplicate"; @@ -1003,8 +930,13 @@ ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) end = start + size; - mcp = ngx_stream_js_create_mem_cache_pool(); - if (mcp == NULL) { + ngx_memzero(&options, sizeof(njs_vm_opt_t)); + + options.backtrace = 1; + + jscf->vm = njs_vm_create(&options); + if (jscf->vm == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM"); return NGX_CONF_ERROR; } @@ -1013,28 +945,14 @@ ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - cln->handler = ngx_stream_js_cleanup_mem_cache_pool; - cln->data = mcp; + cln->handler = ngx_stream_js_cleanup_vm; + cln->data = jscf->vm; - nxt_lvlhsh_init(&externals); + jscf->proto = njs_vm_external_prototype(jscf->vm, + &ngx_stream_js_externals[0]); - if (njs_vm_external_add(&externals, mcp, 0, ngx_stream_js_externals, - nxt_nitems(ngx_stream_js_externals)) - != NJS_OK) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "could not add js externals"); - return NGX_CONF_ERROR; - } - - ngx_memzero(&options, sizeof(njs_vm_opt_t)); - - options.mcp = mcp; - options.backtrace = 1; - options.externals_hash = &externals; - - jscf->vm = njs_vm_create(&options); - if (jscf->vm == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM"); + if (jscf->proto == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add stream proto"); return NGX_CONF_ERROR; } @@ -1056,14 +974,6 @@ ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - ext = nxt_string_value("$s"); - - if (njs_vm_external(jscf->vm, NULL, &ext, &jscf->arg) != NJS_OK) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "js external \"%*s\" not found", ext.length, ext.start); - return NGX_CONF_ERROR; - } - return NGX_CONF_OK; } @@ -1118,7 +1028,7 @@ ngx_stream_js_create_srv_conf(ngx_conf_t *cf) * set by ngx_pcalloc(): * * conf->vm = NULL; - * conf->arg = NULL; + * conf->proto = NULL; * conf->access = { 0, NULL }; * conf->preread = { 0, NULL }; * conf->filter = { 0, NULL }; @@ -1136,7 +1046,7 @@ ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->vm == NULL) { conf->vm = prev->vm; - conf->arg = prev->arg; + conf->proto = prev->proto; } ngx_conf_merge_str_value(conf->access, prev->access, ""); diff --git a/njs/njs.c b/njs/njs.c index ee7cc901..c4bdb1ca 100644 --- a/njs/njs.c +++ b/njs/njs.c @@ -60,7 +60,7 @@ typedef struct { static nxt_int_t njs_get_options(njs_opts_t *opts, int argc, char **argv); -static nxt_int_t njs_externals_init(njs_opts_t *opts, njs_vm_opt_t *vm_options); +static nxt_int_t njs_externals_init(njs_vm_t *vm); static nxt_int_t njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options); static nxt_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options); @@ -119,17 +119,15 @@ static njs_external_t njs_externals[] = { }; -static nxt_lvlhsh_t njs_externals_hash; static njs_completion_t njs_completion; int main(int argc, char **argv) { - nxt_int_t ret; - njs_opts_t opts; - njs_vm_opt_t vm_options; - nxt_mem_cache_pool_t *mcp; + nxt_int_t ret; + njs_opts_t opts; + njs_vm_opt_t vm_options; memset(&opts, 0, sizeof(njs_opts_t)); opts.interactive = 1; @@ -144,22 +142,11 @@ main(int argc, char **argv) return EXIT_SUCCESS; } - mcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL, - NULL, 2 * nxt_pagesize(), 128, 512, 16); - if (nxt_slow_path(mcp == NULL)) { - return EXIT_FAILURE; - } - memset(&vm_options, 0, sizeof(njs_vm_opt_t)); - vm_options.mcp = mcp; vm_options.accumulative = 1; vm_options.backtrace = 1; - if (njs_externals_init(&opts, &vm_options) != NXT_OK) { - return EXIT_FAILURE; - } - if (opts.interactive) { ret = njs_interactive_shell(&opts, &vm_options); @@ -218,30 +205,35 @@ njs_get_options(njs_opts_t *opts, int argc, char** argv) static nxt_int_t -njs_externals_init(njs_opts_t *opts, njs_vm_opt_t *vm_options) +njs_externals_init(njs_vm_t *vm) { - void **ext; - nxt_uint_t i; + nxt_uint_t ret; + const njs_extern_t *proto; + njs_opaque_value_t *value; - nxt_lvlhsh_init(&njs_externals_hash); + static const nxt_str_t name = nxt_string_value("console"); - for (i = 0; i < nxt_nitems(njs_externals); i++) { - if (njs_vm_external_add(&njs_externals_hash, vm_options->mcp, - (uintptr_t) i, &njs_externals[i], 1) - != NXT_OK) - { - fprintf(stderr, "could not add external objects\n"); - return NXT_ERROR; - } + proto = njs_vm_external_prototype(vm, &njs_externals[0]); + if (proto == NULL) { + fprintf(stderr, "failed to add console proto\n"); + return NXT_ERROR; } - ext = nxt_mem_cache_zalloc(vm_options->mcp, sizeof(void *) * i); - if (ext == NULL) { + value = nxt_mem_cache_zalloc(vm->mem_cache_pool, + sizeof(njs_opaque_value_t)); + if (value == NULL) { return NXT_ERROR; } - vm_options->external = ext; - vm_options->externals_hash = &njs_externals_hash; + ret = njs_vm_external_create(vm, value, proto, NULL); + if (ret != NXT_OK) { + return NXT_ERROR; + } + + ret = njs_vm_external_bind(vm, &name, value); + if (ret != NXT_OK) { + return NXT_ERROR; + } return NXT_OK; } @@ -260,6 +252,10 @@ njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options) return NXT_ERROR; } + if (njs_externals_init(vm) != NXT_OK) { + return NXT_ERROR; + } + if (njs_editline_init(vm) != NXT_OK) { fprintf(stderr, "failed to init completions\n"); return NXT_ERROR; @@ -393,6 +389,10 @@ njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options) goto done; } + if (njs_externals_init(vm) != NXT_OK) { + return NXT_ERROR; + } + ret = njs_process_script(vm, opts, &script, &out); if (ret != NXT_OK) { fprintf(stderr, "failed to get retval from VM\n"); @@ -652,10 +652,7 @@ njs_ext_console_help(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } printf("\nEmbedded objects:\n"); - for (i = 0; i < nxt_nitems(njs_externals); i++) { - printf(" %.*s\n", (int) njs_externals[i].name.length, - njs_externals[i].name.start); - } + printf(" console\n"); printf("\n"); diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index d04570a9..314adbcf 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -513,12 +513,13 @@ njs_builtin_completions(njs_vm_t *vm, size_t *size, nxt_str_t *completions) size_t n, len; nxt_str_t string; nxt_uint_t i, k; - njs_extern_t *ext_object, *ext_prop; njs_object_t *objects; njs_keyword_t *keyword; njs_function_t *constructors; njs_object_prop_t *prop; nxt_lvlhsh_each_t lhe, lhe_prop; + njs_extern_value_t *ev; + const njs_extern_t *ext_proto, *ext_prop; njs_object_prototype_t *prototypes; n = 0; @@ -652,26 +653,27 @@ njs_builtin_completions(njs_vm_t *vm, size_t *size, nxt_str_t *completions) } } - nxt_lvlhsh_each_init(&lhe, &njs_extern_hash_proto); + nxt_lvlhsh_each_init(&lhe, &njs_extern_value_hash_proto); for ( ;; ) { - ext_object = nxt_lvlhsh_each(&vm->externals_hash, &lhe); + ev = nxt_lvlhsh_each(&vm->externals_hash, &lhe); - if (ext_object == NULL) { + if (ev == NULL) { break; } + ext_proto = ev->value->external.proto; + nxt_lvlhsh_each_init(&lhe_prop, &njs_extern_hash_proto); if (completions != NULL) { - len = ext_object->name.length + 1; + len = ev->name.length + 1; compl = nxt_mem_cache_zalloc(vm->mem_cache_pool, len); if (compl == NULL) { return NXT_ERROR; } - snprintf(compl, len, "%.*s", - (int) ext_object->name.length, ext_object->name.start); + snprintf(compl, len, "%.*s", (int) ev->name.length, ev->name.start); completions[n].length = len; completions[n++].start = (u_char *) compl; @@ -681,22 +683,22 @@ njs_builtin_completions(njs_vm_t *vm, size_t *size, nxt_str_t *completions) } for ( ;; ) { - ext_prop = nxt_lvlhsh_each(&ext_object->hash, &lhe_prop); + ext_prop = nxt_lvlhsh_each(&ext_proto->hash, &lhe_prop); if (ext_prop == NULL) { break; } if (completions != NULL) { - len = ext_object->name.length + ext_prop->name.length + 2; + len = ev->name.length + ev->name.length + 2; compl = nxt_mem_cache_zalloc(vm->mem_cache_pool, len); if (compl == NULL) { return NXT_ERROR; } - snprintf(compl, len, "%.*s.%.*s", - (int) ext_object->name.length, ext_object->name.start, - (int) ext_prop->name.length, ext_prop->name.start); + snprintf(compl, len, "%.*s.%.*s", (int) ev->name.length, + ev->name.start, (int) ext_prop->name.length, + ext_prop->name.start); completions[n].length = len; completions[n++].start = (u_char *) compl; diff --git a/njs/njs_extern.c b/njs/njs_extern.c index 17bffccb..df2e28bf 100644 --- a/njs/njs_extern.c +++ b/njs/njs_extern.c @@ -29,10 +29,8 @@ njs_extern_hash_test(nxt_lvlhsh_query_t *lhq, void *data) { njs_extern_t *ext; - ext = data; + ext = (njs_extern_t *) data; -// STUB -// if (nxt_strcasestr_eq(&lhq->key, &ext->name)) { if (nxt_strstr_eq(&lhq->key, &ext->name)) { return NXT_OK; } @@ -41,6 +39,21 @@ njs_extern_hash_test(nxt_lvlhsh_query_t *lhq, void *data) } +static nxt_int_t +njs_extern_value_hash_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + njs_extern_value_t *ev; + + ev = (njs_extern_value_t *) data; + + if (nxt_strstr_eq(&lhq->key, &ev->name)) { + return NXT_OK; + } + + return NXT_DECLINED; +} + + const nxt_lvlhsh_proto_t njs_extern_hash_proto nxt_aligned(64) = { @@ -52,71 +65,78 @@ const nxt_lvlhsh_proto_t njs_extern_hash_proto }; -nxt_int_t -njs_vm_external_add(nxt_lvlhsh_t *hash, nxt_mem_cache_pool_t *mcp, - uintptr_t object, njs_external_t *external, nxt_uint_t n) +const nxt_lvlhsh_proto_t njs_extern_value_hash_proto + nxt_aligned(64) = +{ + NXT_LVLHSH_DEFAULT, + NXT_LVLHSH_BATCH_ALLOC, + njs_extern_value_hash_test, + njs_lvlhsh_alloc, + njs_lvlhsh_free, +}; + + +static njs_extern_t * +njs_vm_external_add(njs_vm_t *vm, nxt_lvlhsh_t *hash, njs_external_t *external, + nxt_uint_t n) { nxt_int_t ret; - njs_extern_t *ext; + njs_extern_t *ext, *child; nxt_lvlhsh_query_t lhq; do { - ext = nxt_mem_cache_align(mcp, sizeof(njs_value_t), - sizeof(njs_extern_t)); + ext = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_extern_t)); if (nxt_slow_path(ext == NULL)) { - return NXT_ERROR; - } - - ext->name.length = external->name.length; - ext->name.start = nxt_mem_cache_alloc(mcp, external->name.length); - if (nxt_slow_path(ext->name.start == NULL)) { - return NXT_ERROR; + return NULL; } - memcpy(ext->name.start, external->name.start, external->name.length); - - ext->value.type = NJS_EXTERNAL; - ext->value.data.truth = 1; - ext->value.data.u.external = ext; - - if (external->method != NULL) { - ext->function = nxt_mem_cache_zalloc(mcp, sizeof(njs_function_t)); - if (nxt_slow_path(ext->function == NULL)) { - return NXT_ERROR; - } - - ext->function->native = 1; - ext->function->args_offset = 1; - ext->function->u.native = external->method; - } + ext->name = external->name; nxt_lvlhsh_init(&ext->hash); + ext->type = external->type; ext->get = external->get; ext->set = external->set; ext->find = external->find; ext->foreach = external->foreach; ext->next = external->next; - ext->object = object; ext->data = external->data; - lhq.key_hash = nxt_djb_hash(external->name.start, external->name.length); - lhq.key = ext->name; - lhq.replace = 0; - lhq.value = ext; - lhq.pool = mcp; - lhq.proto = &njs_extern_hash_proto; + if (external->method != NULL) { + ext->function = nxt_mem_cache_zalloc(vm->mem_cache_pool, + sizeof(njs_function_t)); + if (nxt_slow_path(ext->function == NULL)) { + return NULL; + } + + ext->function->native = 1; + ext->function->args_offset = 1; + ext->function->u.native = external->method; - ret = nxt_lvlhsh_insert(hash, &lhq); - if (nxt_slow_path(ret != NXT_OK)) { - return ret; + } else { + ext->function = NULL; } if (external->properties != NULL) { - ret = njs_vm_external_add(&ext->hash, mcp, object, - external->properties, external->nproperties); + child = njs_vm_external_add(vm, &ext->hash, external->properties, + external->nproperties); + if (nxt_slow_path(child == NULL)) { + return NULL; + } + } + + if (hash != NULL) { + lhq.key_hash = nxt_djb_hash(external->name.start, + external->name.length); + lhq.key = ext->name; + lhq.replace = 0; + lhq.value = ext; + lhq.pool = vm->mem_cache_pool; + lhq.proto = &njs_extern_hash_proto; + + ret = nxt_lvlhsh_insert(hash, &lhq); if (nxt_slow_path(ret != NXT_OK)) { - return ret; + return NULL; } } @@ -125,62 +145,99 @@ njs_vm_external_add(nxt_lvlhsh_t *hash, nxt_mem_cache_pool_t *mcp, } while (n != 0); - return NXT_OK; + return ext; +} + + +const njs_extern_t * +njs_vm_external_prototype(njs_vm_t *vm, njs_external_t *external) +{ + return njs_vm_external_add(vm, &vm->external_prototypes_hash, external, 1); } nxt_int_t -njs_vm_external(njs_vm_t *vm, njs_opaque_value_t *obj, nxt_str_t *property, - njs_opaque_value_t *value) +njs_vm_external_create(njs_vm_t *vm, njs_opaque_value_t *value, + const njs_extern_t *proto, void *object) { - uint32_t (*key_hash)(const void *, size_t); - njs_value_t *object; - njs_extern_t *ext; - nxt_lvlhsh_t hash; - nxt_lvlhsh_query_t lhq; + void *obj; + njs_value_t *ext_val; - object = (njs_value_t *) obj; + if (nxt_slow_path(proto == NULL)) { + return NXT_ERROR; + } - key_hash = nxt_djb_hash; - hash = vm->externals_hash; + obj = nxt_array_add(vm->external_objects, &njs_array_mem_proto, + vm->mem_cache_pool); + if (nxt_slow_path(obj == NULL)) { + return NXT_ERROR; + } - if (object != NULL) { - if (!njs_is_external(object)) { - return NXT_ERROR; - } + memcpy(obj, &object, sizeof(void *)); - ext = object->data.u.external; - hash = ext->hash; + ext_val = (njs_value_t *) value; - if (ext->type == NJS_EXTERN_CASELESS_OBJECT) { - key_hash = nxt_djb_hash_lowcase; - } + ext_val->type = NJS_EXTERNAL; + ext_val->data.truth = 1; + ext_val->external.proto = proto; + ext_val->external.index = vm->external_objects->items - 1; + + return NXT_OK; +} + + +nxt_int_t +njs_vm_external_bind(njs_vm_t *vm, const nxt_str_t *var_name, + njs_opaque_value_t *val) +{ + nxt_int_t ret; + njs_value_t *value; + njs_extern_value_t *ev; + nxt_lvlhsh_query_t lhq; + + value = (njs_value_t *) val; + + if (nxt_slow_path(!njs_is_external(value))) { + return NXT_ERROR; + } + + ev = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_extern_value_t)); + if (nxt_slow_path(ev == NULL)) { + return NXT_ERROR; } - lhq.key_hash = key_hash(property->start, property->length); - lhq.key = *property; - lhq.proto = &njs_extern_hash_proto; + ev->name = *var_name; + ev->value = value; - if (nxt_lvlhsh_find(&hash, &lhq) == NXT_OK) { - *value = *(njs_opaque_value_t *) lhq.value; - return NXT_OK; + lhq.key = *var_name; + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.proto = &njs_extern_value_hash_proto; + lhq.value = ev; + lhq.replace = 0; + lhq.pool = vm->mem_cache_pool; + + ret = nxt_lvlhsh_insert(&vm->externals_hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; } - return NXT_ERROR; + return NXT_OK; } -njs_extern_t * +njs_value_t * njs_parser_external(njs_vm_t *vm, njs_parser_t *parser) { nxt_lvlhsh_query_t lhq; + njs_extern_value_t *ev; lhq.key_hash = parser->lexer->key_hash; lhq.key = parser->lexer->text; - lhq.proto = &njs_extern_hash_proto; + lhq.proto = &njs_extern_value_hash_proto; if (nxt_lvlhsh_find(&vm->externals_hash, &lhq) == NXT_OK) { - return lhq.value; + ev = (njs_extern_value_t *) lhq.value; + return ev->value; } return NULL; diff --git a/njs/njs_extern.h b/njs/njs_extern.h index ea39a574..2ef45e82 100644 --- a/njs/njs_extern.h +++ b/njs/njs_extern.h @@ -8,9 +8,11 @@ #define _NJS_EXTERN_H_INCLUDED_ -struct njs_extern_s { - njs_value_t value; +#define njs_extern_object(vm, ext) \ + (*(void **) nxt_array_item((vm)->external_objects, (ext)->external.index)) + +struct njs_extern_s { /* A hash of inclusive njs_extern_t. */ nxt_lvlhsh_t hash; @@ -26,12 +28,18 @@ struct njs_extern_s { njs_function_t *function; - uintptr_t object; uintptr_t data; }; +typedef struct { + nxt_str_t name; + njs_value_t *value; +} njs_extern_value_t; + + extern const nxt_lvlhsh_proto_t njs_extern_hash_proto; +extern const nxt_lvlhsh_proto_t njs_extern_value_hash_proto; #endif /* _NJS_EXTERN_H_INCLUDED_ */ diff --git a/njs/njs_parser.c b/njs/njs_parser.c index 1cdc6445..5083a314 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1807,7 +1808,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) { double num; njs_ret_t ret; - njs_extern_t *ext; + njs_value_t *ext; njs_parser_node_t *node; if (token == NJS_TOKEN_OPEN_PARENTHESIS) { @@ -1846,8 +1847,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) if (ext != NULL) { node->token = NJS_TOKEN_EXTERNAL; - node->u.value.type = NJS_EXTERNAL; - node->u.value.data.truth = 1; + node->u.value = *ext; node->index = (njs_index_t) ext; break; } diff --git a/njs/njs_parser.h b/njs/njs_parser.h index fdbdd231..f9fa8f54 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -264,7 +264,6 @@ struct njs_parser_node_s { njs_value_t value; njs_vmcode_operation_t operation; njs_parser_node_t *object; - njs_extern_t *external; } u; njs_index_t index; @@ -359,7 +358,7 @@ nxt_int_t njs_lexer_keywords_init(nxt_mem_cache_pool_t *mcp, nxt_lvlhsh_t *hash); njs_token_t njs_lexer_keyword(njs_lexer_t *lexer); -njs_extern_t *njs_parser_external(njs_vm_t *vm, njs_parser_t *parser); +njs_value_t *njs_parser_external(njs_vm_t *vm, njs_parser_t *parser); njs_parser_node_t *njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_parser_t *prev); diff --git a/njs/njs_vm.c b/njs/njs_vm.c index 7dd1f60e..bfb35dd1 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -519,15 +519,16 @@ njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object, njs_value_t *property) { + void *obj; int32_t index; uintptr_t data; njs_ret_t ret; - njs_value_t *val; - njs_extern_t *ext; + njs_value_t *val, ext_val; njs_slice_prop_t slice; njs_string_prop_t string; njs_object_prop_t *prop; const njs_value_t *retval; + const njs_extern_t *ext_proto; njs_property_query_t pq; pq.query = NJS_PROPERTY_QUERY_GET; @@ -615,19 +616,24 @@ njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object, break; case NJS_EXTERNAL_VALUE: - ext = object->data.u.external; + ext_proto = object->external.proto; - ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq); + ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq); if (ret == NXT_OK) { - ext = pq.lhq.value; + ext_proto = pq.lhq.value; - if ((ext->type & NJS_EXTERN_OBJECT) != 0) { - retval = &ext->value; + ext_val.type = NJS_EXTERNAL; + ext_val.data.truth = 1; + ext_val.external.proto = ext_proto; + ext_val.external.index = object->external.index; + + if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) { + retval = &ext_val; break; } - data = ext->data; + data = ext_proto->data; } else { data = (uintptr_t) &pq.lhq.key; @@ -635,13 +641,15 @@ njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object, vm->retval = njs_value_void; - if (ext->get != NULL) { - ret = ext->get(vm, &vm->retval, vm->external[ext->object], data); + if (ext_proto->get != NULL) { + obj = njs_extern_object(vm, object); + + ret = ext_proto->get(vm, &vm->retval, obj, data); if (nxt_slow_path(ret != NXT_OK)) { return ret; } - /* The vm->retval is already retained by ext->get(). */ + /* The vm->retval is already retained by ext_proto->get(). */ } return sizeof(njs_vmcode_prop_get_t); @@ -665,12 +673,13 @@ njs_ret_t njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object, njs_value_t *property) { + void *obj; uintptr_t data; nxt_str_t s; njs_ret_t ret; njs_value_t *p, *value; - njs_extern_t *ext; njs_object_prop_t *prop; + const njs_extern_t *ext_proto; njs_property_query_t pq; njs_vmcode_prop_set_t *code; @@ -726,19 +735,19 @@ njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object, return sizeof(njs_vmcode_prop_set_t); case NJS_EXTERNAL_VALUE: - ext = object->data.u.external; + ext_proto = object->external.proto; - ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq); + ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq); if (ret == NXT_OK) { - ext = pq.lhq.value; - data = ext->data; + ext_proto = pq.lhq.value; + data = ext_proto->data; } else { data = (uintptr_t) &pq.lhq.key; } - if (ext->set != NULL) { + if (ext_proto->set != NULL) { ret = njs_vm_value_to_ext_string(vm, &s, value, 0); if (nxt_slow_path(ret != NXT_OK)) { return ret; @@ -746,7 +755,9 @@ njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object, /* TODO retain value if it is string. */ - ret = ext->set(vm, vm->external[ext->object], data, &s); + obj = njs_extern_object(vm, object); + + ret = ext_proto->set(vm, obj, data, &s); if (nxt_slow_path(ret != NXT_OK)) { return ret; } @@ -772,11 +783,12 @@ njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object, njs_ret_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property) { + void *obj; uintptr_t data; njs_ret_t ret; njs_value_t *value; - njs_extern_t *ext; const njs_value_t *retval; + const njs_extern_t *ext_proto; njs_property_query_t pq; retval = &njs_value_false; @@ -810,9 +822,9 @@ njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property) break; case NJS_EXTERNAL_VALUE: - ext = object->data.u.external; + ext_proto = object->external.proto; - ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq); + ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq); if (ret == NXT_OK) { retval = &njs_value_true; @@ -820,8 +832,10 @@ njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property) } else { data = (uintptr_t) &pq.lhq.key; - if (ext->find != NULL) { - ret = ext->find(vm, vm->external[ext->object], data, 0); + if (ext_proto->find != NULL) { + obj = njs_extern_object(vm, object); + + ret = ext_proto->find(vm, obj, data, 0); if (nxt_slow_path(ret == NXT_ERROR)) { return ret; @@ -852,12 +866,13 @@ njs_ret_t njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object, njs_value_t *property) { + void *obj; uintptr_t data; njs_ret_t ret; - njs_value_t *value; - njs_extern_t *ext; + njs_value_t *value, ext_val; const njs_value_t *retval; njs_object_prop_t *prop; + const njs_extern_t *ext_proto; njs_property_query_t pq; retval = &njs_value_false; @@ -896,26 +911,34 @@ njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object, case NJS_EXTERNAL_VALUE: - ext = object->data.u.external; + ext_proto = object->external.proto; - ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq); + ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq); if (ret == NXT_OK) { - ext = pq.lhq.value; + ext_proto = pq.lhq.value; + + if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) { + + ext_val.type = NJS_EXTERNAL; + ext_val.data.truth = 1; + ext_val.external.proto = ext_proto; + ext_val.external.index = object->external.index; - if ((ext->type & NJS_EXTERN_OBJECT) != 0) { - data = (uintptr_t) &ext->value; + data = (uintptr_t) &ext_val; } else { - data = ext->data; + data = ext_proto->data; } } else { data = (uintptr_t) &pq.lhq.key; } - if (ext->find != NULL) { - ret = ext->find(vm, vm->external[ext->object], data, 1); + if (ext_proto->find != NULL) { + obj = njs_extern_object(vm, object); + + ret = ext_proto->find(vm, obj, data, 1); if (nxt_slow_path(ret == NXT_ERROR)) { return ret; @@ -958,12 +981,12 @@ static nxt_noinline njs_ret_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object, njs_value_t *property) { - uint32_t index; - uint32_t (*hash)(const void *, size_t); - njs_ret_t ret; - njs_extern_t *ext; - njs_object_t *obj; - njs_function_t *function; + uint32_t index; + uint32_t (*hash)(const void *, size_t); + njs_ret_t ret; + njs_object_t *obj; + njs_function_t *function; + const njs_extern_t *ext_proto; hash = nxt_djb_hash; @@ -1031,9 +1054,9 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object, break; case NJS_EXTERNAL: - ext = object->data.u.external; + ext_proto = object->external.proto; - if (ext->type == NJS_EXTERN_CASELESS_OBJECT) { + if (ext_proto->type == NJS_EXTERN_CASELESS_OBJECT) { hash = nxt_djb_hash_lowcase; } @@ -1224,9 +1247,10 @@ njs_ret_t njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object, njs_value_t *invld) { + void *obj; njs_ret_t ret; - njs_extern_t *ext; njs_property_next_t *next; + const njs_extern_t *ext_proto; njs_vmcode_prop_foreach_t *code; if (njs_is_object(object)) { @@ -1246,10 +1270,12 @@ njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object, } } else if (njs_is_external(object)) { - ext = object->data.u.external; + ext_proto = object->external.proto; - if (ext->foreach != NULL) { - ret = ext->foreach(vm, vm->external[ext->object], &vm->retval); + if (ext_proto->foreach != NULL) { + obj = njs_extern_object(vm, object); + + ret = ext_proto->foreach(vm, obj, &vm->retval); if (nxt_slow_path(ret != NXT_OK)) { return ret; } @@ -1265,13 +1291,14 @@ njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object, njs_ret_t njs_vmcode_property_next(njs_vm_t *vm, njs_value_t *object, njs_value_t *value) { + void *obj; njs_ret_t ret; nxt_uint_t n; njs_value_t *retval; njs_array_t *array; - njs_extern_t *ext; njs_object_prop_t *prop; njs_property_next_t *next; + const njs_extern_t *ext_proto; njs_vmcode_prop_next_t *code; code = (njs_vmcode_prop_next_t *) vm->current; @@ -1307,10 +1334,12 @@ njs_vmcode_property_next(njs_vm_t *vm, njs_value_t *object, njs_value_t *value) nxt_mem_cache_free(vm->mem_cache_pool, next); } else if (njs_is_external(object)) { - ext = object->data.u.external; + ext_proto = object->external.proto; + + if (ext_proto->next != NULL) { + obj = njs_extern_object(vm, object); - if (ext->next != NULL) { - ret = ext->next(vm, retval, vm->external[ext->object], value); + ret = ext_proto->next(vm, retval, obj, value); if (ret == NXT_OK) { return code->offset; @@ -2313,11 +2342,12 @@ njs_function_new_object(njs_vm_t *vm, njs_value_t *value) njs_ret_t njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name) { + void *obj; njs_ret_t ret; njs_value_t this, *value; - njs_extern_t *ext; njs_object_prop_t *prop; njs_property_query_t pq; + const njs_extern_t *ext_proto; njs_vmcode_method_frame_t *method; method = (njs_vmcode_method_frame_t *) vm->current; @@ -2343,9 +2373,9 @@ njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name) break; case NJS_EXTERNAL_VALUE: - ext = object->data.u.external; + ext_proto = object->external.proto; - ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq); + ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq); if (nxt_slow_path(ret != NXT_OK)) { njs_exception_type_error(vm, @@ -2355,18 +2385,20 @@ njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name) } - ext = pq.lhq.value; + ext_proto = pq.lhq.value; - if (nxt_slow_path(ext->type != NJS_EXTERN_METHOD)) { + if (nxt_slow_path(ext_proto->type != NJS_EXTERN_METHOD)) { njs_exception_type_error(vm, "method '%.*s' of an external object is not callable", (int) pq.lhq.key.length, pq.lhq.key.start); return NXT_ERROR; } - this.data.u.data = vm->external[ext->object]; + obj = njs_extern_object(vm, object); + + this.data.u.data = obj; - ret = njs_function_native_frame(vm, ext->function, &this, NULL, + ret = njs_function_native_frame(vm, ext_proto->function, &this, NULL, method->nargs, 0, method->code.ctor); break; diff --git a/njs/njs_vm.h b/njs/njs_vm.h index afcd2826..b87a9021 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -11,6 +11,7 @@ #include #include #include +#include #define NJS_MAX_STACK_SIZE (16 * 1024 * 1024) @@ -130,7 +131,6 @@ typedef struct njs_function_lambda_s njs_function_lambda_t; typedef struct njs_regexp_s njs_regexp_t; typedef struct njs_regexp_pattern_s njs_regexp_pattern_t; typedef struct njs_date_s njs_date_t; -typedef struct njs_extern_s njs_extern_t; typedef struct njs_frame_s njs_frame_t; typedef struct njs_native_frame_s njs_native_frame_t; typedef struct njs_property_next_s njs_property_next_t; @@ -176,7 +176,6 @@ union njs_value_s { njs_regexp_t *regexp; njs_date_t *date; njs_getter_t getter; - njs_extern_t *external; njs_value_t *value; njs_property_next_t *next; void *data; @@ -207,6 +206,16 @@ union njs_value_s { njs_string_t *data; } long_string; + struct { + njs_value_type_t type:8; /* 5 bits */ + uint8_t truth; + + uint16_t _spare; + + uint32_t index; + const njs_extern_t *proto; + } external; + njs_value_type_t type:8; /* 5 bits */ }; @@ -950,12 +959,16 @@ struct njs_vm_s { njs_value_t *scopes[NJS_SCOPES]; - void **external; + void *external; njs_native_frame_t *top_frame; njs_frame_t *active_frame; + nxt_array_t *external_objects; /* of void * */ + nxt_lvlhsh_t externals_hash; + nxt_lvlhsh_t external_prototypes_hash; + nxt_lvlhsh_t variables_hash; nxt_lvlhsh_t values_hash; nxt_lvlhsh_t modules_hash; diff --git a/njs/njscript.c b/njs/njscript.c index 3f059902..b86874c5 100644 --- a/njs/njscript.c +++ b/njs/njscript.c @@ -110,14 +110,10 @@ njs_vm_create(njs_vm_opt_t *options) nxt_mem_cache_pool_t *mcp; njs_regexp_pattern_t *pattern; - mcp = options->mcp; - - if (mcp == NULL) { - mcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL, - NULL, 2 * nxt_pagesize(), 128, 512, 16); - if (nxt_slow_path(mcp == NULL)) { - return NULL; - } + mcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL, + NULL, 2 * nxt_pagesize(), 128, 512, 16); + if (nxt_slow_path(mcp == NULL)) { + return NULL; } vm = nxt_mem_cache_zalign(mcp, sizeof(njs_value_t), sizeof(njs_vm_t)); @@ -164,18 +160,22 @@ njs_vm_create(njs_vm_opt_t *options) if (nxt_slow_path(ret != NXT_OK)) { return NULL; } - - if (options->externals_hash != NULL) { - vm->external = options->external; - } } nxt_lvlhsh_init(&vm->values_hash); - if (options->externals_hash != NULL) { - vm->externals_hash = *options->externals_hash; + vm->external = options->external; + + vm->external_objects = nxt_array_create(4, sizeof(void *), + &njs_array_mem_proto, + vm->mem_cache_pool); + if (nxt_slow_path(vm->external_objects == NULL)) { + return NULL; } + nxt_lvlhsh_init(&vm->externals_hash); + nxt_lvlhsh_init(&vm->external_prototypes_hash); + vm->trace.level = NXT_LEVEL_TRACE; vm->trace.size = 2048; vm->trace.handler = njs_parser_trace_handler; @@ -294,9 +294,10 @@ fail: njs_vm_t * -njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external) +njs_vm_clone(njs_vm_t *vm, void *external) { njs_vm_t *nvm; + uint32_t items; nxt_int_t ret; nxt_mem_cache_pool_t *nmcp; @@ -306,14 +307,10 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external) return NULL; } - nmcp = mcp; - - if (nmcp == NULL) { - nmcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL, - NULL, 2 * nxt_pagesize(), 128, 512, 16); - if (nxt_slow_path(nmcp == NULL)) { - return NULL; - } + nmcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL, + NULL, 2 * nxt_pagesize(), 128, 512, 16); + if (nxt_slow_path(nmcp == NULL)) { + return NULL; } nvm = nxt_mem_cache_zalloc(nmcp, sizeof(njs_vm_t)); @@ -326,9 +323,26 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external) nvm->variables_hash = vm->variables_hash; nvm->values_hash = vm->values_hash; nvm->modules_hash = vm->modules_hash; + nvm->externals_hash = vm->externals_hash; + nvm->external_prototypes_hash = vm->external_prototypes_hash; + + items = vm->external_objects->items; + nvm->external_objects = nxt_array_create(items + 4, sizeof(void *), + &njs_array_mem_proto, + vm->mem_cache_pool); + if (nxt_slow_path(vm->external_objects == NULL)) { + return NULL; + } + + if (items > 0) { + memcpy(nvm->external_objects->start, vm->external_objects->start, + items * sizeof(void *)); + vm->external_objects->items = items; + } nvm->current = vm->current; + nvm->external = external; nvm->global_scope = vm->global_scope; @@ -348,9 +362,7 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external) fail: - if (mcp == NULL) { - nxt_mem_cache_pool_destroy(nmcp); - } + nxt_mem_cache_pool_destroy(nmcp); return NULL; } @@ -523,6 +535,13 @@ njs_vm_retval(njs_vm_t *vm) } +nxt_noinline void +njs_vm_retval_set(njs_vm_t *vm, njs_opaque_value_t *value) +{ + vm->retval = *(njs_value_t *) value; +} + + njs_ret_t njs_vm_retval_to_ext_string(njs_vm_t *vm, nxt_str_t *retval) { if (vm->top_frame == NULL) { diff --git a/njs/njscript.h b/njs/njscript.h index f1459614..9dd24c92 100644 --- a/njs/njscript.h +++ b/njs/njscript.h @@ -16,6 +16,7 @@ typedef intptr_t njs_ret_t; typedef uintptr_t njs_index_t; typedef struct njs_vm_s njs_vm_t; typedef union njs_value_s njs_value_t; +typedef struct njs_extern_s njs_extern_t; typedef struct njs_function_s njs_function_t; typedef struct njs_vm_shared_s njs_vm_shared_t; @@ -70,10 +71,8 @@ struct njs_external_s { }; typedef struct { - void **external; - nxt_lvlhsh_t *externals_hash; + void *external; njs_vm_shared_t *shared; - nxt_mem_cache_pool_t *mcp; uint8_t trailer; /* 1 bit */ uint8_t accumulative; /* 1 bit */ @@ -94,27 +93,28 @@ typedef struct { #define NJS_DONE NXT_DONE -NXT_EXPORT nxt_int_t njs_vm_external_add(nxt_lvlhsh_t *hash, - nxt_mem_cache_pool_t *mcp, uintptr_t object, njs_external_t *external, - nxt_uint_t n); -NXT_EXPORT nxt_int_t njs_vm_external(njs_vm_t *vm, njs_opaque_value_t *object, - nxt_str_t *property, njs_opaque_value_t *value); - NXT_EXPORT njs_vm_t *njs_vm_create(njs_vm_opt_t *options); NXT_EXPORT void njs_vm_destroy(njs_vm_t *vm); NXT_EXPORT nxt_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end); -NXT_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, - void **external); +NXT_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, void *external); NXT_EXPORT nxt_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function, njs_opaque_value_t *args, nxt_uint_t nargs); NXT_EXPORT nxt_int_t njs_vm_run(njs_vm_t *vm); +NXT_EXPORT const njs_extern_t *njs_vm_external_prototype(njs_vm_t *vm, + njs_external_t *external); +NXT_EXPORT nxt_int_t njs_vm_external_create(njs_vm_t *vm, + njs_opaque_value_t *value, const njs_extern_t *proto, void *object); +NXT_EXPORT nxt_int_t njs_vm_external_bind(njs_vm_t *vm, + const nxt_str_t *var_name, njs_opaque_value_t *value); + NXT_EXPORT void njs_disassembler(njs_vm_t *vm); NXT_EXPORT nxt_array_t *njs_vm_completions(njs_vm_t *vm, nxt_str_t *expression); NXT_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, nxt_str_t *name); NXT_EXPORT njs_value_t *njs_vm_retval(njs_vm_t *vm); +NXT_EXPORT void njs_vm_retval_set(njs_vm_t *vm, njs_opaque_value_t *value); NXT_EXPORT u_char * njs_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size, uint32_t length); diff --git a/njs/test/njs_benchmark.c b/njs/test/njs_benchmark.c index 550a7de6..efbf4657 100644 --- a/njs/test/njs_benchmark.c +++ b/njs/test/njs_benchmark.c @@ -11,8 +11,6 @@ #include #include #include -#include -#include #include #include #include @@ -20,114 +18,68 @@ #include -static void * -njs_alloc(void *mem, size_t size) -{ - return nxt_malloc(size); -} - - -static void * -njs_zalloc(void *mem, size_t size) -{ - void *p; - - p = nxt_malloc(size); - - if (p != NULL) { - memset(p, 0, size); - } - - return p; -} - - -static void * -njs_align(void *mem, size_t alignment, size_t size) -{ - return nxt_memalign(alignment, size); -} - - -static void -njs_free(void *mem, void *p) -{ - nxt_free(p); -} - - -static const nxt_mem_proto_t njs_mem_cache_pool_proto = { - njs_alloc, - njs_zalloc, - njs_align, - NULL, - njs_free, - NULL, - NULL, -}; - - static nxt_int_t njs_unit_test_benchmark(nxt_str_t *script, nxt_str_t *result, const char *msg, nxt_uint_t n) { - u_char *start; - njs_vm_t *vm, *nvm; - uint64_t us; - nxt_int_t ret; - nxt_str_t s; - nxt_uint_t i; - nxt_bool_t success; - njs_vm_opt_t options; - struct rusage usage; - nxt_mem_cache_pool_t *mcp; - - mcp = nxt_mem_cache_pool_create(&njs_mem_cache_pool_proto, NULL, NULL, - 2 * nxt_pagesize(), 128, 512, 16); - if (nxt_slow_path(mcp == NULL)) { - return NXT_ERROR; - } + u_char *start; + njs_vm_t *vm, *nvm; + uint64_t us; + nxt_int_t ret, rc; + nxt_str_t s; + nxt_uint_t i; + nxt_bool_t success; + njs_vm_opt_t options; + struct rusage usage; memset(&options, 0, sizeof(njs_vm_opt_t)); - options.mcp = mcp; + vm = NULL; + nvm = NULL; + rc = NXT_ERROR; vm = njs_vm_create(&options); if (vm == NULL) { - return NXT_ERROR; + printf("njs_vm_create() failed\n"); + goto done; } start = script->start; ret = njs_vm_compile(vm, &start, start + script->length); if (ret != NXT_OK) { - return NXT_ERROR; + printf("njs_vm_compile() failed\n"); + goto done; } for (i = 0; i < n; i++) { - nvm = njs_vm_clone(vm, NULL, NULL); + nvm = njs_vm_clone(vm, NULL); if (nvm == NULL) { - return NXT_ERROR; + printf("njs_vm_clone() failed\n"); + goto done; } (void) njs_vm_run(nvm); if (njs_vm_retval_to_ext_string(nvm, &s) != NXT_OK) { - return NXT_ERROR; + printf("njs_vm_retval_to_ext_string() failed\n"); + goto done; } success = nxt_strstr_eq(result, &s); if (!success) { - return NXT_ERROR; + printf("failed: \"%.*s\" vs \"%.*s\"\n", + (int) result->length, result->start, (int) s.length, + s.start); + goto done; } njs_vm_destroy(nvm); + nvm = NULL; } - nxt_mem_cache_pool_destroy(mcp); - getrusage(RUSAGE_SELF, &usage); us = usage.ru_utime.tv_sec * 1000000 + usage.ru_utime.tv_usec @@ -141,7 +93,19 @@ njs_unit_test_benchmark(nxt_str_t *script, nxt_str_t *result, const char *msg, msg, (double) us / n, (int) ((uint64_t) n * 1000000 / us)); } - return NXT_OK; + rc = NXT_OK; + +done: + + if (nvm != NULL) { + njs_vm_destroy(nvm); + } + + if (vm != NULL) { + njs_vm_destroy(vm); + } + + return rc; } diff --git a/njs/test/njs_interactive_test.c b/njs/test/njs_interactive_test.c index 1b28671a..537d5ad0 100644 --- a/njs/test/njs_interactive_test.c +++ b/njs/test/njs_interactive_test.c @@ -11,8 +11,6 @@ #include #include #include -#include -#include #include #include #include @@ -203,15 +201,9 @@ njs_interactive_test(void) nxt_uint_t i; nxt_bool_t success; njs_vm_opt_t options; - nxt_mem_cache_pool_t *mcp; njs_interactive_test_t *test; - mcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL, NULL, - 2 * nxt_pagesize(), 128, 512, 16); - if (nxt_slow_path(mcp == NULL)) { - return NXT_ERROR; - } - + vm = NULL; ret = NXT_ERROR; for (i = 0; i < nxt_nitems(njs_test); i++) { @@ -223,13 +215,13 @@ njs_interactive_test(void) memset(&options, 0, sizeof(njs_vm_opt_t)); - options.mcp = mcp; options.accumulative = 1; options.backtrace = 1; vm = njs_vm_create(&options); if (vm == NULL) { - goto fail; + printf("njs_vm_create() failed\n"); + goto done; } start = test->script.start; @@ -251,11 +243,14 @@ njs_interactive_test(void) } if (njs_vm_retval_to_ext_string(vm, &s) != NXT_OK) { - return NXT_ERROR; + printf("njs_vm_retval_to_ext_string() failed\n"); + goto done; } success = nxt_strstr_eq(&test->ret, &s); if (success) { + njs_vm_destroy(vm); + vm = NULL; continue; } @@ -264,16 +259,18 @@ njs_interactive_test(void) (int) test->ret.length, test->ret.start, (int) s.length, s.start); - goto fail; + goto done; } ret = NXT_OK; printf("njs interactive tests passed\n"); -fail: +done: - nxt_mem_cache_pool_destroy(mcp); + if (vm != NULL) { + njs_vm_destroy(vm); + } return ret; } diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 1742f0d3..c83e3dbf 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -3750,6 +3751,12 @@ static njs_unit_test_t njs_test[] = { nxt_string("var a = $r.uri, s = a.fromUTF8(); s.length +' '+ s"), nxt_string("3 АБВ") }, + { nxt_string("var a = $r.uri, b = $r2.uri, c = $r3.uri; a+b+c"), + nxt_string("АБВαβγabc") }, + + { nxt_string("var a = $r.uri; $r.uri = $r2.uri; $r2.uri = a; $r2.uri+$r.uri"), + nxt_string("АБВαβγ") }, + { nxt_string("var a = $r.uri, s = a.fromUTF8(2); s.length +' '+ s"), nxt_string("2 БВ") }, @@ -3768,6 +3775,15 @@ static njs_unit_test_t njs_test[] = { nxt_string("$r.uri = $r.uri.substr(2); $r.uri.length +' '+ $r.uri"), nxt_string("4 БВ") }, + { nxt_string("'' + $r.props.a + $r2.props.a + $r.props.a"), + nxt_string("121") }, + + { nxt_string("var p1 = $r.props, p2 = $r2.props; '' + p2.a + p1.a"), + nxt_string("21") }, + + { nxt_string("var p1 = $r.props, p2 = $r2.props; '' + p1.a + p2.a"), + nxt_string("12") }, + { nxt_string("var a = $r.host; a +' '+ a.length +' '+ a"), nxt_string("АБВГДЕЁЖЗИЙ 22 АБВГДЕЁЖЗИЙ") }, @@ -3785,6 +3801,17 @@ static njs_unit_test_t njs_test[] = { nxt_string("$r.some_method('YES')"), nxt_string("АБВ") }, + { nxt_string("$r.create('XXX').uri"), + nxt_string("XXX") }, + + { nxt_string("var sr = $r.create('XXX'); sr.uri = 'YYY'; sr.uri"), + nxt_string("YYY") }, + + { nxt_string("var sr = $r.create('XXX'), sr2 = $r.create('YYY');" + "sr.uri = 'ZZZ'; " + "sr.uri + sr2.uri"), + nxt_string("ZZZYYY") }, + { nxt_string("var p; for (p in $r.some_method);"), nxt_string("undefined") }, @@ -3794,6 +3821,9 @@ static njs_unit_test_t njs_test[] = { nxt_string("'one' in $r"), nxt_string("false") }, + { nxt_string("'a' in $r.props"), + nxt_string("true") }, + { nxt_string("delete $r.uri"), nxt_string("false") }, @@ -8982,18 +9012,20 @@ static njs_unit_test_t njs_test[] = typedef struct { - nxt_mem_cache_pool_t *mem_cache_pool; nxt_str_t uri; -} njs_unit_test_req; + uint32_t a; + nxt_mem_cache_pool_t *mem_cache_pool; + const njs_extern_t *proto; +} njs_unit_test_req_t; static njs_ret_t njs_unit_test_r_get_uri_external(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data) { - njs_unit_test_req *r; + njs_unit_test_req_t *r; - r = (njs_unit_test_req *) obj; + r = (njs_unit_test_req_t *) obj; return njs_string_create(vm, value, r->uri.start, r->uri.length, 0); } @@ -9003,15 +9035,32 @@ static njs_ret_t njs_unit_test_r_set_uri_external(njs_vm_t *vm, void *obj, uintptr_t data, nxt_str_t *value) { - njs_unit_test_req *r; + njs_unit_test_req_t *r; + + r = (njs_unit_test_req_t *) obj; - r = (njs_unit_test_req *) obj; r->uri = *value; return NXT_OK; } +static njs_ret_t +njs_unit_test_r_get_a_external(njs_vm_t *vm, njs_value_t *value, void *obj, + uintptr_t data) +{ + char buf[16]; + size_t len; + njs_unit_test_req_t *r; + + r = (njs_unit_test_req_t *) obj; + + len = sprintf(buf, "%u", r->a); + + return njs_string_create(vm, value, (u_char *) buf, len, 0); +} + + static njs_ret_t njs_unit_test_host_external(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data) @@ -9024,27 +9073,24 @@ static njs_ret_t njs_unit_test_header_external(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data) { - u_char *s, *p; - uint32_t size; - nxt_str_t *h; - njs_unit_test_req *r; + u_char *p; + uint32_t size; + nxt_str_t *h; - r = (njs_unit_test_req *) obj; h = (nxt_str_t *) data; size = 7 + h->length; - s = nxt_mem_cache_alloc(r->mem_cache_pool, size); - if (nxt_slow_path(s == NULL)) { - return NXT_ERROR; + p = njs_string_alloc(vm, value, size, 0); + if (p == NULL) { + return NJS_ERROR; } - p = memcpy(s, h->start, h->length); - p += h->length; + p = nxt_cpymem(p, h->start, h->length); *p++ = '|'; memcpy(p, "АБВ", 6); - return njs_string_create(vm, value, s, size, 0); + return NJS_OK; } @@ -9082,10 +9128,10 @@ static njs_ret_t njs_unit_test_method_external(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { - nxt_int_t ret; - nxt_str_t s; - uintptr_t next; - njs_unit_test_req *r; + nxt_int_t ret; + nxt_str_t s; + uintptr_t next; + njs_unit_test_req_t *r; next = 0; @@ -9106,6 +9152,70 @@ njs_unit_test_method_external(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } +static njs_ret_t +njs_unit_test_create_external(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_int_t ret; + nxt_str_t uri; + njs_opaque_value_t *value; + njs_unit_test_req_t *r, *sr; + + if (nargs > 1) { + r = njs_value_data(njs_argument(args, 0)); + + if (njs_vm_value_to_ext_string(vm, &uri, njs_argument(args, 1), 0) + == NJS_ERROR) + { + return NJS_ERROR; + } + + value = nxt_mem_cache_zalloc(r->mem_cache_pool, + sizeof(njs_opaque_value_t)); + if (value == NULL) { + return NXT_ERROR; + } + + sr = nxt_mem_cache_zalloc(r->mem_cache_pool, + sizeof(njs_unit_test_req_t)); + if (sr == NULL) { + return NXT_ERROR; + } + + sr->uri = uri; + sr->mem_cache_pool = r->mem_cache_pool; + sr->proto = r->proto; + + ret = njs_vm_external_create(vm, value, sr->proto, sr); + if (ret != NXT_OK) { + return NXT_ERROR; + } + + njs_vm_retval_set(vm, value); + + return NXT_OK; + } + + return NXT_ERROR; +} + + +static njs_external_t njs_unit_test_r_props[] = { + + { nxt_string("a"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + njs_unit_test_r_get_a_external, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, +}; + + static njs_external_t njs_unit_test_r_external[] = { { nxt_string("uri"), @@ -9132,6 +9242,18 @@ static njs_external_t njs_unit_test_r_external[] = { NULL, 0 }, + { nxt_string("props"), + NJS_EXTERN_OBJECT, + njs_unit_test_r_props, + nxt_nitems(njs_unit_test_r_props), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, + { nxt_string("header"), NJS_EXTERN_OBJECT, NULL, @@ -9156,12 +9278,24 @@ static njs_external_t njs_unit_test_r_external[] = { njs_unit_test_method_external, 0 }, + { nxt_string("create"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_unit_test_create_external, + 0 }, + }; static njs_external_t nxt_test_external[] = { - { nxt_string("$r"), + { nxt_string("request.proto"), NJS_EXTERN_OBJECT, njs_unit_test_r_external, nxt_nitems(njs_unit_test_r_external), @@ -9176,77 +9310,81 @@ static njs_external_t nxt_test_external[] = { }; -static nxt_int_t -njs_unit_test_externals(nxt_lvlhsh_t *externals, nxt_mem_cache_pool_t *mcp) -{ - nxt_lvlhsh_init(externals); - - return njs_vm_external_add(externals, mcp, 0, nxt_test_external, - nxt_nitems(nxt_test_external)); -} +typedef struct { + nxt_str_t name; + njs_unit_test_req_t request; +} njs_unit_test_req_t_init_t; -static void * -njs_alloc(void *mem, size_t size) -{ - return nxt_malloc(size); -} +static const njs_unit_test_req_t_init_t nxt_test_requests[] = { + { nxt_string("$r"), {.uri = nxt_string("АБВ"), .a = 1}}, + { nxt_string("$r2"), {.uri = nxt_string("αβγ"), .a = 2}}, + { nxt_string("$r3"), {.uri = nxt_string("abc"), .a = 3}}, +}; -static void * -njs_zalloc(void *mem, size_t size) +static nxt_int_t +njs_externals_init(njs_vm_t *vm) { - void *p; + nxt_int_t ret; + nxt_uint_t i; + const njs_extern_t *proto; + njs_opaque_value_t *values; + njs_unit_test_req_t *requests; + + proto = njs_vm_external_prototype(vm, &nxt_test_external[0]); + if (proto == NULL) { + printf("njs_vm_external_prototype() failed\n"); + return NXT_ERROR; + } - p = nxt_malloc(size); + values = nxt_mem_cache_zalloc(vm->mem_cache_pool, + nxt_nitems(nxt_test_requests) + * sizeof(njs_opaque_value_t)); + if (values == NULL) { + return NXT_ERROR; + } - if (p != NULL) { - memset(p, 0, size); + requests = nxt_mem_cache_zalloc(vm->mem_cache_pool, + nxt_nitems(nxt_test_requests) + * sizeof(njs_unit_test_req_t)); + if (requests == NULL) { + return NXT_ERROR; } - return p; -} + for (i = 0; i < nxt_nitems(nxt_test_requests); i++) { + requests[i] = nxt_test_requests[i].request; + requests[i].mem_cache_pool = vm->mem_cache_pool; + requests[i].proto = proto; -static void * -njs_align(void *mem, size_t alignment, size_t size) -{ - return nxt_memalign(alignment, size); -} + ret = njs_vm_external_create(vm, &values[i], proto, &requests[i]); + if (ret != NXT_OK) { + printf("njs_vm_external_create() failed\n"); + return NXT_ERROR; + } + ret = njs_vm_external_bind(vm, &nxt_test_requests[i].name, &values[i]); + if (ret != NXT_OK) { + printf("njs_vm_external_bind() failed\n"); + return NXT_ERROR; + } + } -static void -njs_free(void *mem, void *p) -{ - nxt_free(p); + return NXT_OK; } -static const nxt_mem_proto_t njs_mem_cache_pool_proto = { - njs_alloc, - njs_zalloc, - njs_align, - NULL, - njs_free, - NULL, - NULL, -}; - - static nxt_int_t njs_unit_test(nxt_bool_t disassemble) { - void *ext_object; - u_char *start; - njs_vm_t *vm, *nvm; - nxt_int_t ret; - nxt_str_t s; - nxt_uint_t i; - nxt_bool_t success; - njs_vm_opt_t options; - nxt_lvlhsh_t externals; - njs_unit_test_req r; - nxt_mem_cache_pool_t *mcp; + u_char *start; + njs_vm_t *vm, *nvm; + nxt_int_t ret, rc; + nxt_str_t s; + nxt_uint_t i; + nxt_bool_t success; + njs_vm_opt_t options; /* * Chatham Islands NZ-CHAT time zone. @@ -9255,21 +9393,11 @@ njs_unit_test(nxt_bool_t disassemble) (void) putenv((char *) "TZ=Pacific/Chatham"); tzset(); - mcp = nxt_mem_cache_pool_create(&njs_mem_cache_pool_proto, NULL, NULL, - 2 * nxt_pagesize(), 128, 512, 16); - if (nxt_slow_path(mcp == NULL)) { - return NXT_ERROR; - } - - r.mem_cache_pool = mcp; - r.uri.length = 6; - r.uri.start = (u_char *) "АБВ"; - ext_object = &r; + vm = NULL; + nvm = NULL; - if (njs_unit_test_externals(&externals, mcp) != NXT_OK) { - return NXT_ERROR; - } + rc = NXT_ERROR; for (i = 0; i < nxt_nitems(njs_test); i++) { @@ -9279,12 +9407,15 @@ njs_unit_test(nxt_bool_t disassemble) memset(&options, 0, sizeof(njs_vm_opt_t)); - options.mcp = mcp; - options.externals_hash = &externals; - vm = njs_vm_create(&options); if (vm == NULL) { - return NXT_ERROR; + printf("njs_vm_create() failed\n"); + goto done; + } + + ret = njs_externals_init(vm); + if (ret != NXT_OK) { + goto done; } start = njs_test[i].script.start; @@ -9297,26 +9428,24 @@ njs_unit_test(nxt_bool_t disassemble) fflush(stdout); } - nvm = njs_vm_clone(vm, NULL, &ext_object); + nvm = njs_vm_clone(vm, NULL); if (nvm == NULL) { - return NXT_ERROR; + printf("njs_vm_clone() failed\n"); + goto done; } - r.uri.length = 6; - r.uri.start = (u_char *) "АБВ"; - ret = njs_vm_run(nvm); if (njs_vm_retval_to_ext_string(nvm, &s) != NXT_OK) { - return NXT_ERROR; + printf("njs_vm_retval_to_ext_string() failed\n"); + goto done; } } else { if (njs_vm_retval_to_ext_string(vm, &s) != NXT_OK) { - return NXT_ERROR; + printf("njs_vm_retval_to_ext_string() failed\n"); + goto done; } - - nvm = NULL; } success = nxt_strstr_eq(&njs_test[i].ret, &s); @@ -9324,8 +9453,12 @@ njs_unit_test(nxt_bool_t disassemble) if (success) { if (nvm != NULL) { njs_vm_destroy(nvm); + nvm = NULL; } + njs_vm_destroy(vm); + vm = NULL; + continue; } @@ -9334,12 +9467,24 @@ njs_unit_test(nxt_bool_t disassemble) (int) njs_test[i].ret.length, njs_test[i].ret.start, (int) s.length, s.start); - return NXT_ERROR; + goto done; + } + + rc = NXT_OK; + +done: + + if (nvm != NULL) { + njs_vm_destroy(nvm); } - nxt_mem_cache_pool_destroy(mcp); + if (vm != NULL) { + njs_vm_destroy(vm); + } - printf("njs unit tests passed\n"); + if (rc == NXT_OK) { + printf("njs unit tests passed\n"); + } return NXT_OK; } diff --git a/nxt/nxt_array.h b/nxt/nxt_array.h index b1c4cdf8..df64fa99 100644 --- a/nxt/nxt_array.h +++ b/nxt/nxt_array.h @@ -39,6 +39,10 @@ NXT_EXPORT void *nxt_array_zero_add(nxt_array_t *array, NXT_EXPORT void nxt_array_remove(nxt_array_t *array, void *item); +#define nxt_array_item(array, i) \ + ((void *) ((char *) (array)->start + (array)->item_size * (i))) + + #define nxt_array_last(array) \ ((void *) \ ((char *) (array)->start \ diff --git a/nxt/nxt_lvlhsh.c b/nxt/nxt_lvlhsh.c index 6794cacb..b0f57473 100644 --- a/nxt/nxt_lvlhsh.c +++ b/nxt/nxt_lvlhsh.c @@ -174,7 +174,7 @@ static void *nxt_lvlhsh_bucket_each(nxt_lvlhsh_each_t *lhe); nxt_int_t -nxt_lvlhsh_find(nxt_lvlhsh_t *lh, nxt_lvlhsh_query_t *lhq) +nxt_lvlhsh_find(const nxt_lvlhsh_t *lh, nxt_lvlhsh_query_t *lhq) { void *slot; @@ -739,7 +739,7 @@ nxt_lvlhsh_bucket_delete(nxt_lvlhsh_query_t *lhq, void **bkt) void * -nxt_lvlhsh_each(nxt_lvlhsh_t *lh, nxt_lvlhsh_each_t *lhe) +nxt_lvlhsh_each(const nxt_lvlhsh_t *lh, nxt_lvlhsh_each_t *lhe) { void **slot; diff --git a/nxt/nxt_lvlhsh.h b/nxt/nxt_lvlhsh.h index cf655203..d5d8e709 100644 --- a/nxt/nxt_lvlhsh.h +++ b/nxt/nxt_lvlhsh.h @@ -126,7 +126,8 @@ struct nxt_lvlhsh_query_s { * * The required nxt_lvlhsh_query_t fields: key_hash, key, proto. */ -NXT_EXPORT nxt_int_t nxt_lvlhsh_find(nxt_lvlhsh_t *lh, nxt_lvlhsh_query_t *lhq); +NXT_EXPORT nxt_int_t nxt_lvlhsh_find(const nxt_lvlhsh_t *lh, + nxt_lvlhsh_query_t *lhq); /* * nxt_lvlhsh_insert() adds a hash element. If the element already @@ -179,7 +180,8 @@ typedef struct { (lhe)->proto = _proto; \ } while (0) -NXT_EXPORT void *nxt_lvlhsh_each(nxt_lvlhsh_t *lh, nxt_lvlhsh_each_t *lhe); +NXT_EXPORT void *nxt_lvlhsh_each(const nxt_lvlhsh_t *lh, + nxt_lvlhsh_each_t *lhe); #endif /* _NXT_LVLHSH_H_INCLUDED_ */