[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