Reduce memory consumption of Promise objects (#4607)

No need to keep a reference to resolver functions.
Unused resolvers are cleaned up sooner by GC.

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg
2021-02-24 08:51:23 +01:00
committed by GitHub
parent 1cf2989a64
commit a3bdd36556
5 changed files with 81 additions and 87 deletions
+5 -6
View File
@@ -4285,13 +4285,12 @@ jerry_resolve_or_reject_promise (jerry_value_t promise, /**< the promise value *
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_value_msg_p))); return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_value_msg_p)));
} }
ecma_promise_object_t *promise_p = (ecma_promise_object_t *) ecma_get_object_from_value (promise); if (is_resolve)
ecma_value_t function = is_resolve ? promise_p->resolve : promise_p->reject; {
return ecma_fulfill_promise_with_checks (promise, argument);
}
return ecma_op_function_call (ecma_get_object_from_value (function), return ecma_reject_promise_with_checks (promise, argument);
ECMA_VALUE_UNDEFINED,
&argument,
1);
#else /* !JERRY_BUILTIN_PROMISE */ #else /* !JERRY_BUILTIN_PROMISE */
JERRY_UNUSED (promise); JERRY_UNUSED (promise);
JERRY_UNUSED (argument); JERRY_UNUSED (argument);
-8
View File
@@ -353,14 +353,6 @@ ecma_gc_mark_promise_object (ecma_extended_object_t *ext_object_p) /**< extended
/* Mark all reactions. */ /* Mark all reactions. */
ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) ext_object_p; ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) ext_object_p;
if (!ecma_is_value_empty (promise_object_p->resolve))
{
JERRY_ASSERT (ecma_is_value_object (promise_object_p->resolve)
&& ecma_is_value_object (promise_object_p->reject));
ecma_gc_set_object_visited (ecma_get_object_from_value (promise_object_p->resolve));
ecma_gc_set_object_visited (ecma_get_object_from_value (promise_object_p->reject));
}
ecma_collection_t *collection_p = promise_object_p->reactions; ecma_collection_t *collection_p = promise_object_p->reactions;
if (collection_p != NULL) if (collection_p != NULL)
+7 -20
View File
@@ -420,30 +420,17 @@ static ecma_value_t
ecma_process_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *job_p) /**< the job to be operated */ ecma_process_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *job_p) /**< the job to be operated */
{ {
ecma_promise_object_t *promise_p = (ecma_promise_object_t *) ecma_get_object_from_value (job_p->promise); ecma_promise_object_t *promise_p = (ecma_promise_object_t *) ecma_get_object_from_value (job_p->promise);
ecma_promise_create_resolving_functions (promise_p);
uint16_t new_flags = (uint16_t) (promise_p->header.u.class_prop.extra_info & ~ECMA_PROMISE_ALREADY_RESOLVED); promise_p->header.u.class_prop.extra_info &= (uint16_t) ~ECMA_PROMISE_ALREADY_RESOLVED;
promise_p->header.u.class_prop.extra_info = new_flags;
ecma_value_t argv[] = { promise_p->resolve, promise_p->reject }; ecma_value_t ret = ecma_promise_run_executor ((ecma_object_t *) promise_p, job_p->then, job_p->thenable);
ecma_value_t ret;
ecma_value_t then_call_result = ecma_op_function_call (ecma_get_object_from_value (job_p->then),
job_p->thenable,
argv,
2);
ret = then_call_result; if (ECMA_IS_VALUE_ERROR (ret))
if (ECMA_IS_VALUE_ERROR (then_call_result))
{ {
then_call_result = jcontext_take_exception (); ret = jcontext_take_exception ();
ecma_reject_promise_with_checks (job_p->promise, ret);
ret = ecma_op_function_call (ecma_get_object_from_value (promise_p->reject), ecma_free_value (ret);
ECMA_VALUE_UNDEFINED, ret = ECMA_VALUE_UNDEFINED;
&then_call_result,
1);
ecma_free_value (then_call_result);
} }
ecma_free_promise_resolve_thenable_job (job_p); ecma_free_promise_resolve_thenable_job (job_p);
@@ -309,20 +309,16 @@ ecma_fulfill_promise (ecma_value_t promise, /**< promise */
} /* ecma_fulfill_promise */ } /* ecma_fulfill_promise */
/** /**
* Native handler for Promise Reject Function. * Reject a Promise with a reason. Sanity checks are performed before the reject.
* *
* See also: ES2015 25.4.1.3.1 * See also: ES2015 25.4.1.3.1
* *
* @return ecma value of undefined. * @return ecma value of undefined.
*/ */
ecma_value_t ecma_value_t
ecma_promise_reject_handler (ecma_object_t *function_obj_p, /**< function object */ ecma_reject_promise_with_checks (ecma_value_t promise, /**< promise */
const ecma_value_t args_p[], /**< argument list */ ecma_value_t reason) /**< reason for reject */
const uint32_t args_count) /**< argument number */
{ {
ecma_promise_resolver_t *function_p = (ecma_promise_resolver_t *) function_obj_p;
ecma_value_t promise = function_p->promise;
/* 1. */ /* 1. */
ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise); ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
JERRY_ASSERT (ecma_is_promise (promise_obj_p)); JERRY_ASSERT (ecma_is_promise (promise_obj_p));
@@ -334,28 +330,23 @@ ecma_promise_reject_handler (ecma_object_t *function_obj_p, /**< function object
((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED; ((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
/* 6. */ /* 6. */
ecma_value_t reject_value = (args_count == 0) ? ECMA_VALUE_UNDEFINED : args_p[0]; ecma_reject_promise (promise, reason);
ecma_reject_promise (promise, reject_value);
} }
return ECMA_VALUE_UNDEFINED; return ECMA_VALUE_UNDEFINED;
} /* ecma_promise_reject_handler */ } /* ecma_reject_promise_with_checks */
/** /**
* Native handler for Promise Resolve Function. * Fulfill a Promise with a value. Sanity checks are performed before the resolve.
* *
* See also: ES2015 25.4.1.3.2 * See also: ES2015 25.4.1.3.2
* *
* @return ecma value of undefined. * @return ecma value of undefined.
*/ */
ecma_value_t ecma_value_t
ecma_promise_resolve_handler (ecma_object_t *function_obj_p, /**< function object */ ecma_fulfill_promise_with_checks (ecma_value_t promise, /**< promise */
const ecma_value_t args_p[], /**< argument list */ ecma_value_t value) /**< fulfilled value */
const uint32_t args_count) /**< argument number */
{ {
ecma_promise_resolver_t *function_p = (ecma_promise_resolver_t *) function_obj_p;
ecma_value_t promise = function_p->promise;
/* 1. */ /* 1. */
ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise); ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
JERRY_ASSERT (ecma_is_promise (promise_obj_p)); JERRY_ASSERT (ecma_is_promise (promise_obj_p));
@@ -366,10 +357,42 @@ ecma_promise_resolve_handler (ecma_object_t *function_obj_p, /**< function objec
/* 5. */ /* 5. */
((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED; ((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
ecma_fulfill_promise (promise, (args_count == 0) ? ECMA_VALUE_UNDEFINED : args_p[0]); ecma_fulfill_promise (promise, value);
} }
return ECMA_VALUE_UNDEFINED; return ECMA_VALUE_UNDEFINED;
} /* ecma_fulfill_promise_with_checks */
/**
* Native handler for Promise Reject Function.
*
* @return ecma value of undefined.
*/
ecma_value_t
ecma_promise_reject_handler (ecma_object_t *function_obj_p, /**< function object */
const ecma_value_t args_p[], /**< argument list */
const uint32_t args_count) /**< argument number */
{
ecma_promise_resolver_t *function_p = (ecma_promise_resolver_t *) function_obj_p;
ecma_value_t reject_value = (args_count == 0) ? ECMA_VALUE_UNDEFINED : args_p[0];
return ecma_reject_promise_with_checks (function_p->promise, reject_value);
} /* ecma_promise_reject_handler */
/**
* Native handler for Promise Resolve Function.
*
* @return ecma value of undefined.
*/
ecma_value_t
ecma_promise_resolve_handler (ecma_object_t *function_obj_p, /**< function object */
const ecma_value_t args_p[], /**< argument list */
const uint32_t args_count) /**< argument number */
{
ecma_promise_resolver_t *function_p = (ecma_promise_resolver_t *) function_obj_p;
ecma_value_t fulfilled_value = (args_count == 0) ? ECMA_VALUE_UNDEFINED : args_p[0];
return ecma_fulfill_promise_with_checks (function_p->promise, fulfilled_value);
} /* ecma_promise_resolve_handler */ } /* ecma_promise_resolve_handler */
/** /**
@@ -380,8 +403,8 @@ ecma_promise_resolve_handler (ecma_object_t *function_obj_p, /**< function objec
* @return pointer to the resolving function * @return pointer to the resolving function
*/ */
static ecma_object_t * static ecma_object_t *
ecma_promise_create_resolving_functions_helper (ecma_object_t *promise_p, /**< Promise Object */ ecma_promise_create_resolving_function (ecma_object_t *promise_p, /**< Promise Object */
ecma_native_handler_id_t id) /**< Callback handler */ ecma_native_handler_id_t id) /**< Callback handler */
{ {
ecma_object_t *func_obj_p = ecma_op_create_native_handler (id, sizeof (ecma_promise_resolver_t)); ecma_object_t *func_obj_p = ecma_op_create_native_handler (id, sizeof (ecma_promise_resolver_t));
@@ -389,30 +412,35 @@ ecma_promise_create_resolving_functions_helper (ecma_object_t *promise_p, /**< P
resolver_p->promise = ecma_make_object_value (promise_p); resolver_p->promise = ecma_make_object_value (promise_p);
return func_obj_p; return func_obj_p;
} /* ecma_promise_create_resolving_functions_helper */ } /* ecma_promise_create_resolving_function */
/** /**
* Perform PromiseCreateResolvingFunctions. * Helper function for running an executor.
* *
* See also: ES2015 25.4.1.3 * @return ecma value of the executor callable
* * Returned value must be freed with ecma_free_value
* @return pointer to the resolving functions
*/ */
void ecma_value_t
ecma_promise_create_resolving_functions (ecma_promise_object_t *promise_p) /**< the promise object */ ecma_promise_run_executor (ecma_object_t *promise_p, /**< Promise Object */
ecma_value_t executor, /**< executor function */
ecma_value_t this_value) /**< this value */
{ {
/* 2. - 7. */ ecma_object_t *resolve_func_p, *reject_func_p;
ecma_object_t *resolve_func_p = ecma_promise_create_resolving_functions_helper ((ecma_object_t *) promise_p, resolve_func_p = ecma_promise_create_resolving_function (promise_p,
ECMA_NATIVE_HANDLER_PROMISE_RESOLVE); ECMA_NATIVE_HANDLER_PROMISE_RESOLVE);
ecma_object_t *reject_func_p = ecma_promise_create_resolving_functions_helper ((ecma_object_t *) promise_p, reject_func_p = ecma_promise_create_resolving_function (promise_p,
ECMA_NATIVE_HANDLER_PROMISE_REJECT); ECMA_NATIVE_HANDLER_PROMISE_REJECT);
promise_p->resolve = ecma_make_object_value (resolve_func_p);
promise_p->reject = ecma_make_object_value (reject_func_p);
ecma_value_t argv[] = { ecma_make_object_value (resolve_func_p), ecma_make_object_value (reject_func_p) };
ecma_value_t result = ecma_op_function_call (ecma_get_object_from_value (executor),
this_value,
argv,
2);
ecma_deref_object (resolve_func_p); ecma_deref_object (resolve_func_p);
ecma_deref_object (reject_func_p); ecma_deref_object (reject_func_p);
} /* ecma_promise_create_resolving_functions */
return result;
} /* ecma_promise_run_executor */
/** /**
* Create a promise object. * Create a promise object.
@@ -460,11 +488,6 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
/* 6-8. */ /* 6-8. */
ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) object_p; ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) object_p;
promise_object_p->reactions = reactions; promise_object_p->reactions = reactions;
/* Creating the resolving function may trigger a GC, so these need to be initialized. */
promise_object_p->resolve = ECMA_VALUE_EMPTY;
promise_object_p->reject = ECMA_VALUE_EMPTY;
ecma_promise_create_resolving_functions (promise_object_p);
#if JERRY_PROMISE_CALLBACK #if JERRY_PROMISE_CALLBACK
if (JERRY_UNLIKELY (JERRY_CONTEXT (promise_callback) != NULL)) if (JERRY_UNLIKELY (JERRY_CONTEXT (promise_callback) != NULL))
@@ -483,11 +506,7 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
{ {
JERRY_ASSERT (ecma_op_is_callable (executor)); JERRY_ASSERT (ecma_op_is_callable (executor));
ecma_value_t argv[] = { promise_object_p->resolve, promise_object_p->reject }; completion = ecma_promise_run_executor (object_p, executor, ECMA_VALUE_UNDEFINED);
completion = ecma_op_function_call (ecma_get_object_from_value (executor),
ECMA_VALUE_UNDEFINED,
argv,
2);
} }
ecma_value_t status = ECMA_VALUE_EMPTY; ecma_value_t status = ECMA_VALUE_EMPTY;
@@ -496,10 +515,7 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
{ {
/* 10.a. */ /* 10.a. */
completion = jcontext_take_exception (); completion = jcontext_take_exception ();
status = ecma_op_function_call (ecma_get_object_from_value (promise_object_p->reject), ecma_reject_promise_with_checks (ecma_make_object_value (object_p), completion);
ECMA_VALUE_UNDEFINED,
&completion,
1);
} }
ecma_free_value (completion); ecma_free_value (completion);
@@ -54,8 +54,6 @@ typedef struct
{ {
ecma_extended_object_t header; /**< extended object part */ ecma_extended_object_t header; /**< extended object part */
ecma_collection_t *reactions; /**< list of promise reactions */ ecma_collection_t *reactions; /**< list of promise reactions */
ecma_value_t resolve; /**< resolve function */
ecma_value_t reject; /**< reject function */
} ecma_promise_object_t; } ecma_promise_object_t;
/** /**
@@ -101,6 +99,8 @@ uint16_t ecma_promise_get_flags (ecma_object_t *promise_p);
ecma_value_t ecma_promise_get_result (ecma_object_t *promise_p); ecma_value_t ecma_promise_get_result (ecma_object_t *promise_p);
void ecma_reject_promise (ecma_value_t promise, ecma_value_t reason); void ecma_reject_promise (ecma_value_t promise, ecma_value_t reason);
void ecma_fulfill_promise (ecma_value_t promise, ecma_value_t value); void ecma_fulfill_promise (ecma_value_t promise, ecma_value_t value);
ecma_value_t ecma_reject_promise_with_checks (ecma_value_t promise, ecma_value_t reason);
ecma_value_t ecma_fulfill_promise_with_checks (ecma_value_t promise, ecma_value_t value);
ecma_object_t *ecma_promise_new_capability (ecma_value_t constructor, ecma_value_t parent); ecma_object_t *ecma_promise_new_capability (ecma_value_t constructor, ecma_value_t parent);
ecma_value_t ecma_promise_reject_or_resolve (ecma_value_t this_arg, ecma_value_t value, bool is_resolve); ecma_value_t ecma_promise_reject_or_resolve (ecma_value_t this_arg, ecma_value_t value, bool is_resolve);
ecma_value_t ecma_promise_then (ecma_value_t promise, ecma_value_t on_fulfilled, ecma_value_t on_rejected); ecma_value_t ecma_promise_then (ecma_value_t promise, ecma_value_t on_fulfilled, ecma_value_t on_rejected);
@@ -125,7 +125,7 @@ ecma_value_t ecma_op_get_capabilities_executor_cb (ecma_object_t *function_obj_p
ecma_value_t ecma_promise_finally (ecma_value_t promise, ecma_value_t on_finally); ecma_value_t ecma_promise_finally (ecma_value_t promise, ecma_value_t on_finally);
void ecma_promise_async_then (ecma_value_t promise, ecma_value_t executable_object); void ecma_promise_async_then (ecma_value_t promise, ecma_value_t executable_object);
ecma_value_t ecma_promise_async_await (ecma_extended_object_t *async_generator_object_p, ecma_value_t value); ecma_value_t ecma_promise_async_await (ecma_extended_object_t *async_generator_object_p, ecma_value_t value);
void ecma_promise_create_resolving_functions (ecma_promise_object_t *object_p); ecma_value_t ecma_promise_run_executor (ecma_object_t *promise_p, ecma_value_t executor, ecma_value_t this_value);
uint32_t ecma_promise_remaining_inc_or_dec (ecma_value_t remaining, bool is_inc); uint32_t ecma_promise_remaining_inc_or_dec (ecma_value_t remaining, bool is_inc);