diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index edffa3cd71..c16275671f 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -195,7 +195,7 @@ ecma_gc_mark_property (ecma_property_pair_t *property_pair_p, /**< property pair case ECMA_PROPERTY_TYPE_INTERNAL: { JERRY_ASSERT (ECMA_PROPERTY_GET_NAME_TYPE (property) == ECMA_DIRECT_STRING_MAGIC - && property_pair_p->names_cp[index] == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER); + && property_pair_p->names_cp[index] >= LIT_FIRST_INTERNAL_MAGIC_STRING); break; } default: diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 9815470ce8..bd78b6cba5 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -82,12 +82,19 @@ typedef enum /** * Option flags for script parsing. + * Note: + * The enum members must be kept in sync with parser_general_flags_t */ typedef enum { ECMA_PARSE_NO_OPTS = 0, /**< no options passed */ - ECMA_PARSE_STRICT_MODE = (1 << 0), /**< enable strict mode */ - ECMA_PARSE_DIRECT_EVAL = (1 << 1) /**< is eval called directly (ECMA-262 v5, 15.1.2.1.1) */ + ECMA_PARSE_STRICT_MODE = (1u << 0), /**< enable strict mode */ + ECMA_PARSE_DIRECT_EVAL = (1u << 1), /**< eval is called directly (ECMA-262 v5, 15.1.2.1.1) */ + /* These three status flags must be in this order. See PARSER_CLASS_PARSE_OPTS_OFFSET. */ + ECMA_PARSE_CLASS_CONSTRUCTOR = (1u << 2), /**< a class constructor is being parsed (this value must be kept in + * in sync with PARSER_CLASS_CONSTRUCTOR) */ + ECMA_PARSE_HAS_SUPER = (1u << 3), /**< the current context has super reference */ + ECMA_PARSE_HAS_STATIC_SUPER = (1u << 4), /**< the current context is a static class method */ } ecma_parse_opts_t; /** @@ -170,6 +177,7 @@ enum * ecma_op_object_find */ ECMA_VALUE_REGISTER_REF = ECMA_MAKE_VALUE (8), /**< register reference, * a special "base" value for vm */ + ECMA_VALUE_IMPLICIT_CONSTRUCTOR = ECMA_MAKE_VALUE (9), /**< special value for bound class constructors */ }; #if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 @@ -397,6 +405,12 @@ typedef enum #define ECMA_CONVERT_DATA_PROPERTY_TO_INTERNAL_PROPERTY(property_p) \ *(property_p) = (uint8_t) (*(property_p) + (ECMA_PROPERTY_TYPE_INTERNAL - ECMA_PROPERTY_TYPE_NAMEDDATA)) +/** + * Convert internal property to data property. + */ +#define ECMA_CONVERT_INTERNAL_PROPERTY_TO_DATA_PROPERTY(property_p) \ + *(property_p) = (uint8_t) (*(property_p) - (ECMA_PROPERTY_TYPE_INTERNAL - ECMA_PROPERTY_TYPE_NAMEDDATA)) + /** * Special property identifiers. */ @@ -633,12 +647,42 @@ typedef enum ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE = 13, /**< declarative lexical environment */ ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND = 14, /**< object-bound lexical environment * with provideThis flag */ + ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND = 15, /**< object-bound lexical environment + * with provided super reference */ ECMA_LEXICAL_ENVIRONMENT_TYPE_START = ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE, /**< first lexical - * environment type */ - ECMA_LEXICAL_ENVIRONMENT_TYPE__MAX = ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND /**< maximum value */ + * environment type */ + ECMA_LEXICAL_ENVIRONMENT_TYPE__MAX = ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND /**< maximum value */ } ecma_lexical_environment_type_t; +/** + * Offset for JERRY_CONTEXT (status_flags) top 8 bits. + */ +#define ECMA_SUPER_EVAL_OPTS_OFFSET (32 - 8) + +/** + * Set JERRY_CONTEXT (status_flags) top 8 bits to the specified 'opts'. + */ +#define ECMA_SET_SUPER_EVAL_PARSER_OPTS(opts) \ + do \ + { \ + JERRY_CONTEXT (status_flags) |= ((uint32_t) opts << ECMA_SUPER_EVAL_OPTS_OFFSET) | ECMA_STATUS_DIRECT_EVAL; \ + } while (0) + +/** + * Get JERRY_CONTEXT (status_flags) top 8 bits. + */ +#define ECMA_GET_SUPER_EVAL_PARSER_OPTS() (JERRY_CONTEXT (status_flags) >> ECMA_SUPER_EVAL_OPTS_OFFSET) + +/** + * Clear JERRY_CONTEXT (status_flags) top 8 bits. + */ +#define ECMA_CLEAR_SUPER_EVAL_PARSER_OPTS() \ + do \ + { \ + JERRY_CONTEXT (status_flags) &= ((1 << ECMA_SUPER_EVAL_OPTS_OFFSET) - 1); \ + } while (0) + /** * Ecma object type mask for getting the object type. */ diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index bae15d4cae..0da3bc22d2 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -127,7 +127,7 @@ ecma_create_decl_lex_env (ecma_object_t *outer_lexical_environment_p) /**< outer /** * Create a object lexical environment with specified outer lexical environment - * (or NULL if the environment is not nested), binding object and provideThis flag. + * (or NULL if the environment is not nested), binding object and provided type flag. * * See also: ECMA-262 v5, 10.2.1.2 * @@ -137,16 +137,22 @@ ecma_create_decl_lex_env (ecma_object_t *outer_lexical_environment_p) /**< outer */ ecma_object_t * ecma_create_object_lex_env (ecma_object_t *outer_lexical_environment_p, /**< outer lexical environment */ - ecma_object_t *binding_obj_p) /**< binding object */ + ecma_object_t *binding_obj_p, /**< binding object */ + ecma_lexical_environment_type_t type) /**< type of the new lexical environment */ { +#ifndef CONFIG_DISABLE_ES2015_CLASS + JERRY_ASSERT (type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND + || type == ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND); +#else /* CONFIG_DISABLE_ES2015_CLASS */ + JERRY_ASSERT (type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + JERRY_ASSERT (binding_obj_p != NULL && !ecma_is_lexical_environment (binding_obj_p)); ecma_object_t *new_lexical_environment_p = ecma_alloc_object (); - uint16_t type = ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV | ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND; - - new_lexical_environment_p->type_flags_refs = type; + new_lexical_environment_p->type_flags_refs = (uint16_t) (ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV | type); ecma_init_gc_info (new_lexical_environment_p); @@ -355,7 +361,12 @@ ecma_get_lex_env_binding_object (const ecma_object_t *object_p) /**< object-boun { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (ecma_is_lexical_environment (object_p)); +#ifndef CONFIG_DISABLE_ES2015 + 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_SUPER_OBJECT_BOUND); +#else /* CONFIG_DISABLE_ES2015 */ JERRY_ASSERT (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); +#endif /* !CONFIG_DISABLE_ES2015 */ return ECMA_GET_NON_NULL_POINTER (ecma_object_t, object_p->property_list_or_bound_object_cp); @@ -766,7 +777,7 @@ ecma_free_property (ecma_object_t *object_p, /**< object the property belongs to /* Must be a native pointer. */ JERRY_ASSERT (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_MAGIC - && (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER)); + && name_cp >= LIT_FIRST_INTERNAL_MAGIC_STRING); break; } } diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index a7b7f347ea..78107aba81 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -293,7 +293,8 @@ ecma_collection_iterator_next (ecma_value_t *iterator_p); /* ecma-helpers.c */ ecma_object_t *ecma_create_object (ecma_object_t *prototype_object_p, size_t ext_object_size, ecma_object_type_t type); ecma_object_t *ecma_create_decl_lex_env (ecma_object_t *outer_lexical_environment_p); -ecma_object_t *ecma_create_object_lex_env (ecma_object_t *outer_lexical_environment_p, ecma_object_t *binding_obj_p); +ecma_object_t *ecma_create_object_lex_env (ecma_object_t *outer_lexical_environment_p, ecma_object_t *binding_obj_p, + ecma_lexical_environment_type_t type); bool JERRY_ATTR_PURE ecma_is_lexical_environment (const ecma_object_t *object_p); bool JERRY_ATTR_PURE ecma_get_object_extensible (const ecma_object_t *object_p); void ecma_set_object_extensible (ecma_object_t *object_p, bool is_extensible); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c index 0a1cf8c670..3460dcdec4 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c @@ -223,7 +223,20 @@ ecma_builtin_array_prototype_object_concat (ecma_value_t this_arg, /**< this arg ret_value); /* 2. */ - ecma_value_t new_array = ecma_op_create_array_object (0, 0, false); +#ifndef CONFIG_DISABLE_ES2015_CLASS + ecma_object_t *obj_p = ecma_get_object_from_value (obj_this); + ecma_value_t new_array = ecma_op_create_array_object_by_constructor (NULL, 0, false, obj_p); + + if (ECMA_IS_VALUE_ERROR (new_array)) + { + ecma_free_value (obj_this); + return new_array; + } +#else /* CONFIG_DISABLE_ES2015_CLASS */ + ecma_value_t new_array = ecma_op_create_array_object (NULL, 0, false); + JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (new_array)); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ecma_object_t *new_array_p = ecma_get_object_from_value (new_array); uint32_t new_length = 0; @@ -825,7 +838,20 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t this_arg, /**< 'this' ar JERRY_ASSERT (start <= len && end <= len); - ecma_value_t new_array = ecma_op_create_array_object (0, 0, false); +#ifndef CONFIG_DISABLE_ES2015_CLASS + ecma_value_t new_array = ecma_op_create_array_object_by_constructor (NULL, 0, false, obj_p); + + if (ECMA_IS_VALUE_ERROR (new_array)) + { + ecma_free_value (len_value); + ecma_free_value (obj_this); + return new_array; + } +#else /* CONFIG_DISABLE_ES2015_CLASS */ + ecma_value_t new_array = ecma_op_create_array_object (NULL, 0, false); + JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (new_array)); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ecma_object_t *new_array_p = ecma_get_object_from_value (new_array); /* 9. */ @@ -1178,7 +1204,20 @@ ecma_builtin_array_prototype_object_splice (ecma_value_t this_arg, /**< this arg const uint32_t len = ecma_number_to_uint32 (len_number); - ecma_value_t new_array = ecma_op_create_array_object (0, 0, false); +#ifndef CONFIG_DISABLE_ES2015_CLASS + ecma_value_t new_array = ecma_op_create_array_object_by_constructor (NULL, 0, false, obj_p); + + if (ECMA_IS_VALUE_ERROR (new_array)) + { + ecma_free_value (len_value); + ecma_free_value (obj_this); + return new_array; + } +#else /* CONFIG_DISABLE_ES2015_CLASS */ + ecma_value_t new_array = ecma_op_create_array_object (NULL, 0, false); + JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (new_array)); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ecma_object_t *new_array_p = ecma_get_object_from_value (new_array); uint32_t start = 0; @@ -1973,8 +2012,20 @@ ecma_builtin_array_prototype_object_map (ecma_value_t this_arg, /**< this argume /* 5. arg2 is simply used as T */ /* 6. */ +#ifndef CONFIG_DISABLE_ES2015_CLASS + ecma_value_t new_array = ecma_op_create_array_object_by_constructor (NULL, 0, false, obj_p); + + if (ECMA_IS_VALUE_ERROR (new_array)) + { + ecma_free_value (len_value); + ecma_free_value (obj_this); + return new_array; + } +#else /* CONFIG_DISABLE_ES2015_CLASS */ ecma_value_t new_array = ecma_op_create_array_object (NULL, 0, false); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (new_array)); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ecma_object_t *new_array_p = ecma_get_object_from_value (new_array); /* 7-8. */ @@ -2080,8 +2131,20 @@ ecma_builtin_array_prototype_object_filter (ecma_value_t this_arg, /**< this arg ecma_object_t *func_object_p; /* 6. */ +#ifndef CONFIG_DISABLE_ES2015_CLASS + ecma_value_t new_array = ecma_op_create_array_object_by_constructor (NULL, 0, false, obj_p); + + if (ECMA_IS_VALUE_ERROR (new_array)) + { + ecma_free_value (len_value); + ecma_free_value (obj_this); + return new_array; + } +#else /* CONFIG_DISABLE_ES2015_CLASS */ ecma_value_t new_array = ecma_op_create_array_object (NULL, 0, false); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (new_array)); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ecma_object_t *new_array_p = ecma_get_object_from_value (new_array); /* We already checked that arg1 is callable, so it will always be an object. */ diff --git a/jerry-core/ecma/operations/ecma-array-object.c b/jerry-core/ecma/operations/ecma-array-object.c index 6fe922273a..c3e8e0991c 100644 --- a/jerry-core/ecma/operations/ecma-array-object.c +++ b/jerry-core/ecma/operations/ecma-array-object.c @@ -24,6 +24,7 @@ #include "ecma-number-arithmetic.h" #include "ecma-objects.h" #include "ecma-objects-general.h" +#include "ecma-function-object.h" /** \addtogroup ecma ECMA * @{ @@ -43,14 +44,14 @@ */ ecma_value_t ecma_op_create_array_object (const ecma_value_t *arguments_list_p, /**< list of arguments that - are passed to Array constructor */ + * are passed to Array constructor */ ecma_length_t arguments_list_len, /**< length of the arguments' list */ bool is_treat_single_arg_as_length) /**< if the value is true, - arguments_list_len is 1 - and single argument is Number, - then treat the single argument - as new Array's length rather - than as single item of the Array */ + * arguments_list_len is 1 + * and single argument is Number, + * then treat the single argument + * as new Array's length rather + * than as single item of the Array */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); @@ -131,6 +132,68 @@ ecma_op_create_array_object (const ecma_value_t *arguments_list_p, /**< list of return ecma_make_object_value (object_p); } /* ecma_op_create_array_object */ +#ifndef CONFIG_DISABLE_ES2015_CLASS +/** + * Array object creation with custom prototype. + * + * See also: ECMA-262 v6, 9.4.2.3 + * + * @return ecma value + * Returned value must be freed with ecma_free_value + */ +ecma_value_t +ecma_op_create_array_object_by_constructor (const ecma_value_t *arguments_list_p, /**< list of arguments that + * are passed to + * Array constructor */ + ecma_length_t arguments_list_len, /**< length of the arguments' list */ + bool is_treat_single_arg_as_length, /**< if the value is true, + * arguments_list_len is 1 + * and single argument is Number, + * then treat the single argument + * as new Array's length rather + * than as single item of the + * Array */ + ecma_object_t *object_p) /**< The object from whom the new array object + * is being created */ +{ + ecma_value_t constructor_value = ecma_op_object_get_by_magic_id (object_p, LIT_MAGIC_STRING_CONSTRUCTOR); + + if (ECMA_IS_VALUE_ERROR (constructor_value) + || !ecma_is_value_object (constructor_value) + || !ecma_is_constructor (constructor_value)) + { + ecma_free_value (constructor_value); + return ecma_raise_type_error (ECMA_ERR_MSG ("object.constructor is not a constructor.")); + } + + ecma_object_t *constructor_object_p = ecma_get_object_from_value (constructor_value); + ecma_value_t constructor_prototype = ecma_op_object_get_by_magic_id (constructor_object_p, + LIT_MAGIC_STRING_PROTOTYPE); + + ecma_deref_object (constructor_object_p); + + if (ECMA_IS_VALUE_ERROR (constructor_prototype)) + { + return constructor_prototype; + } + + ecma_value_t result = ecma_op_create_array_object (arguments_list_p, + arguments_list_len, + is_treat_single_arg_as_length); + + if (ecma_is_value_object (constructor_prototype)) + { + ecma_object_t *result_object_p = ecma_get_object_from_value (result); + ecma_object_t *constructor_prototpye_object_p = ecma_get_object_from_value (constructor_prototype); + ECMA_SET_POINTER (result_object_p->prototype_or_outer_reference_cp, constructor_prototpye_object_p); + } + + ecma_free_value (constructor_prototype); + + return result; +} /* ecma_op_create_array_object_by_constructor */ +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + /** * Update the length of an array to a new length * diff --git a/jerry-core/ecma/operations/ecma-array-object.h b/jerry-core/ecma/operations/ecma-array-object.h index f32c6cfc17..7f4c934b14 100644 --- a/jerry-core/ecma/operations/ecma-array-object.h +++ b/jerry-core/ecma/operations/ecma-array-object.h @@ -43,6 +43,12 @@ ecma_value_t ecma_op_create_array_object (const ecma_value_t *arguments_list_p, ecma_length_t arguments_list_len, bool is_treat_single_arg_as_length); +#ifndef CONFIG_DISABLE_ES2015_CLASS +ecma_value_t +ecma_op_create_array_object_by_constructor (const ecma_value_t *arguments_list_p, ecma_length_t arguments_list_len, + bool is_treat_single_arg_as_length, ecma_object_t *object_p); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ecma_value_t ecma_op_array_object_set_length (ecma_object_t *object_p, ecma_value_t new_value, uint32_t flags); diff --git a/jerry-core/ecma/operations/ecma-eval.c b/jerry-core/ecma/operations/ecma-eval.c index b0aa53059b..a149c244f3 100644 --- a/jerry-core/ecma/operations/ecma-eval.c +++ b/jerry-core/ecma/operations/ecma-eval.c @@ -22,10 +22,7 @@ #include "ecma-lex-env.h" #include "js-parser.h" #include "vm.h" - -#ifdef JERRY_ENABLE_LINE_INFO #include "jcontext.h" -#endif /* JERRY_ENABLE_LINE_INFO */ /** \addtogroup ecma ECMA * @{ @@ -98,6 +95,12 @@ ecma_op_eval_chars_buffer (const lit_utf8_byte_t *code_p, /**< code characters b JERRY_CONTEXT (resource_name) = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); #endif /* JERRY_ENABLE_LINE_INFO */ +#ifndef CONFIG_DISABLE_ES2015_CLASS + parse_opts |= ECMA_GET_SUPER_EVAL_PARSER_OPTS (); + + ECMA_CLEAR_SUPER_EVAL_PARSER_OPTS (); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ecma_value_t parse_status = parser_parse_script (NULL, 0, code_p, diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index ca22e233bc..d6f8f3db84 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -453,12 +453,165 @@ ecma_op_function_has_construct_flag (const ecma_value_t *arguments_list_p) /**< { #ifndef CONFIG_DISABLE_ES2015_CLASS return (((uintptr_t) arguments_list_p) & ECMA_CLASS_CONSTRUCT_FLAG); -#else +#else /* CONFIG_DISABLE_ES2015_CLASS */ JERRY_UNUSED (arguments_list_p); return false; #endif /* !CONFIG_DISABLE_ES2015_CLASS */ } /* ecma_op_function_has_construct_flag */ +#ifndef CONFIG_DISABLE_ES2015 +/** + * Returns the closest declarative lexical enviroment to the super object bound lexical enviroment. + * + * @return the found lexical enviroment + */ +static ecma_object_t * +ecma_op_find_super_declerative_lex_env (ecma_object_t *lex_env_p) /**< starting lexical enviroment */ +{ + JERRY_ASSERT (lex_env_p); + JERRY_ASSERT (ecma_op_resolve_super_reference_value (lex_env_p)); + JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) != ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND); + + while (true) + { + ecma_object_t *lex_env_outer_p = ecma_get_lex_env_outer_reference (lex_env_p); + + JERRY_ASSERT (lex_env_outer_p); + + if (ecma_get_lex_env_type (lex_env_outer_p) == ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND) + { + JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); + return lex_env_p; + } + + lex_env_p = lex_env_outer_p; + } +} /* ecma_op_find_super_declerative_lex_env */ + +/** + * Returns with the current class this_binding property + * + * @return NULL - if the property was not found + * the found property - otherwise + */ +static ecma_property_t * +ecma_op_get_class_this_binding_property (ecma_object_t *lex_env_p) /**< starting lexical enviroment */ +{ + JERRY_ASSERT (lex_env_p); + JERRY_ASSERT (ecma_is_lexical_environment (lex_env_p)); + + lex_env_p = ecma_op_find_super_declerative_lex_env (lex_env_p); + ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_THIS_BINDING); + return ecma_find_named_property (lex_env_p, name_p); +} /* ecma_op_get_class_this_binding_property */ + +/** + * Checks whether the 'super(...)' has been called. + * + * @return true - if the 'super (...)' has been called + * false - otherwise + */ +inline bool JERRY_ATTR_PURE +ecma_op_is_super_called (ecma_object_t *lex_env_p) /**< starting lexical enviroment */ +{ + ecma_property_t *property_p = ecma_op_get_class_this_binding_property (lex_env_p); + + JERRY_ASSERT (property_p); + return (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDDATA); +} /* ecma_op_is_super_called */ + +/** + * Sets the value of 'super(...)' has been called. + */ +inline void +ecma_op_set_super_called (ecma_object_t *lex_env_p) /**< starting lexical enviroment */ +{ + ecma_property_t *property_p = ecma_op_get_class_this_binding_property (lex_env_p); + + JERRY_ASSERT (property_p); + + JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_INTERNAL); + ECMA_CONVERT_INTERNAL_PROPERTY_TO_DATA_PROPERTY (property_p); +} /* ecma_op_set_super_called */ + +/** + * Sets the class context this_binding value. + */ +void +ecma_op_set_class_this_binding (ecma_object_t *lex_env_p, /**< starting lexical enviroment */ + ecma_value_t this_binding) /**< 'this' argument's value */ +{ + JERRY_ASSERT (ecma_is_value_object (this_binding)); + ecma_property_t *property_p = ecma_op_get_class_this_binding_property (lex_env_p); + + ecma_property_value_t *value_p; + + if (property_p) + { + JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDDATA); + value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + } + else + { + ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_CLASS_THIS_BINDING); + value_p = ecma_create_named_data_property (lex_env_p, name_p, ECMA_PROPERTY_FLAG_WRITABLE, &property_p); + ECMA_CONVERT_DATA_PROPERTY_TO_INTERNAL_PROPERTY (property_p); + } + + value_p->value = this_binding; +} /* ecma_op_set_class_this_binding */ + +/** + * Gets the class context this binding value. + * + * @return the class context this binding value + */ +ecma_value_t +ecma_op_get_class_this_binding (ecma_object_t *lex_env_p) /**< starting lexical enviroment */ +{ + ecma_property_t *property_p = ecma_op_get_class_this_binding_property (lex_env_p); + + JERRY_ASSERT (property_p); + + ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + + JERRY_ASSERT (ecma_is_value_object (value_p->value)); + return value_p->value; +} /* ecma_op_get_class_this_binding */ + +/** + * Dummy external function for implicit constructor call. + * + * @return ECMA_VALUE_ERROR - TypeError + */ +ecma_value_t +ecma_op_function_implicit_constructor_handler_cb (const ecma_value_t function_obj, /**< the function itself */ + const ecma_value_t this_val, /**< this_arg of the function */ + const ecma_value_t args_p[], /**< argument list */ + const ecma_length_t args_count) /**< argument number */ +{ + JERRY_UNUSED_4 (function_obj, this_val, args_p, args_count); + return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'.")); +} /* ecma_op_function_implicit_constructor_handler_cb */ + +/** + * Sets the completion value [[Prototype]] based on the this_arg value + */ +void +ecma_op_set_class_prototype (ecma_value_t completion_value, /**< completion_value */ + ecma_value_t this_arg) /**< this argument*/ +{ + JERRY_ASSERT (ecma_is_value_object (completion_value)); + JERRY_ASSERT (ecma_is_value_object (this_arg)); + + ecma_object_t *completion_obj_p = ecma_get_object_from_value (completion_value); + ecma_object_t *prototype_obj_p = ecma_get_object_prototype (ecma_get_object_from_value (this_arg)); + + JERRY_ASSERT (prototype_obj_p); + ECMA_SET_POINTER (completion_obj_p->prototype_or_outer_reference_cp, prototype_obj_p); +} /* ecma_op_set_class_prototype */ +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + /** * [[Call]] implementation for Function objects, * created through 13.2 (ECMA_OBJECT_TYPE_FUNCTION) @@ -484,6 +637,8 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ ecma_object_type_t func_type = ecma_get_object_type (func_obj_p); JERRY_ASSERT (func_type == ECMA_OBJECT_TYPE_FUNCTION + || func_type == ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION + || func_type == ECMA_OBJECT_TYPE_BOUND_FUNCTION || !ecma_op_function_has_construct_flag (arguments_list_p)); if (func_type == ECMA_OBJECT_TYPE_FUNCTION) @@ -507,21 +662,20 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ /* 8. */ ecma_value_t this_binding = this_arg_value; bool free_this_binding = false; - bool is_strict; - bool is_no_lex_env; const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p); #ifndef CONFIG_DISABLE_ES2015_CLASS - if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR && - !ecma_op_function_has_construct_flag (arguments_list_p)) + bool is_class_constructor = bytecode_data_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR; + + if (is_class_constructor && !ecma_op_function_has_construct_flag (arguments_list_p)) { return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'.")); } #endif /* !CONFIG_DISABLE_ES2015_CLASS */ - is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) ? true : false; - is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) ? true : false; + bool is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) ? true : false; + bool is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) ? true : false; /* 1. */ if (!is_strict) @@ -561,6 +715,12 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ arguments_list_len, bytecode_data_p); } +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (JERRY_UNLIKELY (is_class_constructor)) + { + ecma_op_set_class_this_binding (local_env_p, this_binding); + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ } ecma_value_t ret_value = vm_run (bytecode_data_p, @@ -658,7 +818,26 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ if (!ecma_is_value_integer_number (args_len_or_this)) { - this_arg_value = args_len_or_this; +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (JERRY_UNLIKELY (args_len_or_this == ECMA_VALUE_IMPLICIT_CONSTRUCTOR)) + { + if (!ecma_op_function_has_construct_flag (arguments_list_p)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'.")); + } + if (ecma_get_object_is_builtin (target_func_obj_p)) + { + arguments_list_p = ecma_op_function_clear_construct_flag (arguments_list_p); + } + } + else + { +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + this_arg_value = args_len_or_this; +#ifndef CONFIG_DISABLE_ES2015_CLASS + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + args_length = 1; } else @@ -675,6 +854,11 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ continue; } +#ifndef CONFIG_DISABLE_ES2015_CLASS + arguments_list_p = ecma_op_function_clear_construct_flag (arguments_list_p); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + + JERRY_ASSERT (!ecma_op_function_has_construct_flag (arguments_list_p)); args_length--; ecma_length_t merged_args_list_len = args_length + arguments_list_len; @@ -721,12 +905,13 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ JERRY_ASSERT (ecma_is_value_object (this_arg_value) || this_arg_value == ECMA_VALUE_UNDEFINED); + ecma_object_t *target_func_obj_p = NULL; + while (JERRY_UNLIKELY (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION)) { /* 1-3. */ ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) func_obj_p; - ecma_object_t *target_func_obj_p; target_func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, ext_function_p->u.bound_function.target_function); @@ -742,9 +927,15 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ JERRY_ASSERT (args_length > 0); + /* 5. */ if (args_length == 1) { - /* 5. */ +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (args_len_or_this == ECMA_VALUE_IMPLICIT_CONSTRUCTOR && ecma_is_value_undefined (this_arg_value)) + { + break; + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ func_obj_p = target_func_obj_p; continue; } @@ -787,9 +978,18 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ return ecma_raise_type_error (ECMA_ERR_MSG ("Built-in routines have no constructor.")); } - return ecma_builtin_dispatch_construct (func_obj_p, - arguments_list_p, - arguments_list_len); + ecma_value_t ret_value = ecma_builtin_dispatch_construct (func_obj_p, + arguments_list_p, + arguments_list_len); + +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (!ecma_is_value_undefined (this_arg_value) && !ECMA_IS_VALUE_ERROR (ret_value)) + { + ecma_op_set_class_prototype (ret_value, this_arg_value); + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + + return ret_value; } ecma_object_t *new_this_obj_p = NULL; @@ -843,10 +1043,33 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ arguments_list_len); break; } +#ifndef CONFIG_DISABLE_ES2015_CLASS + case ECMA_OBJECT_TYPE_BOUND_FUNCTION: + { + JERRY_ASSERT (!ecma_op_function_has_construct_flag (arguments_list_p)); + JERRY_ASSERT (target_func_obj_p != NULL); + + ret_value = ecma_op_function_construct (target_func_obj_p, + this_arg_value, + arguments_list_p, + arguments_list_len); + break; + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ default: { JERRY_ASSERT (type == ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION); +#ifndef CONFIG_DISABLE_ES2015_CLASS + ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p; + + if (ext_func_obj_p->u.external_handler_cb == ecma_op_function_implicit_constructor_handler_cb) + { + ret_value = ECMA_VALUE_UNDEFINED; + break; + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ret_value = ecma_op_function_call (func_obj_p, this_arg_value, arguments_list_p, diff --git a/jerry-core/ecma/operations/ecma-function-object.h b/jerry-core/ecma/operations/ecma-function-object.h index 59d89a83a9..00377766ca 100644 --- a/jerry-core/ecma/operations/ecma-function-object.h +++ b/jerry-core/ecma/operations/ecma-function-object.h @@ -46,6 +46,29 @@ ecma_op_create_external_function_object (ecma_external_handler_t handler_cb); const ecma_compiled_code_t * ecma_op_function_get_compiled_code (ecma_extended_object_t *function_p); +#ifndef CONFIG_DISABLE_ES2015_CLASS +void +ecma_op_set_super_called (ecma_object_t *lex_env_p); + +bool +ecma_op_is_super_called (ecma_object_t *lex_env_p); + +void +ecma_op_set_class_this_binding (ecma_object_t *lex_env_p, ecma_value_t this_binding); + +ecma_value_t +ecma_op_get_class_this_binding (ecma_object_t *lex_env_p); + +ecma_value_t +ecma_op_function_implicit_constructor_handler_cb (const ecma_value_t function_obj, + const ecma_value_t this_val, + const ecma_value_t args_p[], + const ecma_length_t args_count); + +void +ecma_op_set_class_prototype (ecma_value_t completion_value, ecma_value_t this_arg); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION const ecma_compiled_code_t * ecma_op_arrow_function_get_compiled_code (ecma_arrow_function_t *arrow_function_p); diff --git a/jerry-core/ecma/operations/ecma-lex-env.c b/jerry-core/ecma/operations/ecma-lex-env.c index 758055bdce..9d94e06e3d 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.c +++ b/jerry-core/ecma/operations/ecma-lex-env.c @@ -41,7 +41,9 @@ ecma_init_global_lex_env (void) { ecma_object_t *glob_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL); - JERRY_CONTEXT (ecma_global_lex_env_p) = ecma_create_object_lex_env (NULL, glob_obj_p); + JERRY_CONTEXT (ecma_global_lex_env_p) = ecma_create_object_lex_env (NULL, + glob_obj_p, + ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_deref_object (glob_obj_p); } /* ecma_init_global_lex_env */ @@ -86,7 +88,9 @@ ecma_op_has_binding (ecma_object_t *lex_env_p, /**< lexical environment */ JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); - if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) + ecma_lexical_environment_type_t lex_env_type = ecma_get_lex_env_type (lex_env_p); + + if (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); @@ -94,7 +98,12 @@ ecma_op_has_binding (ecma_object_t *lex_env_p, /**< lexical environment */ } else { - JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); +#ifndef CONFIG_DISABLE_ES2015_CLASS + JERRY_ASSERT (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND + || lex_env_type == ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND); +#else /* CONFIG_DISABLE_ES2015_CLASS */ + JERRY_ASSERT (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); diff --git a/jerry-core/ecma/operations/ecma-reference.c b/jerry-core/ecma/operations/ecma-reference.c index 81fc2e59cb..a2efd32e6a 100644 --- a/jerry-core/ecma/operations/ecma-reference.c +++ b/jerry-core/ecma/operations/ecma-reference.c @@ -59,6 +59,29 @@ ecma_op_resolve_reference_base (ecma_object_t *lex_env_p, /**< starting lexical return NULL; } /* ecma_op_resolve_reference_base */ +#ifndef CONFIG_DISABLE_ES2015_CLASS +/** + * Resolve super reference. + * + * @return value of the reference + */ +ecma_object_t * +ecma_op_resolve_super_reference_value (ecma_object_t *lex_env_p) /**< starting lexical environment */ +{ + while (true) + { + JERRY_ASSERT (lex_env_p != NULL); + + if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND) + { + return ecma_get_lex_env_binding_object (lex_env_p); + } + + lex_env_p = ecma_get_lex_env_outer_reference (lex_env_p); + } +} /* ecma_op_resolve_super_reference_value */ +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + /** * Resolve value corresponding to reference. * @@ -72,7 +95,9 @@ ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical while (lex_env_p != NULL) { - if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) + ecma_lexical_environment_type_t lex_env_type = ecma_get_lex_env_type (lex_env_p); + + if (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); @@ -81,10 +106,8 @@ ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical return ecma_fast_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value); } } - else + else if (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND) { - JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); - ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); #ifndef CONFIG_ECMA_LCACHE_DISABLE @@ -120,6 +143,14 @@ ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical return prop_value; } } + else + { +#ifndef CONFIG_DISABLE_ES2015_CLASS + JERRY_ASSERT (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND); +#else /* CONFIG_DISABLE_ES2015_CLASS */ + JERRY_UNREACHABLE (); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + } lex_env_p = ecma_get_lex_env_outer_reference (lex_env_p); } diff --git a/jerry-core/ecma/operations/ecma-reference.h b/jerry-core/ecma/operations/ecma-reference.h index e05934117c..3ffefcadfe 100644 --- a/jerry-core/ecma/operations/ecma-reference.h +++ b/jerry-core/ecma/operations/ecma-reference.h @@ -28,6 +28,9 @@ ecma_object_t *ecma_op_resolve_reference_base (ecma_object_t *lex_env_p, ecma_string_t *name_p); ecma_value_t ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, ecma_string_t *name_p); +#ifndef CONFIG_DISABLE_ES2015_CLASS +ecma_object_t *ecma_op_resolve_super_reference_value (ecma_object_t *lex_env_p); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ /** * @} diff --git a/jerry-core/ecma/operations/ecma-typedarray-object.c b/jerry-core/ecma/operations/ecma-typedarray-object.c index ef403109e4..b28cecb82f 100644 --- a/jerry-core/ecma/operations/ecma-typedarray-object.c +++ b/jerry-core/ecma/operations/ecma-typedarray-object.c @@ -257,7 +257,7 @@ ecma_typedarray_create_object_with_length (ecma_length_t array_length, /**< leng ecma_free_value (new_arraybuffer_p); return ecma_make_object_value (object_p); -} /* !ecma_typedarray_create_object_with_length */ +} /* ecma_typedarray_create_object_with_length */ /** * Create a TypedArray object by given buffer, offset, and array_length @@ -984,6 +984,31 @@ ecma_op_create_typedarray_with_type_and_length (ecma_object_t *obj_p, /**< Typed } } +#ifndef CONFIG_DISABLE_ES2015_CLASS + ecma_value_t constructor_value = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_CONSTRUCTOR); + + if (ECMA_IS_VALUE_ERROR (constructor_value) + || !ecma_is_value_object (constructor_value) + || !ecma_is_constructor (constructor_value)) + { + ecma_deref_object (proto_p); + ecma_free_value (constructor_value); + return ecma_raise_type_error (ECMA_ERR_MSG ("object.constructor is not a constructor.")); + } + + ecma_object_t *constructor_object_p = ecma_get_object_from_value (constructor_value); + ecma_value_t constructor_prototype = ecma_op_object_get_by_magic_id (constructor_object_p, + LIT_MAGIC_STRING_PROTOTYPE); + + ecma_deref_object (constructor_object_p); + + if (ECMA_IS_VALUE_ERROR (constructor_prototype)) + { + ecma_deref_object (proto_p); + return constructor_prototype; + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + ecma_value_t new_obj = ecma_typedarray_create_object_with_length (array_length, proto_p, element_size_shift, @@ -991,6 +1016,14 @@ ecma_op_create_typedarray_with_type_and_length (ecma_object_t *obj_p, /**< Typed ecma_deref_object (proto_p); +#ifndef CONFIG_DISABLE_ES2015_CLASS + ecma_object_t *constructor_prototype_object_p = ecma_get_object_from_value (constructor_prototype); + ecma_object_t *new_obj_p = ecma_get_object_from_value (new_obj); + ECMA_SET_POINTER (new_obj_p->prototype_or_outer_reference_cp, constructor_prototype_object_p); + + ecma_deref_object (constructor_prototype_object_p); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + return new_obj; } /* ecma_op_create_typedarray_with_type_and_length */ diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 5e1d680ea8..6416739c83 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (18u) +#define JERRY_SNAPSHOT_VERSION (19u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index cd15682be1..c888f4d2f2 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -109,7 +109,7 @@ struct jerry_context_t ecma_value_t error_value; /**< currently thrown error value */ uint32_t lit_magic_string_ex_count; /**< external magic strings count */ uint32_t jerry_init_flags; /**< run-time configuration flags */ - uint32_t status_flags; /**< run-time flags */ + uint32_t status_flags; /**< run-time flags (the top 8 bits are used for passing class parsing options) */ #ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE uint8_t ecma_prop_hashmap_alloc_state; /**< property hashmap allocation state: 0-4, diff --git a/jerry-core/jrt/jrt.h b/jerry-core/jrt/jrt.h index 6edfde1fce..12e3d908fa 100644 --- a/jerry-core/jrt/jrt.h +++ b/jerry-core/jrt/jrt.h @@ -151,4 +151,13 @@ void JERRY_ATTR_NORETURN jerry_fatal (jerry_fatal_code_t code); #define JERRY_MIN(v1, v2) (((v1) < (v2)) ? (v1) : (v2)) #define JERRY_MAX(v1, v2) (((v1) < (v2)) ? (v2) : (v1)) +/** + * Calculate the index of the first non-zero bit of a 32 bit integer value + */ +#define JERRY__LOG2_1(n) (((n) >= 2) ? 1 : 0) +#define JERRY__LOG2_2(n) (((n) >= 1 << 2) ? (2 + JERRY__LOG2_1 ((n) >> 2)) : JERRY__LOG2_1 (n)) +#define JERRY__LOG2_4(n) (((n) >= 1 << 4) ? (4 + JERRY__LOG2_2 ((n) >> 4)) : JERRY__LOG2_2 (n)) +#define JERRY__LOG2_8(n) (((n) >= 1 << 8) ? (8 + JERRY__LOG2_4 ((n) >> 8)) : JERRY__LOG2_4 (n)) +#define JERRY_LOG2(n) (((n) >= 1 << 16) ? (16 + JERRY__LOG2_8 ((n) >> 16)) : JERRY__LOG2_8 (n)) + #endif /* !JRT_H */ diff --git a/jerry-core/lit/lit-magic-strings.h b/jerry-core/lit/lit-magic-strings.h index 0e94b17a3f..1e40636a39 100644 --- a/jerry-core/lit/lit-magic-strings.h +++ b/jerry-core/lit/lit-magic-strings.h @@ -52,6 +52,9 @@ typedef enum * deleted properties */ LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< native pointer info associated with an object */ + LIT_FIRST_INTERNAL_MAGIC_STRING = LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< first index of internal + * magic strings */ + LIT_INTERNAL_MAGIC_STRING_CLASS_THIS_BINDING, /**< the this binding of the class constructor */ LIT_MAGIC_STRING__COUNT /**< number of magic strings */ } lit_magic_string_id_t; diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 77623a1538..ba558b03df 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -64,13 +64,29 @@ #define CBC_HAS_POP_STACK_BYTE_ARG (CBC_HAS_BYTE_ARG | CBC_POP_STACK_BYTE_ARG) +#ifndef CONFIG_DISABLE_ES2015_CLASS +/** + * Checks whether the current opcode is a super constructor call + */ +#define CBC_SUPER_CALL_OPERATION(opcode) \ + ((opcode) >= PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL) \ + && (opcode) <= PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL_BLOCK)) +#else /* CONFIG_DISABLE_ES2015_CLASS */ +/** + * Checks whether the current opcode is a super constructor call + */ +#define CBC_SUPER_CALL_OPERATION(opcode) false +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + /* Debug macro. */ #define CBC_ARGS_EQ(op, types) \ ((cbc_flags[op] & CBC_ARG_TYPES) == (types)) /* Debug macro. */ #define CBC_SAME_ARGS(op1, op2) \ - ((cbc_flags[op1] & CBC_ARG_TYPES) == (cbc_flags[op2] & CBC_ARG_TYPES)) + (CBC_SUPER_CALL_OPERATION (op1) ? ((cbc_ext_flags[PARSER_GET_EXT_OPCODE (op1)] & CBC_ARG_TYPES) \ + == (cbc_ext_flags[PARSER_GET_EXT_OPCODE (op2)] & CBC_ARG_TYPES)) \ + : ((cbc_flags[op1] & CBC_ARG_TYPES) == (cbc_flags[op2] & CBC_ARG_TYPES))) #define CBC_UNARY_OPERATION(name, group) \ CBC_OPCODE (name, CBC_NO_FLAG, 0, \ @@ -143,15 +159,14 @@ * Hence CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode) * cannot be true for an opcode which has a result */ - #define CBC_NO_RESULT_OPERATION(opcode) \ - ((opcode) >= CBC_PRE_INCR && (opcode) < CBC_END) + (((opcode) >= CBC_PRE_INCR && (opcode) < CBC_END) || CBC_SUPER_CALL_OPERATION ((opcode))) #define CBC_NO_RESULT_BLOCK(opcode) \ - ((opcode) >= CBC_PRE_INCR && (opcode) < CBC_ASSIGN_ADD) + (((opcode) >= CBC_PRE_INCR && (opcode) < CBC_ASSIGN_ADD) || CBC_SUPER_CALL_OPERATION ((opcode))) #define CBC_NO_RESULT_COMPOUND_ASSIGMENT(opcode) \ - ((opcode) >= CBC_ASSIGN_ADD && (opcode) < CBC_END) + ((opcode) >= CBC_ASSIGN_ADD && (opcode) < CBC_END && !CBC_SUPER_CALL_OPERATION ((opcode))) /** * Branch instructions are organized in group of 8 opcodes. @@ -203,6 +218,8 @@ #define PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION 4 /* PARSER_WITH_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_WITH_CONTEXT_STACK_ALLOCATION 1 +/* PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION must be <= 4 */ +#define PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION 1 /* PARSER_TRY_CONTEXT_STACK_ALLOCATION must be <= 3 */ #define PARSER_TRY_CONTEXT_STACK_ALLOCATION 2 @@ -537,6 +554,10 @@ VM_OC_PUSH_UNDEFINED_BASE | VM_OC_PUT_STACK) \ CBC_FORWARD_BRANCH (CBC_EXT_FINALLY, 0, \ VM_OC_FINALLY) \ + CBC_OPCODE (CBC_EXT_CLASS_EXPR_CONTEXT_END, CBC_NO_FLAG, 0, \ + VM_OC_CLASS_EXPR_CONTEXT_END) \ + CBC_FORWARD_BRANCH (CBC_EXT_SUPER_CLASS_CREATE_CONTEXT, \ + -1 + PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION, VM_OC_CLASS_HERITAGE) \ \ /* Basic opcodes. */ \ CBC_OPCODE (CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_0, CBC_HAS_LITERAL_ARG, 2, \ @@ -570,6 +591,40 @@ CBC_OPCODE (CBC_EXT_SET_STATIC_COMPUTED_SETTER, CBC_HAS_LITERAL_ARG, -1, \ VM_OC_SET_SETTER | VM_OC_GET_STACK_LITERAL) \ \ + /* Class opcodes */ \ + CBC_OPCODE (CBC_EXT_INHERIT_AND_SET_CONSTRUCTOR, CBC_NO_FLAG, 0, \ + VM_OC_CLASS_INHERITANCE) \ + CBC_OPCODE (CBC_EXT_PUSH_CLASS_CONSTRUCTOR, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_CLASS_CONSTRUCTOR | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_IMPLICIT_CONSTRUCTOR_CALL, CBC_NO_FLAG, 0, \ + VM_OC_PUSH_IMPL_CONSTRUCTOR) \ + CBC_OPCODE (CBC_EXT_SET_CLASS_LITERAL, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_SET_CLASS_CONSTRUCTOR | VM_OC_GET_LITERAL) \ + CBC_OPCODE (CBC_EXT_CLASS_EVAL, CBC_HAS_BYTE_ARG, 0, \ + VM_OC_CLASS_EVAL) \ + 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) \ + CBC_OPCODE (CBC_EXT_PUSH_CONSTRUCTOR_SUPER, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_CONSTRUCTOR_SUPER | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_PUSH_CONSTRUCTOR_SUPER_PROP, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_CONSTRUCTOR_SUPER | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_PUSH_SUPER, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_SUPER | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_PUSH_STATIC_SUPER, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_SUPER | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_PUSH_CONSTRUCTOR_THIS, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_CONSTRUCTOR_THIS | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_SUPER_PROP_CALL, CBC_NO_FLAG, 0, \ + VM_OC_SUPER_PROP_REFERENCE) \ + CBC_OPCODE (CBC_EXT_SUPER_PROP_ASSIGN, CBC_NO_FLAG, 0, \ + VM_OC_SUPER_PROP_REFERENCE) \ + CBC_OPCODE (CBC_EXT_CONSTRUCTOR_RETURN, CBC_NO_FLAG, -1, \ + VM_OC_CONSTRUCTOR_RET | VM_OC_GET_STACK) \ + \ /* Binary compound assignment opcodes with pushing the result. */ \ CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_ADD, \ ADD) \ diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index 6fb91c200f..68c6c874ab 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -352,11 +352,23 @@ static const keyword_string_t keyword_length_4[9] = LEXER_KEYWORD_END () }; +#ifndef CONFIG_DISABLE_ES2015 +/** + * Number of keywords with 5 characters. + */ +#define KEYWORD_LENGTH_COUNT 11 +#else /* CONFIG_DISABLE_ES2015 */ +#define KEYWORD_LENGTH_COUNT 10 +#endif /* !CONFIG_DISABLE_ES2015 */ + /** * Keywords with 5 characters. */ -static const keyword_string_t keyword_length_5[10] = +static const keyword_string_t keyword_length_5[KEYWORD_LENGTH_COUNT] = { +#ifndef CONFIG_DISABLE_ES2015 + LEXER_KEYWORD ("await", LEXER_KEYW_AWAIT), +#endif /* !CONFIG_DISABLE_ES2015 */ LEXER_KEYWORD ("break", LEXER_KEYW_BREAK), LEXER_KEYWORD ("catch", LEXER_KEYW_CATCH), LEXER_KEYWORD ("class", LEXER_KEYW_CLASS), @@ -1310,40 +1322,22 @@ lexer_next_token (parser_context_t *context_p) /**< context */ #undef LEXER_TYPE_D_TOKEN /** - * Checks whether the next token is a colon. + * Checks whether the next token is the specified character. * - * @return true - if the next token is a colon + * @return true - if the next is the specified character * false - otherwise */ bool -lexer_check_colon (parser_context_t *context_p) /**< context */ +lexer_check_next_character (parser_context_t *context_p, /**< context */ + lit_utf8_byte_t character) /**< specified character */ { lexer_skip_spaces (context_p); context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); return (context_p->source_p < context_p->source_end_p - && context_p->source_p[0] == (uint8_t) LIT_CHAR_COLON); -} /* lexer_check_colon */ - -#ifndef CONFIG_DISABLE_ES2015_CLASS -/** - * Checks whether the next token is a left parenthesis. - * - * @return true - if the next token is a left parenthesis - * false - otherwise - */ -bool -lexer_check_left_paren (parser_context_t *context_p) /**< context */ -{ - lexer_skip_spaces (context_p); - - context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES); - - return (context_p->source_p < context_p->source_end_p - && context_p->source_p[0] == (uint8_t) LIT_CHAR_LEFT_PAREN); -} /* lexer_check_left_paren */ -#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + && context_p->source_p[0] == (uint8_t) character); +} /* lexer_check_next_character */ #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index 4dee7551a5..7fe392567c 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -151,29 +151,59 @@ typedef enum LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */ LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */ +#ifdef CONFIG_DISABLE_ES2015 /* Future reserved words: these keywords * must form a group after all other keywords. */ #define LEXER_FIRST_FUTURE_RESERVED_WORD LEXER_KEYW_CLASS +#endif /* CONFIG_DISABLE_ES2015 */ LEXER_KEYW_CLASS, /**< class */ - LEXER_KEYW_ENUM, /**< enum */ LEXER_KEYW_EXTENDS, /**< extends */ LEXER_KEYW_SUPER, /**< super */ LEXER_KEYW_CONST, /**< const */ LEXER_KEYW_EXPORT, /**< export */ LEXER_KEYW_IMPORT, /**< import */ +#ifndef CONFIG_DISABLE_ES2015 + /* Future reserved words: these keywords + * must form a group after all other keywords. + * Note: + * Tokens from LEXER_KEYW_CLASS to LEXER_KEYW_IMPORT + * are no longer future reserved words in ES2015. */ +#define LEXER_FIRST_FUTURE_RESERVED_WORD LEXER_KEYW_ENUM +#endif /* !CONFIG_DISABLE_ES2015 */ + LEXER_KEYW_ENUM, /**< enum */ +#ifndef CONFIG_DISABLE_ES2015 + LEXER_KEYW_AWAIT, /**< await */ +#endif /* !CONFIG_DISABLE_ES2015 */ /* Future strict reserved words: these keywords * must form a group after future reserved words. */ #define LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD LEXER_KEYW_IMPLEMENTS LEXER_KEYW_IMPLEMENTS, /**< implements */ - LEXER_KEYW_LET, /**< let */ LEXER_KEYW_PRIVATE, /**< private */ LEXER_KEYW_PUBLIC, /**< public */ - LEXER_KEYW_YIELD, /**< yield */ LEXER_KEYW_INTERFACE, /**< interface */ LEXER_KEYW_PACKAGE, /**< package */ LEXER_KEYW_PROTECTED, /**< protected */ + +#ifndef CONFIG_DISABLE_ES2015 + /* Context dependent strict reserved words: + * See also: ECMA-262 v6, 11.6.2.1 */ +#define LEXER_FIRST_CONTEXT_DEPENDENT_RESERVED_WORD LEXER_KEYW_STATIC + LEXER_KEYW_STATIC, /**< static */ +#else /* CONFIG_DISABLE_ES2015 */ + /* Context dependent strict reserved words: + * See also: ECMA-262 v6, 11.6.2.1 */ +#define LEXER_FIRST_CONTEXT_DEPENDENT_RESERVED_WORD +#endif /* !CONFIG_DISABLE_ES2015 */ + + /* Context dependent future strict reserved words: + * See also: ECMA-262 v6, 11.6.2.1 */ +#define LEXER_FIRST_CONTEXT_DEPENDENT_FUTURE_RESERVED_WORD LEXER_KEYW_LET + LEXER_KEYW_LET, /**< let */ + LEXER_KEYW_YIELD, /**< yield */ +#ifdef CONFIG_DISABLE_ES2015 LEXER_KEYW_STATIC, /**< static */ +#endif /* CONFIG_DISABLE_ES2015 */ } lexer_token_type_t; #define LEXER_NEWLINE_LS_PS_BYTE_1 0xe2 diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index ef39413d12..fb5c04ae0a 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -15,15 +15,9 @@ #include "js-parser-internal.h" -#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS -#include "lit-char-helpers.h" -#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ - #ifndef JERRY_DISABLE_JS_PARSER - -#if !defined (CONFIG_DISABLE_ES2015_CLASS) && (defined (JERRY_DEBUGGER) || defined (JERRY_ENABLE_LINE_INFO)) #include "jcontext.h" -#endif /* !CONFIG_DISABLE_ES2015_CLASS && (JERRY_DEBUGGER || JERRY_ENABLE_LINE_INFO) */ +#include "lit-char-helpers.h" /** \addtogroup parser Parser * @{ @@ -369,19 +363,17 @@ parser_append_object_literal_item (parser_context_t *context_p, /**< context */ * Parse class as an object literal. */ static void -parser_parse_class_literal (parser_context_t *context_p, /**< context */ - lexer_literal_t *constructor_literal_p) /**< constructor literal */ +parser_parse_class_literal (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); - JERRY_ASSERT (constructor_literal_p->type == LEXER_UNUSED_LITERAL); - parser_emit_cbc (context_p, CBC_CREATE_OBJECT); - bool is_static = false; + bool super_called = false; + uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | (context_p->status_flags & PARSER_CLASS_HAS_SUPER); while (true) { - if (!is_static) + if (!(status_flags & PARSER_CLASS_STATIC_FUNCTION)) { lexer_skip_empty_statements (context_p); } @@ -398,8 +390,8 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ uint16_t literal_index, function_literal_index; bool is_getter = (context_p->token.type == LEXER_PROPERTY_GETTER); - uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; - status_flags |= (is_getter ? PARSER_IS_PROPERTY_GETTER : PARSER_IS_PROPERTY_SETTER); + uint32_t accessor_status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; + accessor_status_flags |= (is_getter ? PARSER_IS_PROPERTY_GETTER : PARSER_IS_PROPERTY_SETTER); lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_CLASS_METHOD | LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); literal_index = context_p->lit_object.index; @@ -410,13 +402,14 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ { is_computed = true; } - else if (!is_static && lexer_compare_raw_identifier_to_current (context_p, "constructor", 11)) + else if (!(status_flags & PARSER_CLASS_STATIC_FUNCTION) + && lexer_compare_raw_identifier_to_current (context_p, "constructor", 11)) { parser_raise_error (context_p, PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR); } parser_flush_cbc (context_p); - function_literal_index = lexer_construct_function_object (context_p, status_flags); + function_literal_index = lexer_construct_function_object (context_p, accessor_status_flags); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, @@ -425,6 +418,7 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); cbc_ext_opcode_t opcode; + bool is_static = (status_flags & PARSER_CLASS_STATIC_FUNCTION); if (is_computed) { @@ -454,28 +448,48 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ } context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); - is_static = false; + status_flags &= (uint32_t) ~PARSER_CLASS_STATIC_FUNCTION; continue; } - if (!is_static && context_p->token.type == LEXER_CLASS_CONSTRUCTOR) + if (!(status_flags & PARSER_CLASS_STATIC_FUNCTION) && context_p->token.type == LEXER_CLASS_CONSTRUCTOR) { - if (constructor_literal_p->type == LEXER_FUNCTION_LITERAL) + if (super_called) { /* 14.5.1 */ parser_raise_error (context_p, PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS); } + else + { + super_called = true; + } parser_flush_cbc (context_p); - uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_CLASS_CONSTRUCTOR; - constructor_literal_p->u.bytecode_p = parser_parse_function (context_p, status_flags); - constructor_literal_p->type = LEXER_FUNCTION_LITERAL; + uint32_t constructor_status_flags = status_flags | PARSER_CLASS_CONSTRUCTOR; + + if (context_p->status_flags & PARSER_CLASS_HAS_SUPER) + { + constructor_status_flags |= PARSER_LEXICAL_ENV_NEEDED; + } + + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + literal_p->type = LEXER_UNUSED_LITERAL; + literal_p->status_flags = 0; + literal_p->u.bytecode_p = parser_parse_function (context_p, constructor_status_flags); + literal_p->type = LEXER_FUNCTION_LITERAL; + parser_emit_cbc_literal (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_SET_CLASS_LITERAL), context_p->literal_count); + context_p->literal_count++; continue; } - if (!is_static && context_p->token.type == LEXER_KEYW_STATIC) + if (!(status_flags & PARSER_CLASS_STATIC_FUNCTION) && context_p->token.type == LEXER_KEYW_STATIC) { - is_static = true; + status_flags |= PARSER_CLASS_STATIC_FUNCTION; continue; } @@ -485,7 +499,8 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ { is_computed = true; } - else if (is_static && lexer_compare_raw_identifier_to_current (context_p, "prototype", 9)) + else if ((status_flags & PARSER_CLASS_STATIC_FUNCTION) + && lexer_compare_raw_identifier_to_current (context_p, "prototype", 9)) { parser_raise_error (context_p, PARSER_ERR_CLASS_STATIC_PROTOTYPE); } @@ -493,7 +508,6 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ parser_flush_cbc (context_p); uint16_t literal_index = context_p->lit_object.index; - uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags); parser_emit_cbc_literal (context_p, @@ -504,11 +518,11 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ context_p->last_cbc.value = literal_index; - if (is_static) + if ((status_flags & PARSER_CLASS_STATIC_FUNCTION)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (is_computed ? CBC_EXT_SET_STATIC_COMPUTED_PROPERTY_LITERAL : CBC_EXT_SET_STATIC_PROPERTY_LITERAL); - is_static = false; + status_flags &= (uint32_t) ~PARSER_CLASS_STATIC_FUNCTION; } else { @@ -517,14 +531,15 @@ parser_parse_class_literal (parser_context_t *context_p, /**< context */ } } - if (constructor_literal_p->type == LEXER_UNUSED_LITERAL) + if (!super_called && (context_p->status_flags & PARSER_CLASS_HAS_SUPER)) { - parser_flush_cbc (context_p); - constructor_literal_p->u.bytecode_p = parser_create_class_implicit_constructor (context_p); - constructor_literal_p->type = LEXER_FUNCTION_LITERAL; + parser_emit_cbc_ext (context_p, CBC_EXT_IMPLICIT_CONSTRUCTOR_CALL); } - JERRY_ASSERT (constructor_literal_p->type == LEXER_FUNCTION_LITERAL); + if (context_p->status_flags & PARSER_CLASS_HAS_SUPER) + { + parser_emit_cbc_ext (context_p, CBC_EXT_INHERIT_AND_SET_CONSTRUCTOR); + } } /* parser_parse_class_literal */ /** @@ -570,25 +585,17 @@ parser_parse_class (parser_context_t *context_p, /**< context */ } } - /* Currently heritage is not supported so the next token must be left brace. */ - if (context_p->token.type != LEXER_LEFT_BRACE) + if (context_p->token.type == LEXER_KEYW_EXTENDS) { - parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); + parser_parse_super_class_context_start (context_p); } - /* Create an empty literal for class constructor. */ - if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + if (context_p->token.type != LEXER_LEFT_BRACE) { - parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } - lexer_literal_t *constructor_literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); - constructor_literal_p->type = LEXER_UNUSED_LITERAL; - constructor_literal_p->status_flags = 0; - - parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, context_p->literal_count); - - context_p->literal_count++; + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CLASS_CONSTRUCTOR); bool is_strict = context_p->status_flags & PARSER_IS_STRICT; @@ -596,17 +603,7 @@ parser_parse_class (parser_context_t *context_p, /**< context */ context_p->status_flags |= PARSER_IS_STRICT; /* ClassDeclaration is parsed. Continue with class body. */ - parser_parse_class_literal (context_p, constructor_literal_p); - -#ifdef JERRY_DEBUGGER - if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) - { - jerry_debugger_send_string (JERRY_DEBUGGER_FUNCTION_NAME, - JERRY_DEBUGGER_NO_SUBTYPE, - constructor_literal_p->u.char_p, - constructor_literal_p->prop.length); - } -#endif /* JERRY_DEBUGGER */ + parser_parse_class_literal (context_p); JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE); @@ -621,6 +618,12 @@ parser_parse_class (parser_context_t *context_p, /**< context */ parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, class_ident_index); } + if (context_p->status_flags & PARSER_CLASS_HAS_SUPER) + { + parser_parse_super_class_context_end (context_p, is_statement); + context_p->status_flags &= (uint32_t) ~PARSER_CLASS_HAS_SUPER; + } + parser_flush_cbc (context_p); if (!is_strict) @@ -1226,7 +1229,18 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ } case LEXER_KEYW_THIS: { - parser_emit_cbc (context_p, CBC_PUSH_THIS); +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags)) + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + } + else + { +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + parser_emit_cbc (context_p, CBC_PUSH_THIS); +#ifndef CONFIG_DISABLE_ES2015_CLASS + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ break; } case LEXER_LIT_TRUE: @@ -1250,6 +1264,38 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ parser_parse_class (context_p, false); return; } + case LEXER_KEYW_SUPER: + { + if ((lexer_check_next_character (context_p, LIT_CHAR_DOT) + || lexer_check_next_character (context_p, LIT_CHAR_LEFT_SQUARE)) + && context_p->status_flags & (PARSER_CLASS_HAS_SUPER | PARSER_IS_ARROW_FUNCTION)) + { + if (!LEXER_IS_BINARY_OP_TOKEN (context_p->stack_top_uint8)) + { + context_p->status_flags |= PARSER_CLASS_SUPER_PROP_REFERENCE; + } + + if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_SUPER_PROP); + break; + } + + bool is_static = context_p->status_flags & PARSER_CLASS_STATIC_FUNCTION; + parser_emit_cbc_ext (context_p, is_static ? CBC_EXT_PUSH_STATIC_SUPER : CBC_EXT_PUSH_SUPER); + break; + } + + if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN) + && (context_p->status_flags & PARSER_CLASS_HAS_SUPER) + && (context_p->status_flags & (PARSER_IS_ARROW_FUNCTION | PARSER_CLASS_CONSTRUCTOR))) + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_SUPER); + break; + } + + parser_raise_error (context_p, PARSER_ERR_UNEXPECTED_SUPER_REFERENCE); + } #endif /* !CONFIG_DISABLE_ES2015_CLASS */ #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION case LEXER_RIGHT_PAREN: @@ -1393,6 +1439,12 @@ parser_process_unary_expression (parser_context_t *context_p) /**< context */ context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL_REFERENCE; opcode = CBC_CALL_PROP; } +#ifndef CONFIG_DISABLE_ES2015_CLASS + else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_CONSTRUCTOR_SUPER)) + { + opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL); + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ else if ((context_p->status_flags & (PARSER_INSIDE_WITH | PARSER_RESOLVE_BASE_FOR_CALLS)) && PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) @@ -1452,8 +1504,29 @@ parser_process_unary_expression (parser_context_t *context_p) /**< context */ if (is_eval) { - parser_emit_cbc (context_p, CBC_EVAL); +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (context_p->status_flags & PARSER_CLASS_HAS_SUPER) + { + parser_flush_cbc (context_p); + context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_CLASS_EVAL); + context_p->last_cbc.value = PARSER_GET_CLASS_ECMA_PARSE_OPTS (context_p->status_flags); + } + else + { +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + parser_emit_cbc (context_p, CBC_EVAL); +#ifndef CONFIG_DISABLE_ES2015_CLASS + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + } + +#ifndef CONFIG_DISABLE_ES2015_CLASS + if ((context_p->status_flags & PARSER_CLASS_SUPER_PROP_REFERENCE) && opcode == CBC_CALL_PROP) + { + parser_emit_cbc_ext (context_p, CBC_EXT_SUPER_PROP_CALL); + context_p->status_flags &= (uint32_t) ~PARSER_CLASS_SUPER_PROP_REFERENCE; } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ if (call_arguments == 0) { @@ -1687,6 +1760,14 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */ parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); parser_stack_push_uint8 (context_p, CBC_ASSIGN_PROP_LITERAL); context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (context_p->status_flags & PARSER_CLASS_SUPER_PROP_REFERENCE) + { + parser_emit_cbc_ext (context_p, CBC_EXT_SUPER_PROP_ASSIGN); + parser_flush_cbc (context_p); + } + context_p->status_flags &= (uint32_t) ~PARSER_CLASS_SUPER_PROP_REFERENCE; +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ } else { diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index ae4ff030dc..d9a9aa7f31 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -68,7 +68,12 @@ typedef enum PARSER_ARROW_PARSE_ARGS = (1u << 19), /**< parse the argument list of an arrow function */ #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ #ifndef CONFIG_DISABLE_ES2015_CLASS - PARSER_CLASS_CONSTRUCTOR = (1u << 20), /**< a class constructor is parsed */ + /* These three status flags must be in this order. See PARSER_CLASS_PARSE_OPTS_OFFSET. */ + PARSER_CLASS_CONSTRUCTOR = (1u << 20), /**< a class constructor is parsed (this value must be kept in + * in sync with ECMA_PARSE_CLASS_CONSTRUCTOR) */ + PARSER_CLASS_HAS_SUPER = (1u << 21), /**< class has super reference */ + PARSER_CLASS_STATIC_FUNCTION = (1u << 22), /**< this function is a static class method */ + PARSER_CLASS_SUPER_PROP_REFERENCE = (1u << 23), /**< super property call or assignment */ #endif /* !CONFIG_DISABLE_ES2015_CLASS */ } parser_general_flags_t; @@ -85,6 +90,54 @@ typedef enum * CBC_PUSH_LITERAL instruction */ } parser_expression_flags_t; +/** + * Mask for strict mode code + */ +#define PARSER_STRICT_MODE_MASK 0x1 + +#ifndef CONFIG_DISABLE_ES2015_CLASS +/** + * Offset between PARSER_CLASS_CONSTRUCTOR and ECMA_PARSE_CLASS_CONSTRUCTOR + */ +#define PARSER_CLASS_PARSE_OPTS_OFFSET \ + (JERRY_LOG2 (PARSER_CLASS_CONSTRUCTOR) - JERRY_LOG2 (ECMA_PARSE_CLASS_CONSTRUCTOR)) + +/** + * Count of ecma_parse_opts_t class parsing options related bits + */ +#define PARSER_CLASS_PARSE_OPTS_COUNT \ + (JERRY_LOG2 (ECMA_PARSE_HAS_STATIC_SUPER) - JERRY_LOG2 (ECMA_PARSE_CLASS_CONSTRUCTOR)) + +/** + * Mask for get class option bits from ecma_parse_opts_t + */ +#define PARSER_CLASS_ECMA_PARSE_OPTS_TO_PARSER_OPTS_MASK \ + (((1 << PARSER_CLASS_PARSE_OPTS_COUNT) - 1) << JERRY_LOG2 (ECMA_PARSE_CLASS_CONSTRUCTOR)) + +/** + * Get class option bits from ecma_parse_opts_t + */ +#define PARSER_GET_CLASS_PARSER_OPTS(opts) \ + (((opts) & PARSER_CLASS_ECMA_PARSE_OPTS_TO_PARSER_OPTS_MASK) << PARSER_CLASS_PARSE_OPTS_OFFSET) + +/** + * Get class option bits from parser_general_flags_t + */ +#define PARSER_GET_CLASS_ECMA_PARSE_OPTS(opts) \ + ((uint16_t) (((opts) >> PARSER_CLASS_PARSE_OPTS_OFFSET) & PARSER_CLASS_ECMA_PARSE_OPTS_TO_PARSER_OPTS_MASK)) + +/** + * Class constructor with heritage context representing bits + */ +#define PARSER_CLASS_CONSTRUCTOR_SUPER (PARSER_CLASS_CONSTRUCTOR | PARSER_CLASS_HAS_SUPER) + +/** + * Check the scope is a class constructor with heritage context + */ +#define PARSER_IS_CLASS_CONSTRUCTOR_SUPER(flag) \ + (((flag) & PARSER_CLASS_CONSTRUCTOR_SUPER) == PARSER_CLASS_CONSTRUCTOR_SUPER) +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + /* The maximum of PARSER_CBC_STREAM_PAGE_SIZE is 127. */ #define PARSER_CBC_STREAM_PAGE_SIZE \ ((uint32_t) (64 - sizeof (void *))) @@ -434,9 +487,8 @@ void parser_set_continues_to_current_position (parser_context_t *context_p, pars /* Lexer functions */ void lexer_next_token (parser_context_t *context_p); -bool lexer_check_colon (parser_context_t *context_p); +bool lexer_check_next_character (parser_context_t *context_p, lit_utf8_byte_t character); #ifndef CONFIG_DISABLE_ES2015_CLASS -bool lexer_check_left_paren (parser_context_t *context_p); void lexer_skip_empty_statements (parser_context_t *context_p); #endif /* !CONFIG_DISABLE_ES2015_CLASS */ #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION @@ -469,6 +521,8 @@ bool lexer_compare_raw_identifier_to_current (parser_context_t *context_p, const void parser_parse_expression (parser_context_t *context_p, int options); #ifndef CONFIG_DISABLE_ES2015_CLASS void parser_parse_class (parser_context_t *context_p, bool is_statement); +void parser_parse_super_class_context_start (parser_context_t *context_p); +void parser_parse_super_class_context_end (parser_context_t *context_p, bool is_statement); #endif /* !CONFIG_DISABLE_ES2015_CLASS */ /** @@ -501,9 +555,6 @@ ecma_compiled_code_t *parser_parse_function (parser_context_t *context_p, uint32 #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION ecma_compiled_code_t *parser_parse_arrow_function (parser_context_t *context_p, uint32_t status_flags); #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ -#ifndef CONFIG_DISABLE_ES2015_CLASS -ecma_compiled_code_t *parser_create_class_implicit_constructor (parser_context_t *context_p); -#endif /* !CONFIG_DISABLE_ES2015_CLASS */ /* Error management. */ diff --git a/jerry-core/parser/js/js-parser-scanner.c b/jerry-core/parser/js/js-parser-scanner.c index 9d9a70460c..29e1f2ee3c 100644 --- a/jerry-core/parser/js/js-parser-scanner.c +++ b/jerry-core/parser/js/js-parser-scanner.c @@ -14,10 +14,7 @@ */ #include "js-parser-internal.h" - -#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS #include "lit-char-helpers.h" -#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ #ifndef JERRY_DISABLE_JS_PARSER @@ -75,6 +72,7 @@ typedef enum #endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ #ifndef CONFIG_DISABLE_ES2015_CLASS SCAN_STACK_CLASS, /**< class language element */ + SCAN_STACK_CLASS_EXTENDS, /**< class extends expression */ #endif /* !CONFIG_DISABLE_ES2015_CLASS */ } scan_stack_modes_t; @@ -335,6 +333,9 @@ parser_scan_primary_expression_end (parser_context_t *context_p, /**< context */ if ((type == LEXER_RIGHT_SQUARE && stack_top == SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) || (type == LEXER_RIGHT_PAREN && stack_top == SCAN_STACK_PAREN_EXPRESSION) +#ifndef CONFIG_DISABLE_ES2015_CLASS + || (type == LEXER_LEFT_BRACE && stack_top == SCAN_STACK_CLASS_EXTENDS) +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ || (type == LEXER_RIGHT_BRACE && stack_top == SCAN_STACK_OBJECT_LITERAL)) { parser_stack_pop_uint8 (context_p); @@ -345,6 +346,12 @@ parser_scan_primary_expression_end (parser_context_t *context_p, /**< context */ *mode = SCAN_MODE_ARROW_FUNCTION; } #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (stack_top == SCAN_STACK_CLASS_EXTENDS) + { + *mode = SCAN_MODE_CLASS_METHOD; + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ return false; } @@ -699,8 +706,13 @@ parser_scan_until (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); } - /* Currently heritage is not supported so the next token must be left brace. */ - if (context_p->token.type != LEXER_LEFT_BRACE) + if (context_p->token.type == LEXER_KEYW_EXTENDS) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_EXTENDS); + mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } + else if (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 52feef3467..45b47a926b 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -16,10 +16,8 @@ #include "js-parser-internal.h" #ifndef JERRY_DISABLE_JS_PARSER - -#if defined (JERRY_DEBUGGER) || defined (JERRY_ENABLE_LINE_INFO) #include "jcontext.h" -#endif /* JERRY_DEBUGGER || JERRY_ENABLE_LINE_INFO */ +#include "lit-char-helpers.h" /** \addtogroup parser Parser * @{ @@ -617,6 +615,68 @@ parser_parse_with_statement_end (parser_context_t *context_p) /**< context */ } } /* parser_parse_with_statement_end */ +#ifndef CONFIG_DISABLE_ES2015_CLASS +/** + * Parse super class context like a with statement (starting part). + */ +void +parser_parse_super_class_context_start (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_EXTENDS); + + parser_with_statement_t with_statement; + + lexer_next_token (context_p); + + /* NOTE: Currently there is no proper way to check whether the currently parsed expression + is a valid lefthand-side expression or not, so we do not throw syntax error and parse + the class extending value as an expression. */ + parser_parse_expression (context_p, PARSE_EXPR | PARSE_EXPR_NO_COMMA); + +#ifndef JERRY_NDEBUG + PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + context_p->status_flags |= PARSER_CLASS_HAS_SUPER; + parser_emit_cbc_ext_forward_branch (context_p, + CBC_EXT_SUPER_CLASS_CREATE_CONTEXT, + &with_statement.branch); + + parser_stack_push (context_p, &with_statement, sizeof (parser_with_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_WITH); +} /* parser_parse_super_class_context_start */ + +/** + * Parse super class context like a with statement (ending part). + */ +void +parser_parse_super_class_context_end (parser_context_t *context_p, /**< context */ + bool is_statement) /**< true - if class is parsed as a statement + * false - otherwise (as an expression) */ +{ + parser_with_statement_t with_statement; + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &with_statement, sizeof (parser_with_statement_t)); + + parser_flush_cbc (context_p); + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION); +#ifndef JERRY_NDEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + if (is_statement) + { + parser_emit_cbc (context_p, CBC_CONTEXT_END); + } + else + { + parser_emit_cbc_ext (context_p, CBC_EXT_CLASS_EXPR_CONTEXT_END); + } + + parser_set_branch_to_current_position (context_p, &with_statement.branch); +} /* parser_parse_super_class_context_end */ +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + /** * Parse do-while statement (ending part). */ @@ -1970,22 +2030,52 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ } lexer_next_token (context_p); + if ((context_p->token.flags & LEXER_WAS_NEWLINE) || context_p->token.type == LEXER_SEMICOLON || context_p->token.type == LEXER_RIGHT_BRACE) { - parser_emit_cbc (context_p, CBC_RETURN_WITH_BLOCK); +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (JERRY_UNLIKELY (PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags))) + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + parser_emit_cbc (context_p, CBC_RETURN); + } + else + { +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + parser_emit_cbc (context_p, CBC_RETURN_WITH_BLOCK); +#ifndef CONFIG_DISABLE_ES2015_CLASS + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ break; } parser_parse_expression (context_p, PARSE_EXPR); - if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + + bool return_with_literal = (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); +#ifndef CONFIG_DISABLE_ES2015_CLASS + return_with_literal &= !PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + + if (return_with_literal) { context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL; } else { - parser_emit_cbc (context_p, CBC_RETURN); +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (JERRY_UNLIKELY (PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags))) + { + parser_emit_cbc_ext (context_p, CBC_EXT_CONSTRUCTOR_RETURN); + } + else + { +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + parser_emit_cbc (context_p, CBC_RETURN); +#ifndef CONFIG_DISABLE_ES2015_CLASS + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ } break; } @@ -2003,11 +2093,10 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ lexer_next_token (context_p); break; } - case LEXER_LITERAL: { if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL - && lexer_check_colon (context_p)) + && lexer_check_next_character (context_p, LIT_CHAR_COLON)) { parser_parse_label (context_p); lexer_next_token (context_p); @@ -2087,6 +2176,15 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ #endif /* !JERRY_NDEBUG */ /* There is no lexer_next_token here, since the * next token belongs to the parent context. */ + +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (JERRY_UNLIKELY (PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags))) + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + parser_emit_cbc (context_p, CBC_RETURN); + parser_flush_cbc (context_p); + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ return; } parser_raise_error (context_p, PARSER_ERR_INVALID_RIGHT_SQUARE); diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index 15939edaf3..f8a66ed7fc 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -911,6 +911,10 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Classes may not have a static property called 'prototype'."; } + case PARSER_ERR_UNEXPECTED_SUPER_REFERENCE: + { + return "Super is not allowed to be used here."; + } #endif /* !CONFIG_DISABLE_ES2015_CLASS */ case PARSER_ERR_LEFT_PAREN_EXPECTED: { diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 9fdde53d82..827bd28a2a 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -22,6 +22,14 @@ #ifndef JERRY_DISABLE_JS_PARSER +JERRY_STATIC_ASSERT ((int) ECMA_PARSE_STRICT_MODE == (int) PARSER_IS_STRICT, + ecma_parse_strict_mode_must_be_equal_to_parser_is_strict); + +#ifndef CONFIG_DISABLE_ES2015_CLASS +JERRY_STATIC_ASSERT ((ECMA_PARSE_CLASS_CONSTRUCTOR << PARSER_CLASS_PARSE_OPTS_OFFSET) == PARSER_CLASS_CONSTRUCTOR, + ecma_class_parse_options_must_be_able_to_be_shifted_to_ecma_general_flags); +#endif /* !CONFIG_DISABLE_ES2015 */ + /** \addtogroup parser Parser * @{ * @@ -1210,6 +1218,13 @@ parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code } #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR) + { + JERRY_DEBUG_MSG (",constructor"); + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + JERRY_DEBUG_MSG ("]\n"); JERRY_DEBUG_MSG (" Argument range end: %d\n", (int) argument_end); @@ -1546,6 +1561,13 @@ parser_post_processing (parser_context_t *context_p) /**< context */ PARSER_NEXT_BYTE (page_p, offset); length++; +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (ext_opcode == CBC_EXT_CONSTRUCTOR_RETURN) + { + last_opcode = CBC_RETURN; + } +#endif /* !CONFIG_DISABLE_ES2015 */ + #ifdef JERRY_ENABLE_LINE_INFO if (ext_opcode == CBC_EXT_LINE) { @@ -2302,11 +2324,11 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ context.stack_limit = 0; context.last_context_p = NULL; context.last_statement.current_p = NULL; + context.status_flags |= parse_opts & PARSER_STRICT_MODE_MASK; - if (parse_opts & ECMA_PARSE_STRICT_MODE) - { - context.status_flags |= PARSER_IS_STRICT; - } +#ifndef CONFIG_DISABLE_ES2015_CLASS + context.status_flags |= PARSER_GET_CLASS_PARSER_OPTS (parse_opts); +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ context.token.flags = 0; context.line = 1; @@ -2525,38 +2547,6 @@ parser_restore_context (parser_context_t *context_p, /**< context */ #endif /* !JERRY_NDEBUG */ } /* parser_restore_context */ -#ifndef CONFIG_DISABLE_ES2015_CLASS -/** - * Parse default constructor code - * - * @return compiled code - */ -ecma_compiled_code_t * -parser_create_class_implicit_constructor (parser_context_t *context_p) /**< context */ -{ - parser_saved_context_t saved_context; - parser_save_context (context_p, &saved_context); - -#ifdef JERRY_DEBUGGER - if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) - && jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column)) - { - /* This option has a high memory and performance costs, - * but it is necessary for executing eval operations by the debugger. */ - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE; - } -#endif /* JERRY_DEBUGGER */ - - context_p->status_flags |= PARSER_CLASS_CONSTRUCTOR; - - ecma_compiled_code_t *compiled_code_p = parser_post_processing (context_p); - - parser_restore_context (context_p, &saved_context); - - return compiled_code_p; -} /* parser_create_class_implicit_constructor */ -#endif /* !CONFIG_DISABLE_ES2015_CLASS */ - /** * Parse function code * @@ -2674,6 +2664,13 @@ parser_parse_function (parser_context_t *context_p, /**< context */ } lexer_next_token (context_p); + +#ifndef CONFIG_DISABLE_ES2015_CLASS + if ((context_p->status_flags & PARSER_CLASS_CONSTRUCTOR_SUPER) == PARSER_CLASS_CONSTRUCTOR_SUPER) + { + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ parser_parse_statements (context_p); compiled_code_p = parser_post_processing (context_p); @@ -2713,6 +2710,9 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */ && (status_flags & PARSER_IS_ARROW_FUNCTION)); parser_save_context (context_p, &saved_context); context_p->status_flags |= status_flags | PARSER_ARGUMENTS_NOT_NEEDED; +#ifndef CONFIG_DISABLE_ES2015_CLASS + context_p->status_flags |= saved_context.status_flags & PARSER_CLASS_HAS_SUPER; +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ #ifdef PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index bc1bd56e59..1af566f373 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -83,6 +83,7 @@ typedef enum PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS, /**< multiple class constructor */ PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR, /**< class constructor cannot be an accessor */ PARSER_ERR_CLASS_STATIC_PROTOTYPE, /**< static method name 'prototype' is not allowed */ + PARSER_ERR_UNEXPECTED_SUPER_REFERENCE, /**< unexpected super keyword */ #endif /* !CONFIG_DISABLE_ES2015_CLASS */ PARSER_ERR_LEFT_PAREN_EXPECTED, /**< left paren expected */ diff --git a/jerry-core/vm/vm-stack.c b/jerry-core/vm/vm-stack.c index 6284c0878c..893e91b92c 100644 --- a/jerry-core/vm/vm-stack.c +++ b/jerry-core/vm/vm-stack.c @@ -26,6 +26,9 @@ * @{ */ +JERRY_STATIC_ASSERT (PARSER_WITH_CONTEXT_STACK_ALLOCATION == PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION, + parser_with_context_stack_allocation_must_be_equal_to_parser_super_class_context_stack_allocation); + /** * Abort (finalize) the current stack context, and remove it. * @@ -64,6 +67,9 @@ vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ /* FALLTHRU */ } case VM_CONTEXT_WITH: +#ifndef CONFIG_DISABLE_ES2015_CLASS + case VM_CONTEXT_SUPER_CLASS: +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ { ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; frame_ctx_p->lex_env_p = ecma_get_lex_env_outer_reference (lex_env_p); diff --git a/jerry-core/vm/vm-stack.h b/jerry-core/vm/vm-stack.h index f6963f42f5..323cddaab4 100644 --- a/jerry-core/vm/vm-stack.h +++ b/jerry-core/vm/vm-stack.h @@ -57,6 +57,9 @@ typedef enum VM_CONTEXT_TRY, /**< try context */ VM_CONTEXT_CATCH, /**< catch context */ VM_CONTEXT_WITH, /**< with context */ +#ifndef CONFIG_DISABLE_ES2015_CLASS + VM_CONTEXT_SUPER_CLASS, /**< super class context */ +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ VM_CONTEXT_FOR_IN, /**< for-in context */ } vm_stack_context_type_t; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 29d7998f5d..7831fdfd26 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -382,6 +382,59 @@ vm_get_implicit_this_value (ecma_value_t *this_value_p) /**< [in,out] this value return false; } /* vm_get_implicit_this_value */ +#ifndef CONFIG_DISABLE_ES2015_CLASS +/** + * 'super(...)' function call handler. + */ +static void +vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ +{ + JERRY_ASSERT (frame_ctx_p->call_operation == VM_EXEC_SUPER_CALL); + JERRY_ASSERT (frame_ctx_p->byte_code_p[0] == CBC_EXT_OPCODE); + + uint32_t arguments_list_len = frame_ctx_p->byte_code_p[2]; + + ecma_value_t *stack_top_p = frame_ctx_p->stack_top_p - arguments_list_len; + + ecma_value_t func_value = stack_top_p[-1]; + ecma_value_t completion_value; + ecma_op_set_super_called (frame_ctx_p->lex_env_p); + ecma_value_t this_value = ecma_op_get_class_this_binding (frame_ctx_p->lex_env_p); + + if (!ecma_is_constructor (func_value)) + { + completion_value = ecma_raise_type_error ("Class extends value is not a constructor."); + } + else + { + completion_value = ecma_op_function_construct (ecma_get_object_from_value (func_value), + this_value, + stack_top_p, + arguments_list_len); + + if (this_value != completion_value && ecma_is_value_object (completion_value)) + { + ecma_op_set_class_prototype (completion_value, this_value); + ecma_op_set_class_this_binding (frame_ctx_p->lex_env_p, completion_value); + } + } + + /* Free registers. */ + for (uint32_t i = 0; i < arguments_list_len; i++) + { + ecma_fast_free_value (stack_top_p[i]); + } + + ecma_free_value (stack_top_p[-1]); + stack_top_p[-1] = completion_value; + + frame_ctx_p->stack_top_p = stack_top_p; + + /* 'super (...)' call continues as a normal 'Function call' */ + frame_ctx_p->call_operation = VM_EXEC_CALL; +} /* vm_super_call */ +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + /** * 'Function call' opcode handler. * @@ -1186,6 +1239,313 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ *stack_top_p++ = result; continue; } +#ifndef CONFIG_DISABLE_ES2015_CLASS + case VM_OC_SUPER_CALL: + { + if (frame_ctx_p->call_operation == VM_NO_EXEC_OP) + { + frame_ctx_p->call_operation = VM_EXEC_SUPER_CALL; + frame_ctx_p->byte_code_p = byte_code_start_p; + frame_ctx_p->stack_top_p = stack_top_p; + frame_ctx_p->call_block_result = block_result; + return ECMA_VALUE_UNDEFINED; + } + + byte_code_p++; + frame_ctx_p->call_operation = VM_NO_EXEC_OP; + + result = *(--stack_top_p); + block_result = frame_ctx_p->call_block_result; + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) + { + ecma_fast_free_value (result); + } + else if (opcode_data & VM_OC_PUT_STACK) + { + *stack_top_p++ = result; + } + else + { + ecma_fast_free_value (block_result); + block_result = result; + } + + continue; + } + case VM_OC_CLASS_HERITAGE: + { + ecma_value_t super_value = *(--stack_top_p); + ecma_object_t *super_class_p; + branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); + + JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p); + + if (ecma_is_value_null (super_value)) + { + super_class_p = ecma_create_object (NULL, 0, ECMA_OBJECT_TYPE_GENERAL); + } + else + { + result = ecma_op_to_object (super_value); + ecma_free_value (super_value); + + if (ECMA_IS_VALUE_ERROR (result) || !ecma_is_constructor (result)) + { + if (ECMA_IS_VALUE_ERROR (result)) + { + ecma_free_value (JERRY_CONTEXT (error_value)); + } + + ecma_free_value (result); + + result = ecma_raise_type_error ("Value provided by class extends is not an object or null."); + goto error; + } + else + { + super_class_p = ecma_get_object_from_value (result); + } + } + + ecma_object_t *super_env_p = ecma_create_object_lex_env (frame_ctx_p->lex_env_p, + super_class_p, + ECMA_LEXICAL_ENVIRONMENT_SUPER_OBJECT_BOUND); + + ecma_deref_object (super_class_p); + + VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION); + stack_top_p += PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION; + + stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_SUPER_CLASS, branch_offset); + + frame_ctx_p->lex_env_p = super_env_p; + + continue; + } + case VM_OC_CLASS_INHERITANCE: + { + ecma_value_t child_value = stack_top_p[-2]; + ecma_value_t child_prototype_value = stack_top_p[-1]; + + ecma_object_t *child_class_p = ecma_get_object_from_value (child_value); + ecma_object_t *child_prototype_class_p = ecma_get_object_from_value (child_prototype_value); + ecma_property_value_t *prop_value_p; + + prop_value_p = ecma_create_named_data_property (child_prototype_class_p, + ecma_get_magic_string (LIT_MAGIC_STRING_CONSTRUCTOR), + ECMA_PROPERTY_CONFIGURABLE_WRITABLE, + NULL); + + ecma_named_data_property_assign_value (child_prototype_class_p, prop_value_p, child_value); + + ecma_object_t *super_class_p = ecma_get_lex_env_binding_object (frame_ctx_p->lex_env_p); + + if (ecma_get_object_prototype (super_class_p)) + { + ecma_value_t super_prototype_value = ecma_op_object_get_by_magic_id (super_class_p, + LIT_MAGIC_STRING_PROTOTYPE); + if (ecma_get_object_type (super_class_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION + && !ecma_is_value_object (super_prototype_value)) + { + ecma_free_value (super_prototype_value); + result = ecma_raise_type_error (ECMA_ERR_MSG ("Class extends value does not have valid " + "prototype property.")); + goto error; + } + if (!(ECMA_IS_VALUE_ERROR (super_prototype_value) || !ecma_is_value_object (super_prototype_value))) + { + ecma_object_t *super_prototype_class_p = ecma_get_object_from_value (super_prototype_value); + + ECMA_SET_POINTER (child_prototype_class_p->prototype_or_outer_reference_cp, super_prototype_class_p); + ECMA_SET_POINTER (child_class_p->prototype_or_outer_reference_cp, super_class_p); + + } + ecma_free_value (super_prototype_value); + } + + continue; + } + case VM_OC_PUSH_CLASS_CONSTRUCTOR: + { + ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); + + ecma_object_t *function_obj_p = ecma_create_object (prototype_obj_p, + sizeof (ecma_extended_object_t), + ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION); + + + ecma_deref_object (prototype_obj_p); + + ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) function_obj_p; + ext_func_obj_p->u.external_handler_cb = ecma_op_function_implicit_constructor_handler_cb; + + *stack_top_p++ = ecma_make_object_value (function_obj_p); + + continue; + } + case VM_OC_SET_CLASS_CONSTRUCTOR: + { + ecma_object_t *new_constructor_obj_p = ecma_get_object_from_value (left_value); + ecma_object_t *current_constructor_obj_p = ecma_get_object_from_value (stack_top_p[-2]); + + ecma_extended_object_t *new_ext_func_obj_p = (ecma_extended_object_t *) new_constructor_obj_p; + ecma_extended_object_t *current_ext_func_obj_p = (ecma_extended_object_t *) current_constructor_obj_p; + + uint16_t type_flags_refs = current_constructor_obj_p->type_flags_refs; + const int new_type = ECMA_OBJECT_TYPE_FUNCTION - ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION; + current_constructor_obj_p->type_flags_refs = (uint16_t) (type_flags_refs + new_type); + + ecma_compiled_code_t *bytecode_p; + bytecode_p = (ecma_compiled_code_t *) ecma_op_function_get_compiled_code (new_ext_func_obj_p); + bytecode_p->status_flags |= CBC_CODE_FLAGS_CONSTRUCTOR; + ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_p); + ECMA_SET_INTERNAL_VALUE_POINTER (current_ext_func_obj_p->u.function.bytecode_cp, + bytecode_p); + ECMA_SET_INTERNAL_VALUE_POINTER (current_ext_func_obj_p->u.function.scope_cp, + ECMA_GET_INTERNAL_VALUE_POINTER (const ecma_object_t, + new_ext_func_obj_p->u.function.scope_cp)); + ecma_deref_object (new_constructor_obj_p); + continue; + } + case VM_OC_PUSH_IMPL_CONSTRUCTOR: + { + ecma_object_t *current_constructor_obj_p = ecma_get_object_from_value (stack_top_p[-2]); + + uint16_t type_flags_refs = current_constructor_obj_p->type_flags_refs; + const int new_type = ECMA_OBJECT_TYPE_BOUND_FUNCTION - ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION; + current_constructor_obj_p->type_flags_refs = (uint16_t) (type_flags_refs + new_type); + + ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) current_constructor_obj_p; + ecma_object_t *super_obj_p = ecma_op_resolve_super_reference_value (frame_ctx_p->lex_env_p); + + ECMA_SET_INTERNAL_VALUE_POINTER (ext_function_p->u.bound_function.target_function, + super_obj_p); + ext_function_p->u.bound_function.args_len_or_this = ECMA_VALUE_IMPLICIT_CONSTRUCTOR; + + continue; + } + case VM_OC_CLASS_EXPR_CONTEXT_END: + { + JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p - 1); + + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-2]) == VM_CONTEXT_SUPER_CLASS); + stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p - 1); + + JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p); + stack_top_p++; + stack_top_p[-1] = *stack_top_p; + continue; + } + case VM_OC_CLASS_EVAL: + { + ECMA_SET_SUPER_EVAL_PARSER_OPTS (*byte_code_p++); + continue; + } + case VM_OC_PUSH_CONSTRUCTOR_SUPER: + { + JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE); + + bool is_super_called = ecma_op_is_super_called (frame_ctx_p->lex_env_p); + + if (byte_code_start_p[1] != CBC_EXT_PUSH_CONSTRUCTOR_SUPER_PROP) + { + /* Calling super(...) */ + if (is_super_called) + { + result = ecma_raise_reference_error (ECMA_ERR_MSG ("Super constructor may only be called once.")); + + goto error; + } + } + else if (!is_super_called) + { + /* Reference to super.method or super["method"] */ + result = ecma_raise_reference_error (ECMA_ERR_MSG ("Must call super constructor in derived class before " + "accessing 'super'.")); + goto error; + } + + /* FALLTHRU */ + } + case VM_OC_PUSH_SUPER: + { + JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE); + + if (byte_code_start_p[1] == CBC_EXT_PUSH_SUPER + || byte_code_start_p[1] == CBC_EXT_PUSH_CONSTRUCTOR_SUPER_PROP) + { + ecma_object_t *super_class_p = ecma_op_resolve_super_reference_value (frame_ctx_p->lex_env_p); + + ecma_value_t super_prototype = ecma_op_object_get_by_magic_id (super_class_p, + LIT_MAGIC_STRING_PROTOTYPE); + + if (ECMA_IS_VALUE_ERROR (super_prototype)) + { + result = super_prototype; + goto error; + } + + *stack_top_p++ = super_prototype; + } + else + { + ecma_object_t *super_class_p = ecma_op_resolve_super_reference_value (frame_ctx_p->lex_env_p); + *stack_top_p++ = ecma_fast_copy_value (ecma_make_object_value (super_class_p)); + } + + continue; + } + case VM_OC_PUSH_CONSTRUCTOR_THIS: + { + if (!ecma_op_is_super_called (frame_ctx_p->lex_env_p)) + { + result = ecma_raise_reference_error (ECMA_ERR_MSG ("Must call super constructor in derived class before " + "accessing 'this' or returning from it.")); + goto error; + } + + *stack_top_p++ = ecma_copy_value (ecma_op_get_class_this_binding (frame_ctx_p->lex_env_p)); + continue; + } + case VM_OC_SUPER_PROP_REFERENCE: + { + const int index = (byte_code_start_p[1] == CBC_EXT_SUPER_PROP_ASSIGN) ? -1 : -3; + ecma_free_value (stack_top_p[index]); + stack_top_p[index] = ecma_copy_value (frame_ctx_p->this_binding); + continue; + } + case VM_OC_CONSTRUCTOR_RET: + { + result = left_value; + left_value = ECMA_VALUE_UNDEFINED; + + if (!ecma_is_value_object (result)) + { + if (ecma_is_value_undefined (result)) + { + if (!ecma_op_is_super_called (frame_ctx_p->lex_env_p)) + { + result = ecma_raise_reference_error (ECMA_ERR_MSG ("Must call super constructor in derived class " + "before returning from derived constructor")); + } + } + else + { + ecma_free_value (result); + result = ecma_raise_type_error (ECMA_ERR_MSG ("Derived constructors may only " + "return object or undefined.")); + } + } + + goto error; + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ case VM_OC_PUSH_ELISON: { *stack_top_p++ = ECMA_VALUE_ARRAY_HOLE; @@ -2365,7 +2725,9 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ object_p = ecma_get_object_from_value (result); - with_env_p = ecma_create_object_lex_env (frame_ctx_p->lex_env_p, object_p); + with_env_p = ecma_create_object_lex_env (frame_ctx_p->lex_env_p, + object_p, + ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_deref_object (object_p); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); @@ -3060,6 +3422,12 @@ vm_execute (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ { opfunc_call (frame_ctx_p); } +#ifndef CONFIG_DISABLE_ES2015_CLASS + else if (frame_ctx_p->call_operation == VM_EXEC_SUPER_CALL) + { + vm_super_call (frame_ctx_p); + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ else { JERRY_ASSERT (frame_ctx_p->call_operation == VM_EXEC_CONSTRUCT); diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index f58aa0a3db..0dc8649d33 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -209,6 +209,21 @@ typedef enum VM_OC_FINALLY, /**< finally */ VM_OC_CONTEXT_END, /**< context end */ VM_OC_JUMP_AND_EXIT_CONTEXT, /**< jump and exit context */ +#ifndef CONFIG_DISABLE_ES2015_CLASS + VM_OC_CLASS_HERITAGE, /**< create a super class context */ + VM_OC_CLASS_INHERITANCE, /**< inherit properties from the 'super' class */ + VM_OC_PUSH_CLASS_CONSTRUCTOR, /**< push class constructor */ + VM_OC_SET_CLASS_CONSTRUCTOR, /**< set class constructor to the given function literal */ + VM_OC_PUSH_IMPL_CONSTRUCTOR, /**< create implicit class constructor */ + VM_OC_CLASS_EXPR_CONTEXT_END, /**< class expression heritage context end */ + VM_OC_CLASS_EVAL, /**< eval inside a class */ + VM_OC_SUPER_CALL, /**< call the 'super' constructor */ + VM_OC_SUPER_PROP_REFERENCE, /**< resolve super property reference */ + VM_OC_PUSH_SUPER, /**< push resolvable super reference */ + VM_OC_PUSH_CONSTRUCTOR_SUPER, /**< push 'super' inside a class constructor */ + VM_OC_PUSH_CONSTRUCTOR_THIS, /**< push 'this' inside a class constructor */ + VM_OC_CONSTRUCTOR_RET, /**< explicit return from a class constructor */ +#endif /* !CONFIG_DISABLE_ES2015 */ #ifdef JERRY_DEBUGGER VM_OC_BREAKPOINT_ENABLED, /**< enabled breakpoint for debugger */ VM_OC_BREAKPOINT_DISABLED, /**< disabled breakpoint for debugger */ @@ -217,7 +232,7 @@ typedef enum VM_OC_RESOURCE_NAME, /**< resource name of the current function */ VM_OC_LINE, /**< line number of the next statement */ #endif /* JERRY_ENABLE_LINE_INFO */ - VM_OC_NONE, /**< a special opcode for */ + VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; /** @@ -236,6 +251,21 @@ typedef enum VM_OC_RESOURCE_NAME = VM_OC_NONE, /**< resource name of the current function is unused */ VM_OC_LINE = VM_OC_NONE, /**< line number of the next statement is unused */ #endif /* !JERRY_ENABLE_LINE_INFO */ +#ifdef CONFIG_DISABLE_ES2015_CLASS + VM_OC_CLASS_HERITAGE = VM_OC_NONE, /**< create a super class context */ + VM_OC_CLASS_INHERITANCE = VM_OC_NONE, /**< inherit properties from the 'super' class */ + VM_OC_PUSH_CLASS_CONSTRUCTOR = VM_OC_NONE, /**< push class constructor */ + VM_OC_SET_CLASS_CONSTRUCTOR = VM_OC_NONE, /**< set class constructor to the given function literal */ + VM_OC_PUSH_IMPL_CONSTRUCTOR = VM_OC_NONE, /**< create implicit class constructor */ + VM_OC_CLASS_EXPR_CONTEXT_END = VM_OC_NONE, /**< class expression heritage context end */ + VM_OC_CLASS_EVAL = VM_OC_NONE, /**< eval inside a class */ + VM_OC_SUPER_CALL = VM_OC_NONE, /**< call the 'super' constructor */ + VM_OC_SUPER_PROP_REFERENCE = VM_OC_NONE, /**< resolve super property reference */ + VM_OC_PUSH_SUPER = VM_OC_NONE, /**< push resolvable super reference */ + VM_OC_PUSH_CONSTRUCTOR_SUPER = VM_OC_NONE, /**< push 'super' inside a class constructor */ + VM_OC_PUSH_CONSTRUCTOR_THIS = VM_OC_NONE, /**< push 'this' inside a class constructor */ + VM_OC_CONSTRUCTOR_RET = VM_OC_NONE, /**< explicit return from a class constructor */ +#endif /* CONFIG_DISABLE_ES2015 */ VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ } vm_oc_unused_types; @@ -315,6 +345,7 @@ typedef enum { VM_NO_EXEC_OP, /**< do nothing */ VM_EXEC_CALL, /**< invoke a function */ + VM_EXEC_SUPER_CALL, /**< invoke a function through 'super' keyword */ VM_EXEC_CONSTRUCT, /**< construct a new object */ } vm_call_operation; diff --git a/tests/jerry/es2015/class-inheritance-bound.js b/tests/jerry/es2015/class-inheritance-bound.js new file mode 100644 index 0000000000..0d90a93e07 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-bound.js @@ -0,0 +1,28 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var g = Array.bind (0, 1, 2, 3) +g.prototype = Array.prototype; + +class C extends g {} + +class D extends C { + constructor () { + super (4, 5); + } +} + +var d = new D; +assert (Object.getPrototypeOf (d) == D.prototype); diff --git a/tests/jerry/es2015/class-inheritance-builtin-array.js b/tests/jerry/es2015/class-inheritance-builtin-array.js new file mode 100644 index 0000000000..2aac4735c6 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-builtin-array.js @@ -0,0 +1,116 @@ +/* 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 isInstanceofArray (instance) { + assert (instance instanceof C); + assert (instance instanceof B); + assert (instance instanceof A); + assert (instance instanceof Array); + } + + class A extends Array { + f () { + return 5; + } + } + + class B extends A { + g () { + return eval ("eval ('super.f ()')"); + } + } + + class C extends B { + h () { + return eval ('super.g ()'); + } + } + + var c = new C (1, 2, 3, 4, 5, 6); + + isInstanceofArray (c); + c.push (7); + assert (c.length === 7); + assert (c.f () === 5); + assert (c.g () === 5); + assert (c.h () === 5); + + // Test built-in Array prototype methods + var mapped = c.map ((x) => x * 2); + isInstanceofArray (mapped); + + for (var i = 0; i < mapped.length; i++) { + assert (mapped[i] == c[i] * 2); + } + + var concated = c.concat (c); + isInstanceofArray (concated); + + for (var i = 0; i < concated.length; i++) { + assert (concated[i] == c[i % (concated.length / 2)]); + } + + var sliced = c.slice (c); + isInstanceofArray (sliced); + + for (var i = 0; i < sliced.length; i++) { + assert (sliced[i] == c[i]); + } + + var filtered = c.filter ((x) => x > 100); + isInstanceofArray (sliced); + assert (filtered.length === 0); + + var spliced = c.splice (c.length - 1); + isInstanceofArray (spliced); + assert (spliced.length === 1); + assert (spliced[0] === 7); + + c.constructor = 5; + + try { + mapped = c.map ((x) => x * 2); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + concated = c.concat (c); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + sliced = c.slice (c); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + filtered = c.filter ((x) => x > 100); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + spliced = c.splice (0); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } diff --git a/tests/jerry/es2015/class-inheritance-builtin-typedarray.js b/tests/jerry/es2015/class-inheritance-builtin-typedarray.js new file mode 100644 index 0000000000..9d6088b7fb --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-builtin-typedarray.js @@ -0,0 +1,74 @@ +/* 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 isInstanceofTypedArray (instance) { + assert (instance instanceof C); + assert (instance instanceof B); + assert (instance instanceof A); + assert (instance instanceof Uint8Array); + } + + class A extends Uint8Array { + f () { + return 5; + } + } + + class B extends A { + g () { + return super.f (); + } + } + + class C extends B { + h () { + return super.g (); + } + } + + var c = new C ([1, 2, 3, 4, 5, 6]); + + isInstanceofTypedArray (c); + assert (c.length === 6); + assert (c.f () === 5) + assert (c.g () === 5) + assert (c.h () === 5) + + var mapped = c.map ((x) => x * 2); + isInstanceofTypedArray (mapped); + + for (var i = 0; i < mapped.length; i++) { + assert (mapped[i] == c[i] * 2); + } + + var filtered = c.filter ((x) => x > 100); + isInstanceofTypedArray (filtered); + assert (filtered.length === 0); + + c.constructor = 5; + + try { + mapped = c.map ((x) => x * 2); + assert (false); + } catch (e) { + assert (e instanceof TypeError) + } + + try { + filtered = c.filter ((x) => x > 100); + assert (false); + } catch (e) { + assert (e instanceof TypeError) + } diff --git a/tests/jerry/es2015/class-inheritance-core-1.js b/tests/jerry/es2015/class-inheritance-core-1.js new file mode 100644 index 0000000000..ac8d14840a --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-1.js @@ -0,0 +1,116 @@ +/* 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. + */ + +class Animal { + constructor (name) { + this.name = name; + } + + hello () { + return "Hello I am " + this.name; + } + + static speak () { + return "Animals roar."; + } + + static explain () { + return "I can walk,"; + } + + whoAmI () { + return "I am an Animal."; + } + + breath () { + return "I am breathing."; + } + + get myName () { + return this.name; + } + + set rename (name) { + this.name = name; + } +} + +class Dog extends Animal { + constructor (name, barks) { + super (name); + this.barks = barks; + } + + hello () { + return super.hello () + " and I can " + (this.barks ? "bark" : "not bark"); + } + + whoAmI () { + return "I am a Dog."; + } + + static speak () { + return "Dogs bark."; + } + + static explain () { + return super.explain () + " jump,"; + } + + bark () { + return this.barks ? "Woof" : "----"; + } +} + +class Doge extends Dog { + constructor (name, barks, awesomeness) { + super (name, barks); + this.awesomeness = awesomeness; + } + + hello () { + return super.hello () + " and I'm " + (this.awesomeness > 9000 ? "super awesome" : "awesome") + "."; + } + + whoAmI ( ) { + return "I am a Doge."; + } + + static speak () { + return "Doges wow."; + } + + static explain () { + return super.explain () + " dance."; + } +} + +var doge = new Doge ("doggoe", true, 10000); +assert (doge.name === "doggoe"); +doge.rename = "doggo"; +assert (doge.myName === "doggo"); +assert (doge.barks === true); +assert (doge.awesomeness === 10000); +assert (doge.hello () === "Hello I am doggo and I can bark and I'm super awesome."); +assert (doge.whoAmI () === "I am a Doge."); +assert (doge.breath () === "I am breathing."); +assert (doge.bark () === "Woof"); +assert (Doge.speak () === "Doges wow."); +assert (Doge.explain () === "I can walk, jump, dance."); +assert (doge instanceof Animal); +assert (doge instanceof Dog); +assert (doge instanceof Doge); +assert (Dog.prototype.constructor === Dog) +assert (Doge.prototype.constructor === Doge) diff --git a/tests/jerry/es2015/class-inheritance-core-10.js b/tests/jerry/es2015/class-inheritance-core-10.js new file mode 100644 index 0000000000..957521ecd7 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-10.js @@ -0,0 +1,29 @@ +/* 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. + */ + + class A extends Array { + constructor () { + return null; + } + } + + class B extends A { } + + try { + new B; + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } diff --git a/tests/jerry/es2015/class-inheritance-core-11.js b/tests/jerry/es2015/class-inheritance-core-11.js new file mode 100644 index 0000000000..31c9e9fbf7 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-11.js @@ -0,0 +1,30 @@ +/* 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. + */ + + class A extends Array { + constructor () { + assert (false); + return null; + } + } + + class B extends A { + constructor () { + return { o : 10 }; + } + } + + var b = new B; + assert (b.o === 10); diff --git a/tests/jerry/es2015/class-inheritance-core-12.js b/tests/jerry/es2015/class-inheritance-core-12.js new file mode 100644 index 0000000000..e35db541eb --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-12.js @@ -0,0 +1,50 @@ +/* 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. + */ + + class A extends Array { + constructor (a, b, c) { + eval ("eval ('super (a, b, c)')"); + eval ("eval ('this.f = 5;')"); + assert (this.h ()()() === 5); + + return { o : 4 }; + } + + g () { + return function () { + return 5; + } + } + } + + class B extends A { + constructor (a, b, c) { + eval ("eval ('super (a, b, c)')"); + assert (this.f === undefined) + assert (this.o === 4) + this.k = 5; + return { o : 7 }; + } + + h () { + return super["g"]; + } + } + + var b = new B (1, 2, 3, 4); + assert (b.k === undefined); + assert (b.o === 7); + assert (b.h === undefined); + assert (b.g === undefined); diff --git a/tests/jerry/es2015/class-inheritance-core-13.js b/tests/jerry/es2015/class-inheritance-core-13.js new file mode 100644 index 0000000000..e19f2861fe --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-13.js @@ -0,0 +1,32 @@ +/* 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. + */ + + class C extends Array { + constructor (a, b) { + var a = eval ('super (arguments);'); + } + } + + class D extends C { + constructor () { + var a = eval ("eval ('super (1, 2);')"); + return + } + } + + var d = new D; + assert (JSON.stringify (d) === '[{"0":1,"1":2}]'); + assert (d + "" === "[object Arguments]"); + assert (d.length === 1); diff --git a/tests/jerry/es2015/class-inheritance-core-14.js b/tests/jerry/es2015/class-inheritance-core-14.js new file mode 100644 index 0000000000..6b8435c0aa --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-14.js @@ -0,0 +1,53 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + constructor () { + this.a = 5; + } + + f () { + return 10; + } + + super () { + this.super = 10; + return 15; + } +} + +class B extends A { + constructor () { + super (); + assert (super.f === A.prototype.f); + super.f = 8; + assert (this.f === 8); + assert (super.f === A.prototype.f); + + assert (this.a === 5); + super.a = 10; + assert (this.a === 10); + + assert (super.super () === 15); + assert (this.super === 10); + super.super = 20; + assert (this.super === 20); + assert (super.super () === 15); + } +} + +var b = new B; +assert (b.f === 8); +assert (b.a === 10); diff --git a/tests/jerry/es2015/class-inheritance-core-15.js b/tests/jerry/es2015/class-inheritance-core-15.js new file mode 100644 index 0000000000..859e527d6b --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-15.js @@ -0,0 +1,27 @@ +/* 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. + */ + + class A extends Array { + constructor () { + super (); + return; + } + }; + + var a = new A; + assert (a.length === 0); + assert (a instanceof Array); + assert (a instanceof A); + assert (JSON.stringify (a) === "[]"); diff --git a/tests/jerry/es2015/class-inheritance-core-2.js b/tests/jerry/es2015/class-inheritance-core-2.js new file mode 100644 index 0000000000..508d675367 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-2.js @@ -0,0 +1,38 @@ +/* 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. + */ + + class C { + static a () { + return 5; + } + } + + class D extends C { + constructor () { + super (); + } + } + + assert (D.a () === 5); + + C.a = function () { + return 6; + } + + assert (D.a () === 6); + + C = 5; + + assert (D.a () === 6); diff --git a/tests/jerry/es2015/class-inheritance-core-3.js b/tests/jerry/es2015/class-inheritance-core-3.js new file mode 100644 index 0000000000..e1748c3c13 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-3.js @@ -0,0 +1,27 @@ +/* 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. + */ + +class A extends Array { + constructor (a, b, c) { + super (a, b); + this.f = 5; + } +} + +class B extends A { } + +var b = new B (1, 2, 3, 4); +assert (b.f === 5); +assert (b.length === 2); diff --git a/tests/jerry/es2015/class-inheritance-core-4.js b/tests/jerry/es2015/class-inheritance-core-4.js new file mode 100644 index 0000000000..4a008761d8 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-4.js @@ -0,0 +1,34 @@ +/* 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. + */ + + class A extends Array { + constructor (a, b, c) { + super (a, b); + this.f = 5; + } + } + + class B extends A { + constructor (a, b, c) { + super (a, b); + this.g = super.f; + this.h = this.f; + } + } + + var b = new B (1, 2, 3, 4); + assert (b.g === undefined); + assert (b.h === 5); + assert (b.length === 2); diff --git a/tests/jerry/es2015/class-inheritance-core-5.js b/tests/jerry/es2015/class-inheritance-core-5.js new file mode 100644 index 0000000000..cd47b6fa78 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-5.js @@ -0,0 +1,27 @@ +/* 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. + */ + + class A extends Array { + constructor (a, b, c) { + eval ("eval ('super (a, b, c)')"); + eval ("eval ('this.f = 5;')"); + } + } + + class B extends A { } + + var b = new B (1, 2, 3, 4); + assert (b.f === 5); + assert (b.length === 3); diff --git a/tests/jerry/es2015/class-inheritance-core-6.js b/tests/jerry/es2015/class-inheritance-core-6.js new file mode 100644 index 0000000000..30a0f252f4 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-6.js @@ -0,0 +1,37 @@ +/* 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. + */ + + class A extends Array { + constructor (a, b, c, d, e) { + eval ("eval ('super (a, b, c)')"); + this.a = 6; + return new String ("foo"); + } + + f () { + return 5; + } + } + + class B extends A { + constructor (a, b, c, d) { + eval ("eval ('super (a, b, c, d)')"); + assert (super.f () === 5); + } + } + + var a = new B (1, 2, 3, 4, 5, 6); + assert (a.a === undefined); + assert (a[0] + a[1] + a[2] === "foo"); diff --git a/tests/jerry/es2015/class-inheritance-core-7.js b/tests/jerry/es2015/class-inheritance-core-7.js new file mode 100644 index 0000000000..b06188b4b4 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-7.js @@ -0,0 +1,32 @@ +/* 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. + */ + + class C extends Array { + constructor () { + var a = eval ('super (1, 2); 5'); + assert (a === 5); + } + } + + class D extends C { + constructor () { + var a = eval ("eval ('super (1, 2); 3')"); + assert (a === 3); + } + } + + var d = new D; + + assert (JSON.stringify (d) === "[1,2]"); diff --git a/tests/jerry/es2015/class-inheritance-core-8.js b/tests/jerry/es2015/class-inheritance-core-8.js new file mode 100644 index 0000000000..25b54cb1f9 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-8.js @@ -0,0 +1,85 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + var A = Array; + var order = 0; + + function f () { + order++; + + return function () { + return A; + } + } + + var B = class extends f ()() { + constructor () { + assert (++order === 2); + eval ("eval ('super (1, 2, 3, 4)')"); + try { + super (1, 2, 3, 4, 5) + assert (false); + } catch (e) { + assert (e instanceof ReferenceError) + } + + assert (this.g ()()()() === 10); + assert (eval ("eval ('this.g ()')()")()() === 10); + assert (eval ("eval ('this.g ()')")()()() === 10); + assert (eval ("eval ('this.g ()()()')")() === 10); + assert (eval ("eval ('this.g')")()()()() === 10); + this.push (5); + assert (this.length === 5) + eval ('this.push (6)'); + assert (this.length === 6); + eval ("eval ('this.push (7)')"); + this.j = 6; + return; + } + } + + var C = class extends B { + g () { + return function () { + return () => { + return 10; + } + } + } + } + + var D = class D extends C { + constructor () { + super (); + this.k = 5; + return + } + + g () { + return eval ('super["g"]'); + } + } + + assert (order === 1); + + var d = new D; + assert (d.length === 7); + assert (d.k === 5); + assert (d.j === 6); + assert (d instanceof D); + assert (d instanceof C); + assert (d instanceof B); + assert (d instanceof f ()()); + assert (JSON.stringify (d) === "[1,2,3,4,5,6,7]"); diff --git a/tests/jerry/es2015/class-inheritance-core-9.js b/tests/jerry/es2015/class-inheritance-core-9.js new file mode 100644 index 0000000000..53f97a279d --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-core-9.js @@ -0,0 +1,29 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + var order = 0; + + try { + var A = class extends null { + constructor () { + order++; + } + } + + new A; + } catch (e) { + assert (order === 1); + assert (e instanceof ReferenceError); + } diff --git a/tests/jerry/es2015/class-inheritance-early-semantics.js b/tests/jerry/es2015/class-inheritance-early-semantics.js new file mode 100644 index 0000000000..68b8622b89 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-early-semantics.js @@ -0,0 +1,133 @@ +/* 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 must_throw (str) { + try { + eval ("switch (1) { default: " + str + "}"); + assert (false); + } catch (e) { } + + try { + eval (str); + assert (false); + } + catch (e) { } + + try { + eval ("'use strict'; switch (1) { default: " + str + "}"); + assert (false); + } catch (e) { } + + try { + eval ("'use strict'; " + str); + assert (false); + } catch (e) { } +} + +class A { + constructor (a) { + this.a = a; + } + + f () { + return 5; + } +} + +must_throw ("class B extends 5 + 6 + 5 { constructor (a, b) { super (a) } }"); + +must_throw ("class B extends null { constructor () { super () } }; new B"); + +must_throw ("var o = { a : 5 }; \ + class B extends Object.keys (o)[0] { constructor (a, b) { super (a) } } \ + var b = new B (1, 2);"); + +must_throw ("class B extends A { constructor (a, b) { this.b = b} } \ + var b = new B (1, 2);"); + +must_throw ("class B extends A { constructor (a, b) { super.f () } } \ + var b = new B (1, 2);"); + +must_throw ("class B extends A { constructor (a, b) { eval ('this.b = b') } } \ + var b = new B (1, 2);"); + +must_throw ("class B extends A { constructor (a, b) { eval ('super.f ()') } } \ + var b = new B (1, 2);"); + +must_throw ("class B extends A { constructor (a, b) { super (a); super (a); } } \ + var b = new B (1, 2);"); + +must_throw ("class B extends A { constructor (a, b) { eval ('super (a)'); eval ('super (a)'); } } \ + var b = new B (1, 2);"); + +must_throw ("class B extends A { constructor (a, b) { super (a) } g () { super (a) } } \ + var b = new B (1, 2);"); + +must_throw ("class B extends A { constructor (a, b) { super (a) } g () { eval ('super (a)') } } \ + var b = new B (1, 2); \ + b.g ();"); + +must_throw ("class B extends A { constructor (a, b) { super (a) } g () { return function () { return super.f () } } } \ + var b = new B (1, 2); \ + b.g ()();"); + +must_throw ("class B extends A { constructor (a, b) { super (a) } \ + g () { return function () { return eval ('super.f ()') } } } \ + var b = new B (1, 2); \ + b.g ()();"); + +must_throw ("class B extends A { constructor (a, b) { super (a) } \ + g () { return function () { return eval (\"eval ('super.f ();')\") } } } \ + var b = new B (1, 2); \ + b.g ()();"); + +must_throw ("class A extends Array { constructor () { return 5; } }; new A"); + +must_throw ("class A extends Array { constructor () { return undefined; } }; new A"); + +must_throw ("class B extends undefined { }; new B;"); + +must_throw ("var A = class extends Array { . }"); + +must_throw ("class Array extends Array { }"); + +must_throw ("class A extends A { }"); + +must_throw ("class A extends { constructor () { super () } }"); + +class B extends A { + constructor (a, b) { + super (a); + assert (super.f () === 5); + } + + g () { + return () => { + return super.f (); + } + } + + h () { + return () => { + return () => { + return eval ('super.f ()'); + } + } + } +} + +var b = new B (1, 2); +assert (b.g ()() === 5); +assert (b.h ()()() === 5); diff --git a/tests/jerry/es2015/class-inheritance-mixins-1.js b/tests/jerry/es2015/class-inheritance-mixins-1.js new file mode 100644 index 0000000000..2f75bd4329 --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-mixins-1.js @@ -0,0 +1,38 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var calculatorMixin = Base => class extends Base { + f () { + return 1; + } +}; + +var randomizerMixin = Base => class extends Base { + g () { + return 2; + } +}; + +class A { + constructor () { } +} + +class B extends calculatorMixin (randomizerMixin (A)) { + +} + +var b = new B (); +assert (b.f () === 1) +assert (b.g () === 2); diff --git a/tests/jerry/es2015/class-inheritance-mixins-2.js b/tests/jerry/es2015/class-inheritance-mixins-2.js new file mode 100644 index 0000000000..ae9a68f06e --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-mixins-2.js @@ -0,0 +1,50 @@ +/* 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. + */ + + order = 0; + + var Mixin1 = (superclass) => class extends superclass { + foo () { + assert (order++ == 1) + if (super.foo) { + super.foo (); + } + } + }; + + var Mixin2 = (superclass) => class extends superclass { + foo () { + assert (order++ == 2) + if (super.foo) { + assert (super.foo () === 5); + } + } + }; + + class S { + foo () { + assert (order++ == 3) + return 5; + } + } + + class C extends Mixin1 (Mixin2 (S)) { + foo () { + assert (order++ == 0) + super.foo (); + } + } + + new C ().foo () diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index fe38e2f214..aa9f85fe0b 100644 --- a/tests/unit-core/test-snapshot.c +++ b/tests/unit-core/test-snapshot.c @@ -223,7 +223,7 @@ main (void) /* Check the snapshot data. Unused bytes should be filled with zeroes */ const uint8_t expected_data[] = { - 0x4A, 0x52, 0x52, 0x59, 0x12, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,