[njs] Added arrow function support.

Dmitry Volyntsev xeioex at nginx.com
Fri Apr 12 16:58:15 UTC 2019


details:   https://hg.nginx.org/njs/rev/d7a0eb59a7e7
branches:  
changeset: 888:d7a0eb59a7e7
user:      hongzhidao <hongzhidao at gmail.com>
date:      Sun Apr 07 14:26:13 2019 +0800
description:
Added arrow function support.

This closes #106 issue on Github.

In collaboration with Artem S. Povalyukhin.

diffstat:

 njs/njs_builtin.c            |    6 +
 njs/njs_extern.c             |    3 +-
 njs/njs_function.c           |   27 ++++++-
 njs/njs_function.h           |    2 +
 njs/njs_lexer.c              |    1 +
 njs/njs_lexer.h              |    1 +
 njs/njs_object.c             |    7 +-
 njs/njs_parser.c             |  166 +++++++++++++++++++++++++++++++++++++++++++
 njs/njs_parser.h             |   11 ++-
 njs/njs_parser_terminal.c    |   36 ++++-----
 njs/njs_vm.h                 |    1 +
 njs/test/njs_expect_test.exp |    2 +
 njs/test/njs_unit_test.c     |  144 +++++++++++++++++++++++++++++++++++++
 13 files changed, 381 insertions(+), 26 deletions(-)

diffs (633 lines):

diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_builtin.c
--- a/njs/njs_builtin.c	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_builtin.c	Sun Apr 07 14:26:13 2019 +0800
@@ -257,6 +257,12 @@ njs_builtin_objects_create(njs_vm_t *vm)
         return NXT_ERROR;
     }
 
