[njs] Added getter/setter literal support.

Dmitry Volyntsev xeioex at nginx.com
Mon Aug 12 13:13:08 UTC 2019


details:   https://hg.nginx.org/njs/rev/33c4966abdda
branches:  
changeset: 1120:33c4966abdda
user:      hongzhidao <hongzhidao at gmail.com>
date:      Thu Aug 08 03:52:18 2019 -0400
description:
Added getter/setter literal support.

This closes #118 issue on Github.

diffstat:

 src/njs_disassembler.c    |   31 ++++-
 src/njs_generator.c       |   49 ++++++++
 src/njs_lexer.h           |    2 +
 src/njs_lexer_keyword.c   |    2 +
 src/njs_object.c          |    6 +-
 src/njs_object.h          |    9 +-
 src/njs_object_prop.c     |   66 +++++++----
 src/njs_parser_terminal.c |  257 +++++++++++++++++++++++++++++++++------------
 src/njs_vmcode.c          |   29 ++++-
 src/njs_vmcode.h          |   10 +
 src/test/njs_unit_test.c  |   55 +++++++++
 11 files changed, 411 insertions(+), 105 deletions(-)

diffs (885 lines):

diff -r e22663f2defe -r 33c4966abdda src/njs_disassembler.c
--- a/src/njs_disassembler.c	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_disassembler.c	Thu Aug 08 03:52:18 2019 -0400
@@ -36,15 +36,15 @@ static njs_code_name_t  code_names[] = {
           njs_str("OBJECT COPY     ") },
 
     { NJS_VMCODE_PROPERTY_GET, sizeof(njs_vmcode_prop_get_t),
-          njs_str("PROPERTY GET    ") },
+          njs_str("PROP GET        ") },
     { NJS_VMCODE_PROPERTY_INIT, sizeof(njs_vmcode_prop_set_t),
-          njs_str("PROPERTY INIT   ") },
+          njs_str("PROP INIT       ") },
     { NJS_VMCODE_PROPERTY_SET, sizeof(njs_vmcode_prop_set_t),
-          njs_str("PROPERTY SET    ") },
+          njs_str("PROP SET        ") },
     { NJS_VMCODE_PROPERTY_IN, sizeof(njs_vmcode_3addr_t),
-          njs_str("PROPERTY IN     ") },
+          njs_str("PROP IN         ") },
     { NJS_VMCODE_PROPERTY_DELETE, sizeof(njs_vmcode_3addr_t),
-          njs_str("PROPERTY DELETE ") },
+          njs_str("PROP DELETE     ") },
     { NJS_VMCODE_INSTANCE_OF, sizeof(njs_vmcode_instance_of_t),
           njs_str("INSTANCE OF     ") },
 
