diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index 9745ee7f48..0cc8a51ee6 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -448,6 +448,45 @@ static const keyword_string_t * const keyword_string_list[9] = #undef LEXER_KEYWORD #undef LEXER_KEYWORD_END +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER +/** + * Checks if current identifier is a keyword or reserved word. + * + * @return true - if the current identifier is a keyword or reserved word + * false - otherwise + */ +bool +lexer_is_identifier_keyword (parser_context_t *context_p) /**< context */ +{ + lexer_lit_location_t *literal_p = &context_p->token.lit_location; + + JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL); + JERRY_ASSERT (literal_p->length <= PARSER_MAXIMUM_IDENT_LENGTH); + + if (literal_p->has_escape || literal_p->length < 2 || literal_p->length > 10) + { + return false; + } + + const keyword_string_t *keyword_p = keyword_string_list[literal_p->length - 2]; + + do + { + if (literal_p->char_p[0] == keyword_p->keyword_p[0] + && literal_p->char_p[1] == keyword_p->keyword_p[1] + && memcmp (literal_p->char_p, keyword_p->keyword_p, literal_p->length) == 0) + { + return true; + } + + keyword_p++; + } + while (keyword_p->type != LEXER_EOS); + + return false; +} /* lexer_is_identifier_keyword */ +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ + /** * Parse identifier. */ @@ -1326,7 +1365,7 @@ lexer_check_colon (parser_context_t *context_p) /**< context */ && context_p->source_p[0] == (uint8_t) LIT_CHAR_COLON); } /* lexer_check_colon */ -#ifndef CONFIG_DISABLE_ES2015_CLASS +#if !defined (CONFIG_DISABLE_ES2015_CLASS) || !defined (CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER) /** * Checks whether the next token is a left parenthesis. * @@ -1343,7 +1382,7 @@ lexer_check_left_paren (parser_context_t *context_p) /**< context */ return (context_p->source_p < context_p->source_end_p && context_p->source_p[0] == (uint8_t) LIT_CHAR_LEFT_PAREN); } /* lexer_check_left_paren */ -#endif /* !CONFIG_DISABLE_ES2015_CLASS */ +#endif /* !CONFIG_DISABLE_ES2015_CLASS || !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION @@ -2271,6 +2310,7 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ #ifndef CONFIG_DISABLE_ES2015_CLASS int is_class_method = ((ident_opts & LEXER_OBJ_IDENT_CLASS_METHOD) && !(ident_opts & LEXER_OBJ_IDENT_ONLY_IDENTIFIERS) + && !(ident_opts & LEXER_OBJ_IDENT_OBJ_METHOD) && (context_p->token.type != LEXER_KEYW_STATIC)); #endif /* !CONFIG_DISABLE_ES2015_CLASS */ @@ -2290,8 +2330,18 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ { lexer_skip_spaces (context_p); - if (context_p->source_p < context_p->source_end_p - && context_p->source_p[0] != LIT_CHAR_COLON) + bool not_end_of_literal = (context_p->source_p < context_p->source_end_p + && context_p->source_p[0] != LIT_CHAR_COLON); + +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + not_end_of_literal = (not_end_of_literal + && context_p->source_p[0] != LIT_CHAR_RIGHT_BRACE + && context_p->source_p[0] != LIT_CHAR_COMMA + /* Shorthand notation allows methods named `get` and allows getters. */ + && !lexer_check_left_paren (context_p)); +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ + + if (not_end_of_literal) { if (lexer_compare_raw_identifier_to_current (context_p, "get", 3)) { @@ -2346,6 +2396,12 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ context_p->column++; return; } +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + else if (context_p->source_p[0] == LIT_CHAR_LEFT_PAREN) + { + create_literal_object = true; + } +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ else { const uint8_t *char_p = context_p->source_p; @@ -2365,6 +2421,16 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ } } +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + if (!(ident_opts & LEXER_OBJ_IDENT_ONLY_IDENTIFIERS) + && (ident_opts & LEXER_OBJ_IDENT_OBJ_METHOD) + && context_p->source_p[0] == LIT_CHAR_LEFT_PAREN) + { + context_p->token.type = LEXER_PROPERTY_METHOD; + return; + } +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ + if (create_literal_object) { #ifndef CONFIG_DISABLE_ES2015_CLASS diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index 4dee7551a5..95c074ac3c 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -147,6 +147,7 @@ typedef enum LEXER_EXPRESSION_START, /**< expression start */ LEXER_PROPERTY_GETTER, /**< property getter function */ LEXER_PROPERTY_SETTER, /**< property setter function */ + LEXER_PROPERTY_METHOD, /**< property method (ES6+) */ LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */ LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */ LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */ @@ -216,6 +217,7 @@ typedef enum LEXER_OBJ_IDENT_NO_OPTS = (1u << 0), /**< no options */ LEXER_OBJ_IDENT_ONLY_IDENTIFIERS = (1u << 1), /**< only identifiers are accepted */ LEXER_OBJ_IDENT_CLASS_METHOD = (1u << 2), /**< expect identifier inside a class body */ + LEXER_OBJ_IDENT_OBJ_METHOD = (1u << 3), /**< expect method identifier inside object */ } lexer_obj_ident_opts_t; /** diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index ef39413d12..7e2bdf8570 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -260,7 +260,6 @@ parser_parse_array_literal (parser_context_t *context_p) /**< context */ } } /* parser_parse_array_literal */ -#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER /** * Object literal item types. */ @@ -357,7 +356,6 @@ parser_append_object_literal_item (parser_context_t *context_p, /**< context */ context_p->stack_top_uint8 = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS; } } /* parser_append_object_literal_item */ -#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ #ifndef CONFIG_DISABLE_ES2015_CLASS @@ -649,41 +647,58 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ while (true) { +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_OBJ_METHOD); +#else /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS); +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } - if (context_p->token.type == LEXER_PROPERTY_GETTER - || context_p->token.type == LEXER_PROPERTY_SETTER) + bool token_type_is_method = (context_p->token.type == LEXER_PROPERTY_GETTER + || context_p->token.type == LEXER_PROPERTY_SETTER); + +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + token_type_is_method = token_type_is_method || context_p->token.type == LEXER_PROPERTY_METHOD; +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ + + if (token_type_is_method) { uint32_t status_flags; - cbc_ext_opcode_t opcode; + uint16_t opcode; uint16_t literal_index, function_literal_index; -#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER parser_object_literal_item_types_t item_type; -#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ + lexer_token_type_t token_type = context_p->token.type; - if (context_p->token.type == LEXER_PROPERTY_GETTER) + if (token_type == LEXER_PROPERTY_GETTER) { status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_GETTER; - opcode = CBC_EXT_SET_GETTER; -#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_GETTER); item_type = PARSER_OBJECT_PROPERTY_GETTER; -#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ } - else +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + else if (token_type == LEXER_PROPERTY_METHOD) + { + status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_FUNC_EXPRESSION; + opcode = CBC_SET_LITERAL_PROPERTY; + item_type = PARSER_OBJECT_PROPERTY_VALUE; + } +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ + else /* token_type == LEXER_PROPERTY_SETTER */ { status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_SETTER; - opcode = CBC_EXT_SET_SETTER; -#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_SETTER); item_type = PARSER_OBJECT_PROPERTY_SETTER; -#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ } +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJ_METHOD); +#else /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ /* This assignment is a nop for computed getters/setters. */ literal_index = context_p->lit_object.index; @@ -694,6 +709,11 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ opcode = ((opcode == CBC_EXT_SET_GETTER) ? CBC_EXT_SET_COMPUTED_GETTER : CBC_EXT_SET_COMPUTED_SETTER); } + + if (opcode == CBC_SET_LITERAL_PROPERTY) + { + parser_append_object_literal_item (context_p, literal_index, item_type); + } #else /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ parser_append_object_literal_item (context_p, literal_index, item_type); #endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ @@ -706,6 +726,13 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ { literal_index = function_literal_index; } + /* Property methods aren't extended opcodes, so swap the values here. */ + else if (opcode == CBC_SET_LITERAL_PROPERTY) + { + literal_index ^= function_literal_index; + function_literal_index ^= literal_index; + literal_index ^= function_literal_index; + } #endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ parser_emit_cbc_literal (context_p, @@ -713,7 +740,7 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ literal_index); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); - context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); + context_p->last_cbc_opcode = opcode; context_p->last_cbc.value = function_literal_index; lexer_next_token (context_p); @@ -751,13 +778,35 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ #endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ lexer_next_token (context_p); - if (context_p->token.type != LEXER_COLON) + + if (context_p->token.type == LEXER_COLON) { - parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); } +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + else if (context_p->token.type == LEXER_COMMA + || context_p->token.type == LEXER_RIGHT_BRACE) + { + // TODO somehow clean this up -- is it possible to parse and check for keywords by this step? + lexer_lit_location_t prop_name_literal = context_p->token.lit_location; - lexer_next_token (context_p); - parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + if (prop_name_literal.type != LEXER_IDENT_LITERAL || lexer_is_identifier_keyword (context_p)) + { + parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); + } + + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + context_p->token.lit_location.type); + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); + } +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ + else + { + parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); + } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index ae4ff030dc..e53e4f6b6e 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -433,10 +433,15 @@ void parser_set_continues_to_current_position (parser_context_t *context_p, pars /* Lexer functions */ +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER +bool lexer_is_identifier_keyword (parser_context_t *context_p); +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ void lexer_next_token (parser_context_t *context_p); bool lexer_check_colon (parser_context_t *context_p); -#ifndef CONFIG_DISABLE_ES2015_CLASS +#if !defined (CONFIG_DISABLE_ES2015_CLASS) || !defined (CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER) bool lexer_check_left_paren (parser_context_t *context_p); +#endif /* !CONFIG_DISABLE_ES2015_CLASS || !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ +#ifndef CONFIG_DISABLE_ES2015_CLASS void lexer_skip_empty_statements (parser_context_t *context_p); #endif /* !CONFIG_DISABLE_ES2015_CLASS */ #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION diff --git a/jerry-core/parser/js/js-parser-scanner.c b/jerry-core/parser/js/js-parser-scanner.c index 3352c03d1b..a94f31ed40 100644 --- a/jerry-core/parser/js/js-parser-scanner.c +++ b/jerry-core/parser/js/js-parser-scanner.c @@ -763,16 +763,16 @@ parser_scan_until (parser_context_t *context_p, /**< context */ } case SCAN_MODE_FUNCTION_ARGUMENTS: { + const bool correct_stack_top = (stack_top == SCAN_STACK_BLOCK_STATEMENT + || stack_top == SCAN_STACK_BLOCK_EXPRESSION #ifndef CONFIG_DISABLE_ES2015_CLASS - JERRY_ASSERT (stack_top == SCAN_STACK_BLOCK_STATEMENT - || stack_top == SCAN_STACK_BLOCK_EXPRESSION - || stack_top == SCAN_STACK_CLASS - || stack_top == SCAN_STACK_BLOCK_PROPERTY); -#else /* CONFIG_DISABLE_ES2015_CLASS */ - JERRY_ASSERT (stack_top == SCAN_STACK_BLOCK_STATEMENT - || stack_top == SCAN_STACK_BLOCK_EXPRESSION - || stack_top == SCAN_STACK_BLOCK_PROPERTY); + || stack_top == SCAN_STACK_CLASS #endif /* !CONFIG_DISABLE_ES2015_CLASS */ +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZERS + || stack_top == SCAN_STACK_OBJECT_LITERAL +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZERS */ + || stack_top == SCAN_STACK_BLOCK_PROPERTY); + JERRY_ASSERT (correct_stack_top); if (context_p->token.type == LEXER_LITERAL && (context_p->token.lit_location.type == LEXER_IDENT_LITERAL @@ -834,7 +834,8 @@ parser_scan_until (parser_context_t *context_p, /**< context */ } if (context_p->token.type == LEXER_PROPERTY_GETTER - || context_p->token.type == LEXER_PROPERTY_SETTER) + || context_p->token.type == LEXER_PROPERTY_SETTER + || context_p->token.type == LEXER_PROPERTY_METHOD) { parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_PROPERTY); mode = SCAN_MODE_FUNCTION_ARGUMENTS; @@ -842,7 +843,16 @@ parser_scan_until (parser_context_t *context_p, /**< context */ } lexer_next_token (context_p); - if (context_p->token.type != LEXER_COLON) + + bool not_end_of_property_name = context_p->token.type != LEXER_COLON; + +#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER + not_end_of_property_name = (not_end_of_property_name + && context_p->token.type != LEXER_RIGHT_BRACE + && context_p->token.type != LEXER_COMMA); +#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ + + if (not_end_of_property_name) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } diff --git a/jerry-core/profiles/README.md b/jerry-core/profiles/README.md index ec576048f3..abd27a6180 100644 --- a/jerry-core/profiles/README.md +++ b/jerry-core/profiles/README.md @@ -96,4 +96,4 @@ In JerryScript all of the features are enabled by default, so an empty profile f * `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`: Disable the [ArrayBuffer](http://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-objects) and [TypedArray](http://www.ecma-international.org/ecma-262/6.0/#sec-typedarray-objects) built-ins. * `CONFIG_DISABLE_ES2015`: Disable all of the implemented [ECMAScript2015 features](http://www.ecma-international.org/ecma-262/6.0/). - (equivalent to `CONFIG_DISABLE_ES2015_ARROW_FUNCTION`, `CONFIG_DISABLE_ES2015_BUILTIN`, `CONFIG_DISABLE_ES2015_CLASS`, `CONFIG_DISABLE_ES2015_PROMISE_BUILTIN`, `CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS`, and `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`). + (equivalent to `CONFIG_DISABLE_ES2015_ARROW_FUNCTION`, `CONFIG_DISABLE_ES2015_BUILTIN`, `CONFIG_DISABLE_ES2015_CLASS`, `CONFIG_DISABLE_ES2015_PROMISE_BUILTIN`, `CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS`, `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`, and `CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER`). diff --git a/tests/jerry/es2015/object-literal-shorthand-notation.js b/tests/jerry/es2015/object-literal-shorthand-notation.js new file mode 100644 index 0000000000..f22ef6f26b --- /dev/null +++ b/tests/jerry/es2015/object-literal-shorthand-notation.js @@ -0,0 +1,125 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var x = 1, y = 2, z = 3; +var obj = { x, y, z: { z } }; +assert(obj.x === 1); +assert(obj.y === 2); +assert(obj.z.z === 3); + +try { + var a = { get, set }; + assert(false); +} catch (e) { + assert(e instanceof ReferenceError); +} + +function checkReservedAsVars() { + var get = 1, set = 2; + var a = { get, set }; + assert(a.get + a.set === 3); +} +checkReservedAsVars(); + +var one = 1; +assert({ one, one }.one === 1); +assert({ one: 0, one }.one === 1); +assert({ one, one: 0 }.one === 0); + +var obj2 = { one }; +assert({ obj2 }.obj2.one === 1); + +try { + eval('({ true, false, null })'); + assert(false); +} catch (e) { + assert(e instanceof SyntaxError); +} + +try { + eval('({ 1 })'); + assert(false); +} catch (e) { + assert(e instanceof SyntaxError); +} + +try { + eval('({ "foo" })'); + assert(false); +} catch (e) { + assert(e instanceof SyntaxError); +} + +try { + eval('({ {} })'); + assert(false); +} catch (e) { + assert(e instanceof SyntaxError); +} + +try { + eval('({ 1, "foo", {} })'); + assert(false); +} catch (e) { + assert(e instanceof SyntaxError); +} + +try { + eval('({ static foo() {} })'); + assert(false); +} catch (e) { + assert(e instanceof SyntaxError); +} + +var obj3 = { f() { return 1; } }; +assert(obj3.f() === 1); + +var obj4 = { one, one() { return one; } }; +assert(typeof obj4.one === 'function'); +assert(obj4.one() === 1); + +var obj5 = { x: 123, getX() { return this.x; } }; +assert(obj5.getX() === 123); + +var obj6 = { + if() { return 1; }, else() { return 1; }, try() { return 1; }, catch() { return 1; }, + finally() { return 1; }, let() { return 1; }, true() { return 1; }, false() { return 1; }, + null() { return 1; }, function() { return 1; } +}; +assert( + obj6.if() + obj6.else() + obj6.try() + obj6.catch() + obj6.finally() + obj6.let() + + obj6.true() + obj6.false() + obj6.null() + obj6.function() === 10 +); + +var obj7 = { + _x: 0, + + get x() { + return thix._x; + }, + + set x(x_value) { + this._x = x_value; + } +}; +assert(obj7._x === 7); +assert(obj7.x === 7); +obj7.x = 1; +assert(obj7._x === 1); +assert(obj7.x === 1); + +var obj8 = { + constructor() { return 1; }, static() { return 1; }, get() { return 1; }, set() { return 1; } +}; +assert(obj8.constructor() + obj8.static() + obj8.get() + obj8.set() === 4);