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:
@@ -795,6 +795,23 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->values));
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->remaining_elements));
|
||||
}
|
||||
else if (ext_func_p->u.external_handler_cb == ecma_promise_then_finally_cb
|
||||
|| ext_func_p->u.external_handler_cb == ecma_promise_catch_finally_cb)
|
||||
{
|
||||
ecma_promise_finally_function_t *finally_obj_p = (ecma_promise_finally_function_t *) object_p;
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (finally_obj_p->constructor));
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (finally_obj_p->on_finally));
|
||||
}
|
||||
else if (ext_func_p->u.external_handler_cb == ecma_value_thunk_helper_cb
|
||||
|| ext_func_p->u.external_handler_cb == ecma_value_thunk_thrower_cb)
|
||||
{
|
||||
ecma_promise_value_thunk_t *thunk_obj_p = (ecma_promise_value_thunk_t *) object_p;
|
||||
|
||||
if (ecma_is_value_object (thunk_obj_p->value))
|
||||
{
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (thunk_obj_p->value));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
@@ -1162,6 +1179,20 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
|
||||
{
|
||||
ext_object_size = sizeof (ecma_promise_all_executor_t);
|
||||
}
|
||||
else if (ext_func_p->u.external_handler_cb == ecma_promise_then_finally_cb
|
||||
|| ext_func_p->u.external_handler_cb == ecma_promise_catch_finally_cb)
|
||||
{
|
||||
ext_object_size = sizeof (ecma_promise_finally_function_t);
|
||||
}
|
||||
else if (ext_func_p->u.external_handler_cb == ecma_value_thunk_helper_cb
|
||||
|| ext_func_p->u.external_handler_cb == ecma_value_thunk_thrower_cb)
|
||||
{
|
||||
ecma_promise_value_thunk_t *thunk_obj_p = (ecma_promise_value_thunk_t *) object_p;
|
||||
|
||||
ecma_free_value_if_not_object (thunk_obj_p->value);
|
||||
|
||||
ext_object_size = sizeof (ecma_promise_value_thunk_t);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,22 @@ ecma_builtin_promise_prototype_catch (ecma_value_t this_arg, /**< this argument
|
||||
return ecma_op_invoke_by_magic_id (this_arg, LIT_MAGIC_STRING_THEN, args, 2);
|
||||
} /* ecma_builtin_promise_prototype_catch */
|
||||
|
||||
/**
|
||||
* Promise routine: finally.
|
||||
*
|
||||
* See also:
|
||||
* ECMA-262 v11, 25.6.5.3
|
||||
*
|
||||
* @return ecma value of a new promise object.
|
||||
* Returned value must be freed with ecma_free_value.
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_builtin_promise_prototype_finally (ecma_value_t this_arg, /**< this argument */
|
||||
ecma_value_t on_finally) /**< on_finally function */
|
||||
{
|
||||
return ecma_promise_finally (this_arg, on_finally);
|
||||
} /* ecma_builtin_promise_prototype_finally */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
|
||||
@@ -31,6 +31,7 @@ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG,
|
||||
|
||||
ROUTINE (LIT_MAGIC_STRING_THEN, ecma_builtin_promise_prototype_then, 2, 2)
|
||||
ROUTINE (LIT_MAGIC_STRING_CATCH, ecma_builtin_promise_prototype_catch, 1, 1)
|
||||
ROUTINE (LIT_MAGIC_STRING_FINALLY, ecma_builtin_promise_prototype_finally, 1, 1)
|
||||
|
||||
#endif /* ENABLED (JERRY_BUILTIN_PROMISE) */
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -455,6 +455,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFAULT, "default")
|
||||
|| ENABLED (JERRY_ESNEXT)
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENTRIES, "entries")
|
||||
#endif
|
||||
#if ENABLED (JERRY_BUILTIN_PROMISE)
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FINALLY, "finally")
|
||||
#endif
|
||||
#if ENABLED (JERRY_BUILTIN_ARRAY) \
|
||||
|| ENABLED (JERRY_BUILTIN_MAP) \
|
||||
|| ENABLED (JERRY_BUILTIN_SET) \
|
||||
|
||||
@@ -175,6 +175,7 @@ LIT_MAGIC_STRING_ENTRIES = "entries"
|
||||
LIT_MAGIC_STRING_TO_JSON_UL = "toJSON"
|
||||
LIT_MAGIC_STRING_VALUES = "values"
|
||||
LIT_MAGIC_STRING_BOOLEAN_UL = "Boolean"
|
||||
LIT_MAGIC_STRING_FINALLY = "finally"
|
||||
LIT_MAGIC_STRING_PROMISE_UL = "Promise"
|
||||
LIT_MAGIC_STRING_REFLECT_UL = "Reflect"
|
||||
LIT_MAGIC_STRING_SQRT1_2_U = "SQRT1_2"
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// test basic functionality
|
||||
var order = [];
|
||||
var reason = {};
|
||||
var reject = Promise.reject(reason);
|
||||
reject.then = function() {
|
||||
order.push(1);
|
||||
return Promise.prototype.then.apply(this, arguments);
|
||||
};
|
||||
|
||||
var value = {};
|
||||
var resolve = Promise.resolve(value);
|
||||
resolve.then = function() {
|
||||
order.push(4);
|
||||
return Promise.prototype.then.apply(this, arguments);
|
||||
};
|
||||
|
||||
reject.catch(function(e) {
|
||||
order.push(2);
|
||||
throw e;
|
||||
}).finally(function() {
|
||||
order.push(3);
|
||||
return resolve;
|
||||
}).catch(function(e) {
|
||||
order.push(5);
|
||||
});
|
||||
|
||||
function __checkAsync() {
|
||||
assert(order.length === 5);
|
||||
for (var i = 0; i < order.length; i++)
|
||||
{
|
||||
assert(i + 1 === order[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// check length property
|
||||
var desc = Object.getOwnPropertyDescriptor(Promise.prototype.finally, "length");
|
||||
assert(desc.value === 1);
|
||||
assert(desc.enumerable === false);
|
||||
assert(desc.configurable === true);
|
||||
assert(desc.writable === false);
|
||||
|
||||
// invokes then with function
|
||||
var target = new Promise(function() {});
|
||||
var returnValue = {};
|
||||
var callCount = 0;
|
||||
var thisValue = null;
|
||||
var argCount = null;
|
||||
var firstArg = null;
|
||||
var secondArg = null;
|
||||
|
||||
target.then = function(a, b) {
|
||||
callCount += 1;
|
||||
|
||||
thisValue = this;
|
||||
argCount = arguments.length;
|
||||
firstArg = a;
|
||||
secondArg = b;
|
||||
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
var originalFinallyHandler = function() {};
|
||||
var result = Promise.prototype.finally.call(target, originalFinallyHandler, 2, 3);
|
||||
|
||||
(callCount === 1);
|
||||
assert(thisValue === target);
|
||||
assert(argCount === 2);
|
||||
assert(typeof firstArg === 'function');
|
||||
assert(firstArg.length === 1);
|
||||
assert(typeof secondArg === 'function');
|
||||
assert(secondArg.length === 1);
|
||||
assert(result === returnValue);
|
||||
|
||||
// invokes then with non-function
|
||||
result = Promise.prototype.finally.call(target, 1, 2, 3);
|
||||
|
||||
assert(callCount === 2);
|
||||
assert(thisValue === target);
|
||||
assert(argCount === 2);
|
||||
assert(firstArg === 1);
|
||||
assert(secondArg === 1);
|
||||
assert(result == returnValue);
|
||||
|
||||
// thes when 'then' is not callable
|
||||
var thrower = function() {
|
||||
throw 42;
|
||||
};
|
||||
|
||||
var symbol = Symbol();
|
||||
var p = new Promise(function() {});
|
||||
|
||||
p.then = undefined;
|
||||
try {
|
||||
Promise.prototype.finally.call(p, thrower);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
p.then = null;
|
||||
try {
|
||||
Promise.prototype.finally.call(p, thrower);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
p.then = 1;
|
||||
try {
|
||||
Promise.prototype.finally.call(p, thrower);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
p.then = symbol;
|
||||
try {
|
||||
Promise.prototype.finally.call(p, thrower);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
p.then = {};
|
||||
try {
|
||||
Promise.prototype.finally.call(p, thrower);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
Reference in New Issue
Block a user