@@ -178,6 +178,7 @@ njs_disassemble(u_char *start, u_char *e
     njs_vmcode_equal_jump_t      *equal;
     njs_vmcode_prop_foreach_t    *prop_foreach;
     njs_vmcode_method_frame_t    *method;
+    njs_vmcode_prop_accessor_t   *prop_accessor;
     njs_vmcode_try_trampoline_t  *try_tramp;
     njs_vmcode_function_frame_t  *function;
 
@@ -305,7 +306,7 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_PROPERTY_FOREACH) {
             prop_foreach = (njs_vmcode_prop_foreach_t *) p;
 
-            njs_printf("%05uz PROPERTY FOREACH  %04Xz %04Xz +%uz\n",
+            njs_printf("%05uz PROP FOREACH      %04Xz %04Xz +%uz\n",
                        p - start, (size_t) prop_foreach->next,
                        (size_t) prop_foreach->object,
                        (size_t) prop_foreach->offset);
@@ -317,7 +318,7 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_PROPERTY_NEXT) {
             prop_next = (njs_vmcode_prop_next_t *) p;
 
-            njs_printf("%05uz PROPERTY NEXT     %04Xz %04Xz %04Xz %uz\n",
+            njs_printf("%05uz PROP NEXT         %04Xz %04Xz %04Xz %uz\n",
                        p - start, (size_t) prop_next->retval,
                        (size_t) prop_next->object, (size_t) prop_next->next,
                        (size_t) prop_next->offset);
@@ -327,6 +328,22 @@ njs_disassemble(u_char *start, u_char *e
             continue;
         }
 
+        if (operation == NJS_VMCODE_PROPERTY_ACCESSOR) {
+            prop_accessor = (njs_vmcode_prop_accessor_t *) p;
+
+            njs_printf("%05uz PROP %s ACCESSOR %04Xz %04Xz %04Xz\n",
+                       p - start,
+                       (prop_accessor->type == NJS_OBJECT_PROP_GETTER)
+                           ? "GET" : "SET",
+                       (size_t) prop_accessor->value,
+                       (size_t) prop_accessor->object,
+                       (size_t) prop_accessor->property);
+
+            p += sizeof(njs_vmcode_prop_accessor_t);
+
+            continue;
+        }
+
         if (operation == NJS_VMCODE_TRY_START) {
             try_start = (njs_vmcode_try_start_t *) p;
 
diff -r e22663f2defe -r 33c4966abdda src/njs_generator.c
--- a/src/njs_generator.c	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_generator.c	Thu Aug 08 03:52:18 2019 -0400
@@ -119,6 +119,8 @@ static njs_int_t njs_generate_operation_
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_object(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node);
+static njs_int_t njs_generate_property_accessor(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_array(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node);
 static njs_int_t njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
@@ -398,6 +400,10 @@ njs_generator(njs_vm_t *vm, njs_generato
     case NJS_TOKEN_OBJECT:
         return njs_generate_object(vm, generator, node);
 
+    case NJS_TOKEN_PROPERTY_GETTER:
+    case NJS_TOKEN_PROPERTY_SETTER:
+        return njs_generate_property_accessor(vm, generator, node);
+
     case NJS_TOKEN_ARRAY:
         return njs_generate_array(vm, generator, node);
 
@@ -1896,6 +1902,49 @@ njs_generate_object(njs_vm_t *vm, njs_ge
 
 
 static njs_int_t
+njs_generate_property_accessor(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node)
+{
+    njs_int_t                   ret;
+    njs_parser_node_t           *lvalue, *function, *object, *property;
+    njs_vmcode_prop_accessor_t  *accessor;
+
+    lvalue = node->left;
+    function = node->right;
+
+    object = lvalue->left;
+
+    ret = njs_generator(vm, generator, object);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    property = lvalue->right;
+
+    ret = njs_generator(vm, generator, property);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    ret = njs_generator(vm, generator, function);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    njs_generate_code(generator, njs_vmcode_prop_accessor_t, accessor,
+                      NJS_VMCODE_PROPERTY_ACCESSOR, 3);
+
+    accessor->value = function->index;
+    accessor->object = object->index;
+    accessor->property = property->index;
+    accessor->type = (node->token == NJS_TOKEN_PROPERTY_GETTER)
+                     ? NJS_OBJECT_PROP_GETTER : NJS_OBJECT_PROP_SETTER;
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
 njs_generate_array(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
diff -r e22663f2defe -r 33c4966abdda src/njs_lexer.h
--- a/src/njs_lexer.h	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_lexer.h	Thu Aug 08 03:52:18 2019 -0400
@@ -125,6 +125,8 @@ typedef enum {
     NJS_TOKEN_PROPERTY,
     NJS_TOKEN_PROPERTY_INIT,
     NJS_TOKEN_PROPERTY_DELETE,
+    NJS_TOKEN_PROPERTY_GETTER,
+    NJS_TOKEN_PROPERTY_SETTER,
 
     NJS_TOKEN_ARRAY,
 
diff -r e22663f2defe -r 33c4966abdda src/njs_lexer_keyword.c
--- a/src/njs_lexer_keyword.c	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_lexer_keyword.c	Thu Aug 08 03:52:18 2019 -0400
@@ -186,6 +186,8 @@ njs_lexer_keyword(njs_lexer_t *lexer, nj
     lhq.key = lt->text;
     lhq.proto = &njs_keyword_hash_proto;
 
+    lexer->keyword = 0;
+
     if (njs_lvlhsh_find(&lexer->keywords_hash, &lhq) == NJS_OK) {
         keyword = lhq.value;
         lt->token = keyword->token;
diff -r e22663f2defe -r 33c4966abdda src/njs_object.c
--- a/src/njs_object.c	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_object.c	Thu Aug 08 03:52:18 2019 -0400
@@ -1112,7 +1112,8 @@ njs_object_define_property(njs_vm_t *vm,
 
     name = njs_arg(args, nargs, 2);
 
-    ret = njs_object_prop_define(vm, value, name, desc);
+    ret = njs_object_prop_define(vm, value, name, desc,
+                                 NJS_OBJECT_PROP_DESCRIPTOR);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -1165,7 +1166,8 @@ njs_object_define_properties(njs_vm_t *v
         }
 
         if (prop->enumerable && njs_is_object(&prop->value)) {
-            ret = njs_object_prop_define(vm, value, &prop->name, &prop->value);
+            ret = njs_object_prop_define(vm, value, &prop->name, &prop->value,
+                                         NJS_OBJECT_PROP_DESCRIPTOR);
 
             if (njs_slow_path(ret != NJS_OK)) {
                 return NJS_ERROR;
diff -r e22663f2defe -r 33c4966abdda src/njs_object.h
--- a/src/njs_object.h	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_object.h	Thu Aug 08 03:52:18 2019 -0400
@@ -29,6 +29,13 @@
     } while (0)
 
 
+typedef enum {
+    NJS_OBJECT_PROP_DESCRIPTOR,
+    NJS_OBJECT_PROP_GETTER,
+    NJS_OBJECT_PROP_SETTER,
+} njs_object_prop_define_t;
+
+
 struct njs_object_init_s {
     njs_str_t                   name;
     const njs_object_prop_t     *properties;
@@ -68,7 +75,7 @@ njs_object_prop_t *njs_object_prop_alloc
 njs_int_t njs_object_property(njs_vm_t *vm, const njs_value_t *value,
     njs_lvlhsh_query_t *lhq, njs_value_t *retval);
 njs_int_t njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
-    njs_value_t *name, njs_value_t *value);
+    njs_value_t *name, njs_value_t *value, njs_object_prop_define_t type);
 njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
     njs_value_t *value, njs_value_t *setval);
 const char *njs_prop_type_string(njs_object_prop_type_t type);
diff -r e22663f2defe -r 33c4966abdda src/njs_object_prop.c
--- a/src/njs_object_prop.c	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_object_prop.c	Thu Aug 08 03:52:18 2019 -0400
@@ -8,8 +8,8 @@
 #include <njs_main.h>
 
 
-static njs_object_prop_t *njs_descriptor_prop(njs_vm_t *vm,
-    const njs_value_t *name, const njs_value_t *desc);
+static njs_int_t njs_descriptor_prop(njs_vm_t *vm,
+    njs_object_prop_t *prop, const njs_value_t *desc);
 
 
 njs_object_prop_t *
@@ -102,7 +102,7 @@ found:
  */
 njs_int_t
 njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
-    njs_value_t *name, njs_value_t *value)
+    njs_value_t *name, njs_value_t *value, njs_object_prop_define_t type)
 {
     njs_int_t             ret;
     njs_object_prop_t     *prop, *prev;
@@ -116,11 +116,38 @@ njs_object_prop_define(njs_vm_t *vm, njs
         return ret;
     }
 
-    prop = njs_descriptor_prop(vm, name, value);
+    prop = njs_object_prop_alloc(vm, name, &njs_value_invalid,
+                                 NJS_ATTRIBUTE_UNSET);
     if (njs_slow_path(prop == NULL)) {
         return NJS_ERROR;
     }
 
+    switch (type) {
+
+    case NJS_OBJECT_PROP_DESCRIPTOR:
+        if (njs_descriptor_prop(vm, prop, value) != NJS_OK) {
+            return NJS_ERROR;
+        }
+
+        break;
+
+    case NJS_OBJECT_PROP_GETTER:
+        prop->getter = *value;
+        prop->setter = njs_value_invalid;
+        prop->enumerable = NJS_ATTRIBUTE_TRUE;
+        prop->configurable = NJS_ATTRIBUTE_TRUE;
+
+        break;
+
+    case NJS_OBJECT_PROP_SETTER:
+        prop->setter = *value;
+        prop->getter = njs_value_invalid;
+        prop->enumerable = NJS_ATTRIBUTE_TRUE;
+        prop->configurable = NJS_ATTRIBUTE_TRUE;
+
+        break;
+    }
+
     if (njs_fast_path(ret == NJS_DECLINED)) {
 
         /* 6.2.5.6 CompletePropertypropriptor */
@@ -332,37 +359,30 @@ exception:
 }
 
 
-static njs_object_prop_t *
-njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *name,
+static njs_int_t
+njs_descriptor_prop(njs_vm_t *vm, njs_object_prop_t *prop,
     const njs_value_t *desc)
 {
     njs_int_t           ret;
     njs_bool_t          data, accessor;
     njs_value_t         value;
-    njs_object_prop_t   *prop;
     njs_lvlhsh_query_t  lhq;
 
     data = 0;
     accessor = 0;
 
-    prop = njs_object_prop_alloc(vm, name, &njs_value_invalid,
-                                 NJS_ATTRIBUTE_UNSET);
-    if (njs_slow_path(prop == NULL)) {
-        return NULL;
-    }
-
     njs_object_property_init(&lhq, "get", NJS_GET_HASH);
 
     ret = njs_object_property(vm, desc, &lhq, &value);
 
     if (njs_slow_path(ret == NJS_ERROR)) {
-        return NULL;
+        return NJS_ERROR;
     }
 
     if (ret == NJS_OK) {
         if (njs_is_defined(&value) && !njs_is_function(&value)) {
             njs_type_error(vm, "Getter must be a function");
-            return NULL;
+            return NJS_ERROR;
         }
 
         accessor = 1;
@@ -379,13 +399,13 @@ njs_descriptor_prop(njs_vm_t *vm, const 
     ret = njs_object_property(vm, desc, &lhq, &value);
 
     if (njs_slow_path(ret == NJS_ERROR)) {
-        return NULL;
+        return ret;
     }
 
     if (ret == NJS_OK) {
         if (njs_is_defined(&value) && !njs_is_function(&value)) {
             njs_type_error(vm, "Setter must be a function");
-            return NULL;
+            return NJS_ERROR;
         }
 
         accessor = 1;
@@ -402,7 +422,7 @@ njs_descriptor_prop(njs_vm_t *vm, const 
     ret = njs_object_property(vm, desc, &lhq, &value);
 
     if (njs_slow_path(ret == NJS_ERROR)) {
-        return NULL;
+        return ret;
     }
 
     if (ret == NJS_OK) {
@@ -416,7 +436,7 @@ njs_descriptor_prop(njs_vm_t *vm, const 
     ret = njs_object_property(vm, desc, &lhq, &value);
 
     if (njs_slow_path(ret == NJS_ERROR)) {
-        return NULL;
+        return ret;
     }
 
     if (ret == NJS_OK) {
@@ -430,7 +450,7 @@ njs_descriptor_prop(njs_vm_t *vm, const 
     ret = njs_object_property(vm, desc, &lhq, &value);
 
     if (njs_slow_path(ret == NJS_ERROR)) {
-        return NULL;
+        return ret;
     }
 
     if (ret == NJS_OK) {
@@ -443,7 +463,7 @@ njs_descriptor_prop(njs_vm_t *vm, const 
     ret = njs_object_property(vm, desc, &lhq, &value);
 
     if (njs_slow_path(ret == NJS_ERROR)) {
-        return NULL;
+        return ret;
     }
 
     if (ret == NJS_OK) {
@@ -453,10 +473,10 @@ njs_descriptor_prop(njs_vm_t *vm, const 
     if (accessor && data) {
         njs_type_error(vm, "Cannot both specify accessors "
                            "and a value or writable attribute");
-        return NULL;
+        return NJS_ERROR;
     }
 
-    return prop;
+    return NJS_OK;
 }
 
 
diff -r e22663f2defe -r 33c4966abdda src/njs_parser_terminal.c
--- a/src/njs_parser_terminal.c	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_parser_terminal.c	Thu Aug 08 03:52:18 2019 -0400
@@ -19,6 +19,10 @@ static njs_token_t njs_parser_object(njs
 static njs_int_t njs_parser_object_property(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *parent, njs_parser_node_t *property,
     njs_parser_node_t *value);
+static njs_int_t njs_parser_property_accessor(njs_vm_t *vm,
+    njs_parser_t *parser, njs_parser_node_t *parent,
+    njs_parser_node_t *property, njs_parser_node_t *value,
+    njs_token_t accessor);
 static njs_token_t njs_parser_array(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *array);
 static njs_int_t njs_parser_array_item(njs_vm_t *vm, njs_parser_t *parser,
@@ -467,31 +471,23 @@ njs_parser_builtin(njs_vm_t *vm, njs_par
 }
 
 
-/*
- * ES6: 12.2.6 Object Initializer
- * Supported syntax:
- *   PropertyDefinition:
- *     PropertyName : AssignmentExpression
- *     IdentifierReference
- *   PropertyName:
- *    IdentifierName, StringLiteral, NumericLiteral.
- */
 static njs_token_t
 njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj)
 {
     uint32_t               hash, token_line;
     njs_int_t              ret;
     njs_str_t              name;
-    njs_token_t            token, prop_token;
+    njs_token_t            token, accessor;
     njs_lexer_t            *lexer;
     njs_parser_node_t      *object, *property, *expression;
     njs_function_lambda_t  *lambda;
 
     lexer = parser->lexer;
 
-    /* GCC and Clang complain about uninitialized hash. */
+    /* GCC and Clang complain about uninitialized values. */
     hash = 0;
     token_line = 0;
+    property = NULL;
 
     object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE);
     if (njs_slow_path(object == NULL)) {
@@ -501,16 +497,46 @@ njs_parser_object(njs_vm_t *vm, njs_pars
     object->u.object = obj;
 
     for ( ;; ) {
-        prop_token = njs_parser_token(vm, parser);
-        if (njs_slow_path(prop_token <= NJS_TOKEN_ILLEGAL)) {
-            return prop_token;
+        token = njs_parser_token(vm, parser);
+        if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
         }
 
+        accessor = 0;
         njs_memzero(&name, sizeof(njs_str_t));
 
-        switch (prop_token) {
+        if (token == NJS_TOKEN_NAME || lexer->keyword) {
+            name = *njs_parser_text(parser);
+            hash = njs_parser_key_hash(parser);
+            token_line = njs_parser_token_line(parser);
+
+            property = njs_parser_node_string(vm, parser);
+            if (njs_slow_path(property == NULL)) {
+                return NJS_TOKEN_ERROR;
+            }
+
+            if (token == NJS_TOKEN_NAME && name.length == 3
+                && (memcmp(name.start, "get", 3) == 0
+                    || memcmp(name.start, "set", 3) == 0))
+            {
+                accessor = (name.start[0] == 'g') ? NJS_TOKEN_PROPERTY_GETTER
+                                                  : NJS_TOKEN_PROPERTY_SETTER;
+
+                token = njs_parser_token(vm, parser);
+                if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                    return token;
+                }
+            }
+        }
+
+        switch (token) {
 
         case NJS_TOKEN_CLOSE_BRACE:
+            if (accessor) {
+                accessor = 0;
+                break;
+            }
+
             goto done;
 
         case NJS_TOKEN_OPEN_BRACKET:
@@ -530,13 +556,14 @@ njs_parser_object(njs_vm_t *vm, njs_pars
 
             property = parser->node;
 
-            token = njs_parser_match(vm, parser, token, NJS_TOKEN_CLOSE_BRACKET);
+            token = njs_parser_match(vm, parser, token,
+                                     NJS_TOKEN_CLOSE_BRACKET);
             break;
 
         case NJS_TOKEN_NUMBER:
         case NJS_TOKEN_STRING:
         case NJS_TOKEN_ESCAPE_STRING:
-            token = njs_parser_terminal(vm, parser, prop_token);
+            token = njs_parser_terminal(vm, parser, token);
             if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
                 return token;
             }
@@ -545,58 +572,27 @@ njs_parser_object(njs_vm_t *vm, njs_pars
             break;
 
         default:
-            if (prop_token != NJS_TOKEN_NAME && !lexer->keyword) {
-                return NJS_TOKEN_ILLEGAL;
+            if (token != NJS_TOKEN_NAME && !lexer->keyword) {
+                if (name.length == 0) {
+                    return NJS_TOKEN_ILLEGAL;
+                }
+
+                accessor = 0;
+                break;
             }
 
-            property = njs_parser_node_string(vm, parser);
-            if (njs_slow_path(property == NULL)) {
-                return NJS_TOKEN_ERROR;
+            if (accessor) {
+                property = njs_parser_node_string(vm, parser);
+                if (njs_slow_path(property == NULL)) {
+                    return NJS_TOKEN_ERROR;
+                }
             }
 
-            name = *njs_parser_text(parser);
-            hash = njs_parser_key_hash(parser);
-            token_line = njs_parser_token_line(parser);
-
             token = njs_parser_token(vm, parser);
             break;
         }
 
-        switch (token) {
-
-        case NJS_TOKEN_COMMA:
-        case NJS_TOKEN_CLOSE_BRACE:
-
-            if (name.length == 0
-                || prop_token == NJS_TOKEN_THIS
-                || prop_token == NJS_TOKEN_GLOBAL_THIS)
-            {
-                return NJS_TOKEN_ILLEGAL;
-            }
-
-            expression = njs_parser_reference(vm, parser, prop_token, &name,
-                                              hash, token_line);
-            if (njs_slow_path(expression == NULL)) {
-                return NJS_TOKEN_ERROR;
-            }
-
-            break;
-
-        case NJS_TOKEN_COLON:
-            token = njs_parser_token(vm, parser);
-            if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-                return token;
-            }
-
-            token = njs_parser_assignment_expression(vm, parser, token);
-            if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-                return token;
-            }
-
-            expression = parser->node;
-            break;
-
-        case NJS_TOKEN_OPEN_PARENTHESIS:
+        if (accessor) {
             expression = njs_parser_node_new(vm, parser,
                                              NJS_TOKEN_FUNCTION_EXPRESSION);
             if (njs_slow_path(expression == NULL)) {
@@ -606,7 +602,7 @@ njs_parser_object(njs_vm_t *vm, njs_pars
             expression->token_line = njs_parser_token_line(parser);
             parser->node = expression;
 
-            lambda = njs_function_lambda_alloc(vm, 0);
+            lambda = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_lambda_t));
             if (njs_slow_path(lambda == NULL)) {
                 return NJS_TOKEN_ERROR;
             }
@@ -618,15 +614,95 @@ njs_parser_object(njs_vm_t *vm, njs_pars
                 return token;
             }
 
-            break;
+            if (accessor == NJS_TOKEN_PROPERTY_GETTER) {
+                if (lambda->nargs != 0) {
+                    njs_parser_syntax_error(vm, parser,
+                                  "Getter must not have any formal parameters");
+                    return NJS_TOKEN_ILLEGAL;
+                }
+
+            } else {
+                if (lambda->nargs != 1) {
+                    njs_parser_syntax_error(vm, parser,
+                               "Setter must have exactly one formal parameter");
+                    return NJS_TOKEN_ILLEGAL;
+                }
+            }
+
+            ret = njs_parser_property_accessor(vm, parser, obj, property,
+                                               expression, accessor);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_TOKEN_ERROR;
+            }
+
+        } else {
+            switch (token) {
+
+            case NJS_TOKEN_COMMA:
+            case NJS_TOKEN_CLOSE_BRACE:
+
+                if (name.length == 0
+                    || lexer->prev_token == NJS_TOKEN_THIS
+                    || lexer->prev_token == NJS_TOKEN_GLOBAL_THIS)
+                {
+                    return NJS_TOKEN_ILLEGAL;
+                }
+
+                expression = njs_parser_reference(vm, parser, lexer->prev_token,
+                                                  &name, hash, token_line);
+                if (njs_slow_path(expression == NULL)) {
+                    return NJS_TOKEN_ERROR;
+                }
+
+                break;
 
-        default:
-            return NJS_TOKEN_ILLEGAL;
-        }
+            case NJS_TOKEN_COLON:
+                token = njs_parser_token(vm, parser);
+                if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                    return token;
+                }
+
+                token = njs_parser_assignment_expression(vm, parser, token);
+                if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                    return token;
+                }
+
+                expression = parser->node;
+                break;
+
+            case NJS_TOKEN_OPEN_PARENTHESIS:
+                expression = njs_parser_node_new(vm, parser,
+                                                 NJS_TOKEN_FUNCTION_EXPRESSION);
+                if (njs_slow_path(expression == NULL)) {
+                    return NJS_TOKEN_ERROR;
+                }
+
+                expression->token_line = njs_parser_token_line(parser);
+                parser->node = expression;
 
-        ret = njs_parser_object_property(vm, parser, obj, property, expression);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return NJS_TOKEN_ERROR;
+                lambda = njs_function_lambda_alloc(vm, 0);
+                if (njs_slow_path(lambda == NULL)) {
+                    return NJS_TOKEN_ERROR;
+                }
+
+                expression->u.value.data.u.lambda = lambda;
+
+                token = njs_parser_function_lambda(vm, parser, lambda, token);
+                if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                    return token;
+                }
+
+                break;
+
+            default:
+                return NJS_TOKEN_ILLEGAL;
+            }
+
+            ret = njs_parser_object_property(vm, parser, obj, property,
+                                             expression);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_TOKEN_ERROR;
+            }
         }
 
         if (token == NJS_TOKEN_CLOSE_BRACE) {
@@ -690,6 +766,49 @@ njs_parser_object_property(njs_vm_t *vm,
 }
 
 
+static njs_int_t
+njs_parser_property_accessor(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *parent, njs_parser_node_t *property,
+    njs_parser_node_t *value, njs_token_t accessor)
+{
+    njs_parser_node_t  *node, *stmt, *object, *propref;
+
+    object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE);
+    if (njs_slow_path(object == NULL)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    object->u.object = parent;
+
+    propref = njs_parser_node_new(vm, parser, 0);
+    if (njs_slow_path(propref == NULL)) {
+        return NJS_ERROR;
+    }
+
+    propref->left = object;
+    propref->right = property;
+
+    node = njs_parser_node_new(vm, parser, accessor);
+    if (njs_slow_path(node == NULL)) {
+        return NJS_ERROR;
+    }
+
+    node->left = propref;
+    node->right = value;
+
+    stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT);
+    if (njs_slow_path(stmt == NULL)) {
+        return NJS_ERROR;
+    }
+
+    stmt->right = node;
+    stmt->left = parent->left;
+    parent->left = stmt;
+
+    return NJS_OK;
+}
+
+
 static njs_token_t
 njs_parser_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *array)
 {
diff -r e22663f2defe -r 33c4966abdda src/njs_vmcode.c
--- a/src/njs_vmcode.c	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_vmcode.c	Thu Aug 08 03:52:18 2019 -0400
@@ -86,9 +86,10 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_c
     njs_str_t                    string;
     njs_uint_t                   hint;
     njs_bool_t                   valid, lambda_call;
-    njs_value_t                  *retval, *value1, *value2, *src, *s1, *s2;
-    njs_value_t                  numeric1, numeric2, primitive1, primitive2,
-                                 dst;
+    njs_value_t                  *retval, *value1, *value2;
+    njs_value_t                  *src, *s1, *s2, dst;
+    njs_value_t                  *function, name;
+    njs_value_t                  numeric1, numeric2, primitive1, primitive2;
     njs_frame_t                  *frame;
     njs_jump_off_t               ret;
     njs_vmcode_this_t            *this;
@@ -103,6 +104,7 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_c
     njs_vmcode_equal_jump_t      *equal;
     njs_vmcode_try_return_t      *try_return;
     njs_vmcode_method_frame_t    *method_frame;
+    njs_vmcode_prop_accessor_t   *accessor;
     njs_vmcode_function_frame_t  *function_frame;
 
 next:
@@ -599,6 +601,27 @@ next:
                 ret = sizeof(njs_vmcode_prop_set_t);
                 break;
 
+            case NJS_VMCODE_PROPERTY_ACCESSOR:
+                accessor = (njs_vmcode_prop_accessor_t *) pc;
+                function = njs_vmcode_operand(vm, accessor->value);
+
+                ret = njs_value_to_string(vm, &name, value2);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    njs_internal_error(vm, "failed conversion of type \"%s\" "
+                                       "to string while property define",
+                                       njs_type_string(value2->type));
+                    return NJS_ERROR;
+                }
+
+                ret = njs_object_prop_define(vm, value1, &name, function,
+                                             accessor->type);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    return NJS_ERROR;
+                }
+
+                ret = sizeof(njs_vmcode_prop_accessor_t);
+                break;
+
             case NJS_VMCODE_IF_TRUE_JUMP:
             case NJS_VMCODE_IF_FALSE_JUMP:
                 ret = njs_is_true(value1);
diff -r e22663f2defe -r 33c4966abdda src/njs_vmcode.h
--- a/src/njs_vmcode.h	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/njs_vmcode.h	Thu Aug 08 03:52:18 2019 -0400
@@ -40,6 +40,7 @@ typedef uint8_t                         
 #define NJS_VMCODE_STOP                 VMCODE0(0)
 #define NJS_VMCODE_JUMP                 VMCODE0(1)
 #define NJS_VMCODE_PROPERTY_SET         VMCODE0(2)
+#define NJS_VMCODE_PROPERTY_ACCESSOR    VMCODE0(3)
 #define NJS_VMCODE_IF_TRUE_JUMP         VMCODE0(4)
 #define NJS_VMCODE_IF_FALSE_JUMP        VMCODE0(5)
 #define NJS_VMCODE_IF_EQUAL_JUMP        VMCODE0(6)
@@ -261,6 +262,15 @@ typedef struct {
 
 typedef struct {
     njs_vmcode_t               code;
+    njs_index_t                value;
+    njs_index_t                object;
+    njs_index_t                property;
+    uint8_t                    type;
+} njs_vmcode_prop_accessor_t;
+
+
+typedef struct {
+    njs_vmcode_t               code;
     njs_index_t                next;
     njs_index_t                object;
     njs_jump_off_t             offset;
diff -r e22663f2defe -r 33c4966abdda src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Mon Aug 12 14:54:46 2019 +0300
+++ b/src/test/njs_unit_test.c	Thu Aug 08 03:52:18 2019 -0400
@@ -10096,6 +10096,61 @@ static njs_unit_test_t  njs_test[] =
                 "JSON.stringify(Object.getOwnPropertyDescriptor(o, 'a')).set"),
       njs_str("undefined") },
 
+    { njs_str("var get = 'get'; var o = { get }; o.get"),
+      njs_str("get") },
+
+    { njs_str("var o = { get foo() { return 'bar'; } }; o.foo"),
+      njs_str("bar") },
+
+    { njs_str("var o = { get get() { return 'bar'; } }; o.get"),
+      njs_str("bar") },
+
+    { njs_str("var o = { get() { return 'bar'; } }; o.get()"),
+      njs_str("bar") },
+
+    { njs_str("var o = { get lazy() { delete this.lazy; return this.lazy = Math.pow(2,3)} };o.lazy"),
+      njs_str("8") },
+
+    { njs_str("var o = { get lazy() { delete this.lazy; return this.lazy = Math.pow(2,3)} }; o.lazy;"
+              "Object.getOwnPropertyDescriptor(o, 'lazy').value"),
+      njs_str("8") },
+
+    { njs_str("var expr = 'foo'; var o = { get [expr]() { return 'bar'; } }; o.foo"),
+      njs_str("bar") },
+
+    { njs_str("var o = { get [{toString(){return 'get'}}]() { return 'bar'; } }; o.get"),
+      njs_str("bar") },
+
+    { njs_str("var o = { get [{toString(){return {} }}]() { return 'bar'; } }; o.get"),
+      njs_str("InternalError: failed conversion of type \"object\" to string while property define") },
+
+    { njs_str("var o = { get foo(v1, v2) { return 'bar'; } }; o.foo"),
+      njs_str("SyntaxError: Getter must not have any formal parameters in 1") },
+
+    { njs_str("var o = { baz: 'bar', set foo(v) { this.baz = v; } }; o.foo = 'baz'; o.baz"),
+      njs_str("baz") },
+
+    { njs_str("var o = { baz: 'bar', set set(v) { this.baz = v; } }; o.set = 'baz'; o.baz"),
+      njs_str("baz") },
+
+    { njs_str("var expr = 'foo'; var o = { baz: 'bar', set [expr](v) { this.baz = v; } }; o.foo = 'baz'; o.baz"),
+      njs_str("baz") },
+
+    { njs_str("var o = { baz: 'bar', set foo(v1, v2) { this.baz = v; } }; o.foo = 'baz'; o.baz"),
+      njs_str("SyntaxError: Setter must have exactly one formal parameter in 1") },
+
+    { njs_str("var o = { get foo() { return 'bar'; }, set foo(v) { this.baz = v; } }; o.foo"),
+      njs_str("bar") },
+
+    { njs_str("var expr = 'foo'; var o = { get [expr]() { return 'bar'; }, set [expr](v) { this.baz = v; } }; o.foo"),
+      njs_str("bar") },
+
+    { njs_str("Object.getOwnPropertyDescriptor({get foo() {}}, 'foo').enumerable"),
+      njs_str("true") },
+
+    { njs_str("Object.getOwnPropertyDescriptor({get foo() {}}, 'foo').configurable"),
+      njs_str("true") },
+
     { njs_str("var p = { a:5 }; var o = Object.create(p);"
                  "Object.getPrototypeOf(o) === p"),
       njs_str("true") },


More information about the nginx-devel mailing list