diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index e2a26ce4d..398104465 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -192,7 +192,8 @@ ecma_gc_mark_properties (ecma_property_pair_t *property_pair_p) /**< property pa case ECMA_PROPERTY_TYPE_INTERNAL: { JERRY_ASSERT (ECMA_PROPERTY_GET_NAME_TYPE (property) == ECMA_DIRECT_STRING_MAGIC - && property_pair_p->names_cp[index] >= LIT_FIRST_INTERNAL_MAGIC_STRING); + && property_pair_p->names_cp[index] >= LIT_INTERNAL_MAGIC_STRING_FIRST_DATA + && property_pair_p->names_cp[index] < LIT_MAGIC_STRING__COUNT); break; } default: @@ -1054,33 +1055,51 @@ ecma_gc_free_properties (ecma_object_t *object_p) /**< object */ ecma_property_t *property_p = (ecma_property_t *) (prop_iter_p->types + i); jmem_cpointer_t name_cp = prop_pair_p->names_cp[i]; - if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_MAGIC) + if (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_INTERNAL) { + JERRY_ASSERT (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_MAGIC); + /* Call the native's free callback. */ - if (JERRY_UNLIKELY (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER)) + switch (name_cp) { - ecma_gc_free_native_pointer (property_p); - } #if ENABLED (JERRY_BUILTIN_WEAKMAP) || ENABLED (JERRY_BUILTIN_WEAKSET) - else if (JERRY_UNLIKELY (name_cp == LIT_INTERNAL_MAGIC_STRING_WEAK_REFS)) - { - ecma_collection_t *refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, - ECMA_PROPERTY_VALUE_PTR (property_p)->value); - for (uint32_t j = 0; j < refs_p->item_count; j++) + case LIT_INTERNAL_MAGIC_STRING_WEAK_REFS: { - const ecma_value_t value = refs_p->buffer_p[j]; - if (!ecma_is_value_empty (value)) + ecma_collection_t *refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + prop_pair_p->values[i].value); + for (uint32_t j = 0; j < refs_p->item_count; j++) { - ecma_object_t *container_p = ecma_get_object_from_value (value); + const ecma_value_t value = refs_p->buffer_p[j]; + if (!ecma_is_value_empty (value)) + { + ecma_object_t *container_p = ecma_get_object_from_value (value); - ecma_op_container_remove_weak_entry (container_p, - ecma_make_object_value (object_p)); + ecma_op_container_remove_weak_entry (container_p, + ecma_make_object_value (object_p)); + } } - } - ecma_collection_destroy (refs_p); - } + ecma_collection_destroy (refs_p); + break; + } #endif /* ENABLED (JERRY_BUILTIN_WEAKMAP) || ENABLED (JERRY_BUILTIN_WEAKSET) */ +#if ENABLED (JERRY_ESNEXT) + case LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED: + { + ecma_value_t *compact_collection_p; + compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, + prop_pair_p->values[i].value); + ecma_compact_collection_free (compact_collection_p); + break; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + default: + { + JERRY_ASSERT (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER); + ecma_gc_free_native_pointer (property_p); + break; + } + } } if (prop_iter_p->types[i] != ECMA_PROPERTY_TYPE_DELETED) diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 92ad89d44..af4591697 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -113,17 +113,21 @@ typedef enum ECMA_PARSE_DIRECT_EVAL = (1u << 3), /**< eval is called directly (ECMA-262 v5, 15.1.2.1.1) */ ECMA_PARSE_CLASS_CONSTRUCTOR = (1u << 4), /**< a class constructor is being parsed */ - /* These four status flags must be in this order. The first three are also parser status flags. + /* These five status flags must be in this order. The first four are also parser status flags. * See PARSER_SAVE_STATUS_FLAGS / PARSER_RESTORE_STATUS_FLAGS. */ ECMA_PARSE_ALLOW_SUPER = (1u << 5), /**< allow super property access */ ECMA_PARSE_ALLOW_SUPER_CALL = (1u << 6), /**< allow super constructor call */ - ECMA_PARSE_ALLOW_NEW_TARGET = (1u << 7), /**< allow new.target access */ - ECMA_PARSE_FUNCTION_CONTEXT = (1u << 8), /**< function context is present (ECMA_PARSE_DIRECT_EVAL must be set) */ + ECMA_PARSE_INSIDE_CLASS_FIELD = (1u << 7), /**< a class field is being parsed */ + ECMA_PARSE_ALLOW_NEW_TARGET = (1u << 8), /**< allow new.target access */ + ECMA_PARSE_FUNCTION_CONTEXT = (1u << 9), /**< function context is present (ECMA_PARSE_DIRECT_EVAL must be set) */ - ECMA_PARSE_GENERATOR_FUNCTION = (1u << 9), /**< generator function is parsed */ - ECMA_PARSE_ASYNC_FUNCTION = (1u << 10), /**< async function is parsed */ + ECMA_PARSE_GENERATOR_FUNCTION = (1u << 10), /**< generator function is parsed */ + ECMA_PARSE_ASYNC_FUNCTION = (1u << 11), /**< async function is parsed */ /* These flags are internally used by the parser. */ +#if ENABLED (JERRY_ESNEXT) + ECMA_PARSE_INTERNAL_PRE_SCANNING = (1u << 12), +#endif /* ENABLED (JERRY_ESNEXT) */ #ifndef JERRY_NDEBUG /** * This flag represents an error in for in/of statements, which cannot be set @@ -1439,6 +1443,17 @@ typedef struct */ #define ECMA_COLLECTION_INITIAL_SIZE ECMA_COLLECTION_ALLOCATED_SIZE (ECMA_COLLECTION_INITIAL_CAPACITY) +/** + * Size shift of a compact collection + */ +#define ECMA_COMPACT_COLLECTION_SIZE_SHIFT 3 + +/** + * Get the size of the compact collection + */ +#define ECMA_COMPACT_COLLECTION_GET_SIZE(compact_collection_p) \ + ((compact_collection_p)[0] >> ECMA_COMPACT_COLLECTION_SIZE_SHIFT) + /** * Direct string types (2 bit). */ diff --git a/jerry-core/ecma/base/ecma-helpers-collection.c b/jerry-core/ecma/base/ecma-helpers-collection.c index 96ad4dadf..34a16a370 100644 --- a/jerry-core/ecma/base/ecma-helpers-collection.c +++ b/jerry-core/ecma/base/ecma-helpers-collection.c @@ -248,6 +248,134 @@ ecma_collection_has_string_value (ecma_collection_t *collection_p, /**< collecti return false; } /* ecma_collection_has_string_value */ +/** + * Initial capacity of an ecma-collection + */ +#define ECMA_COMPACT_COLLECTION_GROWTH 8 + +/** + * Set the size of the compact collection + */ +#define ECMA_COMPACT_COLLECTION_SET_SIZE(compact_collection_p, item_count, unused_items) \ + ((compact_collection_p)[0] = (((item_count) << ECMA_COMPACT_COLLECTION_SIZE_SHIFT) | (unused_items))) + +/** + * Set the size of the compact collection + */ +#define ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT(compact_collection_p) \ + ((compact_collection_p)[0] & ((1 << ECMA_COMPACT_COLLECTION_SIZE_SHIFT) - 1)) + +/** + * Allocate a compact collection of ecma values + * + * @return pointer to the compact collection + */ +ecma_value_t * +ecma_new_compact_collection (void) +{ + size_t size = (ECMA_COMPACT_COLLECTION_GROWTH / 2) * sizeof (ecma_value_t); + ecma_value_t *compact_collection_p = (ecma_value_t *) jmem_heap_alloc_block (size); + + ECMA_COMPACT_COLLECTION_SET_SIZE (compact_collection_p, + ECMA_COMPACT_COLLECTION_GROWTH / 2, + (ECMA_COMPACT_COLLECTION_GROWTH / 2) - 1); + return compact_collection_p; +} /* ecma_new_compact_collection */ + +/** + * Append a value to the compact collection + * + * @return updated pointer to the compact collection + */ +ecma_value_t * +ecma_compact_collection_push_back (ecma_value_t *compact_collection_p, /**< compact collection */ + ecma_value_t value) /**< ecma value to append */ +{ + ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); + ecma_value_t unused_items = ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT (compact_collection_p); + + if (unused_items > 0) + { + compact_collection_p[size - unused_items] = value; + (*compact_collection_p)--; + return compact_collection_p; + } + + if (size == ECMA_COMPACT_COLLECTION_GROWTH / 2) + { + size_t old_size = (ECMA_COMPACT_COLLECTION_GROWTH / 2) * sizeof (ecma_value_t); + size_t new_size = ECMA_COMPACT_COLLECTION_GROWTH * sizeof (ecma_value_t); + compact_collection_p = jmem_heap_realloc_block (compact_collection_p, old_size, new_size); + + compact_collection_p[ECMA_COMPACT_COLLECTION_GROWTH / 2] = value; + + ECMA_COMPACT_COLLECTION_SET_SIZE (compact_collection_p, + ECMA_COMPACT_COLLECTION_GROWTH, + (ECMA_COMPACT_COLLECTION_GROWTH / 2) - 1); + return compact_collection_p; + } + + size_t old_size = size * sizeof (ecma_value_t); + size_t new_size = old_size + (ECMA_COMPACT_COLLECTION_GROWTH * sizeof (ecma_value_t)); + + compact_collection_p = jmem_heap_realloc_block (compact_collection_p, old_size, new_size); + compact_collection_p[size] = value; + + ECMA_COMPACT_COLLECTION_SET_SIZE (compact_collection_p, + size + ECMA_COMPACT_COLLECTION_GROWTH, + ECMA_COMPACT_COLLECTION_GROWTH - 1); + return compact_collection_p; +} /* ecma_compact_collection_push_back */ + +/** + * Discard the unused elements of a compact collection + * + * Note: + * further items should not be added after this call + * + * @return updated pointer to the compact collection + */ +ecma_value_t * +ecma_compact_collection_shrink (ecma_value_t *compact_collection_p) /**< compact collection */ +{ + ecma_value_t unused_items = ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT (compact_collection_p); + + if (unused_items == 0) + { + return compact_collection_p; + } + + ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); + + size_t old_size = size * sizeof (ecma_value_t); + size_t new_size = (size - unused_items) * sizeof (ecma_value_t); + + compact_collection_p = jmem_heap_realloc_block (compact_collection_p, old_size, new_size); + + ECMA_COMPACT_COLLECTION_SET_SIZE (compact_collection_p, size - unused_items, 0); + return compact_collection_p; +} /* ecma_compact_collection_shrink */ + +/** + * Free a compact collection + */ +void +ecma_compact_collection_free (ecma_value_t *compact_collection_p) /**< compact collection */ +{ + ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); + ecma_value_t unused_items = ECMA_COMPACT_COLLECTION_GET_UNUSED_ITEM_COUNT (compact_collection_p); + + ecma_value_t *end_p = compact_collection_p + size - unused_items; + ecma_value_t *current_p = compact_collection_p + 1; + + while (current_p < end_p) + { + ecma_free_value (*current_p++); + } + + jmem_heap_free_block (compact_collection_p, size * sizeof (ecma_value_t)); +} /* ecma_compact_collection_free */ + /** * @} * @} diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index cce126483..3f30ab366 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -784,7 +784,8 @@ ecma_free_property (ecma_object_t *object_p, /**< object the property belongs to /* Must be a native pointer. */ JERRY_ASSERT (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_MAGIC - && name_cp >= LIT_FIRST_INTERNAL_MAGIC_STRING); + && name_cp >= LIT_INTERNAL_MAGIC_STRING_FIRST_DATA + && name_cp < LIT_MAGIC_STRING__COUNT); break; } } diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index cdb350e42..03a83ebd4 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -464,6 +464,11 @@ void ecma_collection_free_objects (ecma_collection_t *collection_p); bool ecma_collection_check_duplicated_entries (ecma_collection_t *collection_p); bool ecma_collection_has_string_value (ecma_collection_t *collection_p, ecma_string_t *string_p); +ecma_value_t *ecma_new_compact_collection (void); +ecma_value_t *ecma_compact_collection_push_back (ecma_value_t *compact_collection_p, ecma_value_t value); +ecma_value_t *ecma_compact_collection_shrink (ecma_value_t *compact_collection_p); +void ecma_compact_collection_free (ecma_value_t *compact_collection_p); + /* ecma-helpers.c */ ecma_object_t *ecma_create_object (ecma_object_t *prototype_object_p, size_t ext_object_size, ecma_object_type_t type); ecma_object_t *ecma_create_decl_lex_env (ecma_object_t *outer_lexical_environment_p); diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 9d50248d9..ddebbd73d 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -137,6 +137,9 @@ struct jerry_context_t #endif /* ENABLED (JERRY_CPOINTER_32_BIT) */ const lit_utf8_byte_t * const *lit_magic_string_ex_array; /**< array of external magic strings */ const lit_utf8_size_t *lit_magic_string_ex_sizes; /**< external magic string lengths */ +#if ENABLED (JERRY_ESNEXT) + ecma_value_t *computed_class_fields_p; /**< names of the computed class fields */ +#endif /* ENABLED (JERRY_ESNEXT) */ jmem_cpointer_t string_list_first_cp; /**< first item of the literal string list */ #if ENABLED (JERRY_ESNEXT) jmem_cpointer_t symbol_list_first_cp; /**< first item of the global symbol list */ diff --git a/jerry-core/lit/lit-magic-strings.h b/jerry-core/lit/lit-magic-strings.h index 8baab1cce..db1420bb0 100644 --- a/jerry-core/lit/lit-magic-strings.h +++ b/jerry-core/lit/lit-magic-strings.h @@ -61,16 +61,16 @@ typedef enum LIT_GLOBAL_SYMBOL_TO_STRING_TAG, /**< @@toStringTag well known symbol */ LIT_GLOBAL_SYMBOL_UNSCOPABLES, /**< @@unscopables well known symbol */ LIT_GLOBAL_SYMBOL__LAST = LIT_GLOBAL_SYMBOL_UNSCOPABLES, /**< last global symbol */ - LIT_GC_MARK_REQUIRED_MAGIC_STRING__COUNT, /**< number of internal magic strings which will be used as - * property names, and their values need to be marked during gc. */ - LIT_INTERNAL_MAGIC_STRING_DELETED = LIT_GC_MARK_REQUIRED_MAGIC_STRING__COUNT, /**< special value for - * deleted properties */ + + LIT_INTERNAL_MAGIC_STRING_DELETED, /**< special value for deleted properties */ + LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT, /**< Internal object ID for internal properties */ + LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_INIT, /**< function which initializes properties */ LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< native pointer info associated with an object */ - LIT_FIRST_INTERNAL_MAGIC_STRING = LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< first index of internal - * magic strings */ + LIT_INTERNAL_MAGIC_STRING_FIRST_DATA = LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< first index of special + * data properties */ LIT_INTERNAL_MAGIC_STRING_WEAK_REFS, /**< Weak references to the current object */ - LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT, /**< Internal object ID for internal properties */ + LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED, /**< computed class field name list */ LIT_MAGIC_STRING__COUNT /**< number of magic strings */ } lit_magic_string_id_t; diff --git a/jerry-core/parser/js/byte-code.c b/jerry-core/parser/js/byte-code.c index 38b5bc205..36158a88f 100644 --- a/jerry-core/parser/js/byte-code.c +++ b/jerry-core/parser/js/byte-code.c @@ -27,7 +27,7 @@ JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) % sizeof (jmem_cpointer_t) */ JERRY_STATIC_ASSERT (CBC_END == 238, number_of_cbc_opcodes_changed); -JERRY_STATIC_ASSERT (CBC_EXT_END == 135, +JERRY_STATIC_ASSERT (CBC_EXT_END == 140, number_of_cbc_ext_opcodes_changed); #if ENABLED (JERRY_PARSER) diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index ce6ff9fba..9f76efe48 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -634,6 +634,8 @@ VM_OC_SET_GETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_STACK_STACK) \ CBC_OPCODE (CBC_EXT_SET_COMPUTED_SETTER, CBC_NO_FLAG, -2, \ VM_OC_SET_SETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_STACK_STACK) \ + CBC_OPCODE (CBC_EXT_SET_STATIC_PROPERTY, CBC_HAS_LITERAL_ARG, -1, \ + VM_OC_SET_PROPERTY | VM_OC_GET_STACK_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_STATIC_PROPERTY_LITERAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ VM_OC_SET_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_STATIC_COMPUTED_PROPERTY, CBC_NO_FLAG, -2, \ @@ -648,6 +650,8 @@ VM_OC_SET_SETTER | VM_OC_GET_STACK_STACK) \ CBC_OPCODE (CBC_EXT_SET__PROTO__, CBC_NO_FLAG, -1, \ VM_OC_SET__PROTO__ | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_EXT_ADD_COMPUTED_FIELD, CBC_NO_FLAG, -1, \ + VM_OC_ADD_COMPUTED_FIELD | VM_OC_GET_STACK) \ \ /* Class related opcodes. */ \ CBC_OPCODE (CBC_EXT_PUSH_NAMED_CLASS_ENV, CBC_HAS_LITERAL_ARG, 1, \ @@ -664,6 +668,12 @@ VM_OC_FINALIZE_CLASS | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_FINALIZE_ANONYMOUS_CLASS, CBC_NO_FLAG, -2, \ VM_OC_FINALIZE_CLASS) \ + CBC_OPCODE (CBC_EXT_SET_CLASS_FIELD_INIT, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_SET_CLASS_FIELD_INIT | VM_OC_GET_LITERAL) \ + CBC_OPCODE (CBC_EXT_RUN_CLASS_FIELD_INIT, CBC_NO_FLAG, 0, \ + VM_OC_RUN_CLASS_FIELD_INIT) \ + CBC_OPCODE (CBC_EXT_SET_NEXT_COMPUTED_FIELD, CBC_NO_FLAG, -1, \ + VM_OC_SET_NEXT_COMPUTED_FIELD | VM_OC_PUT_REFERENCE) \ CBC_OPCODE (CBC_EXT_PUSH_SUPER, CBC_NO_FLAG, 1, \ VM_OC_NONE) \ CBC_OPCODE (CBC_EXT_PUSH_SUPER_CONSTRUCTOR, CBC_NO_FLAG, 1, \ diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index e861ecda9..efcf9238a 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -433,6 +433,7 @@ lexer_skip_spaces (parser_context_t *context_p) /**< context */ } /* lexer_skip_spaces */ #if ENABLED (JERRY_ESNEXT) + /** * Skip all the continuous empty statements. */ @@ -450,6 +451,22 @@ lexer_skip_empty_statements (parser_context_t *context_p) /**< context */ context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); } /* lexer_skip_empty_statements */ + +#endif /* ENABLED (JERRY_ESNEXT) */ + +#if ENABLED (JERRY_ESNEXT) +/** + * Checks whether the keyword has escape sequences. + */ +#define LEXER_CHECK_INVALID_KEYWORD(ident_start_p, buffer_p) \ + (JERRY_UNLIKELY ((ident_start_p) == (buffer_p)) \ + && !(context_p->global_status_flags & ECMA_PARSE_INTERNAL_PRE_SCANNING)) +#else /* !ENABLED (JERRY_ESNEXT) */ +/** + * Checks whether the keyword has escape sequences. + */ +#define LEXER_CHECK_INVALID_KEYWORD(ident_start_p, buffer_p) \ + (JERRY_UNLIKELY ((ident_start_p) == (buffer_p))) #endif /* ENABLED (JERRY_ESNEXT) */ /** @@ -807,7 +824,6 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ JERRY_ASSERT (length > 0); context_p->token.type = LEXER_LITERAL; - context_p->token.keyword_type = LEXER_EOS; context_p->token.lit_location.type = LEXER_IDENT_LITERAL; context_p->token.lit_location.has_escape = has_escape; @@ -865,7 +881,7 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ if (context_p->status_flags & PARSER_DISALLOW_AWAIT_YIELD) { - if (ident_start_p == buffer_p) + if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } @@ -877,7 +893,7 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_ESNEXT) */ - if (ident_start_p == buffer_p) + if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p)) { /* Escape sequences are not allowed in a keyword. */ parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); @@ -890,7 +906,7 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ESNEXT) if (keyword_p->type == LEXER_KEYW_LET && (context_p->status_flags & PARSER_IS_STRICT)) { - if (ident_start_p == buffer_p) + if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } @@ -903,7 +919,7 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ { if (context_p->status_flags & PARSER_DISALLOW_AWAIT_YIELD) { - if (ident_start_p == buffer_p) + if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } @@ -913,6 +929,11 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ context_p->token.type = (uint8_t) LEXER_KEYW_YIELD; break; } + + if (keyword_p->type == LEXER_KEYW_ARGUMENTS && (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD)) + { + parser_raise_error (context_p, PARSER_ERR_ARGUMENTS_IN_CLASS_FIELD); + } #endif /* ENABLED (JERRY_ESNEXT) */ if (keyword_p->type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD @@ -944,6 +965,8 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ return true; } /* lexer_parse_identifier */ +#undef LEXER_CHECK_INVALID_KEYWORD + /** * Parse string. */ @@ -1118,9 +1141,6 @@ lexer_parse_string (parser_context_t *context_p, /**< context */ uint32_t escape_length = (*source_p == LIT_CHAR_LOWERCASE_X) ? 3 : 5; lit_code_point_t code_point = UINT32_MAX; - context_p->token.line = line; - context_p->token.column = (parser_line_counter_t) (column - 1); - #if ENABLED (JERRY_ESNEXT) if (source_p + 4 <= source_end_p && source_p[0] == LIT_CHAR_LOWERCASE_U @@ -1142,6 +1162,8 @@ lexer_parse_string (parser_context_t *context_p, /**< context */ if (code_point == UINT32_MAX) { + context_p->token.line = line; + context_p->token.column = (parser_line_counter_t) (column - 1); parser_raise_error (context_p, PARSER_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE); } @@ -1312,7 +1334,6 @@ lexer_parse_number (parser_context_t *context_p) /**< context */ size_t length; context_p->token.type = LEXER_LITERAL; - context_p->token.keyword_type = LEXER_EOS; context_p->token.extra_value = LEXER_NUMBER_DECIMAL; context_p->token.lit_location.char_p = source_p; context_p->token.lit_location.type = LEXER_NUMBER_LITERAL; @@ -1571,6 +1592,7 @@ lexer_next_token (parser_context_t *context_p) /**< context */ lexer_skip_spaces (context_p); + context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; @@ -2079,6 +2101,33 @@ lexer_consume_generator (parser_context_t *context_p) /**< context */ return true; } /* lexer_consume_generator */ +/** + * Checks whether the next token is an equal sign and consumes it. + * + * @return true if the next token is an equal sign + */ +bool +lexer_consume_assign (parser_context_t *context_p) /**< context */ +{ + if (!(context_p->token.flags & LEXER_NO_SKIP_SPACES)) + { + lexer_skip_spaces (context_p); + context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); + } + + if (context_p->source_p >= context_p->source_end_p + || context_p->source_p[0] != LIT_CHAR_EQUALS + || (context_p->source_p + 1 < context_p->source_end_p + && (context_p->source_p[1] == LIT_CHAR_EQUALS || context_p->source_p[1] == LIT_CHAR_GREATER_THAN))) + { + return false; + } + + lexer_consume_next_character (context_p); + context_p->token.type = LEXER_ASSIGN; + return true; +} /* lexer_consume_assign */ + /** * Update await / yield keywords after an arrow function with expression. */ @@ -2409,6 +2458,27 @@ lexer_convert_literal_to_chars (parser_context_t *context_p, /**< context */ return destination_start_p; } /* lexer_convert_literal_to_chars */ +/** + * Construct an unused literal. + * + * @return a newly allocated literal + */ +lexer_literal_t * +lexer_construct_unused_literal (parser_context_t *context_p) /**< context */ +{ + lexer_literal_t *literal_p; + + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + literal_p->type = LEXER_UNUSED_LITERAL; + literal_p->status_flags = 0; + return literal_p; +} /* lexer_construct_unused_literal */ + /** * Construct a literal object from an identifier. */ @@ -2755,34 +2825,32 @@ lexer_construct_function_object (parser_context_t *context_p, /**< context */ lexer_literal_t *literal_p; uint16_t result_index; - if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) - { - parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); - } - - parser_flush_cbc (context_p); - if (context_p->status_flags & PARSER_INSIDE_WITH) { extra_status_flags |= PARSER_INSIDE_WITH; } - literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); - literal_p->type = LEXER_UNUSED_LITERAL; - literal_p->status_flags = 0; - + literal_p = lexer_construct_unused_literal (context_p); result_index = context_p->literal_count; context_p->literal_count++; + parser_flush_cbc (context_p); + #if ENABLED (JERRY_ESNEXT) - if (!(extra_status_flags & PARSER_IS_ARROW_FUNCTION)) + if (JERRY_LIKELY (!(extra_status_flags & PARSER_IS_ARROW_FUNCTION))) { compiled_code_p = parser_parse_function (context_p, extra_status_flags); } - else + else if (JERRY_LIKELY (!(extra_status_flags & PARSER_CLASS_CONSTRUCTOR))) { compiled_code_p = parser_parse_arrow_function (context_p, extra_status_flags); } + else + { + /* Since PARSER_IS_ARROW_FUNCTION and PARSER_CLASS_CONSTRUCTOR bits cannot + * be set at the same time, this bit combination triggers class field parsing. */ + compiled_code_p = parser_parse_class_fields (context_p); + } #else /* !ENABLED (JERRY_ESNEXT) */ compiled_code_p = parser_parse_function (context_p, extra_status_flags); #endif /* ENABLED (JERRY_ESNEXT) */ @@ -3031,14 +3099,12 @@ lexer_construct_regexp_object (parser_context_t *context_p, /**< context */ literal_p->type = literal_type; literal_p->prop.length = (prop_length_t) length; literal_p->status_flags = 0; - context_p->literal_count++; context_p->token.type = LEXER_LITERAL; - context_p->token.keyword_type = LEXER_EOS; context_p->token.lit_location.type = LEXER_REGEXP_LITERAL; context_p->lit_object.literal_p = literal_p; - context_p->lit_object.index = (uint16_t) (context_p->literal_count - 1); + context_p->lit_object.index = context_p->literal_count++; #else /* !ENABLED (JERRY_BUILTIN_REGEXP) */ JERRY_UNUSED (parse_only); parser_raise_error (context_p, PARSER_ERR_UNSUPPORTED_REGEXP); @@ -3057,6 +3123,7 @@ lexer_expect_identifier (parser_context_t *context_p, /**< context */ || literal_type == LEXER_NEW_IDENT_LITERAL); lexer_skip_spaces (context_p); + context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; @@ -3093,7 +3160,6 @@ lexer_expect_identifier (parser_context_t *context_p, /**< context */ /* When parsing default exports for modules, it is not required by functions or classes to have identifiers. * In this case we use a synthetic name for them. */ context_p->token.type = LEXER_LITERAL; - context_p->token.keyword_type = LEXER_EOS; context_p->token.lit_location = lexer_default_literal; lexer_construct_literal_object (context_p, &context_p->token.lit_location, literal_type); context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_DEFAULT_CLASS_OR_FUNC); @@ -3128,16 +3194,14 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED); } -#if ENABLED (JERRY_ESNEXT) - int is_class_method = ((ident_opts & LEXER_OBJ_IDENT_CLASS_METHOD) - && !(ident_opts & LEXER_OBJ_IDENT_ONLY_IDENTIFIERS) - && (context_p->token.type != LEXER_KEYW_STATIC)); -#endif /* ENABLED (JERRY_ESNEXT) */ - + context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; bool create_literal_object = false; + JERRY_ASSERT ((ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER) + || !(ident_opts & LEXER_OBJ_IDENT_CLASS_NO_STATIC)); + if (lexer_parse_identifier (context_p, LEXER_PARSE_NO_OPTS)) { if (!(ident_opts & (LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJECT_PATTERN))) @@ -3150,6 +3214,8 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ && context_p->source_p[0] != LIT_CHAR_COMMA && context_p->source_p[0] != LIT_CHAR_RIGHT_BRACE && context_p->source_p[0] != LIT_CHAR_LEFT_PAREN + && context_p->source_p[0] != LIT_CHAR_SEMICOLON + && context_p->source_p[0] != LIT_CHAR_EQUALS #endif /* ENABLED (JERRY_ESNEXT) */ && context_p->source_p[0] != LIT_CHAR_COLON) { @@ -3171,18 +3237,19 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ context_p->token.type = LEXER_KEYW_ASYNC; return; } + + if (ident_opts & LEXER_OBJ_IDENT_CLASS_NO_STATIC) + { + if (lexer_compare_literal_to_string (context_p, "static", 6)) + { + context_p->token.type = LEXER_KEYW_STATIC; + } + return; + } #endif /* ENABLED (JERRY_ESNEXT) */ } } -#if ENABLED (JERRY_ESNEXT) - if (is_class_method && lexer_compare_literal_to_string (context_p, "static", 6)) - { - context_p->token.type = LEXER_KEYW_STATIC; - return; - } -#endif /* ENABLED (JERRY_ESNEXT) */ - create_literal_object = true; } else @@ -3263,7 +3330,11 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ && char_p[0] <= LIT_CHAR_9) { lexer_parse_number (context_p); - lexer_construct_number_object (context_p, false, false); + + if (!(ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER)) + { + lexer_construct_number_object (context_p, false, false); + } return; } break; @@ -3274,10 +3345,8 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ if (create_literal_object) { #if ENABLED (JERRY_ESNEXT) - if (is_class_method && lexer_compare_literal_to_string (context_p, "constructor", 11)) + if (ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER) { - context_p->token.type = LEXER_CLASS_CONSTRUCTOR; - context_p->token.flags &= (uint8_t) ~LEXER_NO_SKIP_SPACES; return; } #endif /* ENABLED (JERRY_ESNEXT) */ @@ -3300,6 +3369,7 @@ bool lexer_scan_identifier (parser_context_t *context_p) /**< context */ { lexer_skip_spaces (context_p); + context_p->token.keyword_type = LEXER_EOS; context_p->token.line = context_p->line; context_p->token.column = context_p->column; @@ -3309,6 +3379,7 @@ lexer_scan_identifier (parser_context_t *context_p) /**< context */ return true; } + context_p->token.flags |= LEXER_NO_SKIP_SPACES; lexer_next_token (context_p); return false; } /* lexer_scan_identifier */ diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index 6b6cd2c26..67760ec98 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -198,7 +198,6 @@ typedef enum #if ENABLED (JERRY_ESNEXT) LEXER_ASSIGN_GROUP_EXPR, /**< indetifier for the assignment is located in a group expression */ LEXER_ASSIGN_CONST, /**< a const binding is reassigned */ - LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */ LEXER_INVALID_PATTERN, /**< special value for invalid destructuring pattern */ #endif /* ENABLED (JERRY_ESNEXT) */ @@ -276,8 +275,9 @@ 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_OBJECT_PATTERN = (1u << 3), /**< parse "get"/"set" as string literal in object pattern */ + LEXER_OBJ_IDENT_CLASS_IDENTIFIER = (1u << 2), /**< expect identifier inside a class body */ + LEXER_OBJ_IDENT_CLASS_NO_STATIC = (1u << 3), /**< static keyword was not present before the identifier */ + LEXER_OBJ_IDENT_OBJECT_PATTERN = (1u << 4), /**< parse "get"/"set" as string literal in object pattern */ } 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 fe817b745..926f287c7 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -491,22 +491,6 @@ parser_parse_array_initializer (parser_context_t *context_p, parser_pattern_flag static void parser_parse_object_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); -/** - * Description of "get" literal string. - */ -static const lexer_lit_location_t lexer_get_literal = -{ - (const uint8_t *) "get", 3, LEXER_STRING_LITERAL, false -}; - -/** - * Description of "set" literal string. - */ -static const lexer_lit_location_t lexer_set_literal = -{ - (const uint8_t *) "set", 3, LEXER_STRING_LITERAL, false -}; - /** * Class literal parsing options. */ @@ -517,6 +501,18 @@ typedef enum PARSER_CLASS_LITERAL_HERTIAGE_PRESENT = (1 << 1), /**< class heritage is present */ } parser_class_literal_opts_t; +/** + * Checks whether the current string or identifier literal is constructor + * + * @return true, if constructor and false otherwise + */ +static inline bool JERRY_ATTR_ALWAYS_INLINE +parser_is_constructor_literal (parser_context_t *context_p) /**< context */ +{ + return (LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) + && lexer_compare_literal_to_string (context_p, "constructor", 11)); +} /* parser_is_constructor_literal */ + /** * Parse class literal. */ @@ -526,20 +522,11 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); - uint32_t status_flags = PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER; - lexer_literal_t *ctor_literal_p = NULL; if (opts & PARSER_CLASS_LITERAL_CTOR_PRESENT) { - if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) - { - parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); - } - - ctor_literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); - ctor_literal_p->type = LEXER_UNUSED_LITERAL; - ctor_literal_p->status_flags = 0; + ctor_literal_p = lexer_construct_unused_literal (context_p); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, (uint16_t) (context_p->literal_count++)); } else if (opts & PARSER_CLASS_LITERAL_HERTIAGE_PRESENT) @@ -554,6 +541,7 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ parser_emit_cbc_ext (context_p, CBC_EXT_INIT_CLASS); bool is_static = false; + size_t fields_size = 0; while (true) { @@ -562,17 +550,56 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ lexer_skip_empty_statements (context_p); } - lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_CLASS_METHOD); + lexer_expect_object_literal_id (context_p, (LEXER_OBJ_IDENT_CLASS_IDENTIFIER + | (is_static ? 0 : LEXER_OBJ_IDENT_CLASS_NO_STATIC))); if (context_p->token.type == LEXER_RIGHT_BRACE) { - if (JERRY_UNLIKELY (is_static)) - { - parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); - } + JERRY_ASSERT (!is_static); break; } + if (context_p->token.type == LEXER_KEYW_STATIC) + { + JERRY_ASSERT (!is_static); + is_static = true; + continue; + } + + if (!is_static && context_p->token.type == LEXER_LITERAL && parser_is_constructor_literal (context_p)) + { + JERRY_ASSERT (!is_static); + JERRY_ASSERT (opts & PARSER_CLASS_LITERAL_CTOR_PRESENT); + JERRY_ASSERT (ctor_literal_p != NULL); + + if (ctor_literal_p->type == LEXER_FUNCTION_LITERAL) + { + /* 14.5.1 */ + parser_raise_error (context_p, PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS); + } + + uint32_t constructor_status_flags = (PARSER_FUNCTION_CLOSURE + | PARSER_ALLOW_SUPER + | PARSER_CLASS_CONSTRUCTOR + | PARSER_LEXICAL_ENV_NEEDED); + + if (opts & PARSER_CLASS_LITERAL_HERTIAGE_PRESENT) + { + constructor_status_flags |= PARSER_ALLOW_SUPER_CALL; + } + + if (context_p->status_flags & PARSER_INSIDE_WITH) + { + constructor_status_flags |= PARSER_INSIDE_WITH; + } + + parser_flush_cbc (context_p); + ecma_compiled_code_t *compiled_code_p = parser_parse_function (context_p, constructor_status_flags); + ctor_literal_p->u.bytecode_p = compiled_code_p; + ctor_literal_p->type = LEXER_FUNCTION_LITERAL; + continue; + } + bool is_computed = false; if (context_p->token.type == LEXER_PROPERTY_GETTER || context_p->token.type == LEXER_PROPERTY_SETTER) @@ -580,28 +607,17 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ uint16_t literal_index, function_literal_index; bool is_getter = (context_p->token.type == LEXER_PROPERTY_GETTER); - if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) - { - lexer_construct_literal_object (context_p, - (is_getter ? (lexer_lit_location_t *) &lexer_get_literal - : (lexer_lit_location_t *) &lexer_set_literal), - LEXER_STRING_LITERAL); - goto parse_class_method; - } - - uint32_t accessor_status_flags = status_flags; + uint32_t accessor_status_flags = PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER; accessor_status_flags |= (is_getter ? PARSER_IS_PROPERTY_GETTER : PARSER_IS_PROPERTY_SETTER); - lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_CLASS_METHOD | LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); + lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); literal_index = context_p->lit_object.index; if (context_p->token.type == LEXER_RIGHT_SQUARE) { is_computed = true; } - else if (!is_static - && LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) - && lexer_compare_literal_to_string (context_p, "constructor", 11)) + else if (!is_static && parser_is_constructor_literal (context_p)) { parser_raise_error (context_p, PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR); } @@ -659,45 +675,7 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ continue; } - if (!is_static) - { - if (context_p->token.type == LEXER_KEYW_STATIC) - { - is_static = true; - continue; - } - - if (context_p->token.type == LEXER_CLASS_CONSTRUCTOR) - { - JERRY_ASSERT (opts & PARSER_CLASS_LITERAL_CTOR_PRESENT); - JERRY_ASSERT (ctor_literal_p != NULL); - - if (ctor_literal_p->type == LEXER_FUNCTION_LITERAL) - { - /* 14.5.1 */ - parser_raise_error (context_p, PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS); - } - - uint32_t constructor_status_flags = (status_flags - | PARSER_CLASS_CONSTRUCTOR - | PARSER_LEXICAL_ENV_NEEDED); - - if (opts & PARSER_CLASS_LITERAL_HERTIAGE_PRESENT) - { - constructor_status_flags |= PARSER_ALLOW_SUPER_CALL; - } - - parser_flush_cbc (context_p); - ecma_compiled_code_t *compiled_code_p = parser_parse_function (context_p, constructor_status_flags); - ctor_literal_p->u.bytecode_p = compiled_code_p; - ctor_literal_p->type = LEXER_FUNCTION_LITERAL; - continue; - } - } - - status_flags &= (uint32_t) ~(PARSER_IS_GENERATOR_FUNCTION - | PARSER_IS_ASYNC_FUNCTION - | PARSER_DISALLOW_AWAIT_YIELD); + uint32_t status_flags = PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER; if (context_p->token.type == LEXER_KEYW_ASYNC) { @@ -728,15 +706,164 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, PARSER_ERR_CLASS_STATIC_PROTOTYPE); } } - else if ((status_flags & PARSER_IS_GENERATOR_FUNCTION) + else if ((status_flags & (PARSER_IS_ASYNC_FUNCTION | PARSER_IS_GENERATOR_FUNCTION)) && lexer_compare_literal_to_string (context_p, "constructor", 11)) { - parser_raise_error (context_p, PARSER_ERR_CLASS_CONSTRUCTOR_AS_GENERATOR); + parser_raise_error (context_p, PARSER_ERR_INVALID_CLASS_CONSTRUCTOR); + } + } + + if (!(status_flags & (PARSER_IS_ASYNC_FUNCTION | PARSER_IS_GENERATOR_FUNCTION))) + { + if (!is_static && !lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) + { + /* Class field. */ + if (fields_size == 0) + { + parser_stack_push_uint8 (context_p, PARSER_CLASS_FIELD_END); + } + + scanner_range_t range; + uint8_t class_field_type = 0; + + if (!is_computed) + { + range.start_location.source_p = context_p->token.lit_location.char_p; + range.start_location.line = context_p->token.line; + range.start_location.column = context_p->token.column; + class_field_type = PARSER_CLASS_FIELD_NORMAL; + + if (context_p->token.lit_location.type == LEXER_STRING_LITERAL) + { + range.start_location.source_p--; + } + } + else + { + parser_emit_cbc_ext (context_p, CBC_EXT_ADD_COMPUTED_FIELD); + } + + if (lexer_consume_assign (context_p)) + { + class_field_type |= PARSER_CLASS_FIELD_INITIALIZED; + + if (context_p->next_scanner_info_p->source_p != context_p->source_p) + { + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); + } + + if (is_computed) + { + scanner_get_location (&range.start_location, context_p); + } + + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END); + range.source_end_p = ((scanner_location_info_t *) context_p->next_scanner_info_p)->location.source_p; + + scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); + scanner_release_next (context_p, sizeof (scanner_location_info_t)); + scanner_seek (context_p); + + parser_stack_push (context_p, &range, sizeof (scanner_range_t)); + fields_size += sizeof (scanner_range_t); + } + else + { + if (!(context_p->token.flags & LEXER_WAS_NEWLINE) + && !lexer_check_next_characters (context_p, LIT_CHAR_SEMICOLON, LIT_CHAR_RIGHT_BRACE)) + { + lexer_next_token (context_p); + parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); + } + + if (!is_computed) + { + parser_stack_push (context_p, &range.start_location, sizeof (scanner_location_t)); + fields_size += sizeof (scanner_location_t); + } + } + + parser_stack_push_uint8 (context_p, class_field_type); + fields_size++; + is_static = false; + continue; + } + + if (!is_computed) + { + if (context_p->token.lit_location.type != LEXER_NUMBER_LITERAL) + { + JERRY_ASSERT (context_p->token.lit_location.type == LEXER_IDENT_LITERAL + || context_p->token.lit_location.type == LEXER_STRING_LITERAL); + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + LEXER_STRING_LITERAL); + } + else + { + lexer_construct_number_object (context_p, false, false); + } + } + + if (is_static && !lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) + { + if (!is_computed && parser_is_constructor_literal (context_p)) + { + parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED); + } + + uint16_t literal_index = context_p->lit_object.index; + context_p->status_flags |= PARSER_INSIDE_CLASS_FIELD; + + if (lexer_consume_assign (context_p)) + { + if (context_p->next_scanner_info_p->source_p != context_p->source_p) + { + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); + } + + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END); + + /* Changing the source_end_p prevents the lexer to process the name of the next class field + * as normal token which may cause issues if the name is also a keyword (e.g. var). */ + const uint8_t *source_end_p = context_p->source_end_p; + context_p->source_end_p = ((scanner_location_info_t *) context_p->next_scanner_info_p)->location.source_p; + scanner_release_next (context_p, sizeof (scanner_location_info_t)); + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + + if (context_p->token.type != LEXER_EOS) + { + parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); + } + + context_p->source_end_p = source_end_p; + } + else + { + parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); + } + + if (!is_computed) + { + parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_STATIC_PROPERTY, literal_index); + } + else + { + parser_emit_cbc_ext (context_p, CBC_EXT_SET_STATIC_COMPUTED_PROPERTY); + } + + context_p->status_flags &= (uint32_t) ~PARSER_INSIDE_CLASS_FIELD; + is_static = false; + continue; } } -parse_class_method: - ; /* Empty statement to make compiler happy. */ uint16_t literal_index = context_p->lit_object.index; uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags | PARSER_IS_METHOD); @@ -769,6 +896,17 @@ parse_class_method: context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; } } + + if (fields_size > 0) + { + parser_reverse_class_fields (context_p, fields_size); + + /* Since PARSER_IS_ARROW_FUNCTION and PARSER_CLASS_CONSTRUCTOR bits cannot + * be set at the same time, this bit combination triggers class field parsing. */ + uint16_t function_literal_index = lexer_construct_function_object (context_p, (PARSER_IS_ARROW_FUNCTION + | PARSER_CLASS_CONSTRUCTOR)); + parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_FIELD_INIT, function_literal_index); + } } /* parser_parse_class_literal */ /** @@ -1813,7 +1951,9 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); - uint32_t arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION); + uint32_t arrow_status_flags = (PARSER_IS_FUNCTION + | PARSER_IS_ARROW_FUNCTION + | (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD)); if (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC) { @@ -2073,7 +2213,10 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ parser_check_assignment_expr (context_p); - parser_parse_function_expression (context_p, PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION); + uint32_t arrow_status_flags = (PARSER_IS_FUNCTION + | PARSER_IS_ARROW_FUNCTION + | (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD)); + parser_parse_function_expression (context_p, arrow_status_flags); return parser_abort_parsing_after_assignment_expression (context_p); } case LEXER_KEYW_YIELD: @@ -3442,11 +3585,6 @@ parser_parse_object_initializer (parser_context_t *context_p, /**< context */ } parser_reparse_as_common_identifier (context_p, start_line, start_column); - lexer_next_token (context_p); - - JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE - || context_p->token.type == LEXER_ASSIGN - || context_p->token.type == LEXER_COMMA); if (flags & PARSER_PATTERN_ARGUMENTS) { @@ -3462,6 +3600,12 @@ parser_parse_object_initializer (parser_context_t *context_p, /**< context */ #endif /* ENABLED (JERRY_MODULE_SYSTEM) */ parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + + lexer_next_token (context_p); + JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE + || context_p->token.type == LEXER_ASSIGN + || context_p->token.type == LEXER_COMMA); + parser_pattern_form_assignment (context_p, flags, push_prop_opcode, prop_index, start_line); } diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 888370495..d337c62da 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -69,16 +69,17 @@ typedef enum PARSER_FUNCTION_HAS_REST_PARAM = (1u << 19), /**< function has rest parameter */ PARSER_CLASS_CONSTRUCTOR = (1u << 20), /**< a class constructor is parsed * Note: PARSER_ALLOW_SUPER must be present */ - /* These three status flags must be in this order. See PARSER_SAVED_FLAGS_OFFSET. */ + /* These four status flags must be in this order. See PARSER_SAVED_FLAGS_OFFSET. */ PARSER_ALLOW_SUPER = (1u << 21), /**< allow super property access */ PARSER_ALLOW_SUPER_CALL = (1u << 22), /**< allow super constructor call * Note: PARSER_CLASS_CONSTRUCTOR must be present */ - PARSER_ALLOW_NEW_TARGET = (1u << 23), /**< allow new.target parsing in the current context */ - PARSER_IS_METHOD = (1u << 24), /**< method is parsed */ + PARSER_INSIDE_CLASS_FIELD = (1u << 23), /**< a class field is being parsed */ + PARSER_ALLOW_NEW_TARGET = (1u << 24), /**< allow new.target parsing in the current context */ + PARSER_IS_METHOD = (1u << 25), /**< method is parsed */ #endif /* ENABLED (JERRY_ESNEXT) */ #if ENABLED (JERRY_MODULE_SYSTEM) - PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 25), /**< parsing a function or class default export */ - PARSER_MODULE_STORE_IDENT = (1u << 26), /**< store identifier of the current export statement */ + PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 26), /**< parsing a function or class default export */ + PARSER_MODULE_STORE_IDENT = (1u << 27), /**< store identifier of the current export statement */ #endif /* ENABLED (JERRY_MODULE_SYSTEM) */ PARSER_HAS_LATE_LIT_INIT = (1u << 30), /**< there are identifier or string literals which construction * is postponed after the local parser data is freed */ @@ -129,6 +130,20 @@ typedef enum #endif /* ENABLED (JERRY_ESNEXT) */ } parser_check_context_type_t; +#if ENABLED (JERRY_ESNEXT) + +/** + * Class field bits. + */ +typedef enum +{ + PARSER_CLASS_FIELD_END = (1u << 0), /**< last class field */ + PARSER_CLASS_FIELD_NORMAL = (1u << 1), /**< normal (non-computed) class field */ + PARSER_CLASS_FIELD_INITIALIZED = (1u << 2), /**< class field is initialized */ +} parser_class_field_type_t; + +#endif /* ENABLED (JERRY_ESNEXT) */ + /** * Mask for strict mode code */ @@ -704,6 +719,10 @@ void parser_set_continues_to_current_position (parser_context_t *context_p, pars #define parser_emit_cbc_ext_backward_branch(context_p, opcode, offset) \ parser_emit_cbc_backward_branch ((context_p), PARSER_TO_EXT_OPCODE (opcode), (offset)) +#if ENABLED (JERRY_ESNEXT) +void parser_reverse_class_fields (parser_context_t *context_p, size_t fields_size); +#endif /* ENABLED (JERRY_ESNEXT) */ + /** * @} * @@ -725,6 +744,7 @@ bool lexer_check_arrow (parser_context_t *context_p); bool lexer_check_arrow_param (parser_context_t *context_p); bool lexer_check_yield_no_arg (parser_context_t *context_p); bool lexer_consume_generator (parser_context_t *context_p); +bool lexer_consume_assign (parser_context_t *context_p); void lexer_update_await_yield (parser_context_t *context_p, uint32_t status_flags); #endif /* ENABLED (JERRY_ESNEXT) */ void lexer_parse_string (parser_context_t *context_p, lexer_string_options_t opts); @@ -736,7 +756,7 @@ void lexer_convert_ident_to_cesu8 (uint8_t *destination_p, const uint8_t *source const uint8_t *lexer_convert_literal_to_chars (parser_context_t *context_p, const lexer_lit_location_t *literal_p, uint8_t *local_byte_array_p, lexer_string_options_t opts); void lexer_expect_object_literal_id (parser_context_t *context_p, uint32_t ident_opts); -uint16_t scanner_save_literal (parser_context_t *context_p, uint16_t ident_index); +lexer_literal_t *lexer_construct_unused_literal (parser_context_t *context_p); void lexer_construct_literal_object (parser_context_t *context_p, const lexer_lit_location_t *lit_location_p, uint8_t literal_type); bool lexer_construct_number_object (parser_context_t *context_p, bool is_expr, bool is_negative_number); @@ -804,6 +824,7 @@ void scanner_get_location (scanner_location_t *location_p, parser_context_t *con void scanner_set_location (parser_context_t *context_p, scanner_location_t *location_p); uint16_t scanner_decode_map_to (parser_scope_stack_t *stack_item_p); #if ENABLED (JERRY_ESNEXT) +uint16_t scanner_save_literal (parser_context_t *context_p, uint16_t ident_index); bool scanner_literal_is_const_reg (parser_context_t *context_p, uint16_t literal_index); bool scanner_literal_is_created (parser_context_t *context_p, uint16_t literal_index); #endif /* ENABLED (JERRY_ESNEXT) */ @@ -859,6 +880,7 @@ void parser_module_add_names_to_node (parser_context_t *context_p, ecma_compiled_code_t *parser_parse_function (parser_context_t *context_p, uint32_t status_flags); #if ENABLED (JERRY_ESNEXT) ecma_compiled_code_t *parser_parse_arrow_function (parser_context_t *context_p, uint32_t status_flags); +ecma_compiled_code_t *parser_parse_class_fields (parser_context_t *context_p); void parser_set_function_name (parser_context_t *context_p, uint16_t function_literal_index, uint16_t name_index, uint32_t status_flags); void parser_compiled_code_set_function_name (parser_context_t *context_p, ecma_compiled_code_t *bytecode_p, diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index c4ee90b00..eea911caf 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -790,6 +790,78 @@ parser_set_continues_to_current_position (parser_context_t *context_p, /**< cont } } /* parser_set_continues_to_current_position */ +#if ENABLED (JERRY_ESNEXT) + +/** + * Reverse the field list of a class + */ +void +parser_reverse_class_fields (parser_context_t *context_p, /**< context */ + size_t fields_size) /**< size of consumed memory */ +{ + uint8_t *data_p = (uint8_t *) parser_malloc (context_p, fields_size); + uint8_t *data_end_p = data_p + fields_size; + uint8_t *current_p = data_p; + parser_stack_iterator_t iterator; + + parser_stack_iterator_init (context_p, &iterator); + + do + { + uint8_t class_field_type = parser_stack_iterator_read_uint8 (&iterator); + parser_stack_iterator_skip (&iterator, 1); + + if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) + { + parser_stack_iterator_read (&iterator, current_p, sizeof (scanner_range_t)); + parser_stack_iterator_skip (&iterator, sizeof (scanner_range_t)); + current_p += sizeof (scanner_range_t); + } + else if (class_field_type & PARSER_CLASS_FIELD_NORMAL) + { + parser_stack_iterator_read (&iterator, current_p, sizeof (scanner_location_t)); + parser_stack_iterator_skip (&iterator, sizeof (scanner_location_t)); + current_p += sizeof (scanner_location_t); + } + + *current_p++ = class_field_type; + } + while (current_p < data_end_p); + + parser_stack_iterator_init (context_p, &iterator); + current_p = data_end_p; + context_p->stack_top_uint8 = current_p[-1]; + + do + { + uint8_t class_field_type = current_p[-1]; + + if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) + { + current_p -= sizeof (scanner_range_t) + 1; + parser_stack_iterator_write (&iterator, current_p, sizeof (scanner_range_t) + 1); + parser_stack_iterator_skip (&iterator, sizeof (scanner_range_t) + 1); + } + else if (class_field_type & PARSER_CLASS_FIELD_NORMAL) + { + current_p -= sizeof (scanner_location_t) + 1; + parser_stack_iterator_write (&iterator, current_p, sizeof (scanner_location_t) + 1); + parser_stack_iterator_skip (&iterator, sizeof (scanner_location_t) + 1); + } + else + { + current_p--; + parser_stack_iterator_write (&iterator, current_p, 1); + parser_stack_iterator_skip (&iterator, 1); + } + } + while (current_p > data_p); + + parser_free (data_p, fields_size); +} /* parser_reverse_class_fields */ + +#endif /* ENABLED (JERRY_ESNEXT) */ + #if ENABLED (JERRY_ERROR_MESSAGES) /** * Returns with the string representation of the error @@ -1170,9 +1242,9 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Class constructor may not be an accessor."; } - case PARSER_ERR_CLASS_CONSTRUCTOR_AS_GENERATOR: + case PARSER_ERR_INVALID_CLASS_CONSTRUCTOR: { - return "Class constructor may not be a generator."; + return "Class constructor may not be a generator or async function."; } case PARSER_ERR_CLASS_STATIC_PROTOTYPE: { @@ -1182,6 +1254,10 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Super is not allowed to be used here."; } + case PARSER_ERR_ARGUMENTS_IN_CLASS_FIELD: + { + return "In class field declarations 'arguments' is not allowed."; + } case PARSER_ERR_RIGHT_BRACE_EXPECTED: { return "Expected '}' token."; diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 6173e3c16..66a19b082 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -2578,6 +2578,13 @@ parser_parse_function (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, PARSER_ERR_ONE_ARGUMENT_EXPECTED); } +#if ENABLED (JERRY_ESNEXT) + if ((context_p->status_flags & (PARSER_CLASS_CONSTRUCTOR | PARSER_ALLOW_SUPER_CALL)) == PARSER_CLASS_CONSTRUCTOR) + { + parser_emit_cbc_ext (context_p, CBC_EXT_RUN_CLASS_FIELD_INIT); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) if (context_p->is_show_opcodes && (context_p->status_flags & PARSER_HAS_NON_STRICT_ARG)) @@ -2712,6 +2719,152 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */ return compiled_code_p; } /* parser_parse_arrow_function */ +/** + * Parse class fields + * + * @return compiled code + */ +ecma_compiled_code_t * +parser_parse_class_fields (parser_context_t *context_p) /**< context */ +{ + parser_saved_context_t saved_context; + ecma_compiled_code_t *compiled_code_p; + + JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE); + parser_save_context (context_p, &saved_context); + context_p->status_flags |= (PARSER_IS_FUNCTION + | PARSER_ALLOW_SUPER + | PARSER_INSIDE_CLASS_FIELD + | PARSER_ALLOW_NEW_TARGET); + +#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) + if (context_p->is_show_opcodes) + { + JERRY_DEBUG_MSG ("\n--- Class fields parsing start ---\n\n"); + } +#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + +#if ENABLED (JERRY_DEBUGGER) + if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) + { + jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column); + } +#endif /* ENABLED (JERRY_DEBUGGER) */ + + const uint8_t *source_end_p = context_p->source_end_p; + bool first_computed_class_field = true; + scanner_location_t end_location; + scanner_get_location (&end_location, context_p); + + do + { + uint8_t class_field_type = context_p->stack_top_uint8; + parser_stack_pop_uint8 (context_p); + + scanner_range_t range; + + if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) + { + parser_stack_pop (context_p, &range, sizeof (scanner_range_t)); + } + else if (class_field_type & PARSER_CLASS_FIELD_NORMAL) + { + parser_stack_pop (context_p, &range.start_location, sizeof (scanner_location_t)); + } + + uint16_t literal_index = 0; + + if (class_field_type & PARSER_CLASS_FIELD_NORMAL) + { + scanner_set_location (context_p, &range.start_location); + context_p->source_end_p = source_end_p; + scanner_seek (context_p); + + lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); + + literal_index = context_p->lit_object.index; + + if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) + { + lexer_next_token (context_p); + JERRY_ASSERT (context_p->token.type == LEXER_ASSIGN); + } + } + else if (first_computed_class_field) + { + parser_emit_cbc (context_p, CBC_PUSH_NUMBER_0); + first_computed_class_field = false; + } + + if (class_field_type & PARSER_CLASS_FIELD_INITIALIZED) + { + if (!(class_field_type & PARSER_CLASS_FIELD_NORMAL)) + { + scanner_set_location (context_p, &range.start_location); + scanner_seek (context_p); + } + + context_p->source_end_p = range.source_end_p; + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + + if (context_p->token.type != LEXER_EOS) + { + parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); + } + } + else + { + parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); + } + + if (class_field_type & PARSER_CLASS_FIELD_NORMAL) + { + parser_emit_cbc_literal (context_p, CBC_ASSIGN_PROP_THIS_LITERAL, literal_index); + } + else + { + parser_flush_cbc (context_p); + + /* The next opcode pushes two more temporary values onto the stack */ + if (context_p->stack_depth + 1 > context_p->stack_limit) + { + context_p->stack_limit = (uint16_t) (context_p->stack_depth + 1); + if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) + { + parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); + } + } + + parser_emit_cbc_ext (context_p, CBC_EXT_SET_NEXT_COMPUTED_FIELD); + } + } + while (context_p->stack_top_uint8 != PARSER_CLASS_FIELD_END); + + if (!first_computed_class_field) + { + parser_emit_cbc (context_p, CBC_POP); + } + + parser_stack_pop_uint8 (context_p); + parser_flush_cbc (context_p); + context_p->source_end_p = source_end_p; + scanner_set_location (context_p, &end_location); + + compiled_code_p = parser_post_processing (context_p); + +#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) + if (context_p->is_show_opcodes) + { + JERRY_DEBUG_MSG ("\n--- Class fields parsing end ---\n\n"); + } +#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + + parser_restore_context (context_p, &saved_context); + + return compiled_code_p; +} /* parser_parse_class_fields */ + /** * Check whether the last emitted cbc opcode was an anonymous function declaration * diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index 0f569e833..ad29469cd 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -142,9 +142,10 @@ typedef enum PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS, /**< multiple class constructor */ PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR, /**< class constructor cannot be an accessor */ - PARSER_ERR_CLASS_CONSTRUCTOR_AS_GENERATOR, /**< class constructor cannot be a generator */ + PARSER_ERR_INVALID_CLASS_CONSTRUCTOR, /**< class constructor cannot be a generator or async function */ PARSER_ERR_CLASS_STATIC_PROTOTYPE, /**< static method name 'prototype' is not allowed */ PARSER_ERR_UNEXPECTED_SUPER_KEYWORD, /**< unexpected super keyword */ + PARSER_ERR_ARGUMENTS_IN_CLASS_FIELD, /**< arguments is not allowed in class fields */ PARSER_ERR_RIGHT_BRACE_EXPECTED, /**< right brace expected */ PARSER_ERR_OF_EXPECTED, /**< of keyword expected */ diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index b9fffa099..ee7ca7014 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -45,7 +45,8 @@ typedef enum SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS, /**< continue scanning function arguments */ SCAN_MODE_BINDING, /**< array or object binding */ SCAN_MODE_CLASS_DECLARATION, /**< scanning class declaration */ - SCAN_MODE_CLASS_METHOD, /**< scanning class method */ + SCAN_MODE_CLASS_BODY, /**< scanning class body */ + SCAN_MODE_CLASS_BODY_NO_SCAN, /**< scanning class body without calling lexer_scan_identifier */ #endif /* ENABLED (JERRY_ESNEXT) */ } scan_modes_t; @@ -59,7 +60,7 @@ typedef enum SCAN_STACK_BLOCK_STATEMENT, /**< block statement group */ SCAN_STACK_FUNCTION_STATEMENT, /**< function statement */ SCAN_STACK_FUNCTION_EXPRESSION, /**< function expression */ - SCAN_STACK_FUNCTION_PROPERTY, /**< function expression in an object literal or class */ + SCAN_STACK_FUNCTION_PROPERTY, /**< function expression in an object literal */ #if ENABLED (JERRY_ESNEXT) SCAN_STACK_FUNCTION_ARROW, /**< arrow function expression */ #endif /* ENABLED (JERRY_ESNEXT) */ @@ -113,6 +114,7 @@ typedef enum SCAN_STACK_CLASS_STATEMENT, /**< class statement */ SCAN_STACK_CLASS_EXPRESSION, /**< class expression */ SCAN_STACK_CLASS_EXTENDS, /**< class extends expression */ + SCAN_STACK_CLASS_FIELD_INITIALIZER, /**< class field initializer */ SCAN_STACK_FUNCTION_PARAMETERS, /**< function parameter initializer */ SCAN_STACK_FOR_START_PATTERN, /**< possible assignment pattern for "for" iterator */ SCAN_STACK_USE_ASYNC, /**< an "async" identifier is used */ @@ -387,6 +389,7 @@ void scanner_detect_eval_call (parser_context_t *context_p, scanner_context_t *s #if ENABLED (JERRY_ESNEXT) void scanner_push_class_declaration (parser_context_t *context_p, scanner_context_t *scanner_context_p, uint8_t stack_mode); +void scanner_push_class_field_initializer (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_push_destructuring_pattern (parser_context_t *context_p, scanner_context_t *scanner_context_p, uint8_t binding_type, bool is_nested); void scanner_pop_binding_list (scanner_context_t *scanner_context_p); diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index 6ce91b5d1..0e03e5743 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -1494,6 +1494,21 @@ scanner_push_class_declaration (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); } /* scanner_push_class_declaration */ +/** + * Push the start of a class field initializer. + */ +void +scanner_push_class_field_initializer (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p) /* scanner context */ +{ + scanner_source_start_t source_start; + source_start.source_p = context_p->source_p; + + parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t)); + parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_FIELD_INITIALIZER); + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; +} /* scanner_push_class_field_initializer */ + /** * Push the values required for destructuring assignment or binding parsing. */ @@ -1658,6 +1673,7 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ case SCANNER_TYPE_CASE: #if ENABLED (JERRY_ESNEXT) case SCANNER_TYPE_INITIALIZER: + case SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END: #endif /* ENABLED (JERRY_ESNEXT) */ { size = sizeof (scanner_location_info_t); diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 438a00bb9..bed7b0a81 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -430,6 +430,11 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * { break; } + case SCAN_STACK_CLASS_FIELD_INITIALIZER: + { + scanner_raise_error (context_p); + break; + } case SCAN_STACK_FUNCTION_PARAMETERS: { scanner_context_p->mode = SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS; @@ -962,7 +967,7 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * break; } - lexer_next_token (context_p); + lexer_scan_identifier (context_p); parser_stack_pop_uint8 (context_p); stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; @@ -970,11 +975,33 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * if (stack_top == SCAN_STACK_FUNCTION_PROPERTY) { scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); - scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; } + if (stack_top == SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR + || stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR) + { + if (context_p->token.type == LEXER_LEFT_PAREN) + { + scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); + + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); + scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; + return SCAN_KEEP_TOKEN; + } + + if (context_p->token.type == LEXER_ASSIGN) + { + scanner_push_class_field_initializer (context_p, scanner_context_p); + return SCAN_NEXT_TOKEN; + } + + scanner_context_p->mode = (context_p->token.type != LEXER_SEMICOLON ? SCAN_MODE_CLASS_BODY_NO_SCAN + : SCAN_MODE_CLASS_BODY); + return SCAN_KEEP_TOKEN; + } + JERRY_ASSERT (stack_top == SCAN_STACK_OBJECT_LITERAL || stack_top == SCAN_STACK_OBJECT_LITERAL_WITH_SUPER); @@ -1073,11 +1100,87 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * break; } - scanner_context_p->mode = SCAN_MODE_CLASS_METHOD; + scanner_context_p->mode = SCAN_MODE_CLASS_BODY; parser_stack_pop_uint8 (context_p); return SCAN_KEEP_TOKEN; } + case SCAN_STACK_CLASS_FIELD_INITIALIZER: + { + scanner_source_start_t source_start; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t)); + + const uint8_t *source_p = NULL; + + scanner_context_p->mode = SCAN_MODE_CLASS_BODY_NO_SCAN; + + switch (type) + { + case LEXER_SEMICOLON: + { + source_p = context_p->source_p - 1; + scanner_context_p->mode = SCAN_MODE_CLASS_BODY; + break; + } + case LEXER_RIGHT_BRACE: + { + source_p = context_p->source_p - 1; + break; + } + default: + { + if (!(context_p->token.flags & LEXER_WAS_NEWLINE)) + { + break; + } + + if (type == LEXER_LEFT_SQUARE) + { + source_p = context_p->source_p - 1; + break; + } + + if (type == LEXER_LITERAL) + { + if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL + || context_p->token.lit_location.type == LEXER_NUMBER_LITERAL) + { + source_p = context_p->token.lit_location.char_p; + } + else if (context_p->token.lit_location.type == LEXER_STRING_LITERAL) + { + source_p = context_p->token.lit_location.char_p - 1; + } + break; + } + + if (type == context_p->token.keyword_type && type != LEXER_EOS) + { + /* Convert keyword to literal. */ + source_p = context_p->token.lit_location.char_p; + context_p->token.type = LEXER_LITERAL; + } + break; + } + } + + if (JERRY_UNLIKELY (source_p == NULL)) + { + scanner_raise_error (context_p); + } + + scanner_location_info_t *location_info_p; + location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p, + source_start.source_p, + sizeof (scanner_location_info_t)); + location_info_p->info.type = SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END; + location_info_p->location.source_p = source_p; + location_info_p->location.line = context_p->token.line; + location_info_p->location.column = context_p->token.column; + return SCAN_KEEP_TOKEN; + } case SCAN_STACK_FUNCTION_PARAMETERS: { parser_stack_pop_uint8 (context_p); @@ -2027,7 +2130,7 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ if (context_p->stack_top_uint8 == SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR || context_p->stack_top_uint8 == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR) { - scanner_context_p->mode = SCAN_MODE_CLASS_METHOD; + scanner_context_p->mode = SCAN_MODE_CLASS_BODY; return SCAN_KEEP_TOKEN; } @@ -2337,6 +2440,9 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ /* This assignment must be here because of Apple compilers. */ context_p->u.scanner_context_p = &scanner_context; +#if ENABLED (JERRY_ESNEXT) + context_p->global_status_flags |= ECMA_PARSE_INTERNAL_PRE_SCANNING; +#endif /* ENABLED (JERRY_ESNEXT) */ parser_stack_init (context_p); @@ -2437,18 +2543,20 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } - scanner_context.mode = SCAN_MODE_CLASS_METHOD; + scanner_context.mode = SCAN_MODE_CLASS_BODY; /* FALLTHRU */ } - case SCAN_MODE_CLASS_METHOD: + case SCAN_MODE_CLASS_BODY: + { + lexer_skip_empty_statements (context_p); + lexer_scan_identifier (context_p); + /* FALLTHRU */ + } + case SCAN_MODE_CLASS_BODY_NO_SCAN: { JERRY_ASSERT (stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR || stack_top == SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR); - lexer_skip_empty_statements (context_p); - - lexer_scan_identifier (context_p); - if (context_p->token.type == LEXER_RIGHT_BRACE) { scanner_source_start_t source_start; @@ -2476,6 +2584,8 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ break; } + bool identifier_found = false; + if (context_p->token.type == LEXER_LITERAL && LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) && lexer_compare_literal_to_string (context_p, "constructor", 11)) @@ -2491,13 +2601,12 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ parser_stack_push_uint8 (context_p, SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR); } } - - if (lexer_token_is_identifier (context_p, "static", 6)) + else if (lexer_token_is_identifier (context_p, "static", 6)) { lexer_scan_identifier (context_p); + identifier_found = true; } - parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; uint16_t literal_pool_flags = SCANNER_LITERAL_POOL_FUNCTION; @@ -2506,9 +2615,11 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ || lexer_token_is_identifier (context_p, "set", 3)) { lexer_scan_identifier (context_p); + identifier_found = true; if (context_p->token.type == LEXER_LEFT_PAREN) { + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); continue; } @@ -2516,19 +2627,24 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ else if (lexer_token_is_identifier (context_p, "async", 5)) { lexer_scan_identifier (context_p); + identifier_found = true; - if (context_p->token.type == LEXER_LEFT_PAREN) + if (!(context_p->token.flags & LEXER_WAS_NEWLINE)) { - scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); - continue; - } + if (context_p->token.type == LEXER_LEFT_PAREN) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); + scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); + continue; + } - literal_pool_flags |= SCANNER_LITERAL_POOL_ASYNC; + literal_pool_flags |= SCANNER_LITERAL_POOL_ASYNC; - if (context_p->token.type == LEXER_MULTIPLY) - { - lexer_scan_identifier (context_p); - literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR; + if (context_p->token.type == LEXER_MULTIPLY) + { + lexer_scan_identifier (context_p); + literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR; + } } } else if (context_p->token.type == LEXER_MULTIPLY) @@ -2539,23 +2655,63 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_LEFT_SQUARE) { + if (literal_pool_flags != SCANNER_LITERAL_POOL_FUNCTION) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); + } + parser_stack_push_uint8 (context_p, SCANNER_FROM_LITERAL_POOL_TO_COMPUTED (literal_pool_flags)); scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } - if (context_p->token.type != LEXER_LITERAL) + if (context_p->token.type == LEXER_LITERAL) + { + lexer_scan_identifier (context_p); + identifier_found = true; + } + + if (!identifier_found) { scanner_raise_error (context_p); } - if (literal_pool_flags & SCANNER_LITERAL_POOL_GENERATOR) + if (context_p->token.type == LEXER_LEFT_PAREN) { - context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION; + if (literal_pool_flags & SCANNER_LITERAL_POOL_GENERATOR) + { + context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION; + } + + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); + scanner_push_literal_pool (context_p, &scanner_context, literal_pool_flags); + continue; } - scanner_push_literal_pool (context_p, &scanner_context, literal_pool_flags); - lexer_next_token (context_p); + if (literal_pool_flags != SCANNER_LITERAL_POOL_FUNCTION) + { + scanner_raise_error (context_p); + } + + if (context_p->token.type == LEXER_ASSIGN) + { + scanner_push_class_field_initializer (context_p, &scanner_context); + break; + } + + if (context_p->token.type == LEXER_SEMICOLON) + { + scanner_context.mode = SCAN_MODE_CLASS_BODY; + continue; + } + + if (context_p->token.type != LEXER_RIGHT_BRACE + && !(context_p->token.flags & LEXER_WAS_NEWLINE)) + { + scanner_raise_error (context_p); + } + + scanner_context.mode = SCAN_MODE_CLASS_BODY_NO_SCAN; continue; } #endif /* ENABLED (JERRY_ESNEXT) */ @@ -3299,6 +3455,9 @@ scan_completed: PARSER_TRY_END context_p->status_flags = scanner_context.context_status_flags; +#if ENABLED (JERRY_ESNEXT) + context_p->global_status_flags &= (uint32_t) ~ECMA_PARSE_INTERNAL_PRE_SCANNING; +#endif /* ENABLED (JERRY_ESNEXT) */ scanner_reverse_info_list (context_p); #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) @@ -3549,6 +3708,12 @@ scan_completed: print_location = false; break; } + case SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END: + { + name_p = "SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END"; + print_location = true; + break; + } case SCANNER_TYPE_LET_EXPRESSION: { JERRY_DEBUG_MSG (" LET_EXPRESSION: source:%d\n", diff --git a/jerry-core/parser/js/js-scanner.h b/jerry-core/parser/js/js-scanner.h index fb24ac807..0ed07975b 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -48,6 +48,7 @@ typedef enum SCANNER_TYPE_INITIALIZER, /**< destructuring binding or assignment pattern with initializer */ SCANNER_TYPE_FOR_PATTERN, /**< assignment pattern for for-in or for-of interators */ SCANNER_TYPE_CLASS_CONSTRUCTOR, /**< class constructor */ + SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END, /**< class field initializer end */ SCANNER_TYPE_LET_EXPRESSION, /**< let expression */ SCANNER_TYPE_ERR_REDECLARED, /**< syntax error: a variable is redeclared */ SCANNER_TYPE_ERR_ASYNC_FUNCTION, /**< an invalid async function follows */ @@ -65,6 +66,15 @@ typedef struct parser_line_counter_t column; /**< token start column */ } scanner_location_t; +/** + * Source code range with its start and end position. + */ +typedef struct +{ + const uint8_t *source_end_p; /**< end position */ + scanner_location_t start_location; /**< start location */ +} scanner_range_t; + /** * Scanner info blocks which provides information for the parser. */ diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 6a56d468e..656f4fdf9 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -959,6 +959,105 @@ opfunc_async_create_and_await (vm_frame_ctx_t *frame_ctx_p, /**< frame context * return result; } /* opfunc_async_create_and_await */ +/** + * Initialize implicit class fields. + * + * @return ECMA_VALUE_ERROR - initialization fails + * ECMA_VALUE_UNDEFINED - otherwise + */ +ecma_value_t +ecma_op_init_class_fields (ecma_value_t function_object, /**< the function itself */ + ecma_value_t this_val) /**< this_arg of the function */ +{ + JERRY_ASSERT (ecma_is_value_object (function_object)); + JERRY_ASSERT (ecma_is_value_object (this_val)); + + ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_INIT); + ecma_object_t *function_object_p = ecma_get_object_from_value (function_object); + + ecma_property_t *property_p = ecma_find_named_property (function_object_p, name_p); + + if (property_p == NULL) + { + return ECMA_VALUE_UNDEFINED; + } + + ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + ecma_value_t *computed_class_fields_p = NULL; + + name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED); + ecma_property_t *class_field_property_p = ecma_find_named_property (function_object_p, name_p); + + if (class_field_property_p != NULL) + { + ecma_property_value_t *class_field_property_value_p = ECMA_PROPERTY_VALUE_PTR (class_field_property_p); + computed_class_fields_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, class_field_property_value_p->value); + } + + JERRY_ASSERT (ecma_op_is_callable (property_value_p->value)); + + ecma_extended_object_t *ext_function_p; + ext_function_p = (ecma_extended_object_t *) ecma_get_object_from_value (property_value_p->value); + + ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, + ext_function_p->u.function.scope_cp); + const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_function_p); + + ecma_value_t *old_computed_class_fields_p = JERRY_CONTEXT (computed_class_fields_p); + JERRY_CONTEXT (computed_class_fields_p) = computed_class_fields_p; + + ecma_value_t result = vm_run (bytecode_data_p, this_val, scope_p, NULL, 0); + + JERRY_CONTEXT (computed_class_fields_p) = old_computed_class_fields_p; + + JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result) || result == ECMA_VALUE_UNDEFINED); + return result; +} /* ecma_op_init_class_fields */ + +ecma_value_t +ecma_op_add_computed_field (ecma_value_t class_object, /**< class object */ + ecma_value_t name) /**< name of the property */ +{ + ecma_string_t *prop_name_p = ecma_op_to_property_key (name); + + if (JERRY_UNLIKELY (prop_name_p == NULL)) + { + return ECMA_VALUE_ERROR; + } + + if (ecma_prop_name_is_symbol (prop_name_p)) + { + name = ecma_make_symbol_value (prop_name_p); + } + else + { + name = ecma_make_string_value (prop_name_p); + } + + ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED); + ecma_object_t *class_object_p = ecma_get_object_from_value (class_object); + + ecma_property_t *property_p = ecma_find_named_property (class_object_p, name_p); + ecma_value_t *compact_collection_p; + ecma_property_value_t *property_value_p; + + if (property_p == NULL) + { + property_value_p = ecma_create_named_data_property (class_object_p, name_p, ECMA_PROPERTY_FIXED, &property_p); + ECMA_CONVERT_DATA_PROPERTY_TO_INTERNAL_PROPERTY (property_p); + compact_collection_p = ecma_new_compact_collection (); + } + else + { + property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, property_value_p->value); + } + + compact_collection_p = ecma_compact_collection_push_back (compact_collection_p, name); + ECMA_SET_INTERNAL_VALUE_POINTER (property_value_p->value, compact_collection_p); + return ECMA_VALUE_UNDEFINED; +} /* ecma_op_add_computed_field */ + /** * Implicit class constructor handler when the classHeritage is not present. * @@ -973,14 +1072,14 @@ ecma_op_implicit_constructor_handler_cb (const ecma_value_t function_obj, /**< t const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { - JERRY_UNUSED_4 (function_obj, this_val, args_p, args_count); + JERRY_UNUSED_2 (args_p, args_count); if (JERRY_CONTEXT (current_new_target) == NULL) { return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'.")); } - return ECMA_VALUE_UNDEFINED; + return ecma_op_init_class_fields (function_obj, this_val); } /* ecma_op_implicit_constructor_handler_cb */ /** @@ -997,7 +1096,7 @@ ecma_op_implicit_constructor_handler_heritage_cb (const ecma_value_t function_ob const ecma_value_t args_p[], /**< argument list */ const uint32_t args_count) /**< argument number */ { - JERRY_UNUSED_4 (function_obj, this_val, args_p, args_count); + JERRY_UNUSED (this_val); if (JERRY_CONTEXT (current_new_target) == NULL) { @@ -1028,10 +1127,21 @@ ecma_op_implicit_constructor_handler_heritage_cb (const ecma_value_t function_ob ecma_free_value (result); result = ECMA_VALUE_ERROR; } - else if (ecma_is_value_object (proto_value)) + else { - ECMA_SET_POINTER (ecma_get_object_from_value (result)->u2.prototype_cp, - ecma_get_object_from_value (proto_value)); + if (ecma_is_value_object (proto_value)) + { + ECMA_SET_POINTER (ecma_get_object_from_value (result)->u2.prototype_cp, + ecma_get_object_from_value (proto_value)); + } + + ecma_value_t fields_value = ecma_op_init_class_fields (function_obj, result); + + if (ECMA_IS_VALUE_ERROR (fields_value)) + { + ecma_free_value (result); + result = ECMA_VALUE_ERROR; + } } ecma_free_value (proto_value); } @@ -1302,10 +1412,9 @@ opfunc_set_class_attributes (ecma_object_t *obj_p, /**< object */ } else { - JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (property) == ECMA_PROPERTY_TYPE_SPECIAL); - - JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_HASHMAP - || property == ECMA_PROPERTY_TYPE_DELETED); + JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (property) == ECMA_PROPERTY_TYPE_SPECIAL + || (ECMA_PROPERTY_GET_TYPE (property) == ECMA_PROPERTY_TYPE_INTERNAL + && property_pair_p->names_cp[index] == LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED)); } } diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 15c2d8540..068aa97df 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -146,6 +146,12 @@ opfunc_async_generator_yield (ecma_extended_object_t *async_generator_object_p, ecma_value_t opfunc_async_create_and_await (vm_frame_ctx_t *frame_ctx_p, ecma_value_t value, uint16_t extra_flags); +ecma_value_t +ecma_op_init_class_fields (ecma_value_t function_object, ecma_value_t this_val); + +ecma_value_t +ecma_op_add_computed_field (ecma_value_t class_object, ecma_value_t name); + ecma_value_t opfunc_create_implicit_class_constructor (uint8_t opcode); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 2499a574e..e02e816b9 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -610,6 +610,18 @@ vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ ecma_collection_destroy (collection_p); } + if (ecma_is_value_object (completion_value)) + { + ecma_value_t current_function = ecma_make_object_value (JERRY_CONTEXT (current_function_obj_p)); + ecma_value_t fields_value = ecma_op_init_class_fields (current_function, completion_value); + + if (ECMA_IS_VALUE_ERROR (fields_value)) + { + ecma_free_value (completion_value); + completion_value = ECMA_VALUE_ERROR; + } + } + ecma_free_value (func_value); if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (completion_value))) @@ -1787,6 +1799,16 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } goto free_left_value; } + case VM_OC_ADD_COMPUTED_FIELD: + { + result = ecma_op_add_computed_field (stack_top_p[-2], left_value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + goto free_left_value; + } case VM_OC_COPY_DATA_PROPERTIES: { result = *(--stack_top_p); @@ -1991,6 +2013,56 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ opfunc_finalize_class (frame_ctx_p, &stack_top_p, left_value); goto free_left_value; } + case VM_OC_SET_CLASS_FIELD_INIT: + { + ecma_string_t *property_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_INIT); + ecma_object_t *object_p = ecma_get_object_from_value (stack_top_p[-2]); + + ecma_property_value_t *property_value_p = ecma_create_named_data_property (object_p, + property_name_p, + ECMA_PROPERTY_FIXED, + NULL); + property_value_p->value = left_value; + + property_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED); + ecma_property_t *property_p = ecma_find_named_property (object_p, property_name_p); + + if (property_p != NULL) + { + property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + ecma_value_t *compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, + property_value_p->value); + compact_collection_p = ecma_compact_collection_shrink (compact_collection_p); + ECMA_SET_INTERNAL_VALUE_POINTER (property_value_p->value, compact_collection_p); + } + + goto free_left_value; + } + case VM_OC_RUN_CLASS_FIELD_INIT: + { + result = ecma_op_init_class_fields (ecma_make_object_value (JERRY_CONTEXT (current_function_obj_p)), + frame_ctx_p->this_binding); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + continue; + } + case VM_OC_SET_NEXT_COMPUTED_FIELD: + { + ecma_integer_value_t next_index = ecma_get_integer_from_value (stack_top_p[-2]) + 1; + stack_top_p[-2] = ecma_make_integer_value (next_index); + stack_top_p++; + + ecma_value_t *computed_class_fields_p = JERRY_CONTEXT (computed_class_fields_p); + JERRY_ASSERT ((ecma_value_t) next_index < ECMA_COMPACT_COLLECTION_GET_SIZE (computed_class_fields_p)); + + result = stack_top_p[-2]; + stack_top_p[-1] = ecma_copy_value (computed_class_fields_p[next_index]); + stack_top_p[-2] = ecma_copy_value (frame_ctx_p->this_binding); + break; + } case VM_OC_PUSH_SUPER_CONSTRUCTOR: { result = ecma_op_function_get_super_constructor (JERRY_CONTEXT (current_function_obj_p)); diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 5ad9e1b83..998dbafe8 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -260,6 +260,9 @@ typedef enum VM_OC_PUSH_IMPLICIT_CTOR, /**< create implicit class constructor */ VM_OC_INIT_CLASS, /**< initialize class */ VM_OC_FINALIZE_CLASS, /**< finalize class */ + VM_OC_SET_CLASS_FIELD_INIT, /**< store the class field initializer function */ + VM_OC_RUN_CLASS_FIELD_INIT, /**< run the class field initializer function */ + VM_OC_SET_NEXT_COMPUTED_FIELD, /**< set the next computed field of a class */ VM_OC_PUSH_SUPER_CONSTRUCTOR, /**< getSuperConstructor operation */ VM_OC_RESOLVE_LEXICAL_THIS, /**< resolve this_binding from from the lexical environment */ VM_OC_SUPER_REFERENCE, /**< push super reference */ @@ -289,7 +292,8 @@ typedef enum VM_OC_PUSH_NEW_TARGET, /**< push new.target onto the stack */ VM_OC_REQUIRE_OBJECT_COERCIBLE,/**< RequireObjectCoercible opretaion */ VM_OC_ASSIGN_SUPER, /**< assign super reference */ - VM_OC_SET__PROTO__, /**< set prototpe when __proto__: form is used */ + VM_OC_SET__PROTO__, /**< set prototype when __proto__: form is used */ + VM_OC_ADD_COMPUTED_FIELD, /**< add computed field name */ #endif /* ENABLED (JERRY_ESNEXT) */ VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; @@ -337,6 +341,9 @@ typedef enum VM_OC_PUSH_IMPLICIT_CTOR = VM_OC_NONE, /**< create implicit class constructor */ VM_OC_INIT_CLASS = VM_OC_NONE, /**< initialize class */ VM_OC_FINALIZE_CLASS = VM_OC_NONE, /**< finalize class */ + VM_OC_SET_CLASS_FIELD_INIT = VM_OC_NONE, /**< store the class field initializer function */ + VM_OC_RUN_CLASS_FIELD_INIT = VM_OC_NONE, /**< run the class field initializer function */ + VM_OC_SET_NEXT_COMPUTED_FIELD = VM_OC_NONE, /**< set the next computed field of a class */ VM_OC_PUSH_SUPER_CONSTRUCTOR = VM_OC_NONE, /**< getSuperConstructor operation */ VM_OC_RESOLVE_LEXICAL_THIS = VM_OC_NONE, /**< resolve this_binding from from the lexical environment */ VM_OC_SUPER_REFERENCE = VM_OC_NONE, /**< push super reference */ @@ -366,7 +373,8 @@ typedef enum VM_OC_PUSH_NEW_TARGET = VM_OC_NONE, /**< push new.target onto the stack */ VM_OC_REQUIRE_OBJECT_COERCIBLE = VM_OC_NONE,/**< RequireObjectCoercible opretaion */ VM_OC_ASSIGN_SUPER = VM_OC_NONE, /**< assign super reference */ - VM_OC_SET__PROTO__ = VM_OC_NONE, /**< set prototpe when __proto__: form is used */ + VM_OC_SET__PROTO__ = VM_OC_NONE, /**< set prototype when __proto__: form is used */ + VM_OC_ADD_COMPUTED_FIELD = VM_OC_NONE, /**< add computed field name */ #endif /* !ENABLED (JERRY_ESNEXT) */ VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ diff --git a/tests/jerry/es.next/class-fields1.js b/tests/jerry/es.next/class-fields1.js new file mode 100644 index 000000000..2b0817788 --- /dev/null +++ b/tests/jerry/es.next/class-fields1.js @@ -0,0 +1,182 @@ +// 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. + +function check_syntax_error(code) +{ + try { + eval(code) + assert(false) + } catch (e) { + assert(e instanceof SyntaxError) + } +} + +function check_property(obj, name, value) +{ + property = Object.getOwnPropertyDescriptor(obj, name) + assert(typeof property === "object") + assert(property.value === value) +} + +check_syntax_error("class C { get a = 5 }"); +check_syntax_error("class C { id1 id2 }"); +check_syntax_error("class C { a = 5,6 }"); +check_syntax_error("class C { set\na = 6 }"); +check_syntax_error("class C { constructor }"); +check_syntax_error("class C { static constructor }"); +check_syntax_error("class C { constructor = 1 }"); +check_syntax_error("class C { static constructor = 1 }"); +check_syntax_error("class C { f = arguments }"); +check_syntax_error("class C { static f = a\\u0072guments }"); +check_syntax_error("class C { f = () => arguments }"); +check_syntax_error("class C { f = arguments => 1 }"); +check_syntax_error("class C { f = ([arguments]) => 1 }"); +check_syntax_error("new class { f = eval('arguments') }"); +check_syntax_error("new class { f = eval('arguments => 1') }"); + +var res = 10 +var counter = 0 + +function f1() { + counter++ + return 5 +} + +var C1 = class { + get = "a" + f1() + static; set; a = () => Math.cos(0) + v\u0061r + f\u006fr = () => this + arguments = this +} + +res = new C1 +check_property(res, "get", "a5") +check_property(res, "static", undefined) +check_property(res, "set", undefined) +assert(res.a() === 1) +check_property(res, "var", undefined) +assert(res.for() === res) +assert(res.arguments === res) + +class C2 { + constructor(a = this.x, b = this.y) { + assert(a === undefined) + assert(b === undefined) + check_property(this, 'x', 11) + check_property(this, 'y', "ab") + } + x = 5 + 6 + y = "a" + 'b' +} + +res = new C2 + +class C3 { + constructor() { + assert(this.x === 1) + return { z:"zz" } + } + x = 1 +} + +class C4 extends C3 { + constructor() { + super() + assert(Object.getOwnPropertyDescriptor(this, "x") === undefined) + check_property(this, "y", 2) + check_property(this, "z", "zz") + } + y = 2 +} +new C4 + +var o = {} +class C5 extends C3 { + 'pr op' = o + 3 = true +} +res = new C5 +assert(Object.getOwnPropertyDescriptor(res, "x") === undefined) +check_property(res, "pr op", o) +check_property(res, "3", true) +check_property(res, "z", "zz") + +class C6 { + a= () => this + b= this +} + +class C7 extends C6 { + c= () => this + d= this +} + +count = 0 +class C8 extends C7 { + constructor() { + count++ + super() + } + + e= () => this + f= this +} + +var res = new C8 +assert(res.a() === res) +assert(res.b === res) +assert(res.c() === res) +assert(res.d === res) +assert(res.e() === res) +assert(res.f === res) + +count = 0 +class C9 { + a=assert(++count === 5) + a=assert(++count === 6) + a=assert(++count === 7) + a=assert(++count === 8) + static a=assert(++count === 1) + static a=assert(++count === 2) + static a=assert(++count === 3) + static a=assert(++count === 4) +} + +assert(count === 4) +new C9 +assert(count === 8) + +count = 0 +class C10 { + [(assert(++count == 1), "aa")] = assert(++count == 5); + [(assert(++count == 2), "bb")] = assert(++count == 6); + cc = assert(++count == 7); + [(assert(++count == 3), "aa")] = assert(++count == 8); + [(assert(++count == 4), "bb")] = assert(++count == 9); +} + +assert(count == 4) +assert(Reflect.ownKeys(new C10).toString() === "aa,bb,cc"); +assert(count == 9) + +res = "p" +class C11 { + p1 = assert(Reflect.ownKeys(this).toString() === ""); + [res + 2] = assert(Reflect.ownKeys(this).toString() === "p1"); + [res + 1] = assert(Reflect.ownKeys(this).toString() === "p1,p2"); + p3 = assert(Reflect.ownKeys(this).toString() === "p1,p2"); + [res + 4] = assert(Reflect.ownKeys(this).toString() === "p1,p2,p3"); +} +new C11 diff --git a/tests/jerry/es.next/class-fields2.js b/tests/jerry/es.next/class-fields2.js new file mode 100644 index 000000000..626fc55e0 --- /dev/null +++ b/tests/jerry/es.next/class-fields2.js @@ -0,0 +1,139 @@ +// 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 count = 0 +class C1 { + error = assert(++count === 1) + error = function() { throw 40.5 }() +} + +try { + new C1 + assert(false) +} catch(e) { + assert(e === 40.5) + assert(count === 1) +} + +count = 0 +class C2 { + constructor(a = assert(++count === 1)) {} + error = function() { throw "Err" }() + error = assert(false) +} + +try { + new C2 + assert(false) +} catch(e) { + assert(e === "Err") + assert(count === 1) +} + +count = 0 +var o = {} + +class C3 extends class { + error = function() { throw o }() +} { + constructor() { + assert(++count === 1) + super() + assert(false) + } +} + +try { + new C3 + assert(false) +} catch (e) { + assert(e === o) + assert(count === 1) +} + +count = 0 +class C4 { + constructor() { + assert(++count === 2) + } + a = assert(++count === 1) +} + +class C5 extends C4 { + ok = assert(++count === 3) + error = function() { assert(++count === 4); throw "Except" }() + never = assert(false) +} + +try { + new C5 + assert(false) +} catch (e) { + assert(e === "Except") + assert(count === 4) +} + +count = 0 +o = [] +class C6 { + a = assert(++count === 2) +} + +class C7 extends C6 { + constructor() { + assert(++count === 1) + eval('super()') + assert(false) + } + ok = assert(++count === 3) + error = function() { assert(++count === 4); throw o }() + never = assert(false) +} + +try { + new C7 + assert(false) +} catch (e) { + assert(e === o) + assert(count === 4) +} + +var res +class C8 { + /* Create a non-configurable accessor */ + a = (res = this, Object.defineProperty(this, "b", { get() {} })); + b = 6 +} + +try { + new C8 + assert(false) +} catch(e) { + assert(e instanceof TypeError) + assert(Reflect.ownKeys(res).toString() === "b,a") +} + +class C9 { + ["p" + 1] + ["p" + 2] = (res = this, Object.freeze(this)); + p3 +} + +try { + new C9 + assert(false) +} catch(e) { + assert(e instanceof TypeError) + assert(Reflect.ownKeys(res).toString() === "p1") +} diff --git a/tests/jerry/es.next/class-fields3.js b/tests/jerry/es.next/class-fields3.js new file mode 100644 index 000000000..29c9909a5 --- /dev/null +++ b/tests/jerry/es.next/class-fields3.js @@ -0,0 +1,53 @@ +// 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. + +function check_property(obj, name, value) +{ + property = Object.getOwnPropertyDescriptor(obj, name) + assert(typeof property === "object") + assert(property.value === value) +} + +var o = {} +var name = "Pro" +var res = 0 +var counter = 0 + +function f1() { + counter++ +} + +class C1 { + static + v\u0061r + static Prop = + res + = + "msg" + static + Prop + = + f1() + static [name + "p"] = (f1(), o) + static 22 = 3 * 4 ;static 23 = 5 + 6 + static 'a b' +} + +check_property(C1, "var", undefined) +check_property(C1, "Prop", o) +check_property(C1, 22, 12) +check_property(C1, 23, 11) +check_property(C1, "a b", undefined) +assert(res === "msg") +assert(counter === 2) diff --git a/tests/jerry/es.next/class-with.js b/tests/jerry/es.next/class-with.js new file mode 100644 index 000000000..a271bf034 --- /dev/null +++ b/tests/jerry/es.next/class-with.js @@ -0,0 +1,31 @@ +/* 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 called = false +var obj = { f() { assert(this === obj); called = true } } + +function f() { + assert(false) +} + +with (obj) { + new class { + constructor() { + f() + } + } +} + +assert(called) diff --git a/tests/jerry/es.next/regression-test-issue-4055.js b/tests/jerry/es.next/regression-test-issue-4055.js deleted file mode 100644 index 08c1631ba..000000000 --- a/tests/jerry/es.next/regression-test-issue-4055.js +++ /dev/null @@ -1,20 +0,0 @@ -// 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. - -try { - eval("class cla { static }"); - assert(false); -} catch (e) { - assert(e instanceof SyntaxError); -} diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml index 79c70b116..f94d46b07 100644 --- a/tests/test262-esnext-excludelist.xml +++ b/tests/test262-esnext-excludelist.xml @@ -3238,7 +3238,6 @@ - @@ -4204,7 +4203,6 @@ - @@ -5784,17 +5782,11 @@ - - - - - - @@ -5823,18 +5815,11 @@ - - - - - - - @@ -5863,18 +5848,11 @@ - - - - - - - @@ -5903,18 +5881,11 @@ - - - - - - - @@ -5943,18 +5914,11 @@ - - - - - - - @@ -5983,18 +5947,11 @@ - - - - - - - @@ -6023,7 +5980,6 @@ - @@ -6158,33 +6114,10 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -6194,23 +6127,12 @@ - - - - - - - - - - - @@ -6239,18 +6161,11 @@ - - - - - - - @@ -6279,18 +6194,11 @@ - - - - - - - @@ -6319,18 +6227,11 @@ - - - - - - - @@ -6359,18 +6260,11 @@ - - - - - - - @@ -6399,7 +6293,6 @@ - @@ -6500,19 +6393,11 @@ - - - - - - - - @@ -6541,18 +6426,11 @@ - - - - - - - @@ -6581,18 +6459,11 @@ - - - - - - - @@ -6621,18 +6492,11 @@ - - - - - - - @@ -6661,18 +6525,11 @@ - - - - - - - @@ -6701,14 +6558,8 @@ - - - - - - @@ -6727,11 +6578,6 @@ - - - - - @@ -6743,17 +6589,11 @@ - - - - - - @@ -6782,7 +6622,6 @@ - @@ -6822,7 +6661,6 @@ - @@ -7555,18 +7393,11 @@ - - - - - - - @@ -7595,18 +7426,11 @@ - - - - - - - @@ -7635,18 +7459,11 @@ - - - - - - - @@ -7675,18 +7492,11 @@ - - - - - - - @@ -7715,18 +7525,11 @@ - - - - - - - @@ -7755,18 +7558,11 @@ - - - - - - - @@ -7795,7 +7591,6 @@ - @@ -7926,42 +7721,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -7971,23 +7734,12 @@ - - - - - - - - - - - @@ -8016,18 +7768,11 @@ - - - - - - - @@ -8056,18 +7801,11 @@ - - - - - - - @@ -8096,18 +7834,11 @@ - - - - - - - @@ -8136,18 +7867,11 @@ - - - - - - - @@ -8176,7 +7900,6 @@ - @@ -8322,7 +8045,6 @@ - @@ -8332,21 +8054,12 @@ - - - - - - - - - @@ -8375,18 +8088,11 @@ - - - - - - - @@ -8415,18 +8121,11 @@ - - - - - - - @@ -8455,18 +8154,11 @@ - - - - - - - @@ -8495,18 +8187,11 @@ - - - - - - - @@ -8535,23 +8220,15 @@ - - - - - - - - @@ -8569,12 +8246,6 @@ - - - - - - @@ -8586,17 +8257,11 @@ - - - - - - @@ -8625,8 +8290,6 @@ - -