+    ret = njs_object_hash_init(vm, &shared->arrow_instance_hash,
+                               &njs_arrow_instance_init);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NXT_ERROR;
+    }
+
     ret = njs_object_hash_init(vm, &shared->arguments_object_instance_hash,
                                &njs_arguments_object_instance_init);
     if (nxt_slow_path(ret != NXT_OK)) {
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_extern.c
--- a/njs/njs_extern.c	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_extern.c	Sun Apr 07 14:26:13 2019 +0800
@@ -105,11 +105,12 @@ njs_vm_external_add(njs_vm_t *vm, nxt_lv
              * nxt_mp_zalloc() does also:
              *   nxt_lvlhsh_init(&function->object.hash);
              *   function->object.__proto__ = NULL;
+             *   function->ctor = 0;
              */
 
             function->object.__proto__ =
                               &vm->prototypes[NJS_CONSTRUCTOR_FUNCTION].object;
-            function->object.shared_hash = vm->shared->function_instance_hash;
+            function->object.shared_hash = vm->shared->arrow_instance_hash;
             function->object.type = NJS_FUNCTION;
             function->object.shared = 1;
             function->object.extensible = 1;
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_function.c
--- a/njs/njs_function.c	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_function.c	Sun Apr 07 14:26:13 2019 +0800
@@ -38,11 +38,17 @@ njs_function_alloc(njs_vm_t *vm, njs_fun
      *   function->object.__proto__ = NULL;
      */
 
-    function->ctor = 1;
+    function->ctor = !lambda->arrow;
     function->args_offset = 1;
     function->u.lambda = lambda;
 
-    function->object.shared_hash = vm->shared->function_instance_hash;
+    if (lambda->arrow || !function->ctor) {
+        function->object.shared_hash = vm->shared->arrow_instance_hash;
+
+    } else {
+        function->object.shared_hash = vm->shared->function_instance_hash;
+    }
+
     function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
     function->object.type = NJS_FUNCTION;
     function->object.shared = shared;
@@ -1195,6 +1201,23 @@ const njs_object_init_t  njs_function_in
 };
 
 
+const njs_object_prop_t  njs_arrow_instance_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("length"),
+        .value = njs_prop_handler(njs_function_instance_length),
+    },
+};
+
+
+const njs_object_init_t  njs_arrow_instance_init = {
+    nxt_string("Arrow instance"),
+    njs_arrow_instance_properties,
+    nxt_nitems(njs_arrow_instance_properties),
+};
+
+
 njs_ret_t
 njs_eval_function(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_function.h
--- a/njs/njs_function.h	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_function.h	Sun Apr 07 14:26:13 2019 +0800
@@ -30,6 +30,7 @@ struct njs_function_lambda_s {
     /* Function internal block closures levels. */
     uint8_t                        block_closures;    /* 4 bits */
 
+    uint8_t                        arrow;             /* 1 bit */
     uint8_t                        rest_parameters;   /* 1 bit */
 
     /* Initial values of local scope. */
@@ -218,6 +219,7 @@ njs_function_previous_frame(njs_native_f
 extern const njs_object_init_t  njs_function_constructor_init;
 extern const njs_object_init_t  njs_function_prototype_init;
 extern const njs_object_init_t  njs_function_instance_init;
+extern const njs_object_init_t  njs_arrow_instance_init;
 extern const njs_object_init_t  njs_arguments_object_instance_init;
 
 njs_ret_t njs_eval_function(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_lexer.c
--- a/njs/njs_lexer.c	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_lexer.c	Sun Apr 07 14:26:13 2019 +0800
@@ -278,6 +278,7 @@ static const njs_lexer_multi_t  njs_grea
 
 static const njs_lexer_multi_t  njs_assignment_token[] = {
     { '=', NJS_TOKEN_EQUAL, 1, njs_strict_equal_token },
+    { '>', NJS_TOKEN_ARROW, 0, NULL },
 };
 
 
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_lexer.h
--- a/njs/njs_lexer.h	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_lexer.h	Sun Apr 07 14:26:13 2019 +0800
@@ -36,6 +36,7 @@ typedef enum {
     NJS_TOKEN_CONDITIONAL,
 
     NJS_TOKEN_ASSIGNMENT,
+    NJS_TOKEN_ARROW,
     NJS_TOKEN_ADDITION_ASSIGNMENT,
     NJS_TOKEN_SUBSTRACTION_ASSIGNMENT,
     NJS_TOKEN_MULTIPLICATION_ASSIGNMENT,
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_object.c
--- a/njs/njs_object.c	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_object.c	Sun Apr 07 14:26:13 2019 +0800
@@ -764,7 +764,12 @@ njs_method_private_copy(njs_vm_t *vm, nj
         return NXT_ERROR;
     }
 
-    function->object.shared_hash = vm->shared->function_instance_hash;
+    if (function->ctor) {
+        function->object.shared_hash = vm->shared->function_instance_hash;
+
+    } else {
+        function->object.shared_hash = vm->shared->arrow_instance_hash;
+    }
 
     pq->lhq.replace = 0;
     pq->lhq.value = prop;
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_parser.c
--- a/njs/njs_parser.c	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_parser.c	Sun Apr 07 14:26:13 2019 +0800
@@ -2103,6 +2103,172 @@ njs_parser_property_token(njs_vm_t *vm, 
 }
 
 
+nxt_int_t
+njs_parser_match_arrow_expression(njs_vm_t *vm, njs_parser_t *parser,
+    njs_token_t token)
+{
+    size_t      offset;
+    nxt_bool_t  rest_parameters;
+
+    if (token != NJS_TOKEN_OPEN_PARENTHESIS && token != NJS_TOKEN_NAME) {
+        return NXT_DECLINED;
+    }
+
+    offset = 0;
+
+    if (token == NJS_TOKEN_NAME) {
+        goto arrow;
+    }
+
+    token = njs_parser_peek_token(vm, parser, &offset);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return NXT_DECLINED;
+    }
+
+    rest_parameters = 0;
+
+    while (token != NJS_TOKEN_CLOSE_PARENTHESIS) {
+
+        if (rest_parameters) {
+            return NXT_DECLINED;
+        }
+
+        if (nxt_slow_path(token == NJS_TOKEN_ELLIPSIS)) {
+            rest_parameters = 1;
+
+            token = njs_parser_peek_token(vm, parser, &offset);
+            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                return NXT_DECLINED;
+            }
+        }
+
+        if (nxt_slow_path(token != NJS_TOKEN_NAME)) {
+            return NXT_DECLINED;
+        }
+
+        token = njs_parser_peek_token(vm, parser, &offset);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        if (token == NJS_TOKEN_COMMA) {
+            token = njs_parser_peek_token(vm, parser, &offset);
+            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+               return NXT_DECLINED;
+            }
+        }
+    }
+
+arrow:
+
+    token = njs_parser_peek_token(vm, parser, &offset);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return NXT_DECLINED;
+    }
+
+    if (parser->lexer->prev_token == NJS_TOKEN_LINE_END) {
+        return NXT_DECLINED;
+    }
+
+    if (nxt_slow_path(token != NJS_TOKEN_ARROW)) {
+        return NXT_DECLINED;
+    }
+
+    return NXT_OK;
+}
+
+
+njs_token_t
+njs_parser_arrow_expression(njs_vm_t *vm, njs_parser_t *parser,
+    njs_token_t token)
+{
+    njs_ret_t               ret;
+    njs_index_t             index;
+    njs_parser_node_t       *node, *body, *parent;
+    njs_function_lambda_t  *lambda;
+
+    node = njs_parser_node_new(vm, parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+    if (nxt_slow_path(node == NULL)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    node->token_line = njs_parser_token_line(parser);
+    parser->node = node;
+
+    lambda = nxt_mp_zalloc(vm->mem_pool, sizeof(njs_function_lambda_t));
+    if (nxt_slow_path(lambda == NULL)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    lambda->arrow = 1;
+
+    node->u.value.data.u.lambda = lambda;
+
+    ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_FUNCTION);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    parser->scope->arrow_function = 1;
+
+    index = NJS_SCOPE_ARGUMENTS;
+
+    /* A "this" reservation. */
+    index += sizeof(njs_value_t);
+
+    if (token == NJS_TOKEN_OPEN_PARENTHESIS) {
+        token = njs_parser_lambda_arguments(vm, parser, lambda, index, token);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+    } else {
+        token = njs_parser_lambda_argument(vm, parser, index);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        lambda->nargs = 1;
+    }
+
+    if (parser->lexer->prev_token == NJS_TOKEN_LINE_END) {
+        return NJS_TOKEN_ILLEGAL;
+    }
+
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_ARROW);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return token;
+    }
+
+    if (token == NJS_TOKEN_OPEN_BRACE) {
+        token = njs_parser_lambda_body(vm, parser, token);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+    } else {
+        parent = parser->node;
+
+        token = njs_parser_assignment_expression(vm, parser, token);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        body = njs_parser_return_set(vm, parser, parser->node);
+        if (nxt_slow_path(body == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        parent->right = body;
+        parser->node = parent;
+    }
+
+    njs_parser_scope_end(vm, parser);
+
+    return token;
+}
+
+
 nxt_bool_t
 njs_parser_has_side_effect(njs_parser_node_t *node)
 {
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_parser.h
--- a/njs/njs_parser.h	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_parser.h	Sun Apr 07 14:26:13 2019 +0800
@@ -32,6 +32,7 @@ struct njs_parser_scope_s {
     uint8_t                         nesting;     /* 4 bits */
     uint8_t                         argument_closures;
     uint8_t                         module;
+    uint8_t                         arrow_function;
 };
 
 
@@ -81,6 +82,10 @@ njs_token_t njs_parser_expression(njs_vm
 njs_token_t njs_parser_assignment_expression(njs_vm_t *vm,
     njs_parser_t *parser, njs_token_t token);
 njs_token_t njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser);
+nxt_int_t njs_parser_match_arrow_expression(njs_vm_t *vm, njs_parser_t *parser,
+    njs_token_t token);
+njs_token_t njs_parser_arrow_expression(njs_vm_t *vm, njs_parser_t *parser,
+    njs_token_t token);
 njs_token_t njs_parser_module_lambda(njs_vm_t *vm, njs_parser_t *parser);
 njs_token_t njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token);
@@ -226,10 +231,12 @@ njs_parser_global_scope(njs_vm_t *vm)
 
 
 nxt_inline njs_parser_scope_t *
-njs_function_scope(njs_parser_scope_t *scope)
+njs_function_scope(njs_parser_scope_t *scope, nxt_bool_t any)
 {
     while (scope->type != NJS_SCOPE_GLOBAL) {
-        if (scope->type == NJS_SCOPE_FUNCTION) {
+        if (scope->type == NJS_SCOPE_FUNCTION
+            && (any || !scope->arrow_function))
+        {
             return scope;
         }
 
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_parser_terminal.c
--- a/njs/njs_parser_terminal.c	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_parser_terminal.c	Sun Apr 07 14:26:13 2019 +0800
@@ -35,6 +35,11 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
     njs_ret_t          ret;
     njs_parser_node_t  *node;
 
+    ret = njs_parser_match_arrow_expression(vm, parser, token);
+    if (ret == NXT_OK) {
+        return njs_parser_arrow_expression(vm, parser, token);
+    }
+
     if (token == NJS_TOKEN_OPEN_PARENTHESIS) {
 
         token = njs_parser_token(vm, parser);
@@ -211,20 +216,10 @@ njs_parser_reference(njs_vm_t *vm, njs_p
     case NJS_TOKEN_THIS:
         nxt_thread_log_debug("JS: this");
 
-        scope = parser->scope;
-
-        while (scope->type != NJS_SCOPE_GLOBAL) {
-            if (scope->type == NJS_SCOPE_FUNCTION) {
-                break;
-            }
+        scope = njs_function_scope(parser->scope, 0);
 
-            scope = scope->parent;
-        }
-
-        if (scope->type != NJS_SCOPE_GLOBAL) {
-            if (njs_function_scope(scope)
-                == njs_function_scope(parser->scope))
-            {
+        if (scope != NULL) {
+            if (scope == njs_function_scope(parser->scope, 1)) {
                 node->index = NJS_INDEX_THIS;
 
             } else {
@@ -232,14 +227,13 @@ njs_parser_reference(njs_vm_t *vm, njs_p
 
                 node->token_line = token_line;
 
-                ret = njs_variable_reference(vm, parser->scope, node, name,
-                                             hash, NJS_REFERENCE);
+                ret = njs_variable_reference(vm, scope, node, name, hash,
+                                             NJS_REFERENCE);
                 if (nxt_slow_path(ret != NXT_OK)) {
                     return NULL;
                 }
 
-                var = njs_variable_add(vm, parser->scope, name, hash,
-                                       NJS_VARIABLE_VAR);
+                var = njs_variable_add(vm, scope, name, hash, NJS_VARIABLE_VAR);
                 if (nxt_slow_path(var == NULL)) {
                     return NULL;
                 }
@@ -361,7 +355,9 @@ njs_parser_reference(njs_vm_t *vm, njs_p
     case NJS_TOKEN_ARGUMENTS:
         nxt_thread_log_debug("JS: arguments");
 
-        if (parser->scope->type <= NJS_SCOPE_GLOBAL) {
+        scope = njs_function_scope(parser->scope, 0);
+
+        if (scope == NULL) {
             njs_parser_syntax_error(vm, parser, "\"%V\" object "
                                     "in global scope", name);
 
@@ -370,13 +366,13 @@ njs_parser_reference(njs_vm_t *vm, njs_p
 
         node->token_line = token_line;
 
-        ret = njs_variable_reference(vm, parser->scope, node, name, hash,
+        ret = njs_variable_reference(vm, scope, node, name, hash,
                                      NJS_REFERENCE);
         if (nxt_slow_path(ret != NXT_OK)) {
             return NULL;
         }
 
-        var = njs_variable_add(vm, parser->scope, name, hash, NJS_VARIABLE_VAR);
+        var = njs_variable_add(vm, scope, name, hash, NJS_VARIABLE_VAR);
         if (nxt_slow_path(var == NULL)) {
             return NULL;
         }
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/njs_vm.h
--- a/njs/njs_vm.h	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/njs_vm.h	Sun Apr 07 14:26:13 2019 +0800
@@ -1119,6 +1119,7 @@ struct njs_vm_shared_s {
     nxt_lvlhsh_t             array_instance_hash;
     nxt_lvlhsh_t             string_instance_hash;
     nxt_lvlhsh_t             function_instance_hash;
+    nxt_lvlhsh_t             arrow_instance_hash;
     nxt_lvlhsh_t             arguments_object_instance_hash;
 
     njs_object_t             string_object;
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/test/njs_expect_test.exp
--- a/njs/test/njs_expect_test.exp	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/test/njs_expect_test.exp	Sun Apr 07 14:26:13 2019 +0800
@@ -663,6 +663,8 @@ njs_test {
 njs_test {
     {"this\r\n"
      "this\r\nundefined"}
+    {"(() => this)()\r\n"
+     "(() => this)()\r\nundefined"}
 } "-t module"
 
 njs_test {
diff -r 070b635928a9 -r d7a0eb59a7e7 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Wed Apr 10 17:46:29 2019 +0300
+++ b/njs/test/njs_unit_test.c	Sun Apr 07 14:26:13 2019 +0800
@@ -6526,6 +6526,12 @@ static njs_unit_test_t  njs_test[] =
 
     /* arguments object. */
 
+    { nxt_string("arguments"),
+      nxt_string("SyntaxError: \"arguments\" object in global scope in 1") },
+
+    { nxt_string("{arguments}"),
+      nxt_string("SyntaxError: \"arguments\" object in global scope in 1") },
+
     { nxt_string("var arguments"),
       nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in var declaration in 1") },
 
@@ -6625,6 +6631,138 @@ static njs_unit_test_t  njs_test[] =
                  "myFoo(1,2);" ),
       nxt_string("") },
 
+    /* arrow functions. */
+
+    { nxt_string("()"),
+      nxt_string("SyntaxError: Unexpected token \")\" in 1") },
+
+    { nxt_string("() => "),
+      nxt_string("SyntaxError: Unexpected end of input in 1") },
+
+    { nxt_string("() => {"),
+      nxt_string("SyntaxError: Unexpected end of input in 1") },
+
+    { nxt_string("a\n => 1"),
+      nxt_string("SyntaxError: Unexpected token \"=>\" in 2") },
+
+    { nxt_string("new (()=>1)"),
+      nxt_string("TypeError: function is not a constructor")},
+
+    { nxt_string("(\n) => {}"),
+      nxt_string("[object Function]") },
+
+    { nxt_string("a => 1"),
+      nxt_string("[object Function]") },
+
+    { nxt_string("({f:()=>1, g:()=>2}).f()"),
+      nxt_string("1") },
+
+    { nxt_string("var f = f => {return 1;}; f()"),
+      nxt_string("1") },
+
+    { nxt_string("var f = (f) => {return 1;}; f()"),
+      nxt_string("1") },
+
+    { nxt_string("var f = (f, a, b) => {return 1;}; f()"),
+      nxt_string("1") },
+
+    { nxt_string("var f = () => {return 1;}; f()"),
+      nxt_string("1") },
+
+    { nxt_string("(f => {return 1;})()"),
+      nxt_string("1") },
+
+    { nxt_string("((f) => {return 1;})()"),
+      nxt_string("1") },
+
+    { nxt_string("(((f) => {return 1;}))()"),
+      nxt_string("1") },
+
+    { nxt_string("var f = f => 1; f()"),
+      nxt_string("1") },
+
+    { nxt_string("() => 1"),
+      nxt_string("[object Function]") },
+
+    { nxt_string("var f = ()=>{}; f()"),
+      nxt_string("undefined") },
+
+    { nxt_string("var f = ()=>({}); f()"),
+      nxt_string("[object Object]") },
+
+    { nxt_string("var materials = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium'];"
+                 "materials.map(material => { return material.length; });"),
+      nxt_string("8,6,7,9") },
+
+    { nxt_string("var materials = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium'];"
+                 "materials.map(material => material.length);"),
+      nxt_string("8,6,7,9") },
+
+    { nxt_string("var materials = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium'];"
+                 "materials.map(material => { material.length });"),
+      nxt_string(",,,") },
+
+    { nxt_string("function f(a, b, c) {a = 1; return () => { return arguments[1]; };};"
+                 "f(1, 2, 3)('a', 'b');"),
+      nxt_string("2") },
+
+    { nxt_string("var f = (...c) => { return (function() { return arguments.length; }).bind(null, c); };"
+                 "var x = f(1,'a',false, {}); x()"),
+      nxt_string("1") },
+
+    { nxt_string("var f = (...c) => { return (function() { return arguments.length; }).bind(null, c); };"
+                 "var x = f(1,'a',false, {}); x(1,2,3)"),
+      nxt_string("4") },
+
+    { nxt_string("function Car(){ this.age = 0; (() => { this.age++;})();}"
+                 "(new Car()).age"),
+      nxt_string("1") },
+
+    { nxt_string("function Car(){ this.age = 0; (function(){ this.age++;})();}"
+                 "(new Car()).age"),
+      nxt_string("TypeError: cannot get property \"age\" of undefined") },
+
+    /* arrow functions + global this. */
+
+    { nxt_string("(() => this)()"),
+      nxt_string("[object Object]") },
+
+    { nxt_string("(() => this).call('abc')"),
+      nxt_string("[object Object]") },
+
+    { nxt_string("(() => this).apply('abc')"),
+      nxt_string("[object Object]") },
+
+    { nxt_string("(() => this).bind('abc')()"),
+      nxt_string("[object Object]") },
+
+    { nxt_string("(function() { return (() => this); })()()"),
+      nxt_string("undefined") },
+
+    { nxt_string("(function() { return (() => this); }).call('abc')()"),
+      nxt_string("abc") },
+
+    { nxt_string("(function() { return (() => this); }).bind('abc')()()"),
+      nxt_string("abc") },
+
+    { nxt_string("(function() { return (() => this); })"
+                 ".call('abc').call('bca')"),
+      nxt_string("abc") },
+
+    { nxt_string("(function() { return (() => this); })"
+                 ".call('abc').bind('bca')()"),
+      nxt_string("abc") },
+
+    { nxt_string("(function() { return function() { return () => this; }; })"
+                 ".call('bca').call('abc')()"),
+      nxt_string("abc") },
+
+     { nxt_string("var f = () => 1; f.prototype"),
+       nxt_string("undefined") },
+
+     { nxt_string("var f = (a,b) => 0; f.length"),
+       nxt_string("2") },
+
     /* Scopes. */
 
     { nxt_string("function f(x) { a = x } var a; f(5); a"),
@@ -7568,6 +7706,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("this.NaN + 1"),
       nxt_string("NaN") },
 
+    { nxt_string("{this}"),
+      nxt_string("undefined") },
+
     { nxt_string("if (1) {new this}"),
       nxt_string("TypeError: object is not a function") },
 
@@ -7640,6 +7781,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Object.prototype"),
       nxt_string("[object Object]") },
 
+    { nxt_string("Object.prototype.valueOf.prototype"),
+      nxt_string("undefined") },
+
     { nxt_string("Object.constructor === Function"),
       nxt_string("true") },
 


More information about the nginx-devel mailing list