[njs] Added arguments object.
Dmitry Volyntsev
xeioex at nginx.com
Tue Oct 23 17:40:18 UTC 2018
details: http://hg.nginx.org/njs/rev/72539416c466
branches:
changeset: 630:72539416c466
user: Dmitry Volyntsev <xeioex at 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"),
More information about the nginx-devel
mailing list