From 0e52409a353319a6758c84bca3f65e80a1ea8f38 Mon Sep 17 00:00:00 2001 From: hongzhidao Date: Thu, 8 Aug 2019 03:52:18 -0400 Subject: [PATCH] Added getter/setter literal support. This closes #118 issue on Github. --- 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(-) diff --git a/src/njs_disassembler.c b/src/njs_disassembler.c index 57742598..4d60ff0b 100644 --- a/src/njs_disassembler.c +++ b/src/njs_disassembler.c @@ -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 *end) 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 *end) 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 *end) 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 *end) 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 --git a/src/njs_generator.c b/src/njs_generator.c index 7967a14a..fcc8844e 100644 --- a/src/njs_generator.c +++ b/src/njs_generator.c @@ -119,6 +119,8 @@ static njs_int_t njs_generate_operation_assignment(njs_vm_t *vm, 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_generator_t *generator, njs_parser_node_t *node) 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); @@ -1895,6 +1901,49 @@ njs_generate_object(njs_vm_t *vm, njs_generator_t *generator, } +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 --git a/src/njs_lexer.h b/src/njs_lexer.h index 78b47adf..5222e292 100644 --- a/src/njs_lexer.h +++ b/src/njs_lexer.h @@ -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 --git a/src/njs_lexer_keyword.c b/src/njs_lexer_keyword.c index c4287112..a5924338 100644 --- a/src/njs_lexer_keyword.c +++ b/src/njs_lexer_keyword.c @@ -186,6 +186,8 @@ njs_lexer_keyword(njs_lexer_t *lexer, njs_lexer_token_t *lt) 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 --git a/src/njs_object.c b/src/njs_object.c index 2fb1f9e8..b330c815 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -1112,7 +1112,8 @@ njs_object_define_property(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, 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 *vm, njs_value_t *args, njs_uint_t nargs, } 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 --git a/src/njs_object.h b/src/njs_object.h index b9c6e2d3..d6bc79b0 100644 --- a/src/njs_object.h +++ b/src/njs_object.h @@ -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_vm_t *vm, const njs_value_t *name, 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 --git a/src/njs_object_prop.c b/src/njs_object_prop.c index 4b7942cf..a426d854 100644 --- a/src/njs_object_prop.c +++ b/src/njs_object_prop.c @@ -8,8 +8,8 @@ #include -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_value_t *object, 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 njs_value_t *name, 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 njs_value_t *name, 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 njs_value_t *name, 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 njs_value_t *name, 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 njs_value_t *name, 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 njs_value_t *name, 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 --git a/src/njs_parser_terminal.c b/src/njs_parser_terminal.c index a8d002f2..34da344f 100644 --- a/src/njs_parser_terminal.c +++ b/src/njs_parser_terminal.c @@ -19,6 +19,10 @@ static njs_token_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, 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_parser_t *parser, njs_parser_node_t *node, } -/* - * 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_parser_t *parser, njs_parser_node_t *obj) 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_parser_t *parser, njs_parser_node_t *obj) 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_parser_t *parser, njs_parser_node_t *obj) break; default: - if (prop_token != NJS_TOKEN_NAME && !lexer->keyword) { - return NJS_TOKEN_ILLEGAL; - } - - 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 (token != NJS_TOKEN_NAME && !lexer->keyword) { + if (name.length == 0) { + return NJS_TOKEN_ILLEGAL; + } - if (name.length == 0 - || prop_token == NJS_TOKEN_THIS - || prop_token == NJS_TOKEN_GLOBAL_THIS) - { - return NJS_TOKEN_ILLEGAL; + accessor = 0; + break; } - expression = njs_parser_reference(vm, parser, prop_token, &name, - hash, token_line); - if (njs_slow_path(expression == NULL)) { - return NJS_TOKEN_ERROR; + if (accessor) { + property = njs_parser_node_string(vm, parser); + if (njs_slow_path(property == 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_parser_t *parser, njs_parser_node_t *obj) 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_parser_t *parser, njs_parser_node_t *obj) 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; + } - default: - 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_object_property(vm, parser, obj, property, expression); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; + 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; + + 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; + + 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, njs_parser_t *parser, } +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 --git a/src/njs_vmcode.c b/src/njs_vmcode.c index fc07ebf2..ea1dde92 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -86,9 +86,10 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc) 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_char *pc) 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 --git a/src/njs_vmcode.h b/src/njs_vmcode.h index 83a9c1ef..8d4ecdbd 100644 --- a/src/njs_vmcode.h +++ b/src/njs_vmcode.h @@ -40,6 +40,7 @@ typedef uint8_t njs_vmcode_operation_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) @@ -259,6 +260,15 @@ typedef struct { } njs_vmcode_prop_set_t; +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; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 2eb02ead..0ee3e036 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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") }, -- 2.47.3