From 70e275e92f0511570d3d7fb1ea76006427dda781 Mon Sep 17 00:00:00 2001 From: Szilagyi Adam Date: Fri, 26 Nov 2021 12:24:59 +0100 Subject: [PATCH] Implement ECMAScript 2022 private class methods and fields (#4831) Co-authored-by: Robert Fancsik robert.fancsik@h-lab.eu Co-authored-by: Martin Negyokru mnegyokru@inf.u-szeged.hu JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu --- .../ecma/base/ecma-error-messages.inc.h | 20 + jerry-core/ecma/base/ecma-error-messages.ini | 8 +- jerry-core/ecma/base/ecma-gc.c | 55 +- jerry-core/ecma/base/ecma-globals.h | 76 +- .../ecma/base/ecma-helpers-collection.c | 25 + jerry-core/ecma/base/ecma-helpers-string.c | 3 +- jerry-core/ecma/base/ecma-helpers.c | 4 +- jerry-core/ecma/base/ecma-helpers.h | 2 + .../ecma/builtin-objects/ecma-builtins.c | 8 +- .../ecma/operations/ecma-get-put-value.c | 4 +- jerry-core/ecma/operations/ecma-lex-env.c | 4 +- jerry-core/ecma/operations/ecma-objects.c | 9 +- jerry-core/ecma/operations/ecma-reference.c | 4 +- jerry-core/lit/lit-char-helpers.h | 1 + jerry-core/lit/lit-magic-strings.h | 7 + jerry-core/parser/js/byte-code.c | 2 +- jerry-core/parser/js/byte-code.h | 58 +- jerry-core/parser/js/js-lexer.c | 60 +- jerry-core/parser/js/js-lexer.h | 17 +- jerry-core/parser/js/js-parser-expr.c | 279 +- jerry-core/parser/js/js-parser-internal.h | 24 +- jerry-core/parser/js/js-parser.c | 250 +- jerry-core/parser/js/js-scanner-internal.h | 1 + jerry-core/parser/js/js-scanner-util.c | 77 +- jerry-core/parser/js/js-scanner.c | 179 +- jerry-core/parser/js/js-scanner.h | 46 + .../parser/js/parser-error-messages.inc.h | 10 +- .../parser/js/parser-error-messages.ini | 6 +- jerry-core/parser/js/parser-errors.h | 1 + jerry-core/vm/opcodes.c | 598 ++- jerry-core/vm/opcodes.h | 22 + jerry-core/vm/vm.c | 153 +- jerry-core/vm/vm.h | 14 + tests/jerry/es.next/private_fields.js | 322 ++ tests/test262-esnext-excludelist.xml | 4188 ----------------- 35 files changed, 2196 insertions(+), 4341 deletions(-) create mode 100644 tests/jerry/es.next/private_fields.js diff --git a/jerry-core/ecma/base/ecma-error-messages.inc.h b/jerry-core/ecma/base/ecma-error-messages.inc.h index 380c55a26..9a90278ad 100644 --- a/jerry-core/ecma/base/ecma-error-messages.inc.h +++ b/jerry-core/ecma/base/ecma-error-messages.inc.h @@ -298,6 +298,9 @@ ECMA_ERROR_DEF (ECMA_ERR_MODULE_MUST_BE_IN_LINKED_STATE, "Module must be in link ECMA_ERROR_DEF (ECMA_ERR_UNKNOWN_EXPORT, "Native module export not found") #endif /* JERRY_MODULE_SYSTEM */ ECMA_ERROR_DEF (ECMA_ERR_PASSED_ARGUMENT_IS_NOT_A_REALM, "Passed argument is not a realm") +#if JERRY_ESNEXT +ECMA_ERROR_DEF (ECMA_ERR_PRIVATE_METHOD_IS_NOT_WRITABLE, "Private method is not writable") +#endif /* JERRY_ESNEXT */ #if JERRY_BUILTIN_BIGINT || JERRY_BUILTIN_NUMBER ECMA_ERROR_DEF (ECMA_ERR_RADIX_IS_OUT_OF_RANGE, "Radix must be between 2 and 36") #endif /* JERRY_BUILTIN_BIGINT \ @@ -548,6 +551,9 @@ ECMA_ERROR_DEF (ECMA_ERR_VALUE_RECEIVED_BY_YIELD_IS_NOT_OBJECT, "Value received #if JERRY_BUILTIN_BOOLEAN ECMA_ERROR_DEF (ECMA_ERR_ARGUMENT_THIS_NOT_BOOLEAN_OBJECT, "Argument 'this' is not a Boolean object") #endif /* JERRY_BUILTIN_BOOLEAN */ +#if JERRY_ESNEXT +ECMA_ERROR_DEF (ECMA_ERR_CANNOT_DECLARE_SAME_PRIVATE_FIELD_TWICE, "Cannot declare same private field twice") +#endif /* JERRY_ESNEXT */ #if JERRY_BUILTIN_TYPEDARRAY ECMA_ERROR_DEF (ECMA_ERR_CONSTRUCTOR_FLOAT32_ARRAY_REQUIRES_NEW, "Constructor Float32Array requires 'new'") #endif /* JERRY_BUILTIN_TYPEDARRAY */ @@ -608,6 +614,8 @@ ECMA_ERROR_DEF (ECMA_ERR_CANNOT_CONVERT_TO_OBJECT, "Cannot convert undefined or ECMA_ERROR_DEF (ECMA_ERR_PRECISION_DIGITS_MUST_BE_BETWEEN_IN_RANGE, "Precision digits must be between 1 and 100") #endif /* JERRY_BUILTIN_NUMBER */ #if JERRY_ESNEXT +ECMA_ERROR_DEF (ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_GETTER, "Private field was defined without a getter") +ECMA_ERROR_DEF (ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_SETTER, "Private field was defined without a setter") ECMA_ERROR_DEF (ECMA_ERR_PROPERTY_NAME_IS_NEITHER_SYMBOL_NOR_STRING, "Property name is neither Symbol nor string") #endif /* JERRY_ESNEXT */ #if JERRY_BUILTIN_BIGINT @@ -818,8 +826,20 @@ ECMA_ERROR_DEF (ECMA_ERR_TARGET_NOT_EXTENSIBLE_DIFFERENT_PROTOTYPE_RETURNED, "Target object is non-extensible and trap returned different prototype") ECMA_ERROR_DEF (ECMA_ERR_TRAP_TRUISH_ADDING_PROPERTY_NON_EXTENSIBLE_TARGET, "Trap returned truish for adding property to the non-extensible target") +#endif /* JERRY_BUILTIN_PROXY */ +#if JERRY_ESNEXT +ECMA_ERROR_DEF (ECMA_ERR_CANNOT_READ_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT, + "Cannot read private member to an object whose class did not declare it") +#endif /* JERRY_ESNEXT */ +#if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_GIVEN_PROPERTY_IS_A_NON_CONFIGURABLE, "Given property is a non-configurable data property on the proxy target") +#endif /* JERRY_BUILTIN_PROXY */ +#if JERRY_ESNEXT +ECMA_ERROR_DEF (ECMA_ERR_CANNOT_WRITE_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT, + "Cannot write private member to an object whose class did not declare it") +#endif /* JERRY_ESNEXT */ +#if JERRY_BUILTIN_PROXY ECMA_ERROR_DEF (ECMA_ERR_TRAP_FALSISH_PROPERTY_TARGET_NOT_EXTENSIBLE, "Trap returned falsish for property but the proxy target is not extensible") ECMA_ERROR_DEF (ECMA_ERR_PROXY_PROPERTY_NOT_CONFIGURABLE_NOT_HAVE_GETTER, diff --git a/jerry-core/ecma/base/ecma-error-messages.ini b/jerry-core/ecma/base/ecma-error-messages.ini index c4bb36b27..31e082a03 100644 --- a/jerry-core/ecma/base/ecma-error-messages.ini +++ b/jerry-core/ecma/base/ecma-error-messages.ini @@ -323,4 +323,10 @@ ECMA_ERR_PROXY_TARGET_IS_NOT_A_CONSTRUCTOR = "Proxy target is not a constructor" ECMA_ERR_MAXIMUM_CALL_STACK_SIZE_EXCEEDED = "Maximum call stack size exceeded" ECMA_ERR_INVALID_SNAPSHOT_FORMAT = "Invalid snapshot format" ECMA_ERR_INVALID_SNAPSHOT_VERSION_OR_FEATURES = "Invalid snapshot version or unsupported features present" -ECMA_ERR_RECEIVER_MUST_BE_AN_OBJECT = "Receiver must be an object" \ No newline at end of file +ECMA_ERR_RECEIVER_MUST_BE_AN_OBJECT = "Receiver must be an object" +ECMA_ERR_CANNOT_DECLARE_SAME_PRIVATE_FIELD_TWICE = "Cannot declare same private field twice" +ECMA_ERR_CANNOT_WRITE_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT = "Cannot write private member to an object whose class did not declare it" +ECMA_ERR_PRIVATE_METHOD_IS_NOT_WRITABLE = "Private method is not writable" +ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_SETTER = "Private field was defined without a setter" +ECMA_ERR_CANNOT_READ_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT = "Cannot read private member to an object whose class did not declare it" +ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_GETTER = "Private field was defined without a getter" diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 83dc808ba..a1f52aa99 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -328,6 +328,27 @@ ecma_gc_mark_properties (ecma_object_t *object_p, /**< object */ ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->function_object)); break; } + case LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS: + { + ecma_value_t *compact_collection_p; + compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, property_pair_p->values[index].value); + + ecma_value_t *end_p = ecma_compact_collection_end (compact_collection_p); + ecma_value_t *current_p = compact_collection_p + 1; + + while (end_p - current_p >= ECMA_PRIVATE_ELEMENT_LIST_SIZE) + { + current_p++; /* skip the type */ + current_p++; /* skip the name */ + ecma_value_t value = *current_p++; + + if (!ecma_is_value_undefined (value)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (value)); + } + } + break; + } #endif /* JERRY_ESNEXT */ #if JERRY_BUILTIN_CONTAINER case LIT_INTERNAL_MAGIC_STRING_WEAK_REFS: @@ -816,8 +837,12 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ #if JERRY_MODULE_SYSTEM if (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) { - ecma_gc_mark_properties (object_p, true); - ecma_gc_set_object_visited (((ecma_lexical_environment_class_t *) object_p)->module_p); + if (ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)) + { + ecma_gc_mark_properties (object_p, true); + } + + ecma_gc_set_object_visited (((ecma_lexical_environment_class_t *) object_p)->object_p); return; } #endif /* JERRY_MODULE_SYSTEM */ @@ -1591,6 +1616,26 @@ ecma_gc_free_property (ecma_object_t *object_p, /**< object */ ecma_compact_collection_free (compact_collection_p); break; } + case LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS: + { + ecma_value_t *compact_collection_p; + compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, value); + + ecma_value_t *end_p = ecma_compact_collection_end (compact_collection_p); + ecma_value_t *current_p = compact_collection_p + 1; + + JERRY_ASSERT ((end_p - current_p) % ECMA_PRIVATE_ELEMENT_LIST_SIZE == 0); + + while (current_p < end_p) + { + current_p++; /* skip the type */ + ecma_deref_ecma_string (ecma_get_prop_name_from_value (*current_p++)); + current_p++; /* skip the value */ + } + + ecma_compact_collection_destroy (compact_collection_p); + break; + } #endif /* JERRY_ESNEXT */ #if JERRY_BUILTIN_WEAKREF || JERRY_BUILTIN_CONTAINER case LIT_INTERNAL_MAGIC_STRING_WEAK_REFS: @@ -1684,7 +1729,11 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ if (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA)) { - ecma_gc_free_properties (object_p, ECMA_GC_FREE_REFERENCES); + if (ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)) + { + ecma_gc_free_properties (object_p, ECMA_GC_FREE_REFERENCES); + } + ecma_dealloc_extended_object (object_p, sizeof (ecma_lexical_environment_class_t)); return; } diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index db4598574..1273ffc34 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -1184,16 +1184,32 @@ typedef struct ecma_built_in_props_t built_in; /**< built-in object part */ } ecma_extended_built_in_object_t; +/** + * Type of lexical environment with class + */ +typedef enum +{ + ECMA_LEX_ENV_CLASS_TYPE_MODULE, /**< module object reference */ + ECMA_LEX_ENV_CLASS_TYPE_CLASS_ENV, /**< class constructor object reference */ +} ecma_lexical_environment_class_type_t; + /** * Description of lexical environment with class */ typedef struct { ecma_object_t lexical_env; /**< lexical environment header */ - - ecma_object_t *module_p; /**< module reference */ + uint32_t type; /**< element of ecma_lexical_environment_class_type_t */ + ecma_object_t *object_p; /**< object reference */ } ecma_lexical_environment_class_t; +/** + * Check whether the given lexical class environment is a module + */ +#define ECMA_LEX_ENV_CLASS_IS_MODULE(lex_env_p) \ + (((lex_env_p)->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) \ + && ((ecma_lexical_environment_class_t *) (lex_env_p))->type == ECMA_LEX_ENV_CLASS_TYPE_MODULE) + /** * Description of native functions */ @@ -1892,6 +1908,13 @@ typedef struct } u; } ecma_extended_string_t; +#if JERRY_ESNEXT +/** + * Required number of ecma values in a compact collection to represent PrivateElement + */ +#define ECMA_PRIVATE_ELEMENT_LIST_SIZE 3 +#endif /* JERRY_ESNEXT */ + /** * String builder header */ @@ -2248,24 +2271,23 @@ typedef struct } ecma_dataview_object_t; #endif /* JERRY_BUILTIN_DATAVIEW */ -/** - * Flag for indicating whether the symbol is a well known symbol - * - * See also: 6.1.5.1 - */ -#define ECMA_GLOBAL_SYMBOL_FLAG 0x01 - -/** - * Bitshift index for indicating whether the symbol is a well known symbol - * - * See also: 6.1.5.1 - */ -#define ECMA_GLOBAL_SYMBOL_SHIFT 1 +typedef enum +{ + ECMA_SYMBOL_FLAG_NONE = 0, /**< no options */ + ECMA_SYMBOL_FLAG_GLOBAL = (1 << 0), /**< symbol is a well known symbol, See also: 6.1.5.1 */ + ECMA_SYMBOL_FLAG_PRIVATE_KEY = (1 << 1), /**< symbol is a private field */ + ECMA_SYMBOL_FLAG_PRIVATE_INSTANCE_METHOD = (1 << 2), /**< symbol is a private method or accessor */ +} ecma_symbol_flags_t; /** * Bitshift index for the symbol hash property */ -#define ECMA_SYMBOL_HASH_SHIFT 2 +#define ECMA_SYMBOL_FLAGS_SHIFT 3 + +/** + * Bitmask for symbol hash flags + */ +#define ECMA_SYMBOL_FLAGS_MASK ((1 << ECMA_SYMBOL_FLAGS_SHIFT) - 1) #if (JERRY_STACK_LIMIT != 0) /** @@ -2481,6 +2503,28 @@ typedef struct ecma_extended_object_t header; /**< header part */ ecma_value_t sync_next_method; /**< IteratorRecord [[NextMethod]] internal slot */ } ecma_async_from_sync_iterator_object_t; + +/** + * Private method kind + */ +typedef enum +{ + ECMA_PRIVATE_FIELD = 0, /**< private field */ + ECMA_PRIVATE_METHOD, /**< private method */ + ECMA_PRIVATE_GETTER, /**< private setter */ + ECMA_PRIVATE_SETTER, /**< private getter */ +} ecma_private_property_kind_t; + +/** + * Private static property flag + */ +#define ECMA_PRIVATE_PROPERTY_STATIC_FLAG (1 << 2) + +/* + * Get private property kind from a descriptor + */ +#define ECMA_PRIVATE_PROPERTY_KIND(prop) ((prop) & ((ECMA_PRIVATE_PROPERTY_STATIC_FLAG - 1))) + #endif /* JERRY_ESNEXT */ /** diff --git a/jerry-core/ecma/base/ecma-helpers-collection.c b/jerry-core/ecma/base/ecma-helpers-collection.c index d140ab932..cecb3cdb0 100644 --- a/jerry-core/ecma/base/ecma-helpers-collection.c +++ b/jerry-core/ecma/base/ecma-helpers-collection.c @@ -417,6 +417,31 @@ ecma_compact_collection_free (ecma_value_t *compact_collection_p) /**< compact c jmem_heap_free_block (compact_collection_p, size * sizeof (ecma_value_t)); } /* ecma_compact_collection_free */ +/** + * Get the end of a compact collection + * + * @return pointer to the compact collection end + */ +ecma_value_t * +ecma_compact_collection_end (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); + + return compact_collection_p + size - unused_items; +} /* ecma_compact_collection_end */ + +/** + * Destroy a compact collection + */ +void +ecma_compact_collection_destroy (ecma_value_t *compact_collection_p) /**< compact collection */ +{ + ecma_value_t size = ECMA_COMPACT_COLLECTION_GET_SIZE (compact_collection_p); + + jmem_heap_free_block (compact_collection_p, size * sizeof (ecma_value_t)); +} /* ecma_compact_collection_destroy */ + /** * @} * @} diff --git a/jerry-core/ecma/base/ecma-helpers-string.c b/jerry-core/ecma/base/ecma-helpers-string.c index be7e31fd8..12770f362 100644 --- a/jerry-core/ecma/base/ecma-helpers-string.c +++ b/jerry-core/ecma/base/ecma-helpers-string.c @@ -221,8 +221,7 @@ ecma_new_symbol_from_descriptor_string (ecma_value_t string_desc) /**< ecma-stri ecma_extended_string_t *symbol_p = ecma_alloc_extended_string (); symbol_p->header.refs_and_container = ECMA_STRING_REF_ONE | ECMA_STRING_CONTAINER_SYMBOL; symbol_p->u.symbol_descriptor = string_desc; - symbol_p->header.u.hash = (lit_string_hash_t) (((uintptr_t) symbol_p) >> ECMA_SYMBOL_HASH_SHIFT); - JERRY_ASSERT ((symbol_p->header.u.hash & ECMA_GLOBAL_SYMBOL_FLAG) == 0); + symbol_p->header.u.hash = (lit_string_hash_t) (((uintptr_t) symbol_p) & (uintptr_t) ~ECMA_SYMBOL_FLAGS_MASK); return (ecma_string_t *) symbol_p; } /* ecma_new_symbol_from_descriptor_string */ diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index b47e15354..bd25d9d37 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -306,7 +306,7 @@ ecma_get_lex_env_binding_object (const ecma_object_t *object_p) /**< object-boun #if JERRY_ESNEXT JERRY_ASSERT (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND || (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS - && (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) == 0)); + && !ECMA_LEX_ENV_CLASS_IS_MODULE (object_p))); #else /* !JERRY_ESNEXT */ JERRY_ASSERT (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); #endif /* JERRY_ESNEXT */ @@ -593,7 +593,7 @@ ecma_create_named_reference_property (ecma_object_t *object_p, /**< object */ JERRY_ASSERT (ecma_find_named_property (object_p, name_p) == NULL); JERRY_ASSERT ((ecma_is_lexical_environment (object_p) && ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS - && (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA)) + && ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)) || ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_MODULE_NAMESPACE)); uint8_t type_and_flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE; diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 609cf62e6..375fd7b87 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -434,6 +434,8 @@ 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_value_t *ecma_compact_collection_end (ecma_value_t *compact_collection_p); +void ecma_compact_collection_destroy (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); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.c b/jerry-core/ecma/builtin-objects/ecma-builtins.c index b7f4aa45d..a570a0bae 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.c @@ -937,9 +937,9 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * lit_magic_string_id_t magic_string_id = ecma_get_string_magic (property_name_p); #if JERRY_ESNEXT - if (JERRY_UNLIKELY (ecma_prop_name_is_symbol (property_name_p)) && property_name_p->u.hash & ECMA_GLOBAL_SYMBOL_FLAG) + if (JERRY_UNLIKELY (ecma_prop_name_is_symbol (property_name_p)) && property_name_p->u.hash & ECMA_SYMBOL_FLAG_GLOBAL) { - magic_string_id = (property_name_p->u.hash >> ECMA_GLOBAL_SYMBOL_SHIFT); + magic_string_id = (property_name_p->u.hash >> ECMA_SYMBOL_FLAGS_SHIFT); } #endif /* JERRY_ESNEXT */ @@ -1254,9 +1254,9 @@ ecma_builtin_delete_built_in_property (ecma_object_t *object_p, /**< object */ #if JERRY_ESNEXT if (JERRY_UNLIKELY (ecma_prop_name_is_symbol (property_name_p))) { - if (property_name_p->u.hash & ECMA_GLOBAL_SYMBOL_FLAG) + if (property_name_p->u.hash & ECMA_SYMBOL_FLAG_GLOBAL) { - magic_string_id = (property_name_p->u.hash >> ECMA_GLOBAL_SYMBOL_SHIFT); + magic_string_id = (property_name_p->u.hash >> ECMA_SYMBOL_FLAGS_SHIFT); } } #endif /* JERRY_ESNEXT */ diff --git a/jerry-core/ecma/operations/ecma-get-put-value.c b/jerry-core/ecma/operations/ecma-get-put-value.c index 964c39efb..41bdf37d1 100644 --- a/jerry-core/ecma/operations/ecma-get-put-value.c +++ b/jerry-core/ecma/operations/ecma-get-put-value.c @@ -77,7 +77,7 @@ ecma_op_get_value_lex_env_base (ecma_object_t *lex_env_p, /**< lexical environme case ECMA_LEXICAL_ENVIRONMENT_CLASS: { #if JERRY_MODULE_SYSTEM - if (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) + if (ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); @@ -237,7 +237,7 @@ ecma_op_put_value_lex_env_base (ecma_object_t *lex_env_p, /**< lexical environme #if JERRY_ESNEXT case ECMA_LEXICAL_ENVIRONMENT_CLASS: { - if ((lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) == 0) + if (!ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { break; } diff --git a/jerry-core/ecma/operations/ecma-lex-env.c b/jerry-core/ecma/operations/ecma-lex-env.c index d081d3b7a..53890bcc1 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.c +++ b/jerry-core/ecma/operations/ecma-lex-env.c @@ -167,7 +167,7 @@ ecma_op_has_binding (ecma_object_t *lex_env_p, /**< lexical environment */ #if JERRY_ESNEXT case ECMA_LEXICAL_ENVIRONMENT_CLASS: { - if ((lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) == 0) + if (!ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { return ECMA_VALUE_FALSE; } @@ -297,7 +297,7 @@ ecma_op_set_mutable_binding (ecma_object_t *lex_env_p, /**< lexical environment #if JERRY_ESNEXT case ECMA_LEXICAL_ENVIRONMENT_CLASS: { - if ((lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) == 0) + if (!ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { return ECMA_VALUE_EMPTY; } diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index a3843f82e..6de8b4644 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -1008,7 +1008,7 @@ ecma_op_get_global_symbol (lit_magic_string_id_t property_id) /**< property symb ecma_string_t *descriptor_p = ecma_concat_ecma_strings (symbol_dot_p, name_p); ecma_string_t *symbol_p = ecma_new_symbol_from_descriptor_string (ecma_make_string_value (descriptor_p)); - symbol_p->u.hash = (uint16_t) ((property_id << ECMA_GLOBAL_SYMBOL_SHIFT) | ECMA_GLOBAL_SYMBOL_FLAG); + symbol_p->u.hash = (uint16_t) ((property_id << ECMA_SYMBOL_FLAGS_SHIFT) | ECMA_SYMBOL_FLAG_GLOBAL); ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (global_symbols_cp)[symbol_index], symbol_p); @@ -2410,7 +2410,10 @@ ecma_op_object_own_property_keys (ecma_object_t *obj_p, /**< object */ #if JERRY_ESNEXT else if (ecma_prop_name_is_symbol (name_p)) { - symbol_named_props++; + if (!(name_p->u.hash & ECMA_SYMBOL_FLAG_PRIVATE_KEY)) + { + symbol_named_props++; + } } #endif /* JERRY_ESNEXT */ else @@ -2514,7 +2517,7 @@ ecma_op_object_own_property_keys (ecma_object_t *obj_p, /**< object */ #if JERRY_ESNEXT else if (ecma_prop_name_is_symbol (name_p)) { - if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS)) + if (!(filter & JERRY_PROPERTY_FILTER_EXCLUDE_SYMBOLS) && !(name_p->u.hash & ECMA_SYMBOL_FLAG_PRIVATE_KEY)) { *(--symbol_current_p) = ecma_make_symbol_value (name_p); continue; diff --git a/jerry-core/ecma/operations/ecma-reference.c b/jerry-core/ecma/operations/ecma-reference.c index 27bd12fd6..d5e970b24 100644 --- a/jerry-core/ecma/operations/ecma-reference.c +++ b/jerry-core/ecma/operations/ecma-reference.c @@ -113,7 +113,7 @@ ecma_op_resolve_super_base (ecma_object_t *lex_env_p) /**< starting lexical envi JERRY_ASSERT (lex_env_p != NULL); if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS - && (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) == 0) + && !ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { ecma_object_t *home_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u1.home_object_cp); @@ -318,7 +318,7 @@ ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical case ECMA_LEXICAL_ENVIRONMENT_CLASS: { #if JERRY_MODULE_SYSTEM - if (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) + if (ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); diff --git a/jerry-core/lit/lit-char-helpers.h b/jerry-core/lit/lit-char-helpers.h index 16370cd32..2f66b917f 100644 --- a/jerry-core/lit/lit-char-helpers.h +++ b/jerry-core/lit/lit-char-helpers.h @@ -115,6 +115,7 @@ bool lit_code_point_is_identifier_part (lit_code_point_t code_point); #define LIT_CHAR_TILDE ((ecma_char_t) '~') /* tilde */ #define LIT_CHAR_QUESTION ((ecma_char_t) '?') /* question mark */ #define LIT_CHAR_COLON ((ecma_char_t) ':') /* colon */ +#define LIT_CHAR_HASHMARK ((ecma_char_t) '#') /* hashmark */ /* * Special characters for String.prototype.replace. diff --git a/jerry-core/lit/lit-magic-strings.h b/jerry-core/lit/lit-magic-strings.h index 7f556f439..a079396cf 100644 --- a/jerry-core/lit/lit-magic-strings.h +++ b/jerry-core/lit/lit-magic-strings.h @@ -66,11 +66,18 @@ typedef enum * which contains references to other values */ LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD, /**< dynamic environment record needed by class constructors */ LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED, /**< computed class field name list */ + LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS, /**< internal collection for storing private elements */ LIT_INTERNAL_MAGIC_STRING_CONTAINER_WEAK_REFS, /**< Weak references to the current container object */ LIT_INTERNAL_MAGIC_STRING_WEAK_REFS, /**< Weak references to the current object */ LIT_MAGIC_STRING__COUNT /**< number of magic strings */ } lit_magic_string_id_t; +/** + * Properties that are need to be ignored for opfunc_set_class_attributes + */ +#define LIT_INTERNAL_MAGIC_STRING_IGNORED(id) \ + ((id) >= LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED && (id) <= LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS) + /** * Checks whether the given id corresponds to a global symbol */ diff --git a/jerry-core/parser/js/byte-code.c b/jerry-core/parser/js/byte-code.c index 9aee69033..adc152f80 100644 --- a/jerry-core/parser/js/byte-code.c +++ b/jerry-core/parser/js/byte-code.c @@ -28,7 +28,7 @@ JERRY_STATIC_ASSERT (offsetof (cbc_uint8_arguments_t, script_value) == offsetof * whenever new bytecodes are introduced or existing ones have been deleted. */ JERRY_STATIC_ASSERT (CBC_END == 238, number_of_cbc_opcodes_changed); -JERRY_STATIC_ASSERT (CBC_EXT_END == 148, number_of_cbc_ext_opcodes_changed); +JERRY_STATIC_ASSERT (CBC_EXT_END == 165, number_of_cbc_ext_opcodes_changed); #if JERRY_PARSER || JERRY_PARSER_DUMP_BYTE_CODE diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 09b0a78c4..9fb6b013a 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -211,6 +211,8 @@ #define PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION \ (PARSER_FINALLY_CONTEXT_STACK_ALLOCATION - PARSER_TRY_CONTEXT_STACK_ALLOCATION) +#define PARSER_STATIC_PRIVATE_TO_PRIVATE_OFFSET (CBC_EXT_COLLECT_PRIVATE_STATIC_FIELD - CBC_EXT_COLLECT_PRIVATE_FIELD) + /** * Opcode definitions. */ @@ -578,9 +580,56 @@ VM_OC_PUSH_STATIC_FIELD_FUNC | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_ADD_COMPUTED_FIELD, CBC_NO_FLAG, -1, VM_OC_ADD_COMPUTED_FIELD | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_ADD_STATIC_COMPUTED_FIELD, CBC_NO_FLAG, -1, VM_OC_ADD_COMPUTED_FIELD | VM_OC_GET_STACK) \ - \ + /* Class private property related opcodes */ \ + CBC_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_REFERENCE, \ + CBC_HAS_LITERAL_ARG, \ + 2, \ + VM_OC_PRIVATE_PROP_REFERENCE | VM_OC_GET_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL, \ + CBC_HAS_LITERAL_ARG, \ + 0, \ + VM_OC_PRIVATE_PROP_GET | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_IN, \ + CBC_HAS_LITERAL_ARG, \ + 0, \ + VM_OC_PRIVATE_IN | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_PRIVATE_FIELD_ADD, CBC_HAS_LITERAL_ARG, -1, VM_OC_PRIVATE_FIELD_ADD | VM_OC_GET_STACK_LITERAL) \ + /* These 8 opcodes must be in this order */ \ + CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_FIELD, \ + CBC_HAS_LITERAL_ARG, \ + 0, \ + VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL) \ + CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_METHOD, \ + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ + 0, \ + VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ + CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_GETTER, \ + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ + 0, \ + VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ + CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_SETTER, \ + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ + 0, \ + VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ + CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_FIELD, \ + CBC_HAS_LITERAL_ARG, \ + 0, \ + VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL) \ + CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_METHOD, \ + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ + 0, \ + VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ + CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_GETTER, \ + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ + 0, \ + VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ + CBC_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_SETTER, \ + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, \ + 0, \ + VM_OC_COLLECT_PRIVATE_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \ /* Class related opcodes. */ \ CBC_OPCODE (CBC_EXT_PUSH_NAMED_CLASS_ENV, CBC_HAS_LITERAL_ARG, 1, VM_OC_PUSH_CLASS_ENVIRONMENT) \ + CBC_OPCODE (CBC_EXT_DEFINE_FIELD, CBC_HAS_LITERAL_ARG, -1, VM_OC_DEFINE_FIELD | VM_OC_GET_STACK_LITERAL) \ CBC_OPCODE (CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR, CBC_NO_FLAG, 1, VM_OC_PUSH_IMPLICIT_CTOR | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR_HERITAGE, CBC_NO_FLAG, 1, VM_OC_PUSH_IMPLICIT_CTOR | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_INIT_CLASS, CBC_NO_FLAG, 0, VM_OC_INIT_CLASS | VM_OC_PUT_STACK) \ @@ -589,6 +638,10 @@ CBC_OPCODE (CBC_EXT_SET_FIELD_INIT, CBC_HAS_LITERAL_ARG, 0, VM_OC_SET_FIELD_INIT | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_RUN_FIELD_INIT, CBC_NO_FLAG, 0, VM_OC_RUN_FIELD_INIT) \ CBC_OPCODE (CBC_EXT_RUN_STATIC_FIELD_INIT, CBC_NO_FLAG, -1, VM_OC_RUN_STATIC_FIELD_INIT) \ + CBC_OPCODE (CBC_EXT_SET_NEXT_COMPUTED_FIELD_ANONYMOUS_FUNC, \ + CBC_NO_FLAG, \ + -1, \ + VM_OC_SET_NEXT_COMPUTED_FIELD | VM_OC_PUT_REFERENCE) \ 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, VM_OC_PUSH_SUPER_CONSTRUCTOR) \ @@ -610,6 +663,9 @@ CBC_OPCODE (CBC_EXT_ASSIGN_SUPER, CBC_NO_FLAG, -3, VM_OC_ASSIGN_SUPER) \ CBC_OPCODE (CBC_EXT_ASSIGN_SUPER_PUSH_RESULT, CBC_NO_FLAG, -2, VM_OC_ASSIGN_SUPER | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_ASSIGN_SUPER_BLOCK, CBC_NO_FLAG, -3, VM_OC_ASSIGN_SUPER | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_EXT_ASSIGN_PRIVATE, CBC_NO_FLAG, -3, VM_OC_ASSIGN_PRIVATE) \ + CBC_OPCODE (CBC_EXT_ASSIGN_PRIVATE_PUSH_RESULT, CBC_NO_FLAG, -2, VM_OC_ASSIGN_PRIVATE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_ASSIGN_PRIVATE_BLOCK, CBC_NO_FLAG, -3, VM_OC_ASSIGN_PRIVATE | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_EXT_SUPER_CALL, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_SUPER_CALL) \ CBC_OPCODE (CBC_EXT_SUPER_CALL_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, 0, VM_OC_SUPER_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_SUPER_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, VM_OC_SUPER_CALL | VM_OC_PUT_BLOCK) \ diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index d734585cb..11faa3da9 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -570,17 +570,6 @@ static const uint8_t keyword_lengths_list[] = { #undef LEXER_KEYWORD #undef LEXER_KEYWORD_LIST_LENGTH -/** - * Flags for lexer_parse_identifier. - */ -typedef enum -{ - LEXER_PARSE_NO_OPTS = 0, /**< no options */ - LEXER_PARSE_CHECK_KEYWORDS = (1 << 0), /**< check keywords */ - LEXER_PARSE_CHECK_START_AND_RETURN = (1 << 1), /**< check identifier start and return */ - LEXER_PARSE_CHECK_PART_AND_RETURN = (1 << 2), /**< check identifier part and return */ -} lexer_parse_options_t; - JERRY_STATIC_ASSERT (LEXER_FIRST_NON_RESERVED_KEYWORD < LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD, lexer_first_non_reserved_keyword_must_be_before_lexer_first_future_strict_reserved_word); @@ -855,8 +844,8 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ } #endif /* JERRY_ESNEXT */ - if (keyword_p->type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD - && (context_p->status_flags & PARSER_IS_STRICT)) + if (keyword_p->type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD && (context_p->status_flags & PARSER_IS_STRICT) + && !(options & LEXER_PARSE_NO_STRICT_IDENT_ERROR)) { parser_raise_error (context_p, PARSER_ERR_STRICT_IDENT_NOT_ALLOWED); } @@ -1567,6 +1556,9 @@ lexer_next_token (parser_context_t *context_p) /**< context */ LEXER_TYPE_A_TOKEN (LIT_CHAR_RIGHT_SQUARE, LEXER_RIGHT_SQUARE); LEXER_TYPE_A_TOKEN (LIT_CHAR_SEMICOLON, LEXER_SEMICOLON); LEXER_TYPE_A_TOKEN (LIT_CHAR_COMMA, LEXER_COMMA); +#if JERRY_ESNEXT + LEXER_TYPE_A_TOKEN (LIT_CHAR_HASHMARK, LEXER_HASHMARK); +#endif /* JERRY_ESNEXT */ case (uint8_t) LIT_CHAR_DOT: { @@ -2115,6 +2107,21 @@ lexer_update_await_yield (parser_context_t *context_p, /**< context */ } } /* lexer_update_await_yield */ +/** + * Read next token without skipping whitespaces and checking keywords + * + * @return true if the next literal is private identifier, false otherwise + */ +bool +lexer_scan_private_identifier (parser_context_t *context_p) /**< context */ +{ + context_p->token.keyword_type = LEXER_EOS; + context_p->token.line = context_p->line; + context_p->token.column = context_p->column; + + return (context_p->source_p < context_p->source_end_p && lexer_parse_identifier (context_p, LEXER_PARSE_NO_OPTS)); +} /* lexer_scan_private_identifier */ + #endif /* JERRY_ESNEXT */ /** @@ -3142,6 +3149,12 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ create_literal_object = true; } +#if JERRY_ESNEXT + else if (ident_opts & LEXER_OBJ_IDENT_CLASS_PRIVATE) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } +#endif /* JERRY_ESNEXT */ else { switch (context_p->source_p[0]) @@ -3186,6 +3199,16 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ lexer_consume_next_character (context_p); return; } + case LIT_CHAR_HASHMARK: + { + if (ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER) + { + context_p->token.type = LEXER_HASHMARK; + return; + } + + break; + } #endif /* JERRY_ESNEXT */ case LIT_CHAR_RIGHT_BRACE: { @@ -3250,6 +3273,12 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ { return; } + + if (ident_opts & LEXER_OBJ_IDENT_CLASS_PRIVATE) + { + parser_resolve_private_identifier (context_p); + return; + } #endif /* JERRY_ESNEXT */ lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); @@ -3265,14 +3294,15 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ * @return true if the next literal is identifier, false otherwise */ bool -lexer_scan_identifier (parser_context_t *context_p) /**< context */ +lexer_scan_identifier (parser_context_t *context_p, /**< context */ + lexer_parse_options_t opts) /**< identifier parse options */ { 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; - if (context_p->source_p < context_p->source_end_p && lexer_parse_identifier (context_p, LEXER_PARSE_NO_OPTS)) + if (context_p->source_p < context_p->source_end_p && lexer_parse_identifier (context_p, opts)) { return true; } diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index d959cd7d3..e4d661725 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -30,6 +30,18 @@ * @{ */ +/** + * Flags for lexer_parse_identifier. + */ +typedef enum +{ + LEXER_PARSE_NO_OPTS = 0, /**< no options */ + LEXER_PARSE_CHECK_KEYWORDS = (1 << 0), /**< check keywords */ + LEXER_PARSE_NO_STRICT_IDENT_ERROR = (1 << 1), /**< do not throw exception for strict mode keywords */ + LEXER_PARSE_CHECK_START_AND_RETURN = (1 << 2), /**< check identifier start and return */ + LEXER_PARSE_CHECK_PART_AND_RETURN = (1 << 3), /**< check identifier part and return */ +} lexer_parse_options_t; + /** * Lexer token types. */ @@ -161,6 +173,7 @@ typedef enum LEXER_COMMA, /**< "," */ #if JERRY_ESNEXT LEXER_ARROW, /**< "=>" */ + LEXER_HASHMARK, /**< "#" */ #endif /* JERRY_ESNEXT */ LEXER_KEYW_BREAK, /**< break */ @@ -203,6 +216,7 @@ typedef enum LEXER_ASSIGN_GROUP_EXPR, /**< indetifier for the assignment is located in a group expression */ LEXER_ASSIGN_CONST, /**< a const binding is reassigned */ LEXER_INVALID_PATTERN, /**< special value for invalid destructuring pattern */ + LEXER_PRIVATE_PRIMARY_EXPR, /**< private field in primary expession position */ #endif /* JERRY_ESNEXT */ /* Keywords which are not keyword tokens. */ @@ -278,8 +292,9 @@ typedef enum LEXER_OBJ_IDENT_CLASS_IDENTIFIER = (1u << 1), /**< expect identifier inside a class body */ LEXER_OBJ_IDENT_CLASS_NO_STATIC = (1u << 2), /**< static keyword was not present before the identifier */ LEXER_OBJ_IDENT_OBJECT_PATTERN = (1u << 3), /**< parse "get"/"set" as string literal in object pattern */ + LEXER_OBJ_IDENT_CLASS_PRIVATE = (1u << 4), /**< static keyword was not present before the identifier */ #if JERRY_FUNCTION_TO_STRING - LEXER_OBJ_IDENT_SET_FUNCTION_START = (1u << 4), /**< set function start */ + LEXER_OBJ_IDENT_SET_FUNCTION_START = (1u << 5), /**< set function start */ #else /* !JERRY_FUNCTION_TO_STRING */ LEXER_OBJ_IDENT_SET_FUNCTION_START = 0, /**< set function start (disabled) */ #endif /* JERRY_FUNCTION_TO_STRING */ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index c656a29d7..e8ea35473 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -285,6 +285,11 @@ parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */ if (opcode == CBC_DELETE_PUSH_RESULT) { #if JERRY_ESNEXT + if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL)) + { + parser_raise_error (context_p, PARSER_ERR_DELETE_PRIVATE_FIELD); + } + if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL) || context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) { @@ -513,6 +518,40 @@ parser_is_constructor_literal (parser_context_t *context_p) /**< context */ && lexer_compare_literal_to_string (context_p, "constructor", 11)); } /* parser_is_constructor_literal */ +/** + * Checks if current private field is already declared + */ +static void +parser_check_duplicated_private_field (parser_context_t *context_p, /**< context */ + uint8_t opts) /**< options */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL); + JERRY_ASSERT (context_p->private_context_p); + scanner_class_private_member_t *iter = context_p->private_context_p->members_p; + + bool search_for_property = (opts & SCANNER_PRIVATE_FIELD_PROPERTY); + + while (iter != NULL) + { + if (lexer_compare_identifiers (context_p, &context_p->token.lit_location, &iter->loc) && (iter->u8_arg & opts)) + { + if (iter->u8_arg & SCANNER_PRIVATE_FIELD_SEEN) + { + parser_raise_error (context_p, PARSER_ERR_DUPLICATED_PRIVATE_FIELD); + } + + iter->u8_arg |= SCANNER_PRIVATE_FIELD_SEEN; + + if (!search_for_property) + { + break; + } + } + + iter = iter->prev_p; + } +} /* parser_check_duplicated_private_field */ + /** * Parse class literal. * @@ -550,6 +589,7 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ parser_emit_cbc_ext (context_p, CBC_EXT_INIT_CLASS); bool is_static = false; + bool is_private = false; size_t fields_size = 0; uint32_t computed_field_count = 0; @@ -560,9 +600,19 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ lexer_skip_empty_statements (context_p); } - lexer_expect_object_literal_id (context_p, - (LEXER_OBJ_IDENT_CLASS_IDENTIFIER | LEXER_OBJ_IDENT_SET_FUNCTION_START - | (is_static ? 0 : LEXER_OBJ_IDENT_CLASS_NO_STATIC))); + uint32_t flags = (LEXER_OBJ_IDENT_CLASS_IDENTIFIER | LEXER_OBJ_IDENT_SET_FUNCTION_START); + + if (!is_static) + { + flags |= LEXER_OBJ_IDENT_CLASS_NO_STATIC; + } + + if (is_private) + { + flags |= LEXER_OBJ_IDENT_CLASS_PRIVATE; + } + + lexer_expect_object_literal_id (context_p, flags); if (context_p->token.type == LEXER_RIGHT_BRACE) { @@ -570,6 +620,14 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ break; } + if (context_p->token.type == LEXER_HASHMARK) + { + is_private = true; + lexer_next_token (context_p); + context_p->token.flags |= LEXER_NO_SKIP_SPACES; + continue; + } + if (context_p->token.type == LEXER_KEYW_STATIC) { JERRY_ASSERT (!is_static); @@ -577,7 +635,19 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ continue; } - if (!is_static && context_p->token.type == LEXER_LITERAL && parser_is_constructor_literal (context_p)) + if (is_private) + { + parser_check_duplicated_private_field (context_p, SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER); + } + + bool is_constructor_literal = context_p->token.type == LEXER_LITERAL && parser_is_constructor_literal (context_p); + + if (is_private && is_constructor_literal && lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) + { + parser_raise_error (context_p, PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR); + } + + if (!is_static && is_constructor_literal) { JERRY_ASSERT (!is_static); JERRY_ASSERT (opts & PARSER_CLASS_LITERAL_CTOR_PRESENT); @@ -619,14 +689,31 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ 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_ONLY_IDENTIFIERS); + uint8_t ident_opts = LEXER_OBJ_IDENT_ONLY_IDENTIFIERS; + + if (lexer_check_next_character (context_p, LIT_CHAR_HASHMARK)) + { + lexer_next_token (context_p); + context_p->token.flags |= LEXER_NO_SKIP_SPACES; + ident_opts |= LEXER_OBJ_IDENT_CLASS_PRIVATE; + is_private = true; + } + + lexer_expect_object_literal_id (context_p, ident_opts); + + if (is_private) + { + parser_check_duplicated_private_field (context_p, + is_getter ? SCANNER_PRIVATE_FIELD_GETTER : SCANNER_PRIVATE_FIELD_SETTER); + } + literal_index = context_p->lit_object.index; if (context_p->token.type == LEXER_RIGHT_SQUARE) { is_computed = true; } - else if (is_static) + else if (is_static && !is_private) { if (LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) && lexer_compare_identifier_to_string (&context_p->token.lit_location, (uint8_t *) "prototype", 9)) @@ -667,11 +754,13 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ if (is_getter) { - opcode = is_static ? CBC_EXT_SET_STATIC_GETTER : CBC_EXT_SET_GETTER; + opcode = is_static ? (is_private ? CBC_EXT_COLLECT_PRIVATE_STATIC_GETTER : CBC_EXT_SET_STATIC_GETTER) + : (is_private ? CBC_EXT_COLLECT_PRIVATE_GETTER : CBC_EXT_SET_GETTER); } else { - opcode = is_static ? CBC_EXT_SET_STATIC_SETTER : CBC_EXT_SET_SETTER; + opcode = is_static ? (is_private ? CBC_EXT_COLLECT_PRIVATE_STATIC_SETTER : CBC_EXT_SET_STATIC_SETTER) + : (is_private ? CBC_EXT_COLLECT_PRIVATE_SETTER : CBC_EXT_SET_SETTER); } } @@ -683,11 +772,16 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ } else { + if (is_private) + { + accessor_status_flags |= PARSER_PRIVATE_FUNCTION_NAME; + } parser_set_function_name (context_p, function_literal_index, literal_index, accessor_status_flags); context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); } is_static = false; + is_private = false; continue; } @@ -697,16 +791,57 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ { status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; + uint8_t ident_opts = LEXER_OBJ_IDENT_ONLY_IDENTIFIERS; + + if (lexer_check_next_character (context_p, LIT_CHAR_HASHMARK)) + { + lexer_next_token (context_p); + context_p->token.flags |= LEXER_NO_SKIP_SPACES; + ident_opts |= LEXER_OBJ_IDENT_CLASS_PRIVATE; + is_private = true; + } + if (!lexer_consume_generator (context_p)) { - lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); + lexer_expect_object_literal_id (context_p, ident_opts); + } + + if (is_private) + { + if (context_p->token.type == LEXER_LITERAL && parser_is_constructor_literal (context_p)) + { + parser_raise_error (context_p, PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR); + } + + parser_check_duplicated_private_field (context_p, SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER); } } if (context_p->token.type == LEXER_MULTIPLY) { - lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); + uint8_t ident_opts = LEXER_OBJ_IDENT_ONLY_IDENTIFIERS; + + if (lexer_check_next_character (context_p, LIT_CHAR_HASHMARK)) + { + lexer_next_token (context_p); + context_p->token.flags |= LEXER_NO_SKIP_SPACES; + ident_opts |= LEXER_OBJ_IDENT_CLASS_PRIVATE; + is_private = true; + } + + lexer_expect_object_literal_id (context_p, ident_opts); + status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; + + if (is_private) + { + if (context_p->token.type == LEXER_LITERAL && parser_is_constructor_literal (context_p)) + { + parser_raise_error (context_p, PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR); + } + + parser_check_duplicated_private_field (context_p, SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER); + } } if (context_p->token.type == LEXER_RIGHT_SQUARE) @@ -715,7 +850,7 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ } else if (LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type)) { - if (is_static) + if (is_static && !is_private) { if (lexer_compare_identifier_to_string (&context_p->token.lit_location, (uint8_t *) "prototype", 9)) { @@ -744,6 +879,13 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ if (!is_computed) { + if (is_private) + { + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); + uint8_t field_opcode = is_static ? CBC_EXT_COLLECT_PRIVATE_STATIC_FIELD : CBC_EXT_COLLECT_PRIVATE_FIELD; + parser_emit_cbc_ext_literal_from_token (context_p, field_opcode); + } + if (is_static && parser_is_constructor_literal (context_p)) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED); @@ -825,6 +967,7 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ parser_stack_push_uint8 (context_p, class_field_type); fields_size++; is_static = false; + is_private = false; continue; } @@ -834,7 +977,14 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ { 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); + if (is_private) + { + parser_resolve_private_identifier (context_p); + } + else + { + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); + } } else { @@ -856,7 +1006,14 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ continue; } - parser_set_function_name (context_p, function_literal_index, literal_index, 0); + uint32_t function_name_status_flags = 0; + + if (is_private) + { + function_name_status_flags = PARSER_PRIVATE_FUNCTION_NAME; + } + + parser_set_function_name (context_p, function_literal_index, literal_index, function_name_status_flags); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); @@ -864,13 +1021,20 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ if (is_static) { - context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_STATIC_PROPERTY_LITERAL); + context_p->last_cbc_opcode = (is_private ? PARSER_TO_EXT_OPCODE (CBC_EXT_COLLECT_PRIVATE_STATIC_METHOD) + : PARSER_TO_EXT_OPCODE (CBC_EXT_SET_STATIC_PROPERTY_LITERAL)); is_static = false; } + else if (is_private) + { + context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_COLLECT_PRIVATE_METHOD); + } else { context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; } + + is_private = false; } if (fields_size == 0) @@ -930,12 +1094,22 @@ parser_parse_class (parser_context_t *context_p, /**< context */ uint16_t class_ident_index = PARSER_INVALID_LITERAL_INDEX; uint16_t class_name_index = PARSER_INVALID_LITERAL_INDEX; parser_class_literal_opts_t opts = PARSER_CLASS_LITERAL_NO_OPTS; + scanner_info_t *scanner_info_p = context_p->next_scanner_info_p; - if (context_p->next_scanner_info_p->source_p == context_p->source_p) + scanner_class_info_t *class_info_p = (scanner_class_info_t *) scanner_info_p; + parser_private_context_t private_ctx; + + if (scanner_info_p->source_p == context_p->source_p) { - JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CLASS_CONSTRUCTOR); - scanner_release_next (context_p, sizeof (scanner_info_t)); - opts |= PARSER_CLASS_LITERAL_CTOR_PRESENT; + JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_CLASS_CONSTRUCTOR); + parser_save_private_context (context_p, &private_ctx, class_info_p); + + if (scanner_info_p->u8_arg & SCANNER_CONSTRUCTOR_EXPLICIT) + { + opts |= PARSER_CLASS_LITERAL_CTOR_PRESENT; + } + + scanner_release_next (context_p, sizeof (scanner_class_info_t)); } if (is_statement) @@ -1019,6 +1193,8 @@ parser_parse_class (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } + context_p->private_context_p->opts |= SCANNER_PRIVATE_FIELD_ACTIVE; + /* ClassDeclaration is parsed. Continue with class body. */ bool has_static_field = parser_parse_class_body (context_p, opts, class_name_index); @@ -1057,6 +1233,8 @@ parser_parse_class (parser_context_t *context_p, /**< context */ } context_p->status_flags &= (uint32_t) ~PARSER_ALLOW_SUPER; + parser_restore_private_context (context_p, &private_ctx); + lexer_next_token (context_p); } /* parser_parse_class */ #endif /* JERRY_ESNEXT */ @@ -1943,6 +2121,26 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ switch (context_p->token.type) { #if JERRY_ESNEXT + case LEXER_HASHMARK: + { + if (!lexer_scan_private_identifier (context_p)) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } + + parser_resolve_private_identifier (context_p); + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_KEYW_IN) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } + + parser_stack_push_uint16 (context_p, context_p->lit_object.index); + parser_stack_push_uint8 (context_p, LEXER_PRIVATE_PRIMARY_EXPR); + return false; + } case LEXER_TEMPLATE_LITERAL: { if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) @@ -2345,7 +2543,30 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ { parser_push_result (context_p); +#if JERRY_ESNEXT + if (lexer_check_next_character (context_p, LIT_CHAR_HASHMARK)) + { + lexer_next_token (context_p); + + if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER)) + { + parser_raise_error (context_p, PARSER_ERR_UNEXPECTED_PRIVATE_FIELD); + } + + if (!lexer_scan_private_identifier (context_p)) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); + } + + parser_resolve_private_identifier (context_p); + + parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_PRIVATE_PROP_LITERAL, context_p->lit_object.index); + lexer_next_token (context_p); + continue; + } +#endif /* JERRY_ESNEXT */ lexer_expect_identifier (context_p, LEXER_STRING_LITERAL); + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->lit_object.literal_p->type == LEXER_STRING_LITERAL); context_p->token.lit_location.type = LEXER_STRING_LITERAL; @@ -2466,6 +2687,11 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_REFERENCE); opcode = CBC_CALL_PROP; } + else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL)) + { + context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_REFERENCE); + opcode = CBC_CALL_PROP; + } #endif /* JERRY_ESNEXT */ else if (JERRY_UNLIKELY (context_p->status_flags & PARSER_INSIDE_WITH) && PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) @@ -2649,7 +2875,6 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ parser_emit_cbc_call (context_p, opcode, call_arguments); continue; } - default: { if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) @@ -2893,6 +3118,12 @@ parser_append_binary_single_assignment_token (parser_context_t *context_p, /**< parser_stack_push_uint8 (context_p, CBC_EXT_ASSIGN_SUPER); assign_opcode = CBC_EXT_OPCODE; } + else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_PRIVATE_PROP_LITERAL)) + { + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + parser_stack_push_uint8 (context_p, CBC_EXT_ASSIGN_PRIVATE); + assign_opcode = CBC_EXT_OPCODE; + } #endif /* JERRY_ESNEXT */ else { @@ -3068,7 +3299,8 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ if (JERRY_UNLIKELY (opcode == CBC_EXT_OPCODE)) { parser_stack_pop_uint8 (context_p); - JERRY_ASSERT (context_p->stack_top_uint8 == CBC_EXT_ASSIGN_SUPER); + JERRY_ASSERT (context_p->stack_top_uint8 == CBC_EXT_ASSIGN_SUPER + || context_p->stack_top_uint8 == CBC_EXT_ASSIGN_PRIVATE); opcode = PARSER_TO_EXT_OPCODE (context_p->stack_top_uint8); parser_stack_pop_uint8 (context_p); } @@ -3170,6 +3402,13 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ parser_set_branch_to_current_position (context_p, &branch); continue; } + else if (token == LEXER_KEYW_IN && context_p->stack_top_uint8 == LEXER_PRIVATE_PRIMARY_EXPR) + { + parser_stack_pop_uint8 (context_p); + uint16_t lit_id = parser_stack_pop_uint16 (context_p); + parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_IN, lit_id); + continue; + } #endif /* JERRY_ESNEXT */ else { diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 0ce1b49c4..1043878e0 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -76,6 +76,8 @@ typedef enum 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 */ + PARSER_PRIVATE_FUNCTION_NAME = PARSER_IS_FUNC_EXPRESSION, /**< represents private method for + * parser_set_function_name*/ #endif /* JERRY_ESNEXT */ #if JERRY_MODULE_SYSTEM PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 26), /**< parsing a function or class default export */ @@ -481,6 +483,18 @@ typedef struct #endif /* JERRY_LINE_INFO */ +#if JERRY_ESNEXT +/** + * List of private field contexts + */ +typedef struct parser_private_context_t +{ + scanner_class_private_member_t *members_p; /**< current private field context members */ + struct parser_private_context_t *prev_p; /**< previous private field context */ + uint8_t opts; /**< options */ +} parser_private_context_t; +#endif /* JERRY_ESNEXT */ + /** * Those members of a context which needs * to be saved when a sub-function is parsed. @@ -605,6 +619,7 @@ typedef struct #if JERRY_ESNEXT uint16_t scope_stack_global_end; /**< end of global declarations of a function */ ecma_value_t tagged_template_literal_cp; /**< compessed pointer to the tagged template literal collection */ + parser_private_context_t *private_context_p; /**< private context */ #endif /* JERRY_ESNEXT */ uint8_t stack_top_uint8; /**< top byte stored on the stack */ @@ -763,10 +778,11 @@ 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); +bool lexer_scan_private_identifier (parser_context_t *context_p); #endif /* JERRY_ESNEXT */ void lexer_parse_string (parser_context_t *context_p, lexer_string_options_t opts); void lexer_expect_identifier (parser_context_t *context_p, uint8_t literal_type); -bool lexer_scan_identifier (parser_context_t *context_p); +bool lexer_scan_identifier (parser_context_t *context_p, lexer_parse_options_t opts); void lexer_check_property_modifier (parser_context_t *context_p); void lexer_convert_ident_to_cesu8 (uint8_t *destination_p, const uint8_t *source_p, prop_length_t length); @@ -812,6 +828,11 @@ void parser_parse_block_expression (parser_context_t *context_p, int options); void parser_parse_expression_statement (parser_context_t *context_p, int options); void parser_parse_expression (parser_context_t *context_p, int options); #if JERRY_ESNEXT +void parser_resolve_private_identifier (parser_context_t *context_p); +void parser_save_private_context (parser_context_t *context_p, + parser_private_context_t *private_ctx_p, + scanner_class_info_t *class_info_p); +void parser_restore_private_context (parser_context_t *context_p, parser_private_context_t *private_ctx_p); void parser_parse_class (parser_context_t *context_p, bool is_statement); void parser_parse_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); void parser_parse_initializer_by_next_char (parser_context_t *context_p, parser_pattern_flags_t flags); @@ -829,6 +850,7 @@ void scanner_set_active (parser_context_t *context_p); void scanner_revert_active (parser_context_t *context_p); void scanner_release_active (parser_context_t *context_p, size_t size); void scanner_release_switch_cases (scanner_case_info_t *case_p); +void scanner_release_private_fields (scanner_class_private_member_t *member_p); void scanner_seek (parser_context_t *context_p); void scanner_reverse_info_list (parser_context_t *context_p); void scanner_cleanup (parser_context_t *context_p); diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 41356b409..b55baed57 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -22,6 +22,7 @@ #include "debugger.h" #include "jcontext.h" #include "js-parser-internal.h" +#include "lit-char-helpers.h" #if JERRY_PARSER @@ -1421,6 +1422,174 @@ parser_post_processing (parser_context_t *context_p) /**< context */ #undef PARSER_NEXT_BYTE #undef PARSER_NEXT_BYTE_UPDATE +#if JERRY_ESNEXT +/** + * Resolve private identifier in direct eval context + */ +static bool +parser_resolve_private_identifier_eval (parser_context_t *context_p) /**< context */ +{ + ecma_string_t *search_key_p; + uint8_t *destination_p = (uint8_t *) parser_malloc (context_p, context_p->token.lit_location.length); + + lexer_convert_ident_to_cesu8 (destination_p, + context_p->token.lit_location.char_p, + context_p->token.lit_location.length); + + search_key_p = ecma_new_ecma_string_from_utf8 (destination_p, context_p->token.lit_location.length); + + parser_free (destination_p, context_p->token.lit_location.length); + + ecma_object_t *lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p; + + while (true) + { + JERRY_ASSERT (lex_env_p != NULL); + + if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS + && (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) != 0 + && !ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) + { + ecma_object_t *class_object_p = ((ecma_lexical_environment_class_t *) lex_env_p)->object_p; + + ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); + ecma_property_t *prop_p = ecma_find_named_property (class_object_p, internal_string_p); + + if (prop_p != NULL) + { + ecma_value_t *collection_p = + ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, ECMA_PROPERTY_VALUE_PTR (prop_p)->value); + ecma_value_t *current_p = collection_p + 1; + ecma_value_t *end_p = ecma_compact_collection_end (collection_p); + + while (current_p < end_p) + { + current_p++; /* skip kind */ + ecma_string_t *private_key_p = ecma_get_prop_name_from_value (*current_p++); + current_p++; /* skip value */ + + JERRY_ASSERT (ecma_prop_name_is_symbol (private_key_p)); + + ecma_string_t *private_key_desc_p = + ecma_get_string_from_value (((ecma_extended_string_t *) private_key_p)->u.symbol_descriptor); + + if (ecma_compare_ecma_strings (private_key_desc_p, search_key_p)) + { + ecma_deref_ecma_string (search_key_p); + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); + return true; + } + } + } + } + + if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) + { + break; + } + + lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + } + + ecma_deref_ecma_string (search_key_p); + return false; +} /* parser_resolve_private_identifier_eval */ + +/** + * Resolve private identifier + */ +void +parser_resolve_private_identifier (parser_context_t *context_p) /**< context */ +{ + if ((context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL) && parser_resolve_private_identifier_eval (context_p)) + { + return; + } + + parser_private_context_t *context_iter_p = context_p->private_context_p; + + while (context_iter_p) + { + if (context_iter_p == NULL || !(context_iter_p->opts & SCANNER_PRIVATE_FIELD_ACTIVE)) + { + parser_raise_error (context_p, PARSER_ERR_UNDECLARED_PRIVATE_FIELD); + } + + if (!(context_iter_p->opts & SCANNER_SUCCESSFUL_CLASS_SCAN)) + { + return; + } + + parser_private_context_t *private_context_p = context_iter_p; + + if (private_context_p == NULL) + { + parser_raise_error (context_p, PARSER_ERR_UNDECLARED_PRIVATE_FIELD); + } + + scanner_class_private_member_t *ident_iter = private_context_p->members_p; + + while (ident_iter) + { + if (lexer_compare_identifiers (context_p, &context_p->token.lit_location, &ident_iter->loc)) + { + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); + return; + } + + ident_iter = ident_iter->prev_p; + } + + context_iter_p = context_iter_p->prev_p; + } + + parser_raise_error (context_p, PARSER_ERR_UNDECLARED_PRIVATE_FIELD); +} /* parser_resolve_private_identifier */ + +/** + * Save private field context + */ +void +parser_save_private_context (parser_context_t *context_p, /**< context */ + parser_private_context_t *private_ctx_p, /**< private context */ + scanner_class_info_t *class_info_p) /**< class scanner info */ +{ + private_ctx_p->prev_p = context_p->private_context_p; + context_p->private_context_p = private_ctx_p; + + context_p->private_context_p->members_p = class_info_p->members; + context_p->private_context_p->opts = class_info_p->info.u8_arg; + class_info_p->members = NULL; +} /* parser_save_private_context */ + +/** + * Release contexts private fields + */ +static void +parser_free_private_fields (parser_context_t *context_p) /**< context */ +{ + parser_private_context_t *iter = context_p->private_context_p; + + while (iter != NULL) + { + parser_private_context_t *prev_p = iter->prev_p; + scanner_release_private_fields (iter->members_p); + iter = prev_p; + } +} /* parser_free_private_fields */ + +/** + * Restore contexts private fields + */ +void +parser_restore_private_context (parser_context_t *context_p, /**< context */ + parser_private_context_t *private_ctx_p) /**< private context */ +{ + scanner_release_private_fields (context_p->private_context_p->members_p); + context_p->private_context_p = private_ctx_p->prev_p; +} /* parser_restore_private_context */ +#endif /* JERRY_ESNEXT */ + /** * Free identifiers and literals. */ @@ -1990,6 +2159,7 @@ parser_parse_source (void *source_p, /**< source code */ #if JERRY_ESNEXT context.scope_stack_global_end = 0; context.tagged_template_literal_cp = JMEM_CP_NULL; + context.private_context_p = NULL; #endif /* JERRY_ESNEXT */ #ifndef JERRY_NDEBUG @@ -2774,14 +2944,23 @@ parser_parse_class_fields (parser_context_t *context_p) /**< context */ } uint16_t literal_index = 0; + bool is_private = false; if (class_field_type & PARSER_CLASS_FIELD_NORMAL) { scanner_set_location (context_p, &range.start_location); + uint32_t ident_opts = LEXER_OBJ_IDENT_ONLY_IDENTIFIERS; + is_private = context_p->source_p[-1] == LIT_CHAR_HASHMARK; + + if (is_private) + { + ident_opts |= LEXER_OBJ_IDENT_CLASS_PRIVATE; + } + context_p->source_end_p = source_end_p; scanner_seek (context_p); - lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); + lexer_expect_object_literal_id (context_p, ident_opts); literal_index = context_p->lit_object.index; @@ -2826,7 +3005,26 @@ parser_parse_class_fields (parser_context_t *context_p) /**< context */ if (class_field_type & PARSER_CLASS_FIELD_NORMAL) { - parser_emit_cbc_literal (context_p, CBC_ASSIGN_PROP_THIS_LITERAL, literal_index); + uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); + + if (function_literal_index == PARSER_ANONYMOUS_CLASS) + { + parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, literal_index); + } + else if (function_literal_index < PARSER_NAMED_FUNCTION) + { + uint32_t function_name_status_flags = is_private ? PARSER_PRIVATE_FUNCTION_NAME : 0; + parser_set_function_name (context_p, function_literal_index, literal_index, function_name_status_flags); + } + + if (is_private) + { + parser_emit_cbc_ext_literal (context_p, CBC_EXT_PRIVATE_FIELD_ADD, literal_index); + } + else + { + parser_emit_cbc_ext_literal (context_p, CBC_EXT_DEFINE_FIELD, literal_index); + } /* Prepare stack slot for assignment property reference base. Needed by vm.c */ if (context_p->stack_limit == context_p->stack_depth) @@ -2837,6 +3035,14 @@ parser_parse_class_fields (parser_context_t *context_p) /**< context */ } else { + uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); + uint16_t opcode = CBC_EXT_SET_NEXT_COMPUTED_FIELD; + + if (function_literal_index < PARSER_NAMED_FUNCTION || function_literal_index == PARSER_ANONYMOUS_CLASS) + { + opcode = CBC_EXT_SET_NEXT_COMPUTED_FIELD_ANONYMOUS_FUNC; + } + parser_flush_cbc (context_p); /* The next opcode pushes two more temporary values onto the stack */ @@ -2849,7 +3055,7 @@ parser_parse_class_fields (parser_context_t *context_p) /**< context */ } } - parser_emit_cbc_ext (context_p, CBC_EXT_SET_NEXT_COMPUTED_FIELD); + parser_emit_cbc_ext (context_p, opcode); } } while (!(context_p->stack_top_uint8 & PARSER_CLASS_FIELD_END)); @@ -2947,6 +3153,26 @@ parser_set_function_name (parser_context_t *context_p, /**< context */ parser_compiled_code_set_function_name (context_p, bytecode_p, name_index, status_flags); } /* parser_set_function_name */ +/** + * Prepend the given prefix into the current function name literal + * + * @return pointer to the newly allocated buffer + */ +static uint8_t * +parser_add_function_name_prefix (parser_context_t *context_p, /**< context */ + const char *prefix_p, /**< prefix */ + uint32_t prefix_size, /**< prefix's length */ + uint32_t *name_length_p, /**< [out] function name's size */ + lexer_literal_t *name_lit_p) /**< function name literal */ +{ + *name_length_p += prefix_size; + uint8_t *name_buffer_p = (uint8_t *) parser_malloc (context_p, *name_length_p * sizeof (uint8_t)); + memcpy (name_buffer_p, prefix_p, prefix_size); + memcpy (name_buffer_p + prefix_size, name_lit_p->u.char_p, name_lit_p->prop.length); + + return name_buffer_p; +} /* parser_add_function_name_prefix */ + /** * Set the function name of the given compiled code * to the given character buffer of literal corresponds to the given name index. @@ -2989,13 +3215,17 @@ parser_compiled_code_set_function_name (parser_context_t *context_p, /**< contex uint8_t *name_buffer_p = (uint8_t *) name_lit_p->u.char_p; uint32_t name_length = name_lit_p->prop.length; - if (status_flags & (PARSER_IS_PROPERTY_GETTER | PARSER_IS_PROPERTY_SETTER)) + if (status_flags & PARSER_PRIVATE_FUNCTION_NAME) { - name_length += 4; - name_buffer_p = (uint8_t *) parser_malloc (context_p, name_length * sizeof (uint8_t)); - char *prefix_p = (status_flags & PARSER_IS_PROPERTY_GETTER) ? "get " : "set "; - memcpy (name_buffer_p, prefix_p, 4); - memcpy (name_buffer_p + 4, name_lit_p->u.char_p, name_lit_p->prop.length); + name_buffer_p = parser_add_function_name_prefix (context_p, "#", 1, &name_length, name_lit_p); + } + else if (status_flags & (PARSER_IS_PROPERTY_GETTER | PARSER_IS_PROPERTY_SETTER)) + { + name_buffer_p = parser_add_function_name_prefix (context_p, + (status_flags & PARSER_IS_PROPERTY_GETTER) ? "get " : "set ", + 4, + &name_length, + name_lit_p); } *func_name_start_p = @@ -3061,6 +3291,8 @@ parser_raise_error (parser_context_t *context_p, /**< context */ } #if JERRY_ESNEXT + parser_free_private_fields (context_p); + if (context_p->tagged_template_literal_cp != JMEM_CP_NULL) { ecma_collection_t *collection = diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index d4117fbe1..369983272 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -410,6 +410,7 @@ lexer_lit_location_t *scanner_add_custom_literal (parser_context_t *context_p, lexer_lit_location_t *scanner_add_literal (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_add_reference (parser_context_t *context_p, scanner_context_t *scanner_context_p); lexer_lit_location_t *scanner_append_argument (parser_context_t *context_p, scanner_context_t *scanner_context_p); +void scanner_add_private_identifier (parser_context_t *context_p, scanner_private_field_flags_t opts); #if JERRY_ESNEXT void scanner_detect_invalid_var (parser_context_t *context_p, 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 bd501dae8..f94b12090 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -331,6 +331,21 @@ scanner_release_switch_cases (scanner_case_info_t *case_p) /**< case list */ } } /* scanner_release_switch_cases */ +/** + * Release private fields. + */ +void +scanner_release_private_fields (scanner_class_private_member_t *member_p) /**< private member list */ +{ + while (member_p != NULL) + { + scanner_class_private_member_t *prev_p = member_p->prev_p; + + jmem_heap_free_block (member_p, sizeof (scanner_class_private_member_t)); + member_p = prev_p; + } +} /* scanner_release_private_fields */ + /** * Seek to correct position in the scanner info list. */ @@ -406,7 +421,7 @@ scanner_scope_find_lexical_declaration (parser_context_t *context_p, /**< contex if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))) { - name_p = parser_new_ecma_string_from_literal ((lexer_literal_t *) literal_p); + name_p = ecma_new_ecma_string_from_utf8 (literal_p->char_p, literal_p->length); } else { @@ -414,7 +429,7 @@ scanner_scope_find_lexical_declaration (parser_context_t *context_p, /**< contex lexer_convert_ident_to_cesu8 (destination_p, literal_p->char_p, literal_p->length); - name_p = parser_new_ecma_string_from_literal ((lexer_literal_t *) literal_p); + name_p = ecma_new_ecma_string_from_utf8 (destination_p, literal_p->length); scanner_free (destination_p, literal_p->length); } @@ -1535,6 +1550,46 @@ scanner_append_argument (parser_context_t *context_p, /**< context */ return literal_p; } /* scanner_append_argument */ +/** + * Add private identifiers to private ident pool + */ +void +scanner_add_private_identifier (parser_context_t *context_p, /**< context */ + scanner_private_field_flags_t opts) /**< options */ +{ + scan_stack_modes_t stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; + parser_stack_pop_uint8 (context_p); + scanner_class_info_t *class_info_p; + parser_stack_pop (context_p, &class_info_p, sizeof (scanner_class_info_t *)); + + scanner_class_private_member_t *iter = class_info_p->members; + + scanner_private_field_flags_t search_flag = + ((opts & SCANNER_PRIVATE_FIELD_PROPERTY) ? SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER + : (opts & SCANNER_PRIVATE_FIELD_GETTER_SETTER)); + + while (iter != NULL) + { + if (lexer_compare_identifiers (context_p, &context_p->token.lit_location, &iter->loc) + && (iter->u8_arg & search_flag)) + { + scanner_raise_error (context_p); + } + + iter = iter->prev_p; + } + + scanner_class_private_member_t *p_member; + p_member = (scanner_class_private_member_t *) scanner_malloc (context_p, sizeof (scanner_class_private_member_t)); + p_member->loc = context_p->token.lit_location; + p_member->u8_arg = (uint8_t) opts; + p_member->prev_p = class_info_p->members; + class_info_p->members = p_member; + + parser_stack_push (context_p, &class_info_p, sizeof (scanner_class_info_t *)); + parser_stack_push_uint8 (context_p, (uint8_t) stack_top); +} /* scanner_add_private_identifier */ + /** * Check whether an eval call is performed and update the status flags accordingly. */ @@ -1729,6 +1784,15 @@ scanner_push_class_declaration (parser_context_t *context_p, /**< context */ literal_pool_p->source_p = source_p; literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_CLASS_NAME; + const uint8_t *class_source_p = scanner_context_p->active_literal_pool_p->source_p; + scanner_class_info_t *class_info_p = + (scanner_class_info_t *) scanner_insert_info (context_p, class_source_p, sizeof (scanner_class_info_t)); + + class_info_p->info.type = SCANNER_TYPE_CLASS_CONSTRUCTOR; + class_info_p->members = NULL; + class_info_p->info.u8_arg = SCANNER_CONSTRUCTOR_IMPLICIT; + + parser_stack_push (context_p, &class_info_p, sizeof (scanner_class_info_t *)); parser_stack_push_uint8 (context_p, SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR); scanner_context_p->mode = SCAN_MODE_CLASS_DECLARATION; @@ -1935,12 +1999,19 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ size = sizeof (scanner_switch_info_t); break; } +#if JERRY_ESNEXT + case SCANNER_TYPE_CLASS_CONSTRUCTOR: + { + scanner_release_private_fields (((scanner_class_info_t *) scanner_info_p)->members); + size = sizeof (scanner_class_info_t); + break; + } +#endif /* JERRY_ESNEXT */ default: { #if JERRY_ESNEXT JERRY_ASSERT ( scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS || scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS - || scanner_info_p->type == SCANNER_TYPE_CLASS_CONSTRUCTOR || scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION || scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED || scanner_info_p->type == SCANNER_TYPE_ERR_ASYNC_FUNCTION || scanner_info_p->type == SCANNER_TYPE_EXPORT_MODULE_SPECIFIER); diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 4e0291930..7007bb97a 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -64,6 +64,37 @@ JERRY_STATIC_ASSERT (SCANNER_FROM_COMPUTED_TO_LITERAL_POOL (SCAN_STACK_COMPUTED_ #endif /* JERRY_ESNEXT */ +/** + * Change scanner mode from primary expression to post primary expression. + * + * @return SCAN_NEXT_TOKEN to read the next token, or SCAN_KEEP_TOKEN to do nothing + */ +static scan_return_types_t +scanner_primary_to_post_primary_expression (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p) /* scanner context */ +{ + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + +#if JERRY_ESNEXT + if (JERRY_UNLIKELY (context_p->stack_top_uint8 == SCAN_STACK_CLASS_FIELD_INITIALIZER + && (context_p->status_flags & PARSER_IS_STRICT))) + { + lexer_scan_identifier (context_p, LEXER_PARSE_CHECK_KEYWORDS | LEXER_PARSE_NO_STRICT_IDENT_ERROR); + + if (context_p->token.type == LEXER_LITERAL && lexer_compare_literal_to_string (context_p, "static", 6)) + { + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + } + + return SCAN_KEEP_TOKEN; + } +#else /* !JERRY_ESNEXT */ + JERRY_UNUSED (context_p); +#endif /* JERRY_ESNEXT */ + + return SCAN_NEXT_TOKEN; +} /* scanner_primary_to_post_primary_expression */ + /** * Scan primary expression. * @@ -84,7 +115,7 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ #if JERRY_ESNEXT if (scanner_try_scan_new_target (context_p)) { - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } #endif /* JERRY_ESNEXT */ break; @@ -93,8 +124,7 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ case LEXER_ASSIGN_DIVIDE: { lexer_construct_regexp_object (context_p, true); - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - break; + return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } case LEXER_KEYW_FUNCTION: { @@ -177,6 +207,15 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ return SCAN_KEEP_TOKEN; } #if JERRY_ESNEXT + case LEXER_HASHMARK: + { + if (!lexer_scan_private_identifier (context_p)) + { + scanner_raise_error (context_p); + } + + return SCAN_KEEP_TOKEN; + } case LEXER_TEMPLATE_LITERAL: { if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) @@ -217,8 +256,7 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); location_p->type |= (SCANNER_LITERAL_IS_USED | SCANNER_LITERAL_IS_VAR); scanner_detect_eval_call (context_p, scanner_context_p); - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - break; + return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } #endif /* JERRY_MODULE_SYSTEM */ @@ -231,15 +269,13 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ case LEXER_LIT_FALSE: case LEXER_LIT_NULL: { - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - break; + return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } #if JERRY_ESNEXT case LEXER_KEYW_SUPER: { scanner_context_p->active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE; - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - break; + return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } case LEXER_KEYW_CLASS: { @@ -338,8 +374,7 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ } #endif /* JERRY_ESNEXT */ - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - break; + return scanner_primary_to_post_primary_expression (context_p, scanner_context_p); } /* FALLTHRU */ } @@ -366,7 +401,15 @@ scanner_scan_post_primary_expression (parser_context_t *context_p, /**< context { case LEXER_DOT: { - lexer_scan_identifier (context_p); + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); + +#if JERRY_ESNEXT + if (context_p->token.type == LEXER_HASHMARK) + { + context_p->token.flags |= LEXER_NO_SKIP_SPACES; + lexer_next_token (context_p); + } +#endif /* JERRY_ESNEXT */ if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { @@ -1041,7 +1084,7 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * break; } - lexer_scan_identifier (context_p); + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); parser_stack_pop_uint8 (context_p); stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; @@ -2568,7 +2611,7 @@ scanner_scan_all (parser_context_t *context_p) /**< context */ case SCAN_MODE_CLASS_BODY: { lexer_skip_empty_statements (context_p); - lexer_scan_identifier (context_p); + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); /* FALLTHRU */ } case SCAN_MODE_CLASS_BODY_NO_SCAN: @@ -2580,13 +2623,18 @@ scanner_scan_all (parser_context_t *context_p) /**< context */ if (context_p->token.type == LEXER_RIGHT_BRACE) { parser_stack_pop_uint8 (context_p); - stack_top = context_p->stack_top_uint8; + + scanner_class_info_t *private_members_p; + parser_stack_pop (context_p, &private_members_p, sizeof (scanner_class_info_t *)); + + private_members_p->info.u8_arg |= SCANNER_SUCCESSFUL_CLASS_SCAN; scanner_pop_literal_pool (context_p, &scanner_context); - JERRY_ASSERT (stack_top == SCAN_STACK_CLASS_STATEMENT || stack_top == SCAN_STACK_CLASS_EXPRESSION); + JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_CLASS_STATEMENT + || context_p->stack_top_uint8 == SCAN_STACK_CLASS_EXPRESSION); - if (stack_top == SCAN_STACK_CLASS_STATEMENT) + if (context_p->stack_top_uint8 == SCAN_STACK_CLASS_STATEMENT) { /* The token is kept to disallow consuming a semicolon after it. */ scanner_context.mode = SCAN_MODE_STATEMENT_END; @@ -2608,37 +2656,58 @@ scanner_scan_all (parser_context_t *context_p) /**< context */ break; } + bool is_private = false; + scanner_private_field_flags_t private_field_flags = SCANNER_PRIVATE_FIELD_PROPERTY; + + if (context_p->token.type == LEXER_HASHMARK) + { + is_private = true; + context_p->token.flags |= LEXER_NO_SKIP_SPACES; + lexer_next_token (context_p); + } + 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)) + && lexer_compare_literal_to_string (context_p, "constructor", 11) + && stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR) { - if (stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR) - { - const uint8_t *class_source_p = scanner_context.active_literal_pool_p->source_p; - scanner_info_t *info_p = scanner_insert_info (context_p, class_source_p, sizeof (scanner_info_t)); - - info_p->type = SCANNER_TYPE_CLASS_CONSTRUCTOR; - parser_stack_change_last_uint8 (context_p, SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR); - } + parser_stack_pop_uint8 (context_p); + scanner_class_info_t *private_members_p; + parser_stack_pop (context_p, &private_members_p, sizeof (scanner_class_info_t *)); + private_members_p->info.u8_arg = SCANNER_CONSTRUCTOR_EXPLICIT; + parser_stack_push (context_p, &private_members_p, sizeof (scanner_class_info_t *)); + parser_stack_push_uint8 (context_p, SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR); } else if (lexer_token_is_identifier (context_p, "static", 6)) { - lexer_scan_identifier (context_p); + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); identifier_found = true; + private_field_flags |= SCANNER_PRIVATE_FIELD_STATIC; } scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; uint16_t literal_pool_flags = SCANNER_LITERAL_POOL_FUNCTION; - if (lexer_token_is_identifier (context_p, "get", 3) || lexer_token_is_identifier (context_p, "set", 3)) + private_field_flags |= lexer_token_is_identifier (context_p, "get", 3) ? SCANNER_PRIVATE_FIELD_GETTER : 0; + private_field_flags |= lexer_token_is_identifier (context_p, "set", 3) ? SCANNER_PRIVATE_FIELD_SETTER : 0; + + if (private_field_flags & SCANNER_PRIVATE_FIELD_GETTER_SETTER) { - lexer_scan_identifier (context_p); + private_field_flags &= ~(uint32_t) SCANNER_PRIVATE_FIELD_PROPERTY; + + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); identifier_found = true; if (context_p->token.type == LEXER_LEFT_PAREN) { + if (is_private) + { + private_field_flags |= SCANNER_PRIVATE_FIELD_METHOD; + scanner_add_private_identifier (context_p, private_field_flags); + } + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); continue; @@ -2646,13 +2715,19 @@ scanner_scan_all (parser_context_t *context_p) /**< context */ } else if (lexer_token_is_identifier (context_p, "async", 5)) { - lexer_scan_identifier (context_p); + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); identifier_found = true; if (!(context_p->token.flags & LEXER_WAS_NEWLINE)) { if (context_p->token.type == LEXER_LEFT_PAREN) { + if (is_private) + { + private_field_flags |= SCANNER_PRIVATE_FIELD_METHOD; + scanner_add_private_identifier (context_p, private_field_flags); + } + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); continue; @@ -2662,19 +2737,29 @@ scanner_scan_all (parser_context_t *context_p) /**< context */ if (context_p->token.type == LEXER_MULTIPLY) { - lexer_scan_identifier (context_p); + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR; } } } else if (context_p->token.type == LEXER_MULTIPLY) { - lexer_scan_identifier (context_p); + if (is_private) + { + scanner_raise_error (context_p); + } + + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR; } if (context_p->token.type == LEXER_LEFT_SQUARE) { + if (is_private) + { + scanner_raise_error (context_p); + } + if (literal_pool_flags != SCANNER_LITERAL_POOL_FUNCTION) { parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); @@ -2685,9 +2770,31 @@ scanner_scan_all (parser_context_t *context_p) /**< context */ break; } + if (context_p->token.type == LEXER_HASHMARK) + { + if (is_private) + { + scanner_raise_error (context_p); + } + + is_private = true; + context_p->token.flags |= LEXER_NO_SKIP_SPACES; + lexer_next_token (context_p); + } + + if (is_private) + { + if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) + { + private_field_flags |= SCANNER_PRIVATE_FIELD_METHOD; + } + + scanner_add_private_identifier (context_p, private_field_flags); + } + if (context_p->token.type == LEXER_LITERAL) { - lexer_scan_identifier (context_p); + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); identifier_found = true; } @@ -3103,7 +3210,7 @@ scanner_scan_all (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (stack_top == SCAN_STACK_OBJECT_LITERAL); - if (lexer_scan_identifier (context_p)) + if (lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS)) { lexer_check_property_modifier (context_p); } @@ -3160,7 +3267,7 @@ scanner_scan_all (parser_context_t *context_p) /**< context */ #endif /* JERRY_ESNEXT */ parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); - lexer_scan_identifier (context_p); + lexer_scan_identifier (context_p, LEXER_PARSE_NO_OPTS); #if JERRY_ESNEXT if (context_p->token.type == LEXER_LEFT_SQUARE) @@ -3740,7 +3847,7 @@ scan_completed: } case SCANNER_TYPE_CLASS_CONSTRUCTOR: { - JERRY_DEBUG_MSG (" CLASS_CONSTRUCTOR: source:%d\n", (int) (info_p->source_p - source_start_p)); + JERRY_DEBUG_MSG (" CLASS: source:%d\n", (int) (info_p->source_p - source_start_p)); print_location = false; break; } diff --git a/jerry-core/parser/js/js-scanner.h b/jerry-core/parser/js/js-scanner.h index 9513d099f..c9c2eda02 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -87,6 +87,25 @@ typedef struct scanner_info_t uint16_t u16_arg; /**< custom 16-bit value */ } scanner_info_t; +/** + * Scanner info for class private field + */ +typedef struct scanner_class_private_member_t +{ + lexer_lit_location_t loc; /**< loc */ + uint8_t u8_arg; /**< custom 8-bit value */ + struct scanner_class_private_member_t *prev_p; /**< prev private field */ +} scanner_class_private_member_t; + +/** + * Scanner info extended with class private fields. + */ +typedef struct +{ + scanner_info_t info; /**< header */ + scanner_class_private_member_t *members; /**< first private field */ +} scanner_class_info_t; + /** * Scanner info extended with a location. */ @@ -259,6 +278,33 @@ typedef enum SCANNER_FUNCTION_IS_STRICT = (1 << 5), /**< function is strict */ } scanner_function_flags_t; +/** + * Constants for u8_arg flags in scanner_class_info_t. + */ +typedef enum +{ + SCANNER_CONSTRUCTOR_IMPLICIT = 0, /**< implicit constructor */ + SCANNER_CONSTRUCTOR_EXPLICIT = (1 << 0), /**< explicit constructor */ + SCANNER_SUCCESSFUL_CLASS_SCAN = (1 << 1), /**< class scan was successful */ + SCANNER_PRIVATE_FIELD_ACTIVE = (1 << 2), /**< private field is active */ +} scanner_constuctor_flags_t; + +/** + * Constants for u8_arg flags in scanner_class_private_member_t. + */ +typedef enum +{ + SCANNER_PRIVATE_FIELD_PROPERTY = (1 << 0), /**< private field initializer */ + SCANNER_PRIVATE_FIELD_METHOD = (1 << 1), /**< private field method */ + SCANNER_PRIVATE_FIELD_STATIC = (1 << 2), /**< static private property */ + SCANNER_PRIVATE_FIELD_GETTER = (1 << 3), /**< private field getter */ + SCANNER_PRIVATE_FIELD_SETTER = (1 << 4), /**< private field setter */ + SCANNER_PRIVATE_FIELD_SEEN = (1 << 5), /**< private field has already been seen */ + SCANNER_PRIVATE_FIELD_IGNORED = SCANNER_PRIVATE_FIELD_METHOD | SCANNER_PRIVATE_FIELD_STATIC, + SCANNER_PRIVATE_FIELD_GETTER_SETTER = (SCANNER_PRIVATE_FIELD_GETTER | SCANNER_PRIVATE_FIELD_SETTER), + SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER = (SCANNER_PRIVATE_FIELD_PROPERTY | SCANNER_PRIVATE_FIELD_GETTER_SETTER), +} scanner_private_field_flags_t; + #if JERRY_ESNEXT /** diff --git a/jerry-core/parser/js/parser-error-messages.inc.h b/jerry-core/parser/js/parser-error-messages.inc.h index 870cef424..45b158ea8 100644 --- a/jerry-core/parser/js/parser-error-messages.inc.h +++ b/jerry-core/parser/js/parser-error-messages.inc.h @@ -16,9 +16,6 @@ /* This file is automatically generated by the gen-strings.py script * from parser-error-messages.ini. Do not edit! */ -#if JERRY_PARSER -PARSER_ERROR_DEF (PARSER_ERR_NO_ERROR, "No error") -#endif /* JERRY_PARSER */ #if JERRY_PARSER && !(JERRY_ESNEXT) PARSER_ERROR_DEF (PARSER_ERR_INVALID_NUMBER, "Invalid number") #endif /* JERRY_PARSER && !(JERRY_ESNEXT) */ @@ -95,6 +92,9 @@ PARSER_ERROR_DEF (PARSER_ERR_META_EXPECTED, "Expected 'meta' keyword") #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_UNEXPECTED_END, "Unexpected end of input") #endif /* JERRY_PARSER */ +#if JERRY_ESNEXT && JERRY_PARSER +PARSER_ERROR_DEF (PARSER_ERR_UNEXPECTED_PRIVATE_FIELD, "Unexpected private field") +#endif /* JERRY_ESNEXT && JERRY_PARSER */ #if JERRY_MODULE_SYSTEM && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_LEFT_BRACE_MULTIPLY_EXPECTED, "Expected '{' or '*' token") #endif /* JERRY_MODULE_SYSTEM && JERRY_PARSER */ @@ -147,6 +147,7 @@ PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_IMPORT_BINDING, "Duplicated imported bin PARSER_ERROR_DEF (PARSER_ERR_WHILE_EXPECTED, "While expected for do-while loop") #endif /* JERRY_PARSER */ #if JERRY_ESNEXT && JERRY_PARSER +PARSER_ERROR_DEF (PARSER_ERR_DELETE_PRIVATE_FIELD, "Private fields can not be deleted") PARSER_ERROR_DEF (PARSER_ERR_INVALID_LHS_FOR_LOOP, "Invalid left-hand-side in for-loop") #endif /* JERRY_ESNEXT && JERRY_PARSER */ #if JERRY_PARSER @@ -185,6 +186,7 @@ PARSER_ERROR_DEF (PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED, "Expected ',' or ']' #if JERRY_ESNEXT && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_ILLEGAL_PROPERTY_IN_DECLARATION, "Illegal property in declaration context") PARSER_ERROR_DEF (PARSER_ERR_INVALID_DESTRUCTURING_PATTERN, "Invalid destructuring assignment target") +PARSER_ERROR_DEF (PARSER_ERR_DUPLICATED_PRIVATE_FIELD, "Private field has already been declared") #endif /* JERRY_ESNEXT && JERRY_PARSER */ #if JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_NO_ARGUMENTS_EXPECTED, "Property getters must have no arguments") @@ -231,6 +233,7 @@ PARSER_ERROR_DEF (PARSER_ERR_NEWLINE_NOT_ALLOWED, "Newline is not allowed in str PARSER_ERROR_DEF (PARSER_ERR_OCTAL_NUMBER_NOT_ALLOWED, "Octal numbers are not allowed in strict mode") #endif /* JERRY_PARSER */ #if JERRY_ESNEXT && JERRY_PARSER +PARSER_ERROR_DEF (PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR, "Class constructor may not be a private method") PARSER_ERROR_DEF (PARSER_ERR_FOR_AWAIT_NO_OF, "only 'of' form is allowed for for-await loops") #endif /* JERRY_ESNEXT && JERRY_PARSER */ #if JERRY_PARSER @@ -262,6 +265,7 @@ PARSER_ERROR_DEF (PARSER_ERR_INVALID_CONTINUE_LABEL, "Labeled statement targeted #endif /* JERRY_PARSER */ #if JERRY_ESNEXT && JERRY_PARSER PARSER_ERROR_DEF (PARSER_ERR_LEXICAL_LET_BINDING, "Let binding cannot appear in let/const declarations") +PARSER_ERROR_DEF (PARSER_ERR_UNDECLARED_PRIVATE_FIELD, "Private field must be declared in an enclosing class") PARSER_ERROR_DEF (PARSER_ERR_INVALID_LHS_PREFIX_OP, "Invalid left-hand side expression in prefix operation") #endif /* JERRY_ESNEXT && JERRY_PARSER */ #if JERRY_PARSER diff --git a/jerry-core/parser/js/parser-error-messages.ini b/jerry-core/parser/js/parser-error-messages.ini index 4360b8cd6..d709f39a5 100644 --- a/jerry-core/parser/js/parser-error-messages.ini +++ b/jerry-core/parser/js/parser-error-messages.ini @@ -137,4 +137,8 @@ PARSER_ERR_VARIABLE_REDECLARED = "Local variable is redeclared" PARSER_ERR_WHILE_EXPECTED = "While expected for do-while loop" PARSER_ERR_WITH_NOT_ALLOWED = "With statement not allowed in strict mode" PARSER_ERR_YIELD_NOT_ALLOWED = "Yield expression is not allowed here" -PARSER_ERR_NO_ERROR = "No error" +PARSER_ERR_DUPLICATED_PRIVATE_FIELD = "Private field has already been declared" +PARSER_ERR_UNDECLARED_PRIVATE_FIELD = "Private field must be declared in an enclosing class" +PARSER_ERR_DELETE_PRIVATE_FIELD = "Private fields can not be deleted" +PARSER_ERR_UNEXPECTED_PRIVATE_FIELD = "Unexpected private field" +PARSER_ERR_CLASS_PRIVATE_CONSTRUCTOR = "Class constructor may not be a private method" diff --git a/jerry-core/parser/js/parser-errors.h b/jerry-core/parser/js/parser-errors.h index 6f0bfdebf..5a8ffe588 100644 --- a/jerry-core/parser/js/parser-errors.h +++ b/jerry-core/parser/js/parser-errors.h @@ -32,6 +32,7 @@ typedef enum /** @endcond */ PARSER_ERR_OUT_OF_MEMORY, PARSER_ERR_INVALID_REGEXP, + PARSER_ERR_NO_ERROR } parser_error_msg_t; const lit_utf8_byte_t* parser_get_error_utf8 (uint32_t id); diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 7df87a6e3..b8904e3b1 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -894,6 +894,115 @@ opfunc_async_create_and_await (vm_frame_ctx_t *frame_ctx_p, /**< frame context * return result; } /* opfunc_async_create_and_await */ +/** + * PrivateMethodOrAccessorAdd abstact operation. + * + * See also: ECMAScript v12, 7.3.29. + * + * @return ECMA_VALUE_ERROR - initialization fails + * ECMA_VALUE_UNDEFINED - otherwise + */ +static ecma_value_t +opfunc_private_method_or_accessor_add (ecma_object_t *class_object_p, /**< the function itself */ + ecma_object_t *this_obj_p, /**< this object */ + uint32_t static_flag) +{ + ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); + ecma_property_t *prop_p = ecma_find_named_property (class_object_p, internal_string_p); + + if (prop_p == NULL) + { + return ECMA_VALUE_UNDEFINED; + } + + ecma_value_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, ECMA_PROPERTY_VALUE_PTR (prop_p)->value); + ecma_value_t *current_p = collection_p + 1; + ecma_value_t *end_p = ecma_compact_collection_end (collection_p); + + while (current_p < end_p) + { + uint32_t prop_desc = *current_p++; + ecma_private_property_kind_t kind = ECMA_PRIVATE_PROPERTY_KIND (prop_desc); + + if ((prop_desc & ECMA_PRIVATE_PROPERTY_STATIC_FLAG) != static_flag || kind == ECMA_PRIVATE_FIELD) + { + current_p += 2; + continue; + } + + ecma_string_t *prop_name_p = ecma_get_symbol_from_value (*current_p++); + ecma_value_t method = *current_p++; + + JERRY_ASSERT (prop_name_p->u.hash & ECMA_SYMBOL_FLAG_PRIVATE_INSTANCE_METHOD); + + prop_p = ecma_find_named_property (this_obj_p, prop_name_p); + ecma_object_t *method_p = ecma_get_object_from_value (method); + + if (kind == ECMA_PRIVATE_METHOD) + { + if (prop_p != NULL) + { + return ecma_raise_type_error (ECMA_ERR_CANNOT_DECLARE_SAME_PRIVATE_FIELD_TWICE); + } + + ecma_property_value_t *prop_value_p = + ecma_create_named_data_property (this_obj_p, prop_name_p, ECMA_PROPERTY_FIXED, NULL); + prop_value_p->value = method; + continue; + } + + if (prop_p == NULL) + { + ecma_object_t *getter_p = (kind == ECMA_PRIVATE_GETTER) ? method_p : NULL; + ecma_object_t *setter_p = (kind == ECMA_PRIVATE_SETTER) ? method_p : NULL; + ecma_create_named_accessor_property (this_obj_p, prop_name_p, getter_p, setter_p, ECMA_PROPERTY_FIXED, NULL); + continue; + } + + ecma_property_value_t *accessor_objs_p = ECMA_PROPERTY_VALUE_PTR (prop_p); + ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (accessor_objs_p); + + if (kind == ECMA_PRIVATE_GETTER) + { + ECMA_SET_POINTER (get_set_pair_p->getter_cp, method_p); + } + else + { + JERRY_ASSERT (kind == ECMA_PRIVATE_SETTER); + ECMA_SET_POINTER (get_set_pair_p->setter_cp, method_p); + } + } + + return ECMA_VALUE_UNDEFINED; +} /* opfunc_private_method_or_accessor_add */ + +/** + * DefineField abstract operation. + * + * See also: ECMAScript v12, 7.3.32. + * + * @return ECMA_VALUE_ERROR - operation fails + * ECMA_VALUE_{TRUE/FALSE} - otherwise + */ +ecma_value_t +opfunc_define_field (ecma_value_t base, ecma_value_t property, ecma_value_t value) +{ + ecma_string_t *property_key_p = ecma_op_to_property_key (property); + + JERRY_ASSERT (property_key_p != NULL); + ecma_object_t *obj_p = ecma_get_object_from_value (base); + ecma_property_descriptor_t desc = ecma_make_empty_property_descriptor (); + desc.value = value; + desc.flags = (JERRY_PROP_IS_WRITABLE | JERRY_PROP_IS_WRITABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE + | JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE | JERRY_PROP_IS_CONFIGURABLE_DEFINED + | JERRY_PROP_IS_VALUE_DEFINED | JERRY_PROP_SHOULD_THROW); + + ecma_value_t result = ecma_op_object_define_own_property (obj_p, property_key_p, &desc); + ecma_deref_ecma_string (property_key_p); + + return result; +} /* opfunc_define_field */ + /** * Initialize class fields. * @@ -905,6 +1014,14 @@ opfunc_init_class_fields (ecma_object_t *class_object_p, /**< the function itsel ecma_value_t this_val) /**< this_arg of the function */ { JERRY_ASSERT (ecma_is_value_object (this_val)); + ecma_object_t *this_obj_p = ecma_get_object_from_value (this_val); + + ecma_value_t result = opfunc_private_method_or_accessor_add (class_object_p, this_obj_p, 0); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_INIT); ecma_property_t *property_p = ecma_find_named_property (class_object_p, name_p); @@ -938,7 +1055,7 @@ opfunc_init_class_fields (ecma_object_t *class_object_p, /**< the function itsel ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_function_p->u.function.scope_cp); - ecma_value_t result = vm_run (&shared_class_fields.header, this_val, scope_p); + result = vm_run (&shared_class_fields.header, this_val, scope_p); JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result) || result == ECMA_VALUE_UNDEFINED); return result; @@ -1097,6 +1214,407 @@ opfunc_set_home_object (ecma_object_t *func_p, /**< function object */ } } /* opfunc_set_home_object */ +/** + * Make private key from descriptor + */ +ecma_string_t * +opfunc_make_private_key (ecma_value_t descriptor) /**< descriptor */ +{ + ecma_string_t *private_key_p = ecma_new_symbol_from_descriptor_string (descriptor); + private_key_p->u.hash |= ECMA_SYMBOL_FLAG_PRIVATE_KEY; + + return (ecma_string_t *) private_key_p; +} /* opfunc_make_private_key */ + +/** + * Find a private property in the private elements internal property given the key + */ +static ecma_property_t * +opfunc_find_private_key (ecma_object_t *class_object_p, /**< class environment */ + ecma_object_t *obj_p, /**< object */ + ecma_string_t *search_key_p, /**< key */ + ecma_string_t **out_private_key_p) /**< [out] private key */ +{ + ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); + ecma_property_t *prop_p = ecma_find_named_property (class_object_p, internal_string_p); + + if (prop_p == NULL) + { + return NULL; + } + + ecma_value_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, ECMA_PROPERTY_VALUE_PTR (prop_p)->value); + ecma_value_t *current_p = collection_p + 1; + ecma_value_t *end_p = ecma_compact_collection_end (collection_p); + + while (current_p < end_p) + { + current_p++; /* skip kind */ + ecma_string_t *private_key_p = ecma_get_prop_name_from_value (*current_p++); + current_p++; /* skip value */ + + JERRY_ASSERT (ecma_prop_name_is_symbol (private_key_p)); + + ecma_string_t *private_key_desc_p = + ecma_get_string_from_value (((ecma_extended_string_t *) private_key_p)->u.symbol_descriptor); + + if (ecma_compare_ecma_strings (private_key_desc_p, search_key_p)) + { + prop_p = ecma_find_named_property (obj_p, private_key_p); + + if (out_private_key_p) + { + *out_private_key_p = private_key_p; + } + + return prop_p; + } + } + + return NULL; +} /* opfunc_find_private_key */ + +/** + * PrivateElementFind abstact operation + * + * See also: ECMAScript v12, 7.3.27 + * + * @return - ECMA_VALUE_ERROR - if the operation fails + * ECMA_VALUE_EMPTY - otherwise + */ +static ecma_property_t * +opfunc_find_private_element (ecma_object_t *obj_p, /**< object */ + ecma_string_t *key_p, /**< key */ + ecma_string_t **private_key_p, /**< [out] private key */ + bool allow_heritage) +{ + JERRY_ASSERT (private_key_p != NULL); + JERRY_ASSERT (*private_key_p == NULL); + ecma_object_t *lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p; + + while (true) + { + JERRY_ASSERT (lex_env_p != NULL); + + if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS + && (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) != 0 + && !ECMA_LEX_ENV_CLASS_IS_MODULE (lex_env_p)) + { + ecma_object_t *class_object_p = ((ecma_lexical_environment_class_t *) lex_env_p)->object_p; + + ecma_property_t *prop_p = opfunc_find_private_key (class_object_p, obj_p, key_p, private_key_p); + + if (prop_p || *private_key_p != NULL) + { + /* Found non shadowed property */ + return prop_p; + } + + if (!allow_heritage) + { + return NULL; + } + } + + if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL) + { + break; + } + + lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + } + + return NULL; +} /* opfunc_find_private_element */ + +/** + * In expression runtime evaluation in case of private identifiers + * + * See also: ECMAScript v12, 13.10.1 + * + * @return - ECMA_VALUE_ERROR - if the operation fails + * ECMA_VALUE_TRUE - if the property was found in the base object + * ECMA_VALUE_FALSE - otherwise + */ +ecma_value_t +opfunc_private_in (ecma_value_t base, ecma_value_t property) +{ + if (!ecma_is_value_object (base)) + { + return ecma_raise_type_error (ECMA_ERR_RIGHT_VALUE_OF_IN_MUST_BE_AN_OBJECT); + } + + ecma_object_t *obj_p = ecma_get_object_from_value (base); + ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (property); + ecma_string_t *private_key_p = NULL; + + ecma_property_t *prop_p = opfunc_find_private_element (obj_p, prop_name_p, &private_key_p, false); + + return ecma_make_boolean_value (prop_p != NULL); +} /* opfunc_private_in */ + +/** + * PrivateFieldAdd abstact operation + * + * See also: ECMAScript v12, 7.3.28 + * + * @return - ECMA_VALUE_ERROR - if the operation fails + * ECMA_VALUE_EMPTY - otherwise + */ +ecma_value_t +opfunc_private_field_add (ecma_value_t base, /**< base object */ + ecma_value_t property, /**< property name */ + ecma_value_t value) /**< ecma value */ +{ + ecma_object_t *obj_p = ecma_get_object_from_value (base); + ecma_string_t *prop_name_p = ecma_get_string_from_value (property); + ecma_string_t *private_key_p = NULL; + + ecma_property_t *prop_p = opfunc_find_private_element (obj_p, prop_name_p, &private_key_p, false); + + if (prop_p != NULL) + { + return ecma_raise_type_error (ECMA_ERR_CANNOT_DECLARE_SAME_PRIVATE_FIELD_TWICE); + } + + ecma_property_value_t *value_p = + ecma_create_named_data_property (obj_p, private_key_p, ECMA_PROPERTY_FLAG_WRITABLE, NULL); + + value_p->value = ecma_copy_value_if_not_object (value); + + return ECMA_VALUE_EMPTY; +} /* opfunc_private_field_add */ + +/** + * PrivateSet abstact operation + * + * See also: ECMAScript v12, 7.3.31 + * + * @return - ECMA_VALUE_ERROR - if the operation fails + * ECMA_VALUE_EMPTY - otherwise + */ +ecma_value_t +opfunc_private_set (ecma_value_t base, /**< this object */ + ecma_value_t property, /**< property name */ + ecma_value_t value) /**< ecma value */ +{ + ecma_object_t *obj_p = ecma_get_object_from_value (base); + ecma_string_t *prop_name_p = ecma_get_string_from_value (property); + ecma_string_t *private_key_p = NULL; + + ecma_property_t *prop_p = opfunc_find_private_element (obj_p, prop_name_p, &private_key_p, true); + + if (prop_p == NULL) + { + return ecma_raise_type_error (ECMA_ERR_CANNOT_WRITE_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT); + } + + if (*prop_p & ECMA_PROPERTY_FLAG_DATA) + { + JERRY_ASSERT (ecma_prop_name_is_symbol (private_key_p)); + + if (private_key_p->u.hash & ECMA_SYMBOL_FLAG_PRIVATE_INSTANCE_METHOD) + { + return ecma_raise_type_error (ECMA_ERR_PRIVATE_METHOD_IS_NOT_WRITABLE); + } + + ecma_value_assign_value (&ECMA_PROPERTY_VALUE_PTR (prop_p)->value, value); + return ecma_copy_value (value); + } + + ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (ECMA_PROPERTY_VALUE_PTR (prop_p)); + + if (get_set_pair_p->setter_cp == JMEM_CP_NULL) + { + return ecma_raise_type_error (ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_SETTER); + } + + ecma_object_t *setter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp); + + return ecma_op_function_call (setter_p, base, &value, 1); +} /* opfunc_private_set */ + +/** + * PrivateGet abstact operation + * + * See also: ECMAScript v12, 7.3.30 + * + * @return - ECMA_VALUE_ERROR - if the operation fails + * private property value - otherwise + */ +ecma_value_t +opfunc_private_get (ecma_value_t base, /**< this object */ + ecma_value_t property) /**< property name */ +{ + ecma_value_t base_obj = ecma_op_to_object (base); + + if (ECMA_IS_VALUE_ERROR (base_obj)) + { + return base_obj; + } + + ecma_object_t *obj_p = ecma_get_object_from_value (base_obj); + ecma_string_t *prop_name_p = ecma_get_string_from_value (property); + ecma_string_t *private_key_p = NULL; + + ecma_property_t *prop_p = opfunc_find_private_element (obj_p, prop_name_p, &private_key_p, true); + + ecma_value_t result; + + if (prop_p == NULL) + { + result = ecma_raise_type_error (ECMA_ERR_CANNOT_READ_PRIVATE_MEMBER_TO_AN_OBJECT_WHOSE_CLASS_DID_NOT_DECLARE_IT); + } + else if (*prop_p & ECMA_PROPERTY_FLAG_DATA) + { + result = ecma_copy_value (ECMA_PROPERTY_VALUE_PTR (prop_p)->value); + } + else + { + ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (ECMA_PROPERTY_VALUE_PTR (prop_p)); + + if (get_set_pair_p->getter_cp == JMEM_CP_NULL) + { + result = ecma_raise_type_error (ECMA_ERR_PRIVATE_FIELD_WAS_DEFINED_WITHOUT_A_GETTER); + } + else + { + ecma_object_t *getter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp); + result = ecma_op_function_call (getter_p, base, NULL, 0); + } + } + + ecma_deref_object (obj_p); + + return result; +} /* opfunc_private_get */ + +/** + * Find the private property in the object who's private key descriptor matches the given key + */ +static ecma_string_t * +opfunc_create_private_key (ecma_value_t *collection_p, /**< TODO */ + ecma_value_t search_key, /**< key */ + ecma_private_property_kind_t search_kind) +{ + if (search_kind < ECMA_PRIVATE_GETTER) + { + return opfunc_make_private_key (search_key); + } + + ecma_string_t *search_key_p = ecma_get_string_from_value (search_key); + + ecma_value_t *current_p = collection_p + 1; + ecma_value_t *end_p = ecma_compact_collection_end (collection_p); + + while (current_p < end_p) + { + ecma_private_property_kind_t kind = ECMA_PRIVATE_PROPERTY_KIND (*current_p++); + ecma_string_t *private_key_p = ecma_get_prop_name_from_value (*current_p++); + current_p++; /* skip value */ + + if (kind < ECMA_PRIVATE_GETTER) + { + continue; + } + + JERRY_ASSERT (ecma_prop_name_is_symbol (private_key_p)); + + ecma_string_t *private_key_desc_p = + ecma_get_string_from_value (((ecma_extended_string_t *) private_key_p)->u.symbol_descriptor); + + if (ecma_compare_ecma_strings (private_key_desc_p, search_key_p)) + { + ecma_deref_ecma_string (search_key_p); + ecma_ref_ecma_string (private_key_p); + return private_key_p; + } + } + + return opfunc_make_private_key (search_key); +} /* opfunc_create_private_key */ + +/** + * Collect private members for PrivateMethodOrAccessorAdd and PrivateFieldAdd abstract operations + */ +void +opfunc_collect_private_properties (ecma_value_t constructor, ecma_value_t prop_name, ecma_value_t value, uint8_t opcode) +{ + ecma_private_property_kind_t kind = ECMA_PRIVATE_FIELD; + bool is_static = false; + + if (opcode >= CBC_EXT_COLLECT_PRIVATE_STATIC_FIELD) + { + opcode = (uint8_t) (opcode - PARSER_STATIC_PRIVATE_TO_PRIVATE_OFFSET); + is_static = true; + } + + if (opcode == CBC_EXT_COLLECT_PRIVATE_METHOD) + { + prop_name ^= value; + value ^= prop_name; + prop_name ^= value; + kind = ECMA_PRIVATE_METHOD; + } + else if (opcode == CBC_EXT_COLLECT_PRIVATE_GETTER) + { + kind = ECMA_PRIVATE_GETTER; + } + else if (opcode == CBC_EXT_COLLECT_PRIVATE_SETTER) + { + kind = ECMA_PRIVATE_SETTER; + } + + JERRY_ASSERT (ecma_is_value_object (constructor)); + JERRY_ASSERT (ecma_is_value_string (prop_name)); + JERRY_ASSERT (ecma_is_value_object (value) || ecma_is_value_undefined (value)); + + ecma_object_t *constructor_p = ecma_get_object_from_value (constructor); + ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); + ecma_property_t *prop_p = ecma_find_named_property (constructor_p, internal_string_p); + ecma_value_t *collection_p; + ecma_property_value_t *prop_value_p; + + if (prop_p == NULL) + { + collection_p = ecma_new_compact_collection (); + ECMA_CREATE_INTERNAL_PROPERTY (constructor_p, internal_string_p, prop_p, prop_value_p); + ECMA_SET_INTERNAL_VALUE_POINTER (prop_value_p->value, collection_p); + } + else + { + prop_value_p = ECMA_PROPERTY_VALUE_PTR (prop_p); + collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, prop_value_p->value); + } + + ecma_string_t *key_p = opfunc_create_private_key (collection_p, prop_name, kind); + + if (kind != ECMA_PRIVATE_FIELD) + { + key_p->u.hash |= ECMA_SYMBOL_FLAG_PRIVATE_INSTANCE_METHOD; + } + + if (is_static) + { + kind |= ECMA_PRIVATE_PROPERTY_STATIC_FLAG; + } + + collection_p = ecma_compact_collection_push_back (collection_p, (ecma_value_t) kind); + collection_p = ecma_compact_collection_push_back (collection_p, ecma_make_symbol_value (key_p)); + collection_p = ecma_compact_collection_push_back (collection_p, value); + +#ifndef JERRY_NDEBUG + ecma_value_t *end_p = ecma_compact_collection_end (collection_p); + ecma_value_t *current_p = collection_p + 1; + + JERRY_ASSERT ((end_p - current_p) % ECMA_PRIVATE_ELEMENT_LIST_SIZE == 0); +#endif /* !defined (JERRY_NDEBUG) */ + + ECMA_SET_INTERNAL_VALUE_POINTER (prop_value_p->value, collection_p); + + ecma_free_value (value); +} /* opfunc_collect_private_properties */ + /** * ClassDefinitionEvaluation environment initialization part * @@ -1222,18 +1740,13 @@ opfunc_init_class (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ if (ecma_get_object_type (ctor_p) == ECMA_OBJECT_TYPE_FUNCTION) { - ecma_object_t *proto_env_p = ecma_create_lex_env_class (frame_ctx_p->lex_env_p, 0); - ECMA_SET_NON_NULL_POINTER (proto_env_p->u1.bound_object_cp, proto_p); - - ECMA_SET_NON_NULL_POINTER_TAG (((ecma_extended_object_t *) ctor_p)->u.function.scope_cp, proto_env_p, 0); + opfunc_bind_class_environment (frame_ctx_p->lex_env_p, proto_p, ctor_p, ctor_p); /* 15. set F’s [[ConstructorKind]] internal slot to "derived". */ if (heritage_present) { ECMA_SET_THIRD_BIT_TO_POINTER_TAG (((ecma_extended_object_t *) ctor_p)->u.function.scope_cp); } - - ecma_deref_object (proto_env_p); } stack_top_p[-2] = stack_top_p[-1]; @@ -1242,6 +1755,34 @@ opfunc_init_class (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ return ECMA_VALUE_EMPTY; } /* opfunc_init_class */ +/** + * Creates a new class lexical environment and binds the bound object and the class's object + * + * @return newly created class lexical environment - if func_obj_p is not present + * NULL - otherwise, also the environment is set as the func_obj_p's scope + */ +ecma_object_t * +opfunc_bind_class_environment (ecma_object_t *lex_env_p, /**< lexical environment */ + ecma_object_t *home_object_p, /**< bound object */ + ecma_object_t *ctor_p, /**< constructor object */ + ecma_object_t *func_obj_p) /**< function object */ +{ + ecma_object_t *proto_env_p = ecma_create_lex_env_class (lex_env_p, sizeof (ecma_lexical_environment_class_t)); + ECMA_SET_NON_NULL_POINTER (proto_env_p->u1.bound_object_cp, home_object_p); + ((ecma_lexical_environment_class_t *) proto_env_p)->object_p = ctor_p; + ((ecma_lexical_environment_class_t *) proto_env_p)->type = ECMA_LEX_ENV_CLASS_TYPE_CLASS_ENV; + + if (func_obj_p) + { + JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION); + ECMA_SET_NON_NULL_POINTER_TAG (((ecma_extended_object_t *) func_obj_p)->u.function.scope_cp, proto_env_p, 0); + ecma_deref_object (proto_env_p); + return NULL; + } + + return proto_env_p; +} /* opfunc_bind_class_environment */ + /** * Set [[Enumerable]] and [[HomeObject]] attributes for all class method */ @@ -1277,7 +1818,7 @@ opfunc_set_class_attributes (ecma_object_t *obj_p, /**< object */ { JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_DELETED || (ECMA_PROPERTY_IS_INTERNAL (property) - && property_pair_p->names_cp[index] == LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED)); + && LIT_INTERNAL_MAGIC_STRING_IGNORED (property_pair_p->names_cp[index]))); continue; } @@ -1311,6 +1852,37 @@ opfunc_set_class_attributes (ecma_object_t *obj_p, /**< object */ } } /* opfunc_set_class_attributes */ +/** + * Set [[HomeObject]] attributes for all class private elements + */ +static void +opfunc_set_private_instance_method_attributes (ecma_object_t *class_object_p, /**< class constructor */ + ecma_object_t *parent_env_p) /**< parent environment */ +{ + ecma_string_t *internal_string_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS); + ecma_property_t *prop_p = ecma_find_named_property (class_object_p, internal_string_p); + + if (prop_p == NULL) + { + return; + } + + ecma_value_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, ECMA_PROPERTY_VALUE_PTR (prop_p)->value); + ecma_value_t *current_p = collection_p + 1; + ecma_value_t *end_p = ecma_compact_collection_end (collection_p); + + while (current_p < end_p) + { + current_p += 2; /* skip kind, name */ + ecma_value_t value = *current_p++; + + if (!ecma_is_value_undefined (value)) + { + opfunc_set_home_object (ecma_get_object_from_value (value), parent_env_p); + } + } +} /* opfunc_set_private_instance_method_attributes */ + /** * Pop the current lexical environment referenced by the frame context */ @@ -1346,13 +1918,12 @@ opfunc_finalize_class (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_op_initialize_binding (class_env_p, ecma_get_string_from_value (class_name), stack_top_p[-2]); } - ecma_object_t *ctor_env_p = ecma_create_lex_env_class (class_env_p, 0); - ECMA_SET_NON_NULL_POINTER (ctor_env_p->u1.bound_object_cp, ctor_p); - ecma_object_t *proto_env_p = ecma_create_lex_env_class (class_env_p, 0); - ECMA_SET_NON_NULL_POINTER (proto_env_p->u1.bound_object_cp, proto_p); + ecma_object_t *ctor_env_p = opfunc_bind_class_environment (class_env_p, ctor_p, ctor_p, NULL); + ecma_object_t *proto_env_p = opfunc_bind_class_environment (class_env_p, proto_p, ctor_p, NULL); opfunc_set_class_attributes (ctor_p, ctor_env_p); opfunc_set_class_attributes (proto_p, proto_env_p); + opfunc_set_private_instance_method_attributes (ctor_p, proto_env_p); ecma_deref_object (proto_env_p); ecma_deref_object (ctor_env_p); @@ -1367,6 +1938,9 @@ opfunc_finalize_class (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ opfunc_pop_lexical_environment (frame_ctx_p); } + ecma_value_t result = opfunc_private_method_or_accessor_add (ctor_p, ctor_p, ECMA_PRIVATE_PROPERTY_STATIC_FLAG); + JERRY_ASSERT (ecma_is_value_undefined (result)); + stack_top_p[-3] = stack_top_p[-2]; *vm_stack_top_p -= 2; } /* opfunc_finalize_class */ diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 7fdb26df3..1657755d1 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -132,10 +132,32 @@ ecma_value_t opfunc_create_implicit_class_constructor (uint8_t opcode, const ecm void opfunc_set_home_object (ecma_object_t *func_p, ecma_object_t *parent_env_p); +ecma_value_t opfunc_define_field (ecma_value_t base, ecma_value_t property, ecma_value_t value); + +ecma_string_t *opfunc_make_private_key (ecma_value_t descriptor); + +ecma_value_t opfunc_private_in (ecma_value_t base, ecma_value_t property); + +ecma_value_t opfunc_private_field_add (ecma_value_t base, ecma_value_t property, ecma_value_t value); + +ecma_value_t opfunc_private_set (ecma_value_t base, ecma_value_t property, ecma_value_t value); + +ecma_value_t opfunc_private_get (ecma_value_t base, ecma_value_t property); + +void opfunc_collect_private_properties (ecma_value_t constructor, + ecma_value_t prop_name, + ecma_value_t method, + uint8_t opcode); + void opfunc_push_class_environment (vm_frame_ctx_t *frame_ctx_p, ecma_value_t **vm_stack_top, ecma_value_t class_name); ecma_value_t opfunc_init_class (vm_frame_ctx_t *frame_context_p, ecma_value_t *stack_top_p); +ecma_object_t *opfunc_bind_class_environment (ecma_object_t *lex_env_p, + ecma_object_t *home_object_p, + ecma_object_t *ctor_p, + ecma_object_t *func_obj_p); + void opfunc_pop_lexical_environment (vm_frame_ctx_t *frame_ctx_p); void opfunc_finalize_class (vm_frame_ctx_t *frame_ctx_p, ecma_value_t **vm_stack_top_p, ecma_value_t class_name); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 50f96a4ae..dacb35415 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -597,6 +597,9 @@ vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ if (ecma_is_value_object (completion_value)) { + ecma_op_bind_this_value (environment_record_p, completion_value); + frame_ctx_p->this_binding = completion_value; + ecma_value_t fields_value = opfunc_init_class_fields (vm_get_class_function (frame_ctx_p), completion_value); if (ECMA_IS_VALUE_ERROR (fields_value)) @@ -617,9 +620,6 @@ vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } else { - ecma_op_bind_this_value (environment_record_p, completion_value); - frame_ctx_p->this_binding = completion_value; - frame_ctx_p->byte_code_p = byte_code_p; uint32_t opcode_data = vm_decode_table[(CBC_END + 1) + opcode]; @@ -1652,7 +1652,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ JERRY_ASSERT (literal_index >= register_end); JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE || (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS - && (frame_ctx_p->lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA))); + && ECMA_LEX_ENV_CLASS_IS_MODULE (frame_ctx_p->lex_env_p))); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); ecma_property_t *property_p = ecma_find_named_property (frame_ctx_p->lex_env_p, name_p); @@ -1822,6 +1822,10 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ memmove (stack_top_p - 3, stack_top_p - 4, 3 * sizeof (ecma_value_t)); stack_top_p[-4] = left_value; + ecma_object_t *class_object_p = ecma_get_object_from_value (stack_top_p[-2]); + ecma_object_t *initializer_func_p = ecma_get_object_from_value (left_value); + opfunc_bind_class_environment (frame_ctx_p->lex_env_p, class_object_p, class_object_p, initializer_func_p); + if (!push_computed) { continue; @@ -1998,6 +2002,100 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ *stack_top_p++ = opfunc_create_implicit_class_constructor (opcode, frame_ctx_p->shared_p->bytecode_header_p); continue; } + case VM_OC_DEFINE_FIELD: + { + result = opfunc_define_field (frame_ctx_p->this_binding, right_value, left_value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + goto free_both_values; + } + case VM_OC_ASSIGN_PRIVATE: + { + result = opfunc_private_set (stack_top_p[-3], stack_top_p[-2], stack_top_p[-1]); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + ecma_free_value (stack_top_p[-3]); + ecma_free_value (stack_top_p[-2]); + ecma_free_value (stack_top_p[-1]); + stack_top_p -= 3; + + if (opcode_data & VM_OC_PUT_STACK) + { + *stack_top_p++ = result; + } + else if (opcode_data & VM_OC_PUT_BLOCK) + { + ecma_fast_free_value (VM_GET_REGISTER (frame_ctx_p, 0)); + VM_GET_REGISTERS (frame_ctx_p)[0] = result; + } + else + { + ecma_free_value (result); + } + + goto free_both_values; + } + case VM_OC_PRIVATE_FIELD_ADD: + { + result = opfunc_private_field_add (frame_ctx_p->this_binding, right_value, left_value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + goto free_both_values; + } + case VM_OC_PRIVATE_PROP_GET: + { + result = opfunc_private_get (left_value, right_value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + *stack_top_p++ = result; + goto free_both_values; + } + case VM_OC_PRIVATE_PROP_REFERENCE: + { + result = opfunc_private_get (stack_top_p[-1], left_value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + *stack_top_p++ = left_value; + *stack_top_p++ = result; + continue; + } + case VM_OC_PRIVATE_IN: + { + result = opfunc_private_in (left_value, right_value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + *stack_top_p++ = result; + goto free_both_values; + } + case VM_OC_COLLECT_PRIVATE_PROPERTY: + { + opfunc_collect_private_properties (stack_top_p[-2], left_value, right_value, opcode); + continue; + } case VM_OC_INIT_CLASS: { result = opfunc_init_class (frame_ctx_p, stack_top_p); @@ -2025,14 +2123,18 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_OC_SET_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_object_t *proto_object_p = ecma_get_object_from_value (stack_top_p[-1]); + ecma_object_t *class_object_p = ecma_get_object_from_value (stack_top_p[-2]); + ecma_object_t *initializer_func_p = ecma_get_object_from_value (left_value); + + opfunc_bind_class_environment (frame_ctx_p->lex_env_p, proto_object_p, class_object_p, initializer_func_p); ecma_property_value_t *property_value_p = - ecma_create_named_data_property (object_p, property_name_p, ECMA_PROPERTY_FIXED, NULL); + ecma_create_named_data_property (class_object_p, property_name_p, ECMA_PROPERTY_FIXED, NULL); property_value_p->value = left_value; property_name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED); - ecma_property_t *property_p = ecma_find_named_property (object_p, property_name_p); + ecma_property_t *property_p = ecma_find_named_property (class_object_p, property_name_p); if (property_p != NULL) { @@ -2074,17 +2176,41 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { 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++; JERRY_ASSERT (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_CLASS_FIELDS); ecma_value_t *computed_class_fields_p = VM_GET_COMPUTED_CLASS_FIELDS (frame_ctx_p); JERRY_ASSERT ((ecma_value_t) next_index < ECMA_COMPACT_COLLECTION_GET_SIZE (computed_class_fields_p)); + ecma_value_t prop_name = computed_class_fields_p[next_index]; - 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; + if (opcode == CBC_EXT_SET_NEXT_COMPUTED_FIELD_ANONYMOUS_FUNC) + { + ecma_object_t *func_obj_p = ecma_get_object_from_value (result); + + JERRY_ASSERT (ecma_find_named_property (func_obj_p, ecma_get_magic_string (LIT_MAGIC_STRING_NAME)) == NULL); + ecma_property_value_t *value_p; + value_p = ecma_create_named_data_property (func_obj_p, + ecma_get_magic_string (LIT_MAGIC_STRING_NAME), + ECMA_PROPERTY_FLAG_CONFIGURABLE, + NULL); + + if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION) + { + ECMA_SET_SECOND_BIT_TO_POINTER_TAG (((ecma_extended_object_t *) func_obj_p)->u.function.scope_cp); + } + + value_p->value = ecma_copy_value (prop_name); + } + + result = opfunc_define_field (frame_ctx_p->this_binding, prop_name, stack_top_p[-1]); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + ecma_free_value (*(--stack_top_p)); + continue; } case VM_OC_PUSH_SUPER_CONSTRUCTOR: { @@ -4884,7 +5010,8 @@ vm_init_module_scope (ecma_module_t *module_p) /**< module without scope */ uint16_t encoding_limit; uint16_t encoding_delta; - ((ecma_lexical_environment_class_t *) scope_p)->module_p = (ecma_object_t *) module_p; + ((ecma_lexical_environment_class_t *) scope_p)->object_p = (ecma_object_t *) module_p; + ((ecma_lexical_environment_class_t *) scope_p)->type = ECMA_LEX_ENV_CLASS_TYPE_MODULE; module_p->scope_p = scope_p; ecma_deref_object (scope_p); diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index e42296266..c831ad1e6 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -237,6 +237,13 @@ typedef enum VM_OC_BREAKPOINT_DISABLED, /**< disabled breakpoint for debugger */ #endif /* JERRY_DEBUGGER */ #if JERRY_ESNEXT + VM_OC_DEFINE_FIELD, /**< define class field */ + VM_OC_PRIVATE_PROP_REFERENCE, /**< reference to class private method */ + VM_OC_ASSIGN_PRIVATE, /**< assign to private field */ + VM_OC_PRIVATE_FIELD_ADD, /**< add private field */ + VM_OC_PRIVATE_PROP_GET, /**< get private field */ + VM_OC_PRIVATE_IN, /**< 'in' opcode handler for private identifiers */ + VM_OC_COLLECT_PRIVATE_PROPERTY, /**< collect private properties */ VM_OC_CHECK_VAR, /**< check redeclared vars in the global scope */ VM_OC_CHECK_LET, /**< check redeclared lets in the global scope */ VM_OC_ASSIGN_LET_CONST, /**< assign values to let/const declarations */ @@ -324,6 +331,13 @@ typedef enum VM_OC_BREAKPOINT_DISABLED = VM_OC_NONE, /**< disabled breakpoint for debugger is unused */ #endif /* !JERRY_DEBUGGER */ #if !JERRY_ESNEXT + VM_OC_DEFINE_FIELD = VM_OC_NONE, /**< define class field */ + VM_OC_PRIVATE_PROP_REFERENCE = VM_OC_NONE, /* reference to class private method */ + VM_OC_ASSIGN_PRIVATE = VM_OC_NONE, /**< assign to private field */ + VM_OC_PRIVATE_FIELD_ADD = VM_OC_NONE, /**< add private field */ + VM_OC_PRIVATE_PROP_GET = VM_OC_NONE, /**< get private field */ + VM_OC_PRIVATE_IN = VM_OC_NONE, /**< 'in' opcode handler for private identifiers */ + VM_OC_COLLECT_PRIVATE_PROPERTY = VM_OC_NONE, /**< collect private properties */ VM_OC_EXT_VAR_EVAL = VM_OC_NONE, /**< variable and function evaluation for * functions with separate argument context */ VM_OC_CHECK_VAR = VM_OC_NONE, /**< check redeclared vars in the global scope */ diff --git a/tests/jerry/es.next/private_fields.js b/tests/jerry/es.next/private_fields.js new file mode 100644 index 000000000..19d533b8c --- /dev/null +++ b/tests/jerry/es.next/private_fields.js @@ -0,0 +1,322 @@ +/* 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) + } +} + +check_syntax_error("#a"); +check_syntax_error("class A { #a; #a; }"); +check_syntax_error("class A { # a; }"); +check_syntax_error("class A { #5; }"); +check_syntax_error("class A { #\"bar\"; }"); +check_syntax_error("class A { ##; }"); +check_syntax_error("class A { ##a; }"); +check_syntax_error("class A { #a; foo(){ delete this.#a } }"); +check_syntax_error("class A { #constructor; }"); +check_syntax_error("class A { #constructor(){}; }"); +check_syntax_error("class A { #a; foo(){ if(#a){ } } }"); +check_syntax_error("class A { #a; foo(){ if(#b){ } } }"); +check_syntax_error("class A { #a; foo(){ if(#a in this){ let b = #b } } }"); +check_syntax_error("class A { #a; }; let b = new A(); b.#a"); +check_syntax_error("class A { #a; }; class B extends A { foo(){ return this.#a; } }"); +check_syntax_error("class A { #a; get #a(){}; }"); +check_syntax_error("class A { set #a(){}; get #a(){}; #a; }"); +check_syntax_error("class A { get #a(){}; get #a(){}; }"); +check_syntax_error("class A { async *#a(){}; #a }"); +check_syntax_error("class A { async get #a(){}; }"); +check_syntax_error("class A { get *#a(){}; }"); +check_syntax_error("class A { static get #a(){}; set #a(){}; #a; }"); +check_syntax_error("class A { static #a(){}; #a; }"); +check_syntax_error("class A extends B{ foo(){ super.#a }}"); +check_syntax_error("class A extends function() { x = this.#foo; }{ #foo; };"); + +class A { + #a = 1; + static #k = 12; + #m_; + b() { + return this.#a; + } + #c() { + return 5 + 6; + } + callC() { + return this.#c(); + } + get #m() { + return this.#m_; + } + set #m(value) { + this.#m_ = value; + } + setM() { + this.#m = "foo"; + } + getM() { + return this.#m; + } + static getK() { + return A.#k; + } +} + +var var1 = new A(); +assert(var1.b() == 1); +assert(var1.callC() == 11); +var1.setM(); +assert(var1.getM() == "foo"); +assert(A.getK() == 12); + +class B extends A { + #a = 2; + c() { + return this.#a; + } +} + +var var2 = new B(); +assert(var2.b() == 1); +assert(var2.c() == 2); + +// Every evaluation of class body creates a new private field +class C { + #a = 3; + b(o) { + return o.#a; + } +} + +class D { + #a = 4; +} + +var var3 = new C(); +var var4 = new D(); + +try { + var3.b(var4); + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +function createClass() { + return class { + #a = 1; + b() { + return this.#a; + } + } +} + +var C1 = createClass(); +var C2 = createClass(); + +var var5 = new C1(); +var var6 = new C2(); + +assert(var5.b.call(var5) == 1); + +try { + var5.b.call(var6); + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +// Cannot access private member from an object whose class did not declare it +class E { + #a = 5; + b = class { + c() { + return this.#a; + } + } + d() { + var var7 = new this.b(); + return var7.c(); + } +} + +var var8 = new E(); + +try { + var8.d(); + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +class F { + #a = 5; + b = class { + #a = 6; + c() { + return this.#a; + } + } + d() { + var var9 = new this.b(); + return var9.c(); + } +} + +var var10 = new F(); +assert(var10.d() == 6); + +// Private field is defined without a getter +class G { + set #a(o) { } + b() { + return this.#a; + } +} + +var var11 = new G(); + +try { + var11.b(); + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +// Private field is defined without a setter +class H { + get #a() { + return 12; + } + b() { + this.#a = 5; + } +} + +var var12 = new H(); + +try { + var12.b(); + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +// Private method is not writable +class I { + #a() { } + b() { + this.#a = function () { } + } +} + +var var13 = new I(); + +try { + var13.b(); + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +// Cannot declare the same private field twice +class J { + constructor(arg) { + return arg; + } +} + +class K extends J { + #a; + constructor(arg) { + super(arg); + } +} + +var var14 = new K(); + +try { + new K(var14) + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +// Private methods are installed when the super returns +var L = class { + a = this.b(); +} + +class M extends L { + b() { + this.#m(); + } + #m() { + return 42; + } +} + +try { + new M() + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +// Private field can be shadowed on inner classes +class N { + static #a = 1; + static getA() { + return this.#a; + } + static b = class { + set #a(v) { this._v = v; } + static access(o) { + o.#a = 2; + } + } +} + +var var15 = new N.b(); +N.b.access(var15); + +assert(N.getA() == 1); +assert(var15._v == 2); + +try { + N.b.access(N); + assert(false) +} catch (e) { + assert(e instanceof TypeError); +} + +// Private fields are accessible in eval code +class O { + #a; + b() { + eval("this.#a = 12;") + } + c() { + return this.#a; + } +} + +var var16 = new O(); +var16.b(); +assert(var16.c() == 12); diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml index 7e8e0daf7..492e5a302 100644 --- a/tests/test262-esnext-excludelist.xml +++ b/tests/test262-esnext-excludelist.xml @@ -1349,3246 +1349,24 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -