mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
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:
parent
c0b8845530
commit
7aa3bfdc6d
@ -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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user