From db34b1921fce3a101570cf0e33cf5a3a6725fee6 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Tue, 18 Jun 2019 17:57:38 +0300 Subject: [PATCH] Added process global object. process object properties: argv - an array containing the command line arguments env - an object containing the user environment pid - process PID ppid - process parent PID This closes #84 issue on Github. --- nginx/ngx_http_js_module.c | 2 + nginx/ngx_stream_js_module.c | 2 + njs/njs.h | 3 + njs/njs_builtin.c | 274 ++++++++++++++++++++++++++++++++--- njs/njs_generator.c | 1 + njs/njs_lexer.h | 1 + njs/njs_lexer_keyword.c | 1 + njs/njs_object_hash.h | 15 ++ njs/njs_parser_terminal.c | 1 + njs/njs_shell.c | 2 + njs/njs_vm.h | 2 + njs/test/njs_expect_test.exp | 14 ++ 12 files changed, 298 insertions(+), 20 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index cd8a8f95..787098a0 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -2283,6 +2283,8 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) options.backtrace = 1; options.ops = &ngx_http_js_ops; + options.argv = ngx_argv; + options.argc = ngx_argc; file = value[1]; options.file.start = file.data; diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index 4375ae1a..5471f6bd 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -1473,6 +1473,8 @@ ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) options.backtrace = 1; options.ops = &ngx_stream_js_ops; + options.argv = ngx_argv; + options.argc = ngx_argc; file = value[1]; options.file.start = file.data; diff --git a/njs/njs.h b/njs/njs.h index a6adc2b0..d3863cf6 100644 --- a/njs/njs.h +++ b/njs/njs.h @@ -143,6 +143,9 @@ typedef struct { njs_vm_ops_t *ops; nxt_str_t file; + char **argv; + nxt_uint_t argc; + uint8_t trailer; /* 1 bit */ uint8_t init; /* 1 bit */ uint8_t accumulative; /* 1 bit */ diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index 27587b01..012c0caf 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -28,17 +28,21 @@ static njs_ret_t njs_prototype_function(njs_vm_t *vm, njs_value_t *args, static nxt_array_t *njs_vm_expression_completions(njs_vm_t *vm, nxt_str_t *expression); static nxt_array_t *njs_object_completions(njs_vm_t *vm, njs_object_t *object); +static nxt_int_t njs_env_hash_init(njs_vm_t *vm, nxt_lvlhsh_t *hash, + char **environment); -const njs_object_init_t njs_njs_object_init; const njs_object_init_t njs_global_this_init; +const njs_object_init_t njs_njs_object_init; +const njs_object_init_t njs_process_object_init; const njs_object_init_t *njs_object_init[] = { - &njs_global_this_init, /* global this */ - &njs_njs_object_init, /* global njs object */ - &njs_math_object_init, /* Math */ - &njs_json_object_init, /* JSON */ + &njs_global_this_init, + &njs_njs_object_init, + &njs_process_object_init, + &njs_math_object_init, + &njs_json_object_init, NULL }; @@ -214,6 +218,9 @@ const njs_object_prototype_t njs_prototype_values[] = { }; +extern char **environ; + + nxt_inline nxt_int_t njs_object_hash_init(njs_vm_t *vm, nxt_lvlhsh_t *hash, const njs_object_init_t *init) @@ -229,8 +236,8 @@ njs_builtin_objects_create(njs_vm_t *vm) njs_module_t *module; njs_object_t *object, *string_object; njs_function_t *func; - nxt_lvlhsh_query_t lhq; njs_vm_shared_t *shared; + nxt_lvlhsh_query_t lhq; njs_object_prototype_t *prototype; const njs_object_init_t *obj, **p; const njs_function_init_t *f; @@ -285,6 +292,11 @@ njs_builtin_objects_create(njs_vm_t *vm) object++; } + ret = njs_env_hash_init(vm, &shared->env_hash, environ); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + lhq.replace = 0; lhq.pool = vm->mem_pool; @@ -1075,6 +1087,35 @@ njs_dump_value(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } +static const njs_object_prop_t njs_global_this_object_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("NaN"), + .value = njs_value(NJS_NUMBER, 0, NAN), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("Infinity"), + .value = njs_value(NJS_NUMBER, 0, INFINITY), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("undefined"), + .value = njs_value(NJS_UNDEFINED, 0, NAN), + }, +}; + + +const njs_object_init_t njs_global_this_init = { + nxt_string("this"), + njs_global_this_object_properties, + nxt_nitems(njs_global_this_object_properties) +}; + + static const njs_object_prop_t njs_njs_object_properties[] = { { @@ -1102,30 +1143,223 @@ const njs_object_init_t njs_njs_object_init = { }; -static const njs_object_prop_t njs_global_this_object_properties[] = +static njs_ret_t +njs_process_object_argv(njs_vm_t *vm, njs_value_t *process, + njs_value_t *unused, njs_value_t *retval) +{ + char **arg; + nxt_int_t ret; + nxt_uint_t i; + njs_array_t *argv; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + static const njs_value_t argv_string = njs_string("argv"); + + if (nxt_slow_path(vm->options.argv == NULL)) { + njs_internal_error(vm, "argv was not provided by host environment"); + return NXT_ERROR; + } + + argv = njs_array_alloc(vm, vm->options.argc, 0); + if (nxt_slow_path(argv == NULL)) { + return NXT_ERROR; + } + + i = 0; + + for (arg = vm->options.argv; i < vm->options.argc; arg++) { + njs_string_set(vm, &argv->start[i++], (u_char *) *arg, + nxt_strlen(*arg)); + } + + prop = njs_object_prop_alloc(vm, &argv_string, &njs_value_undefined, 1); + if (nxt_slow_path(prop == NULL)) { + return NJS_ERROR; + } + + prop->value.data.u.array = argv; + prop->value.type = NJS_ARRAY; + prop->value.data.truth = 1; + + lhq.value = prop; + lhq.key_hash = NJS_ARGV_HASH; + lhq.key = nxt_string_value("argv"); + lhq.replace = 0; + lhq.pool = vm->mem_pool; + lhq.proto = &njs_object_hash_proto; + + ret = nxt_lvlhsh_insert(&process->data.u.object->hash, &lhq); + + if (nxt_fast_path(ret == NXT_OK)) { + *retval = prop->value; + return NXT_OK; + } + + njs_internal_error(vm, "lvlhsh insert failed"); + + return NXT_ERROR; +} + + +static nxt_int_t +njs_env_hash_init(njs_vm_t *vm, nxt_lvlhsh_t *hash, char **environment) +{ + char **ep; + u_char *val, *entry; + nxt_int_t ret; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + lhq.replace = 0; + lhq.pool = vm->mem_pool; + lhq.proto = &njs_object_hash_proto; + + ep = environment; + + while (*ep != NULL) { + prop = njs_object_prop_alloc(vm, &njs_value_undefined, + &njs_value_undefined, 1); + if (nxt_slow_path(prop == NULL)) { + return NXT_ERROR; + } + + entry = (u_char *) *ep++; + + val = nxt_strchr(entry, '='); + if (nxt_slow_path(val == NULL)) { + continue; + } + + ret = njs_string_set(vm, &prop->name, entry, val - entry); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + val++; + + ret = njs_string_set(vm, &prop->value, val, nxt_strlen(val)); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + lhq.value = prop; + njs_string_get(&prop->name, &lhq.key); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + + ret = nxt_lvlhsh_insert(hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + njs_internal_error(vm, "lvlhsh insert failed"); + return NXT_ERROR; + } + } + + return NXT_OK; +} + + +static njs_ret_t +njs_process_object_env(njs_vm_t *vm, njs_value_t *process, + njs_value_t *unused, njs_value_t *retval) +{ + nxt_int_t ret; + njs_object_t *env; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + static const njs_value_t env_string = njs_string("env"); + + env = njs_object_alloc(vm); + if (nxt_slow_path(env == NULL)) { + return NXT_ERROR; + } + + env->shared_hash = vm->shared->env_hash; + + prop = njs_object_prop_alloc(vm, &env_string, &njs_value_undefined, 1); + if (nxt_slow_path(prop == NULL)) { + return NXT_ERROR; + } + + prop->value.data.u.object = env; + prop->value.type = NJS_OBJECT; + prop->value.data.truth = 1; + + lhq.replace = 0; + lhq.pool = vm->mem_pool; + lhq.proto = &njs_object_hash_proto; + lhq.value = prop; + lhq.key = nxt_string_value("env"); + lhq.key_hash = NJS_ENV_HASH; + + ret = nxt_lvlhsh_insert(&process->data.u.object->hash, &lhq); + + if (nxt_fast_path(ret == NXT_OK)) { + *retval = prop->value; + return NXT_OK; + } + + njs_internal_error(vm, "lvlhsh insert failed"); + + return NXT_ERROR; +} + + +static njs_ret_t +njs_process_object_pid(njs_vm_t *vm, njs_value_t *unused, + njs_value_t *unused2, njs_value_t *retval) +{ + retval->data.u.number = getpid(); + retval->type = NJS_NUMBER; + retval->data.truth = njs_is_number_true(retval->data.u.number); + + return NJS_OK; +} + + +static njs_ret_t +njs_process_object_ppid(njs_vm_t *vm, njs_value_t *unused, + njs_value_t *unused2, njs_value_t *retval) +{ + retval->data.u.number = getppid(); + retval->type = NJS_NUMBER; + retval->data.truth = njs_is_number_true(retval->data.u.number); + + return NJS_OK; +} + + +static const njs_object_prop_t njs_process_object_properties[] = { { - .type = NJS_PROPERTY, - .name = njs_string("NaN"), - .value = njs_value(NJS_NUMBER, 0, NAN), + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("argv"), + .value = njs_prop_handler(njs_process_object_argv), }, { - .type = NJS_PROPERTY, - .name = njs_string("Infinity"), - .value = njs_value(NJS_NUMBER, 0, INFINITY), + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("env"), + .value = njs_prop_handler(njs_process_object_env), }, { - .type = NJS_PROPERTY, - .name = njs_string("undefined"), - .value = njs_value(NJS_UNDEFINED, 0, NAN), + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("pid"), + .value = njs_prop_handler(njs_process_object_pid), + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("ppid"), + .value = njs_prop_handler(njs_process_object_ppid), }, + }; -const njs_object_init_t njs_global_this_init = { - nxt_string("this"), - njs_global_this_object_properties, - nxt_nitems(njs_global_this_object_properties) +const njs_object_init_t njs_process_object_init = { + nxt_string("process"), + njs_process_object_properties, + nxt_nitems(njs_process_object_properties), }; diff --git a/njs/njs_generator.c b/njs/njs_generator.c index 58615f4e..c1ea3e49 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -451,6 +451,7 @@ njs_generator(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) /* Fall through. */ case NJS_TOKEN_NJS: + case NJS_TOKEN_PROCESS: case NJS_TOKEN_MATH: case NJS_TOKEN_JSON: case NJS_TOKEN_EVAL: diff --git a/njs/njs_lexer.h b/njs/njs_lexer.h index bc7ec764..159952af 100644 --- a/njs/njs_lexer.h +++ b/njs/njs_lexer.h @@ -171,6 +171,7 @@ typedef enum { NJS_TOKEN_GLOBAL_THIS, NJS_TOKEN_NJS, + NJS_TOKEN_PROCESS, NJS_TOKEN_MATH, NJS_TOKEN_JSON, diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c index f1d6d071..0a827574 100644 --- a/njs/njs_lexer_keyword.c +++ b/njs/njs_lexer_keyword.c @@ -55,6 +55,7 @@ static const njs_keyword_t njs_keywords[] = { { nxt_string("this"), NJS_TOKEN_THIS, 0 }, { nxt_string("arguments"), NJS_TOKEN_ARGUMENTS, 0 }, { nxt_string("njs"), NJS_TOKEN_NJS, 0 }, + { nxt_string("process"), NJS_TOKEN_PROCESS, 0 }, { nxt_string("Math"), NJS_TOKEN_MATH, 0 }, { nxt_string("JSON"), NJS_TOKEN_JSON, 0 }, diff --git a/njs/njs_object_hash.h b/njs/njs_object_hash.h index f5672401..4c028bce 100644 --- a/njs/njs_object_hash.h +++ b/njs/njs_object_hash.h @@ -8,6 +8,14 @@ #define _NJS_OBJECT_HASH_H_INCLUDED_ +#define NJS_ARGV_HASH \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add(NXT_DJB_HASH_INIT, \ + 'a'), 'r'), 'g'), 'v') + + #define NJS_CONFIGURABLE_HASH \ nxt_djb_hash_add( \ nxt_djb_hash_add( \ @@ -74,6 +82,13 @@ 'e'), 'n'), 'c'), 'o'), 'd'), 'i'), 'n'), 'g') +#define NJS_ENV_HASH \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add(NXT_DJB_HASH_INIT, \ + 'e'), 'n'), 'v') + + #define NJS_FLAG_HASH \ nxt_djb_hash_add( \ nxt_djb_hash_add( \ diff --git a/njs/njs_parser_terminal.c b/njs/njs_parser_terminal.c index 3c85ccda..c5ef1912 100644 --- a/njs/njs_parser_terminal.c +++ b/njs/njs_parser_terminal.c @@ -270,6 +270,7 @@ njs_parser_reference(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token, /* Fall through. */ case NJS_TOKEN_NJS: + case NJS_TOKEN_PROCESS: case NJS_TOKEN_MATH: case NJS_TOKEN_JSON: ret = njs_parser_builtin(vm, parser, node, NJS_OBJECT, name, hash); diff --git a/njs/njs_shell.c b/njs/njs_shell.c index 706c4111..9fcd1534 100644 --- a/njs/njs_shell.c +++ b/njs/njs_shell.c @@ -265,6 +265,8 @@ main(int argc, char **argv) vm_options.ops = &njs_console_ops; vm_options.external = &njs_console; + vm_options.argv = argv; + vm_options.argc = argc; if (opts.interactive) { ret = njs_interactive_shell(&opts, &vm_options); diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 52f914e9..b4c168c3 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -1154,6 +1154,8 @@ struct njs_vm_shared_s { nxt_lvlhsh_t arrow_instance_hash; nxt_lvlhsh_t arguments_object_instance_hash; + nxt_lvlhsh_t env_hash; + njs_object_t string_object; njs_object_t objects[NJS_OBJECT_MAX]; njs_function_t functions[NJS_FUNCTION_MAX]; diff --git a/njs/test/njs_expect_test.exp b/njs/test/njs_expect_test.exp index 351981a2..9ff3fb5a 100644 --- a/njs/test/njs_expect_test.exp +++ b/njs/test/njs_expect_test.exp @@ -658,6 +658,20 @@ njs_run {"-c" "console.log(\"a b c\")"} "a b c" njs_run {"-c" "console.log("} "SyntaxError: Unexpected end of input in string:1" +# process + +njs_run {"-c" "console.log(typeof process.argv)"} "object" +njs_run {"-c" "console.log(process.argv[3])" "AAA"} "AAA" + +njs_run {"-c" "console.log(typeof process.env)"} "object" +njs_run {"-c" "console.log(process.env.HOME != undefined)"} "true" +njs_run {"-c" "console.log(process.env.___UNDECLARED != undefined)"} "false" + +njs_run {"-c" "console.log(process.pid)"} "\\d+" + +njs_run {"-c" "console.log(process.ppid)"} "\\d+" + + # disassemble njs_test { -- 2.47.3