[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