From 7aa3bfdc6dec2c662132ca9488766a1796c8d81e Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Wed, 17 Jul 2019 14:10:05 +0200 Subject: [PATCH] Use custom dispatcher for Function.prototype routines (#2967) Binary size gain: - Intel: ~70B (gcc-7.3) - Arm: 50 B (arm-linux-gnueabi-gcc-7.3) Co-authored-by: Gabor Loki loki@inf.u-szeged.hu JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu --- .../ecma-builtin-function-prototype.c | 387 +++++++++--------- .../ecma-builtin-function-prototype.inc.h | 8 +- 2 files changed, 204 insertions(+), 191 deletions(-) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c index e9a78075a..bfca2bcfb 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c @@ -29,6 +29,23 @@ #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" +/** + * This object has a custom dispatch function. + */ +#define BUILTIN_CUSTOM_DISPATCH + +/** + * List of built-in routine identifiers. + */ +enum +{ + ECMA_FUNCTION_PROTOTYPE_ROUTINE_START = ECMA_BUILTIN_ID__COUNT - 1, + ECMA_FUNCTION_PROTOTYPE_TO_STRING, + ECMA_FUNCTION_PROTOTYPE_CALL, + ECMA_FUNCTION_PROTOTYPE_APPLY, + ECMA_FUNCTION_PROTOTYPE_BIND, +}; + #define BUILTIN_INC_HEADER_NAME "ecma-builtin-function-prototype.inc.h" #define BUILTIN_UNDERSCORED_ID function_prototype #include "ecma-builtin-internal-routines-template.inc.h" @@ -58,19 +75,9 @@ * Returned value must be freed with ecma_free_value. */ static ecma_value_t -ecma_builtin_function_prototype_object_to_string (ecma_value_t this_arg) /**< this argument */ +ecma_builtin_function_prototype_object_to_string (void) { - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - if (!ecma_op_is_callable (this_arg)) - { - ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a function.")); - } - else - { - ret_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__FUNCTION_TO_STRING); - } - return ret_value; + return ecma_make_magic_string_value (LIT_MAGIC_STRING__FUNCTION_TO_STRING); } /* ecma_builtin_function_prototype_object_to_string */ /** @@ -83,104 +90,87 @@ ecma_builtin_function_prototype_object_to_string (ecma_value_t this_arg) /**< th * Returned value must be freed with ecma_free_value. */ static ecma_value_t -ecma_builtin_function_prototype_object_apply (ecma_value_t this_arg, /**< this argument */ +ecma_builtin_function_prototype_object_apply (ecma_object_t *func_obj_p, /**< this argument object */ ecma_value_t arg1, /**< first argument */ ecma_value_t arg2) /**< second argument */ { + /* 2. */ + if (ecma_is_value_null (arg2) || ecma_is_value_undefined (arg2)) + { + return ecma_op_function_call (func_obj_p, arg1, NULL, 0); + } + + /* 3. */ + if (!ecma_is_value_object (arg2)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Argument is not an object.")); + } + + ecma_object_t *obj_p = ecma_get_object_from_value (arg2); + + /* 4. */ + ecma_value_t length_value = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_LENGTH); + if (ECMA_IS_VALUE_ERROR (length_value)) + { + return length_value; + } + + ecma_number_t length_number; + ecma_value_t get_result = ecma_get_number (length_value, &length_number); + + ecma_free_value (length_value); + + if (ECMA_IS_VALUE_ERROR (get_result)) + { + return get_result; + } + JERRY_ASSERT (ecma_is_value_empty (get_result)); + + /* 5. */ + const uint32_t length = ecma_number_to_uint32 (length_number); + + if (length >= ECMA_FUNCTION_APPLY_ARGUMENT_COUNT_LIMIT) + { + return ecma_raise_range_error (ECMA_ERR_MSG ("Too many arguments declared for Function.apply().")); + } + + /* 6. */ ecma_value_t ret_value = ECMA_VALUE_EMPTY; + JMEM_DEFINE_LOCAL_ARRAY (arguments_list_p, length, ecma_value_t); + uint32_t index = 0; - /* 1. */ - if (!ecma_op_is_callable (this_arg)) + /* 7. */ + for (index = 0; index < length; index++) { - ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a function.")); + ecma_string_t *curr_idx_str_p = ecma_new_ecma_string_from_uint32 (index); + ecma_value_t get_value = ecma_op_object_get (obj_p, curr_idx_str_p); + ecma_deref_ecma_string (curr_idx_str_p); + + if (ECMA_IS_VALUE_ERROR (get_value)) + { + ret_value = get_value; + break; + } + + arguments_list_p[index] = get_value; } - else + + if (ecma_is_value_empty (ret_value)) { - ecma_object_t *func_obj_p = ecma_get_object_from_value (this_arg); - - /* 2. */ - if (ecma_is_value_null (arg2) || ecma_is_value_undefined (arg2)) - { - ret_value = ecma_op_function_call (func_obj_p, arg1, NULL, 0); - } - else - { - /* 3. */ - if (!ecma_is_value_object (arg2)) - { - ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Argument is not an object.")); - } - else - { - ecma_object_t *obj_p = ecma_get_object_from_value (arg2); - - /* 4. */ - ecma_value_t length_value = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_LENGTH); - if (ECMA_IS_VALUE_ERROR (length_value)) - { - return length_value; - } - - ecma_number_t length_number; - ecma_value_t get_result = ecma_get_number (length_value, &length_number); - - ecma_free_value (length_value); - - if (ECMA_IS_VALUE_ERROR (get_result)) - { - return get_result; - } - JERRY_ASSERT (ecma_is_value_empty (get_result)); - - /* 5. */ - const uint32_t length = ecma_number_to_uint32 (length_number); - - if (length >= ECMA_FUNCTION_APPLY_ARGUMENT_COUNT_LIMIT) - { - ret_value = ecma_raise_range_error (ECMA_ERR_MSG ("Too many arguments declared for Function.apply().")); - } - else - { - /* 6. */ - JMEM_DEFINE_LOCAL_ARRAY (arguments_list_p, length, ecma_value_t); - uint32_t index = 0; - - /* 7. */ - for (index = 0; index < length; index++) - { - ecma_string_t *curr_idx_str_p = ecma_new_ecma_string_from_uint32 (index); - ecma_value_t get_value = ecma_op_object_get (obj_p, curr_idx_str_p); - ecma_deref_ecma_string (curr_idx_str_p); - - if (ECMA_IS_VALUE_ERROR (get_value)) - { - ret_value = get_value; - break; - } - - arguments_list_p[index] = get_value; - } - - if (ecma_is_value_empty (ret_value)) - { - JERRY_ASSERT (index == length); - ret_value = ecma_op_function_call (func_obj_p, - arg1, - arguments_list_p, - length); - } - - for (uint32_t remove_index = 0; remove_index < index; remove_index++) - { - ecma_free_value (arguments_list_p[remove_index]); - } - - JMEM_FINALIZE_LOCAL_ARRAY (arguments_list_p); - } - } - } + JERRY_ASSERT (index == length); + ret_value = ecma_op_function_call (func_obj_p, + arg1, + arguments_list_p, + length); } + for (uint32_t remove_index = 0; remove_index < index; remove_index++) + { + ecma_free_value (arguments_list_p[remove_index]); + } + + JMEM_FINALIZE_LOCAL_ARRAY (arguments_list_p); + return ret_value; } /* ecma_builtin_function_prototype_object_apply */ @@ -194,34 +184,23 @@ ecma_builtin_function_prototype_object_apply (ecma_value_t this_arg, /**< this a * Returned value must be freed with ecma_free_value. */ static ecma_value_t -ecma_builtin_function_prototype_object_call (ecma_value_t this_arg, /**< this argument */ +ecma_builtin_function_prototype_object_call (ecma_object_t *func_obj_p , /**< this argument object */ const ecma_value_t *arguments_list_p, /**< list of arguments */ ecma_length_t arguments_number) /**< number of arguments */ { - if (!ecma_op_is_callable (this_arg)) + if (arguments_number == 0) { - return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a function.")); + /* Even a 'this' argument is missing. */ + return ecma_op_function_call (func_obj_p, + ECMA_VALUE_UNDEFINED, + NULL, + 0); } - else - { - ecma_object_t *func_obj_p = ecma_get_object_from_value (this_arg); - if (arguments_number == 0) - { - /* Even a 'this' argument is missing. */ - return ecma_op_function_call (func_obj_p, - ECMA_VALUE_UNDEFINED, - NULL, - 0); - } - else - { - return ecma_op_function_call (func_obj_p, - arguments_list_p[0], - arguments_list_p + 1, - (ecma_length_t) (arguments_number - 1u)); - } - } + return ecma_op_function_call (func_obj_p, + arguments_list_p[0], + arguments_list_p + 1, + (ecma_length_t) (arguments_number - 1u)); } /* ecma_builtin_function_prototype_object_call */ /** @@ -234,86 +213,72 @@ ecma_builtin_function_prototype_object_call (ecma_value_t this_arg, /**< this ar * Returned value must be freed with ecma_free_value. */ static ecma_value_t -ecma_builtin_function_prototype_object_bind (ecma_value_t this_arg, /**< this argument */ +ecma_builtin_function_prototype_object_bind (ecma_object_t *this_arg_obj_p , /**< this argument object */ const ecma_value_t *arguments_list_p, /**< list of arguments */ ecma_length_t arguments_number) /**< number of arguments */ { - ecma_value_t ret_value = ECMA_VALUE_EMPTY; + /* 4. 11. 18. */ + ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); - /* 2. */ - if (!ecma_op_is_callable (this_arg)) + ecma_object_t *function_p; + ecma_extended_object_t *ext_function_p; + + if (arguments_number == 0 + || (arguments_number == 1 && !ecma_is_value_integer_number (arguments_list_p[0]))) { - ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a function.")); + function_p = ecma_create_object (prototype_obj_p, + sizeof (ecma_extended_object_t), + ECMA_OBJECT_TYPE_BOUND_FUNCTION); + + /* 8. */ + ext_function_p = (ecma_extended_object_t *) function_p; + ECMA_SET_INTERNAL_VALUE_POINTER (ext_function_p->u.bound_function.target_function, + this_arg_obj_p); + + ext_function_p->u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED; + + if (arguments_number != 0) + { + ext_function_p->u.bound_function.args_len_or_this = ecma_copy_value_if_not_object (arguments_list_p[0]); + } } else { - /* 4. 11. 18. */ - ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); + JERRY_ASSERT (arguments_number > 0); - ecma_object_t *function_p; - ecma_extended_object_t *ext_function_p; + size_t obj_size = sizeof (ecma_extended_object_t) + (arguments_number * sizeof (ecma_value_t)); - if (arguments_number == 0 - || (arguments_number == 1 && !ecma_is_value_integer_number (arguments_list_p[0]))) + function_p = ecma_create_object (prototype_obj_p, + obj_size, + ECMA_OBJECT_TYPE_BOUND_FUNCTION); + + /* 8. */ + ext_function_p = (ecma_extended_object_t *) function_p; + ECMA_SET_INTERNAL_VALUE_POINTER (ext_function_p->u.bound_function.target_function, + this_arg_obj_p); + + /* NOTE: This solution provides temporary false data about the object's size + but prevents GC from freeing it until it's not fully initialized. */ + ext_function_p->u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED; + ecma_value_t *args_p = (ecma_value_t *) (ext_function_p + 1); + + for (ecma_length_t i = 0; i < arguments_number; i++) { - function_p = ecma_create_object (prototype_obj_p, - sizeof (ecma_extended_object_t), - ECMA_OBJECT_TYPE_BOUND_FUNCTION); - - /* 8. */ - ext_function_p = (ecma_extended_object_t *) function_p; - ecma_object_t *this_arg_obj_p = ecma_get_object_from_value (this_arg); - ECMA_SET_INTERNAL_VALUE_POINTER (ext_function_p->u.bound_function.target_function, - this_arg_obj_p); - - ext_function_p->u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED; - - if (arguments_number != 0) - { - ext_function_p->u.bound_function.args_len_or_this = ecma_copy_value_if_not_object (arguments_list_p[0]); - } - } - else - { - JERRY_ASSERT (arguments_number > 0); - - size_t obj_size = sizeof (ecma_extended_object_t) + (arguments_number * sizeof (ecma_value_t)); - - function_p = ecma_create_object (prototype_obj_p, - obj_size, - ECMA_OBJECT_TYPE_BOUND_FUNCTION); - - /* 8. */ - ext_function_p = (ecma_extended_object_t *) function_p; - ecma_object_t *this_arg_obj_p = ecma_get_object_from_value (this_arg); - ECMA_SET_INTERNAL_VALUE_POINTER (ext_function_p->u.bound_function.target_function, - this_arg_obj_p); - - /* NOTE: This solution provides temporary false data about the object's size - but prevents GC from freeing it until it's not fully initialized. */ - ext_function_p->u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED; - ecma_value_t *args_p = (ecma_value_t *) (ext_function_p + 1); - - for (ecma_length_t i = 0; i < arguments_number; i++) - { - *args_p++ = ecma_copy_value_if_not_object (arguments_list_p[i]); - } - - ecma_value_t args_len_or_this = ecma_make_integer_value ((ecma_integer_value_t) arguments_number); - ext_function_p->u.bound_function.args_len_or_this = args_len_or_this; + *args_p++ = ecma_copy_value_if_not_object (arguments_list_p[i]); } - /* - * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_FUNCTION type. - * - * See also: ecma_object_get_class_name - */ - - /* 22. */ - ret_value = ecma_make_object_value (function_p); + ecma_value_t args_len_or_this = ecma_make_integer_value ((ecma_integer_value_t) arguments_number); + ext_function_p->u.bound_function.args_len_or_this = args_len_or_this; } - return ret_value; + /* + * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_FUNCTION type. + * + * See also: ecma_object_get_class_name + */ + + /* 22. */ + return ecma_make_object_value (function_p); } /* ecma_builtin_function_prototype_object_bind */ /** @@ -344,6 +309,54 @@ ecma_builtin_function_prototype_dispatch_construct (const ecma_value_t *argument return ecma_raise_type_error (ECMA_ERR_MSG ("'Function.prototype' is not a constructor.")); } /* ecma_builtin_function_prototype_dispatch_construct */ +/** + * Dispatcher of the built-in's routines + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_builtin_function_prototype_dispatch_routine (uint16_t builtin_routine_id, /**< built-in wide routine + * identifier */ + ecma_value_t this_arg, /**< 'this' argument value */ + const ecma_value_t arguments_list_p[], /**< list of arguments + * passed to routine */ + ecma_length_t arguments_number) /**< length of arguments' list */ +{ + if (!ecma_op_is_callable (this_arg)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a function.")); + } + + ecma_object_t *func_obj_p = ecma_get_object_from_value (this_arg); + + switch (builtin_routine_id) + { + case ECMA_FUNCTION_PROTOTYPE_TO_STRING: + { + return ecma_builtin_function_prototype_object_to_string (); + } + case ECMA_FUNCTION_PROTOTYPE_APPLY: + { + return ecma_builtin_function_prototype_object_apply (func_obj_p, + arguments_list_p[0], + arguments_list_p[1]); + } + case ECMA_FUNCTION_PROTOTYPE_CALL: + { + return ecma_builtin_function_prototype_object_call (func_obj_p, arguments_list_p, arguments_number); + } + case ECMA_FUNCTION_PROTOTYPE_BIND: + { + return ecma_builtin_function_prototype_object_bind (func_obj_p, arguments_list_p, arguments_number); + } + default: + { + JERRY_UNREACHABLE (); + } + } +} /* ecma_builtin_function_prototype_dispatch_routine */ + /** * @} * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h index 9d3856793..945a14fc8 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h @@ -37,9 +37,9 @@ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ -ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ecma_builtin_function_prototype_object_to_string, 0, 0) -ROUTINE (LIT_MAGIC_STRING_APPLY, ecma_builtin_function_prototype_object_apply, 2, 2) -ROUTINE (LIT_MAGIC_STRING_CALL, ecma_builtin_function_prototype_object_call, NON_FIXED, 1) -ROUTINE (LIT_MAGIC_STRING_BIND, ecma_builtin_function_prototype_object_bind, NON_FIXED, 1) +ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_FUNCTION_PROTOTYPE_TO_STRING, 0, 0) +ROUTINE (LIT_MAGIC_STRING_APPLY, ECMA_FUNCTION_PROTOTYPE_APPLY, 2, 2) +ROUTINE (LIT_MAGIC_STRING_CALL, ECMA_FUNCTION_PROTOTYPE_CALL, NON_FIXED, 1) +ROUTINE (LIT_MAGIC_STRING_BIND, ECMA_FUNCTION_PROTOTYPE_BIND, NON_FIXED, 1) #include "ecma-builtin-helpers-macro-undefs.inc.h"