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
This commit is contained in:
Robert Fancsik 2019-07-17 14:10:05 +02:00 committed by Dániel Bátyai
parent c0b8845530
commit 7aa3bfdc6d
2 changed files with 204 additions and 191 deletions

View File

@ -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 */
/**
* @}
* @}

View File

@ -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"