details: http://hg.nginx.org/njs/rev/72539416c466 branches: changeset: 630:72539416c466 user: Dmitry Volyntsev <xei...@nginx.com> date: Tue Oct 23 20:40:08 2018 +0300 description: Added arguments object.
This closes #19 issue on Github. diffstat: njs/njs_builtin.c | 23 +++++++++++ njs/njs_disassembler.c | 2 + njs/njs_function.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ njs/njs_function.h | 12 +++++- njs/njs_generator.c | 30 +++++++++++++++ njs/njs_lexer_keyword.c | 1 + njs/njs_parser.c | 39 +++++++++++++++++++ njs/njs_parser.h | 3 + njs/njs_parser_expression.c | 15 ++++++- njs/njs_vm.c | 23 +++++++++++ njs/njs_vm.h | 9 ++++ njs/test/njs_unit_test.c | 68 ++++++++++++++++++++++++++++++++++ 12 files changed, 310 insertions(+), 4 deletions(-) diffs (542 lines): diff -r de0974c3e90f -r 72539416c466 njs/njs_builtin.c --- a/njs/njs_builtin.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_builtin.c Tue Oct 23 20:40:08 2018 +0300 @@ -127,6 +127,22 @@ const njs_function_init_t njs_native_fu }; +const njs_object_prop_t njs_arguments_object_properties[] = +{ + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("caller"), + .value = njs_prop_handler(njs_function_arguments_thrower), + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("callee"), + .value = njs_prop_handler(njs_function_arguments_thrower), + }, +}; + + static njs_ret_t njs_prototype_function(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) @@ -236,6 +252,13 @@ njs_builtin_objects_create(njs_vm_t *vm) return NXT_ERROR; } + ret = njs_object_hash_create(vm, &vm->shared->arguments_object_hash, + njs_arguments_object_properties, + nxt_nitems(njs_arguments_object_properties)); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + objects = vm->shared->objects; for (i = NJS_OBJECT_THIS; i < NJS_OBJECT_MAX; i++) { diff -r de0974c3e90f -r 72539416c466 njs/njs_disassembler.c --- a/njs/njs_disassembler.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_disassembler.c Tue Oct 23 20:40:08 2018 +0300 @@ -24,6 +24,8 @@ static njs_code_name_t code_names[] = { nxt_string("OBJECT ") }, { njs_vmcode_function, sizeof(njs_vmcode_function_t), nxt_string("FUNCTION ") }, + { njs_vmcode_arguments, sizeof(njs_vmcode_arguments_t), + nxt_string("ARGUMENTS ") }, { njs_vmcode_regexp, sizeof(njs_vmcode_regexp_t), nxt_string("REGEXP ") }, { njs_vmcode_object_copy, sizeof(njs_vmcode_object_copy_t), diff -r de0974c3e90f -r 72539416c466 njs/njs_function.c --- a/njs/njs_function.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_function.c Tue Oct 23 20:40:08 2018 +0300 @@ -95,6 +95,87 @@ njs_function_value_copy(njs_vm_t *vm, nj } +/* + * ES5.1, 10.6: CreateArgumentsObject. + */ +njs_ret_t +njs_function_arguments_object_init(njs_vm_t *vm, njs_native_frame_t *frame) +{ + nxt_int_t ret; + nxt_uint_t nargs, n; + njs_value_t value; + njs_object_t *arguments; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + static const njs_value_t njs_string_length = njs_string("length"); + + arguments = njs_object_alloc(vm); + if (nxt_slow_path(arguments == NULL)) { + return NXT_ERROR; + } + + arguments->shared_hash = vm->shared->arguments_object_hash; + + nargs = frame->nargs; + + njs_value_number_set(&value, nargs); + + prop = njs_object_prop_alloc(vm, &njs_string_length, &value, 1); + if (nxt_slow_path(prop == NULL)) { + return NXT_ERROR; + } + + prop->enumerable = 0; + + lhq.value = prop; + lhq.key_hash = NJS_LENGTH_HASH; + njs_string_get(&prop->name, &lhq.key); + + lhq.replace = 0; + lhq.pool = vm->mem_cache_pool; + lhq.proto = &njs_object_hash_proto; + + ret = nxt_lvlhsh_insert(&arguments->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + njs_internal_error(vm, "lvlhsh insert failed"); + return NXT_ERROR; + } + + for (n = 0; n < nargs; n++) { + njs_uint32_to_string(&value, n); + + prop = njs_object_prop_alloc(vm, &value, &frame->arguments[n + 1], 1); + if (nxt_slow_path(prop == NULL)) { + 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(&arguments->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + njs_internal_error(vm, "lvlhsh insert failed"); + return NXT_ERROR; + } + } + + frame->arguments_object = arguments; + + return NXT_OK; +} + + +njs_ret_t +njs_function_arguments_thrower(njs_vm_t *vm, njs_value_t *value, + njs_value_t *setval, njs_value_t *retval) +{ + njs_type_error(vm, "'caller', 'callee' properties may not be accessed"); + return NXT_ERROR; +} + + njs_ret_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs, @@ -315,6 +396,7 @@ nxt_noinline njs_ret_t njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance) { size_t size; + njs_ret_t ret; nxt_uint_t n, nesting; njs_frame_t *frame; njs_value_t *value; @@ -393,6 +475,13 @@ njs_function_call(njs_vm_t *vm, njs_inde vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values; } + if (lambda->arguments_object) { + ret = njs_function_arguments_object_init(vm, &frame->native); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + vm->active_frame = frame; return NJS_APPLIED; diff -r de0974c3e90f -r 72539416c466 njs/njs_function.h --- a/njs/njs_function.h Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_function.h Tue Oct 23 20:40:08 2018 +0300 @@ -25,10 +25,12 @@ struct njs_function_lambda_s { uint32_t closure_size; /* Function nesting level. */ - uint8_t nesting; /* 4 bits */ + uint8_t nesting; /* 4 bits */ /* Function internal block closures levels. */ - uint8_t block_closures; /* 4 bits */ + uint8_t block_closures; /* 4 bits */ + + uint8_t arguments_object; /* 1 bit */ /* Initial values of local scope. */ njs_value_t *local_scope; @@ -102,7 +104,9 @@ struct njs_native_frame_s { njs_function_t *function; njs_native_frame_t *previous; + njs_value_t *arguments; + njs_object_t *arguments_object; njs_exception_t exception; @@ -147,6 +151,10 @@ struct njs_frame_s { njs_function_t *njs_function_alloc(njs_vm_t *vm); njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value); njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size); +njs_ret_t njs_function_arguments_object_init(njs_vm_t *vm, + njs_native_frame_t *frame); +njs_ret_t njs_function_arguments_thrower(njs_vm_t *vm, njs_value_t *value, + njs_value_t *setval, njs_value_t *retval); njs_ret_t njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_value_t *njs_function_property_prototype_create(njs_vm_t *vm, diff -r de0974c3e90f -r 72539416c466 njs/njs_generator.c --- a/njs/njs_generator.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_generator.c Tue Oct 23 20:40:08 2018 +0300 @@ -14,6 +14,8 @@ static nxt_int_t njs_generate_name(njs_v njs_parser_node_t *node); static nxt_int_t njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node); +static nxt_int_t njs_generate_arguments_object(njs_vm_t *vm, + njs_parser_t *parser, njs_parser_node_t *node); static nxt_int_t njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node); static nxt_int_t njs_generate_var_statement(njs_vm_t *vm, njs_parser_t *parser, @@ -308,6 +310,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t case NJS_TOKEN_CLEAR_TIMEOUT: return njs_generate_builtin_object(vm, parser, node); + case NJS_TOKEN_ARGUMENTS: + return njs_generate_arguments_object(vm, parser, node); + case NJS_TOKEN_FUNCTION: return njs_generate_function_declaration(vm, parser, node); @@ -396,6 +401,29 @@ njs_generate_builtin_object(njs_vm_t *vm static nxt_int_t +njs_generate_arguments_object(njs_vm_t *vm, njs_parser_t *parser, + njs_parser_node_t *node) +{ + njs_vmcode_arguments_t *gen; + + parser->arguments_object = 1; + + node->index = njs_generator_object_dest_index(vm, parser, node); + if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) { + return NXT_ERROR; + } + + njs_generate_code(parser, njs_vmcode_arguments_t, gen); + gen->code.operation = njs_vmcode_arguments; + gen->code.operands = NJS_VMCODE_1OPERAND; + gen->code.retval = NJS_VMCODE_RETVAL; + gen->retval = node->index; + + return NXT_OK; +} + + +static nxt_int_t njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) { @@ -2010,6 +2038,7 @@ njs_generate_function_scope(njs_vm_t *vm parser->code_size += node->scope->argument_closures * sizeof(njs_vmcode_move_t); + parser->arguments_object = 0; ret = njs_generate_scope(vm, parser, node); if (nxt_fast_path(ret == NXT_OK)) { @@ -2023,6 +2052,7 @@ njs_generate_function_scope(njs_vm_t *vm lambda->nesting = node->scope->nesting; lambda->closure_size = size; + lambda->arguments_object = parser->arguments_object; lambda->local_size = parser->scope_size; lambda->local_scope = parser->local_scope; diff -r de0974c3e90f -r 72539416c466 njs/njs_lexer_keyword.c --- a/njs/njs_lexer_keyword.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_lexer_keyword.c Tue Oct 23 20:40:08 2018 +0300 @@ -53,6 +53,7 @@ static const njs_keyword_t njs_keywords /* Builtin objects. */ { nxt_string("this"), NJS_TOKEN_THIS, 0 }, + { nxt_string("arguments"), NJS_TOKEN_ARGUMENTS, 0 }, { nxt_string("njs"), NJS_TOKEN_NJS, 0 }, { nxt_string("Math"), NJS_TOKEN_MATH, 0 }, { nxt_string("JSON"), NJS_TOKEN_JSON, 0 }, diff -r de0974c3e90f -r 72539416c466 njs/njs_parser.c --- a/njs/njs_parser.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_parser.c Tue Oct 23 20:40:08 2018 +0300 @@ -455,6 +455,13 @@ njs_parser_function_declaration(njs_vm_t } if (token != NJS_TOKEN_NAME) { + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) { + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" " + "is forbidden in function declaration", + (int) parser->lexer->text.length, + parser->lexer->text.start); + } + return NJS_TOKEN_ILLEGAL; } @@ -821,6 +828,13 @@ njs_parser_var_statement(njs_vm_t *vm, n } if (token != NJS_TOKEN_NAME) { + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) { + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" " + "is forbidden in var declaration", + (int) parser->lexer->text.length, + parser->lexer->text.start); + } + return NJS_TOKEN_ILLEGAL; } @@ -1306,6 +1320,13 @@ njs_parser_for_var_statement(njs_vm_t *v } if (token != NJS_TOKEN_NAME) { + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) { + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" " + "is forbidden in for-in var declaration", + (int) parser->lexer->text.length, + parser->lexer->text.start); + } + return NJS_TOKEN_ILLEGAL; } @@ -1973,6 +1994,24 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa case NJS_TOKEN_JSON: return njs_parser_builtin_object(vm, parser, node); + case NJS_TOKEN_ARGUMENTS: + nxt_thread_log_debug("JS: arguments"); + + if (parser->scope->type <= NJS_SCOPE_GLOBAL) { + njs_parser_syntax_error(vm, parser, "\"%.*s\" object " + "in global scope", + (int) parser->lexer->text.length, + parser->lexer->text.start); + + return NJS_TOKEN_ILLEGAL; + } + + node->token = NJS_TOKEN_ARGUMENTS; + + parser->code_size += sizeof(njs_vmcode_arguments_t); + + break; + case NJS_TOKEN_OBJECT_CONSTRUCTOR: node->index = NJS_INDEX_OBJECT; break; diff -r de0974c3e90f -r 72539416c466 njs/njs_parser.h --- a/njs/njs_parser.h Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_parser.h Tue Oct 23 20:40:08 2018 +0300 @@ -161,6 +161,7 @@ typedef enum { NJS_TOKEN_THROW, NJS_TOKEN_THIS, + NJS_TOKEN_ARGUMENTS, #define NJS_TOKEN_FIRST_OBJECT NJS_TOKEN_GLOBAL_THIS @@ -346,6 +347,8 @@ struct njs_parser_s { u_char *code_end; njs_parser_t *parent; + + nxt_uint_t arguments_object; }; diff -r de0974c3e90f -r 72539416c466 njs/njs_parser_expression.c --- a/njs/njs_parser_expression.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_parser_expression.c Tue Oct 23 20:40:08 2018 +0300 @@ -414,8 +414,19 @@ njs_parser_assignment_expression(njs_vm_ } if (!njs_parser_is_lvalue(parser->node)) { - njs_parser_ref_error(vm, parser, - "Invalid left-hand side in assignment"); + token = parser->node->token; + + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) { + njs_parser_syntax_error(vm, parser, "Identifier \"%s\" " + "is forbidden as left-hand in assignment", + (token == NJS_TOKEN_EVAL) ? "eval" + : "arguments"); + + } else { + njs_parser_ref_error(vm, parser, + "Invalid left-hand side in assignment"); + } + return NJS_TOKEN_ILLEGAL; } diff -r de0974c3e90f -r 72539416c466 njs/njs_vm.c --- a/njs/njs_vm.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_vm.c Tue Oct 23 20:40:08 2018 +0300 @@ -413,6 +413,29 @@ njs_vmcode_function(njs_vm_t *vm, njs_va njs_ret_t +njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) +{ + njs_ret_t ret; + njs_frame_t *frame; + + frame = (njs_frame_t *) vm->active_frame; + + if (frame->native.arguments_object == NULL) { + ret = njs_function_arguments_object_init(vm, &frame->native); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + + vm->retval.data.u.object = frame->native.arguments_object; + vm->retval.type = NJS_OBJECT; + vm->retval.data.truth = 1; + + return sizeof(njs_vmcode_arguments_t); +} + + +njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) { njs_regexp_t *regexp; diff -r de0974c3e90f -r 72539416c466 njs/njs_vm.h --- a/njs/njs_vm.h Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/njs_vm.h Tue Oct 23 20:40:08 2018 +0300 @@ -635,6 +635,12 @@ typedef struct { typedef struct { njs_vmcode_t code; njs_index_t retval; +} njs_vmcode_arguments_t; + + +typedef struct { + njs_vmcode_t code; + njs_index_t retval; uintptr_t length; } njs_vmcode_array_t; @@ -1084,6 +1090,7 @@ struct njs_vm_shared_s { nxt_lvlhsh_t keywords_hash; nxt_lvlhsh_t values_hash; nxt_lvlhsh_t function_prototype_hash; + nxt_lvlhsh_t arguments_object_hash; njs_object_t objects[NJS_OBJECT_MAX]; njs_function_t functions[NJS_FUNCTION_MAX]; @@ -1110,6 +1117,8 @@ njs_ret_t njs_vmcode_array(njs_vm_t *vm, njs_value_t *inlvd2); njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *invld2); +njs_ret_t njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *inlvd1, + njs_value_t *invld2); njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *invld2); njs_ret_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value, diff -r de0974c3e90f -r 72539416c466 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Tue Oct 23 20:40:02 2018 +0300 +++ b/njs/test/njs_unit_test.c Tue Oct 23 20:40:08 2018 +0300 @@ -5713,6 +5713,74 @@ static njs_unit_test_t njs_test[] = "var b = a(); b(2)"), nxt_string("3") }, + /* arguments object. */ + + { nxt_string("var arguments"), + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in var declaration in 1") }, + + { nxt_string("for (var arguments in []) {}"), + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in for-in var declaration in 1") }, + + { nxt_string("function arguments(){}"), + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in function declaration in 1") }, + + { nxt_string("(function () {arguments = [];})"), + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden as left-hand in assignment in 1") }, + + { nxt_string("(function(){return arguments[0];})(1,2,3)"), + nxt_string("1") }, + + { nxt_string("(function(){return arguments[2];})(1,2,3)"), + nxt_string("3") }, + + { nxt_string("(function(){return arguments[3];})(1,2,3)"), + nxt_string("undefined") }, + + { nxt_string("(function(a,b,c){return a;})(1,2,3)"), + nxt_string("1") }, + + { nxt_string("(function(a,b,c){arguments[0] = 4; return a;})(1,2,3)"), + nxt_string("1") }, + + { nxt_string("(function(a,b,c){a = 4; return arguments[0];})(1,2,3)"), + nxt_string("1") }, + + { nxt_string("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; " + "function f() {check(arguments.length > 1); return 1}; f()"), + nxt_string("TypeError: Too few arguments") }, + + { nxt_string("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; " + "function f() {check(arguments.length > 1); return 1}; f(1,2)"), + nxt_string("1") }, + + { nxt_string("(function(a,b){delete arguments[0]; return arguments[0]})(1,1)"), + nxt_string("undefined") }, + + { nxt_string("(function(){return arguments.length;})()"), + nxt_string("0") }, + + { nxt_string("(function(){return arguments.length;})(1,2,3)"), + nxt_string("3") }, + + { nxt_string("(function(){arguments.length = 1; return arguments.length;})(1,2,3)"), + nxt_string("1") }, + + { nxt_string("(function(){return arguments.callee;})()"), + nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") }, + + { nxt_string("(function(){return arguments.caller;})()"), + nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") }, + + { nxt_string("function sum() { var args = Array.prototype.slice.call(arguments); " + "return args.reduce(function(prev, curr) {return prev + curr})};" + "[sum(1), sum(1,2), sum(1,2,3), sum(1,2,3,4)]"), + nxt_string("1,3,6,10") }, + + { nxt_string("function concat(sep) { var args = Array.prototype.slice.call(arguments, 1); " + "return args.join(sep)};" + "[concat('.',1,2,3), concat('+',1,2,3,4)]"), + nxt_string("1.2.3,1+2+3+4") }, + /* Scopes. */ { nxt_string("function f(x) { a = x } var a; f(5); a"), _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel