[njs] Added the rest parameters support.
Dmitry Volyntsev
xeioex at nginx.com
Sun Dec 23 17:40:37 UTC 2018
details: https://hg.nginx.org/njs/rev/daebcd564289
branches:
changeset: 691:daebcd564289
user: Alexander Pyshchev <yftuls at gmail.com>
date: Mon Dec 17 21:13:02 2018 +0200
description:
Added the rest parameters support.
This closes #21 issue on Github.
diffstat:
njs/njs_function.c | 60 ++++++++++++++++++++++++++++++++++++++++++++---
njs/njs_function.h | 3 ++
njs/njs_lexer.c | 9 +++++++
njs/njs_parser.c | 13 ++++++++++
njs/njs_parser.h | 1 +
njs/test/njs_unit_test.c | 40 ++++++++++++++++++++++++++++++++
6 files changed, 122 insertions(+), 4 deletions(-)
diffs (226 lines):
diff -r 0709c3d38212 -r daebcd564289 njs/njs_function.c
--- a/njs/njs_function.c Fri Dec 07 18:58:27 2018 +0300
+++ b/njs/njs_function.c Mon Dec 17 21:13:02 2018 +0200
@@ -168,6 +168,42 @@ njs_function_arguments_object_init(njs_v
njs_ret_t
+njs_function_rest_parameters_init(njs_vm_t *vm, njs_native_frame_t *frame)
+{
+ uint32_t length;
+ nxt_uint_t nargs, n, i;
+ njs_array_t *array;
+ njs_value_t *rest_arguments;
+
+ nargs = frame->nargs;
+ n = frame->function->u.lambda->nargs;
+ length = (nargs >= n) ? (nargs - n + 1) : 0;
+
+ array = njs_array_alloc(vm, length, 0);
+ if (nxt_slow_path(array == NULL)) {
+ return NXT_ERROR;
+ }
+
+ if (n <= nargs) {
+ i = 0;
+ do {
+ /* GC: retain. */
+ array->start[i++] = frame->arguments[n++];
+ } while (n <= nargs);
+ }
+
+ rest_arguments = &frame->arguments[frame->function->u.lambda->nargs];
+
+ /* GC: retain. */
+ rest_arguments->type = NJS_ARRAY;
+ rest_arguments->data.u.array = array;
+ rest_arguments->data.truth = 1;
+
+ 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)
{
@@ -482,6 +518,13 @@ njs_function_call(njs_vm_t *vm, njs_inde
}
}
+ if (lambda->rest_parameters) {
+ ret = njs_function_rest_parameters_init(vm, &frame->native);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
vm->active_frame = frame;
return NJS_APPLIED;
@@ -600,8 +643,9 @@ static njs_ret_t
njs_function_prototype_length(njs_vm_t *vm, njs_value_t *value,
njs_value_t *setval, njs_value_t *retval)
{
- nxt_uint_t n;
- njs_function_t *function;
+ nxt_uint_t n;
+ njs_function_t *function;
+ njs_function_lambda_t *lambda;
function = value->data.u.function;
@@ -613,10 +657,18 @@ njs_function_prototype_length(njs_vm_t *
}
} else {
- n = function->u.lambda->nargs + 1;
+ lambda = function->u.lambda;
+ n = lambda->nargs + 1 - lambda->rest_parameters;
}
- njs_value_number_set(retval, n - function->args_offset);
+ if (n >= function->args_offset) {
+ n -= function->args_offset;
+
+ } else {
+ n = 0;
+ }
+
+ njs_value_number_set(retval, n);
return NXT_OK;
}
diff -r 0709c3d38212 -r daebcd564289 njs/njs_function.h
--- a/njs/njs_function.h Fri Dec 07 18:58:27 2018 +0300
+++ b/njs/njs_function.h Mon Dec 17 21:13:02 2018 +0200
@@ -31,6 +31,7 @@ struct njs_function_lambda_s {
uint8_t block_closures; /* 4 bits */
uint8_t arguments_object; /* 1 bit */
+ uint8_t rest_parameters; /* 1 bit */
/* Initial values of local scope. */
njs_value_t *local_scope;
@@ -151,6 +152,8 @@ njs_function_t *njs_function_value_copy(
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_rest_parameters_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,
diff -r 0709c3d38212 -r daebcd564289 njs/njs_lexer.c
--- a/njs/njs_lexer.c Fri Dec 07 18:58:27 2018 +0300
+++ b/njs/njs_lexer.c Mon Dec 17 21:13:02 2018 +0200
@@ -326,6 +326,15 @@ njs_lexer_next_token(njs_lexer_t *lexer)
case NJS_TOKEN_DOT:
p = lexer->start;
+ if (p + 1 < lexer->end
+ && njs_tokens[p[0]] == NJS_TOKEN_DOT
+ && njs_tokens[p[1]] == NJS_TOKEN_DOT)
+ {
+ lexer->text.length = (p - lexer->text.start) + 2;
+ lexer->start += 2;
+ return NJS_TOKEN_ELLIPSIS;
+ }
+
if (p == lexer->end || njs_tokens[*p] != NJS_TOKEN_DIGIT) {
lexer->text.length = p - lexer->text.start;
return NJS_TOKEN_DOT;
diff -r 0709c3d38212 -r daebcd564289 njs/njs_parser.c
--- a/njs/njs_parser.c Fri Dec 07 18:58:27 2018 +0300
+++ b/njs/njs_parser.c Mon Dec 17 21:13:02 2018 +0200
@@ -629,6 +629,19 @@ njs_parser_function_lambda(njs_vm_t *vm,
while (token != NJS_TOKEN_CLOSE_PARENTHESIS) {
+ if (nxt_slow_path(lambda->rest_parameters)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ if (nxt_slow_path(token == NJS_TOKEN_ELLIPSIS)) {
+ lambda->rest_parameters = 1;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+ }
+
if (nxt_slow_path(token != NJS_TOKEN_NAME)) {
return NJS_TOKEN_ILLEGAL;
}
diff -r 0709c3d38212 -r daebcd564289 njs/njs_parser.h
--- a/njs/njs_parser.h Fri Dec 07 18:58:27 2018 +0300
+++ b/njs/njs_parser.h Mon Dec 17 21:13:02 2018 +0200
@@ -29,6 +29,7 @@ typedef enum {
NJS_TOKEN_COMMA,
NJS_TOKEN_DOT,
+ NJS_TOKEN_ELLIPSIS,
NJS_TOKEN_SEMICOLON,
NJS_TOKEN_COLON,
diff -r 0709c3d38212 -r daebcd564289 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c Fri Dec 07 18:58:27 2018 +0300
+++ b/njs/test/njs_unit_test.c Mon Dec 17 21:13:02 2018 +0200
@@ -5310,9 +5310,19 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("function f() { }; f.length = 1"),
nxt_string("TypeError: Cannot assign to read-only property 'length' of function") },
+ { nxt_string("function f(...rest) { }; f.length"),
+ nxt_string("0") },
+
+ { nxt_string("function f(...rest) { }; var binded = f.bind(this, [1,2]);"
+ "binded.length"),
+ nxt_string("0") },
+
{ nxt_string("function f(a,b) { }; f.length"),
nxt_string("2") },
+ { nxt_string("function f(a,...rest) { }; f.length"),
+ nxt_string("1") },
+
{ nxt_string("function f(a,b) { }; var ff = f.bind(f, 1); ff.length"),
nxt_string("1") },
@@ -6063,6 +6073,36 @@ static njs_unit_test_t njs_test[] =
"[concat('.',1,2,3), concat('+',1,2,3,4)]"),
nxt_string("1.2.3,1+2+3+4") },
+ /* rest parameters. */
+
+ { nxt_string("function myFoo(a,b,...other) { return other };"
+ "myFoo(1,2,3,4,5);" ),
+ nxt_string("3,4,5") },
+
+ { nxt_string("function myFoo(a,b,...other, c) { return other };"),
+ nxt_string("SyntaxError: Unexpected token \"c\" in 1") },
+
+ { nxt_string("function sum(a, b, c, ...other) { return a+b+c+other[2] };"
+ "sum(\"one \",2,\" three \",\"four \",\"five \",\"the long enough sixth argument \");"),
+ nxt_string("one 2 three the long enough sixth argument ") },
+
+ { nxt_string("function myFoo1(a,...other) { return other };"
+ "function myFoo2(a,b,...other) { return other };"
+ "myFoo1(1,2,3,4,5,myFoo2(1,2,3,4));"),
+ nxt_string("2,3,4,5,3,4") },
+
+ { nxt_string("function myFoo(...other) { return (other instanceof Array) };"
+ "myFoo(1);" ),
+ nxt_string("true") },
+
+ { nxt_string("function myFoo(a,...other) { return other.length };"
+ "myFoo(1,2,3,4,5);" ),
+ nxt_string("4") },
+
+ { nxt_string("function myFoo(a,b,...other) { return other };"
+ "myFoo(1,2);" ),
+ nxt_string("") },
+
/* Scopes. */
{ nxt_string("function f(x) { a = x } var a; f(5); a"),
More information about the nginx-devel
mailing list