Implement Promise.prototype.onFinally (#3987)
The algorith is based on ECMA-262 v11, 25.6.5.3 JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "ecma-function-object.h"
|
||||
#include "ecma-gc.h"
|
||||
#include "ecma-helpers.h"
|
||||
#include "ecma-promise-object.h"
|
||||
#include "lit-char-helpers.h"
|
||||
#include "ecma-lex-env.h"
|
||||
#include "ecma-objects.h"
|
||||
@@ -1616,6 +1617,25 @@ ecma_op_external_function_try_to_lazy_instantiate_property (ecma_object_t *objec
|
||||
return ecma_op_lazy_instantiate_prototype_object (object_p);
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH))
|
||||
{
|
||||
ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p;
|
||||
|
||||
if (ext_obj_p->u.external_handler_cb == ecma_promise_then_finally_cb
|
||||
|| ext_obj_p->u.external_handler_cb == ecma_promise_catch_finally_cb)
|
||||
{
|
||||
ecma_property_t *value_prop_p;
|
||||
ecma_property_value_t *value_p = ecma_create_named_data_property (object_p,
|
||||
property_name_p,
|
||||
ECMA_PROPERTY_FLAG_CONFIGURABLE,
|
||||
&value_prop_p);
|
||||
value_p->value = ecma_make_uint32_value (1);
|
||||
return value_prop_p;
|
||||
}
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
return NULL;
|
||||
} /* ecma_op_external_function_try_to_lazy_instantiate_property */
|
||||
|
||||
|
||||
@@ -944,6 +944,242 @@ ecma_promise_then (ecma_value_t promise, /**< the promise which call 'then' */
|
||||
return ret;
|
||||
} /* ecma_promise_then */
|
||||
|
||||
/**
|
||||
* Definition of valueThunk function
|
||||
*
|
||||
* See also:
|
||||
* ES2020 25.6.5.3.1 step 8.
|
||||
*
|
||||
* @return ecma value
|
||||
*/
|
||||
ecma_value_t
|
||||
ecma_value_thunk_helper_cb (const ecma_value_t function_obj, /**< the function itself */
|
||||
const ecma_value_t this_val, /**< this_arg of the function */
|
||||
const ecma_value_t args_p[], /**< argument list */
|
||||
const uint32_t args_count) /**< argument number */
|
||||
{
|
||||
JERRY_UNUSED_3 (this_val, args_p, args_count);
|
||||
|
||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (function_obj);
|
||||
ecma_promise_value_thunk_t *value_thunk_obj_p = (ecma_promise_value_thunk_t *) func_obj_p;
|
||||
|
||||
return ecma_copy_value (value_thunk_obj_p->value);
|
||||
} /* ecma_value_thunk_helper_cb */
|
||||
|
||||
/**
|
||||
* Definition of thrower function
|
||||
*
|
||||
* See also:
|
||||
* ES2020 25.6.5.3.2 step 8.
|
||||
*
|
||||
* @return ecma value
|
||||
*/
|
||||
ecma_value_t
|
||||
ecma_value_thunk_thrower_cb (const ecma_value_t function_obj, /**< the function itself */
|
||||
const ecma_value_t this_val, /**< this_arg of the function */
|
||||
const ecma_value_t args_p[], /**< argument list */
|
||||
const uint32_t args_count) /**< argument number */
|
||||
{
|
||||
JERRY_UNUSED_3 (this_val, args_p, args_count);
|
||||
|
||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (function_obj);
|
||||
ecma_promise_value_thunk_t *value_thunk_obj_p = (ecma_promise_value_thunk_t *) func_obj_p;
|
||||
|
||||
jcontext_raise_exception (ecma_copy_value (value_thunk_obj_p->value));
|
||||
|
||||
return ECMA_VALUE_ERROR;
|
||||
} /* ecma_value_thunk_thrower_cb */
|
||||
|
||||
/**
|
||||
* Helper function for Then Finally and Catch Finally common parts
|
||||
*
|
||||
* See also:
|
||||
* ES2020 25.6.5.3.1
|
||||
* ES2020 25.6.5.3.2
|
||||
*
|
||||
* @return ecma value
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_promise_than_catch_finally_helper (ecma_value_t function_obj, /**< the function itself */
|
||||
ecma_external_handler_t ext_func_obj, /**< external function object */
|
||||
ecma_value_t arg) /**< callback function argument */
|
||||
{
|
||||
/* 2. */
|
||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (function_obj);
|
||||
ecma_promise_finally_function_t *finally_func_obj = (ecma_promise_finally_function_t *) func_obj_p;
|
||||
|
||||
/* 3. */
|
||||
JERRY_ASSERT (ecma_op_is_callable (finally_func_obj->on_finally));
|
||||
|
||||
/* 4. */
|
||||
ecma_value_t result = ecma_op_function_call (ecma_get_object_from_value (finally_func_obj->on_finally),
|
||||
ECMA_VALUE_UNDEFINED,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
/* 6. */
|
||||
JERRY_ASSERT (ecma_is_constructor (finally_func_obj->constructor));
|
||||
|
||||
/* 7. */
|
||||
ecma_value_t promise = ecma_promise_reject_or_resolve (finally_func_obj->constructor, result, true);
|
||||
|
||||
ecma_free_value (result);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (promise))
|
||||
{
|
||||
return promise;
|
||||
}
|
||||
|
||||
/* 8. */
|
||||
ecma_object_t *value_thunk_func_p;
|
||||
value_thunk_func_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE),
|
||||
sizeof (ecma_promise_value_thunk_t),
|
||||
ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION);
|
||||
|
||||
ecma_promise_value_thunk_t *value_thunk_func_obj = (ecma_promise_value_thunk_t *) value_thunk_func_p;
|
||||
value_thunk_func_obj->header.u.external_handler_cb = ext_func_obj;
|
||||
|
||||
value_thunk_func_obj->value = ecma_copy_value_if_not_object (arg);
|
||||
|
||||
/* 9. */
|
||||
ecma_value_t value_thunk = ecma_make_object_value (value_thunk_func_p);
|
||||
ecma_value_t ret_value = ecma_op_invoke_by_magic_id (promise, LIT_MAGIC_STRING_THEN, &value_thunk, 1);
|
||||
|
||||
ecma_free_value (promise);
|
||||
ecma_deref_object (value_thunk_func_p);
|
||||
|
||||
return ret_value;
|
||||
} /* ecma_promise_than_catch_finally_helper */
|
||||
|
||||
/**
|
||||
* Definition of Then Finally Function
|
||||
*
|
||||
* See also:
|
||||
* ES2020 25.6.5.3.1
|
||||
*
|
||||
* @return ecma value
|
||||
*/
|
||||
ecma_value_t
|
||||
ecma_promise_then_finally_cb (const ecma_value_t function_obj, /**< the function itself */
|
||||
const ecma_value_t this_val, /**< this_arg of the function */
|
||||
const ecma_value_t args_p[], /**< argument list */
|
||||
const uint32_t args_count) /**< argument number */
|
||||
{
|
||||
JERRY_UNUSED_2 (this_val, args_count);
|
||||
JERRY_ASSERT (args_count > 0);
|
||||
|
||||
return ecma_promise_than_catch_finally_helper (function_obj, ecma_value_thunk_helper_cb, args_p[0]);
|
||||
} /* ecma_promise_then_finally_cb */
|
||||
|
||||
/**
|
||||
* Definition of Catch Finally Function
|
||||
*
|
||||
* See also:
|
||||
* ES2020 25.6.5.3.2
|
||||
*
|
||||
* @return ecma value
|
||||
*/
|
||||
ecma_value_t
|
||||
ecma_promise_catch_finally_cb (const ecma_value_t function_obj, /**< the function itself */
|
||||
const ecma_value_t this_val, /**< this_arg of the function */
|
||||
const ecma_value_t args_p[], /**< argument list */
|
||||
const uint32_t args_count) /**< argument number */
|
||||
{
|
||||
JERRY_UNUSED_2 (this_val, args_count);
|
||||
JERRY_ASSERT (args_count > 0);
|
||||
|
||||
return ecma_promise_than_catch_finally_helper (function_obj, ecma_value_thunk_thrower_cb, args_p[0]);
|
||||
} /* ecma_promise_catch_finally_cb */
|
||||
|
||||
/**
|
||||
* The common function for ecma_builtin_promise_prototype_finally
|
||||
*
|
||||
* @return ecma value of a new promise object.
|
||||
* Returned value must be freed with ecma_free_value.
|
||||
*/
|
||||
ecma_value_t
|
||||
ecma_promise_finally (ecma_value_t promise, /**< the promise which call 'finally' */
|
||||
ecma_value_t on_finally) /**< on_finally function */
|
||||
{
|
||||
/* 2. */
|
||||
if (!ecma_is_value_object (promise))
|
||||
{
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("'this' is not an object."));
|
||||
}
|
||||
|
||||
ecma_object_t *obj = ecma_get_object_from_value (promise);
|
||||
|
||||
/* 3. */
|
||||
ecma_value_t species = ecma_op_species_constructor (obj, ECMA_BUILTIN_ID_PROMISE);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (species))
|
||||
{
|
||||
return species;
|
||||
}
|
||||
|
||||
/* 4. */
|
||||
JERRY_ASSERT (ecma_is_constructor (species));
|
||||
|
||||
/* 5. */
|
||||
if (!ecma_op_is_callable (on_finally))
|
||||
{
|
||||
ecma_free_value (species);
|
||||
ecma_value_t invoke_args[2] = {on_finally, on_finally};
|
||||
return ecma_op_invoke_by_magic_id (promise, LIT_MAGIC_STRING_THEN, invoke_args, 2);
|
||||
}
|
||||
|
||||
/* 6. a,b */
|
||||
ecma_object_t *then_finally_obj_p;
|
||||
then_finally_obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE),
|
||||
sizeof (ecma_promise_finally_function_t),
|
||||
ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION);
|
||||
|
||||
ecma_promise_finally_function_t *then_finally_func_obj = (ecma_promise_finally_function_t *) then_finally_obj_p;
|
||||
then_finally_func_obj->header.u.external_handler_cb = ecma_promise_then_finally_cb;
|
||||
|
||||
/* 6.c */
|
||||
then_finally_func_obj->constructor = species;
|
||||
|
||||
/* 6.d*/
|
||||
then_finally_func_obj->on_finally = on_finally;
|
||||
|
||||
/* 6. e,f */
|
||||
ecma_object_t *catch_finally_obj_p;
|
||||
catch_finally_obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE),
|
||||
sizeof (ecma_promise_finally_function_t),
|
||||
ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION);
|
||||
|
||||
ecma_promise_finally_function_t *catch_finally_func_obj = (ecma_promise_finally_function_t *) catch_finally_obj_p;
|
||||
catch_finally_func_obj->header.u.external_handler_cb = ecma_promise_catch_finally_cb;
|
||||
|
||||
/* 6.g */
|
||||
catch_finally_func_obj->constructor = species;
|
||||
|
||||
/* 6.h */
|
||||
catch_finally_func_obj->on_finally = on_finally;
|
||||
|
||||
ecma_free_value (species);
|
||||
|
||||
/* 7. */
|
||||
ecma_value_t invoke_args[2] =
|
||||
{
|
||||
ecma_make_object_value (then_finally_obj_p),
|
||||
ecma_make_object_value (catch_finally_obj_p)
|
||||
};
|
||||
|
||||
ecma_value_t ret_value = ecma_op_invoke_by_magic_id (promise, LIT_MAGIC_STRING_THEN, invoke_args, 2);
|
||||
|
||||
ecma_deref_object (then_finally_obj_p);
|
||||
ecma_deref_object (catch_finally_obj_p);
|
||||
|
||||
return ret_value;
|
||||
} /* ecma_promise_finally */
|
||||
|
||||
/**
|
||||
* Resume the execution of an async function after the promise is resolved
|
||||
*/
|
||||
|
||||
@@ -64,6 +64,25 @@ typedef struct
|
||||
ecma_collection_t *reactions; /**< list of promise reactions */
|
||||
} ecma_promise_object_t;
|
||||
|
||||
/**
|
||||
* Description of the finally function object
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ecma_extended_object_t header; /**< extended object part */
|
||||
ecma_value_t constructor; /**< [[Constructor]] internal slot */
|
||||
ecma_value_t on_finally; /**< [[OnFinally]] internal slot */
|
||||
} ecma_promise_finally_function_t;
|
||||
|
||||
/**
|
||||
* Description of the thunk function object
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ecma_extended_object_t header; /**< extended object part */
|
||||
ecma_value_t value; /**< value thunk */
|
||||
} ecma_promise_value_thunk_t;
|
||||
|
||||
/* The Promise reaction is a compressed structure, where each item can
|
||||
* be a sequence of up to three ecma object values as seen below:
|
||||
*
|
||||
@@ -90,6 +109,23 @@ void ecma_fulfill_promise (ecma_value_t promise, ecma_value_t value);
|
||||
ecma_object_t *ecma_promise_new_capability (ecma_value_t constructor);
|
||||
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_value_thunk_helper_cb (const ecma_value_t function_obj,
|
||||
const ecma_value_t this_val,
|
||||
const ecma_value_t args_p[],
|
||||
const uint32_t args_count);
|
||||
ecma_value_t ecma_value_thunk_thrower_cb (const ecma_value_t function_obj,
|
||||
const ecma_value_t this_val,
|
||||
const ecma_value_t args_p[],
|
||||
const uint32_t args_count);
|
||||
ecma_value_t ecma_promise_then_finally_cb (const ecma_value_t function_obj,
|
||||
const ecma_value_t this_val,
|
||||
const ecma_value_t args_p[],
|
||||
const uint32_t args_count);
|
||||
ecma_value_t ecma_promise_catch_finally_cb (const ecma_value_t function_obj,
|
||||
const ecma_value_t this_val,
|
||||
const ecma_value_t args_p[],
|
||||
const uint32_t args_count);
|
||||
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);
|
||||
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_object_t *object_p, ecma_promise_resolving_functions_t *funcs,
|
||||
|
||||
Reference in New Issue
Block a user