Skip to content

Implement ES2015 class feature (part II.) #2439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jerry-core/ecma/base/ecma-gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
52 changes: 48 additions & 4 deletions jerry-core/ecma/base/ecma-globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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.
*/
Expand Down
23 changes: 17 additions & 6 deletions jerry-core/ecma/base/ecma-helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
Expand Down
3 changes: 2 additions & 1 deletion jerry-core/ecma/base/ecma-helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
69 changes: 66 additions & 3 deletions jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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. */
Expand Down
75 changes: 69 additions & 6 deletions jerry-core/ecma/operations/ecma-array-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
* @{
Expand All @@ -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);
Expand Down Expand Up @@ -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 */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this function be merged into ecma_op_create_array_object? If CONFIG_DISABLE_ES2015_CLASS, the constructor/prototype-specific code path could be guarded out and object_p could be made JERRY_UNUSED. So, compiler/linker could optimise things out for ES5.1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not bother this part of the code. ecma_op_create_array_object is used by a lot of other operations, but only the Array.prototype.{slice, filter, splice, concat, map} methods have this specific behavior. See the corresponding part of the standard here.

{
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
*
Expand Down
6 changes: 6 additions & 0 deletions jerry-core/ecma/operations/ecma-array-object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading