[ESnext] Implement Promise.prototype.finally
Adds five new TF builtins for the spec defined functions/closures. This follows mechanism similar to promise resolving functions approach where we store the closure variables in a custom context. Adds a new --harmony-promise-finally flag. BUG=v8:5967 Review-Url: https://codereview.chromium.org/2695753002 Cr-Commit-Position: refs/heads/master@{#43294}
This commit is contained in:
parent
00a379a03e
commit
18ad0f13af
@ -3619,6 +3619,67 @@ void Genesis::InitializeGlobal_harmony_async_iteration() {
|
||||
factory()->async_iterator_symbol());
|
||||
}
|
||||
|
||||
void Genesis::InitializeGlobal_harmony_promise_finally() {
|
||||
if (!FLAG_harmony_promise_finally) return;
|
||||
|
||||
Handle<JSFunction> constructor(native_context()->promise_function());
|
||||
Handle<JSObject> prototype(JSObject::cast(constructor->instance_prototype()));
|
||||
SimpleInstallFunction(prototype, "finally", Builtins::kPromiseFinally, 1,
|
||||
true, DONT_ENUM);
|
||||
|
||||
// The promise prototype map has changed because we added a property
|
||||
// to prototype, so we update the saved map.
|
||||
Handle<Map> prototype_map(prototype->map());
|
||||
Map::SetShouldBeFastPrototypeMap(prototype_map, true, isolate());
|
||||
native_context()->set_promise_prototype_map(*prototype_map);
|
||||
|
||||
{
|
||||
Handle<Code> code =
|
||||
handle(isolate()->builtins()->builtin(Builtins::kPromiseThenFinally),
|
||||
isolate());
|
||||
Handle<SharedFunctionInfo> info = factory()->NewSharedFunctionInfo(
|
||||
factory()->empty_string(), code, false);
|
||||
info->set_internal_formal_parameter_count(1);
|
||||
info->set_length(1);
|
||||
info->set_native(true);
|
||||
native_context()->set_promise_then_finally_shared_fun(*info);
|
||||
}
|
||||
|
||||
{
|
||||
Handle<Code> code =
|
||||
handle(isolate()->builtins()->builtin(Builtins::kPromiseCatchFinally),
|
||||
isolate());
|
||||
Handle<SharedFunctionInfo> info = factory()->NewSharedFunctionInfo(
|
||||
factory()->empty_string(), code, false);
|
||||
info->set_internal_formal_parameter_count(1);
|
||||
info->set_length(1);
|
||||
info->set_native(true);
|
||||
native_context()->set_promise_catch_finally_shared_fun(*info);
|
||||
}
|
||||
|
||||
{
|
||||
Handle<Code> code = handle(
|
||||
isolate()->builtins()->builtin(Builtins::kPromiseValueThunkFinally),
|
||||
isolate());
|
||||
Handle<SharedFunctionInfo> info = factory()->NewSharedFunctionInfo(
|
||||
factory()->empty_string(), code, false);
|
||||
info->set_internal_formal_parameter_count(0);
|
||||
info->set_length(0);
|
||||
native_context()->set_promise_value_thunk_finally_shared_fun(*info);
|
||||
}
|
||||
|
||||
{
|
||||
Handle<Code> code =
|
||||
handle(isolate()->builtins()->builtin(Builtins::kPromiseThrowerFinally),
|
||||
isolate());
|
||||
Handle<SharedFunctionInfo> info = factory()->NewSharedFunctionInfo(
|
||||
factory()->empty_string(), code, false);
|
||||
info->set_internal_formal_parameter_count(0);
|
||||
info->set_length(0);
|
||||
native_context()->set_promise_thrower_finally_shared_fun(*info);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef V8_I18N_SUPPORT
|
||||
void Genesis::InitializeGlobal_datetime_format_to_parts() {
|
||||
if (!FLAG_datetime_format_to_parts) return;
|
||||
@ -4153,6 +4214,7 @@ bool Genesis::InstallExperimentalNatives() {
|
||||
static const char* harmony_object_rest_spread_natives[] = {nullptr};
|
||||
static const char* harmony_async_iteration_natives[] = {nullptr};
|
||||
static const char* harmony_dynamic_import_natives[] = {nullptr};
|
||||
static const char* harmony_promise_finally_natives[] = {nullptr};
|
||||
|
||||
for (int i = ExperimentalNatives::GetDebuggerCount();
|
||||
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
|
||||
|
@ -438,7 +438,6 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(
|
||||
|
||||
Bind(&if_onresolvenotcallable);
|
||||
{
|
||||
Isolate* isolate = this->isolate();
|
||||
Node* const default_resolve_handler_symbol = HeapConstant(
|
||||
isolate->factory()->promise_default_resolve_handler_symbol());
|
||||
var_on_resolve.Bind(default_resolve_handler_symbol);
|
||||
@ -1563,5 +1562,221 @@ TF_BUILTIN(InternalPromiseReject, PromiseBuiltinsAssembler) {
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
|
||||
Node* PromiseBuiltinsAssembler::CreatePromiseFinallyContext(
|
||||
Node* on_finally, Node* native_context) {
|
||||
Node* const context =
|
||||
CreatePromiseContext(native_context, kOnFinallyContextLength);
|
||||
StoreContextElementNoWriteBarrier(context, kOnFinallySlot, on_finally);
|
||||
return context;
|
||||
}
|
||||
|
||||
std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions(
|
||||
Node* on_finally, Node* native_context) {
|
||||
Node* const promise_context =
|
||||
CreatePromiseFinallyContext(on_finally, native_context);
|
||||
Node* const map = LoadContextElement(
|
||||
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
|
||||
Node* const then_finally_info = LoadContextElement(
|
||||
native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN);
|
||||
Node* const then_finally = AllocateFunctionWithMapAndContext(
|
||||
map, then_finally_info, promise_context);
|
||||
Node* const catch_finally_info = LoadContextElement(
|
||||
native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN);
|
||||
Node* const catch_finally = AllocateFunctionWithMapAndContext(
|
||||
map, catch_finally_info, promise_context);
|
||||
return std::make_pair(then_finally, catch_finally);
|
||||
}
|
||||
|
||||
TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) {
|
||||
Node* const context = Parameter(3);
|
||||
|
||||
Node* const value = LoadContextElement(context, kOnFinallySlot);
|
||||
Return(value);
|
||||
}
|
||||
|
||||
Node* PromiseBuiltinsAssembler::CreateValueThunkFunctionContext(
|
||||
Node* value, Node* native_context) {
|
||||
Node* const context =
|
||||
CreatePromiseContext(native_context, kOnFinallyContextLength);
|
||||
StoreContextElementNoWriteBarrier(context, kOnFinallySlot, value);
|
||||
return context;
|
||||
}
|
||||
|
||||
Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value,
|
||||
Node* native_context) {
|
||||
Node* const value_thunk_context =
|
||||
CreateValueThunkFunctionContext(value, native_context);
|
||||
Node* const map = LoadContextElement(
|
||||
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
|
||||
Node* const value_thunk_info = LoadContextElement(
|
||||
native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN);
|
||||
Node* const value_thunk = AllocateFunctionWithMapAndContext(
|
||||
map, value_thunk_info, value_thunk_context);
|
||||
return value_thunk;
|
||||
}
|
||||
|
||||
TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) {
|
||||
CSA_ASSERT_JS_ARGC_EQ(this, 1);
|
||||
|
||||
Node* const value = Parameter(1);
|
||||
Node* const context = Parameter(4);
|
||||
|
||||
Node* const on_finally = LoadContextElement(context, kOnFinallySlot);
|
||||
|
||||
// 2.a Let result be ? Call(onFinally, undefined).
|
||||
Callable call_callable = CodeFactory::Call(isolate());
|
||||
Node* result =
|
||||
CallJS(call_callable, context, on_finally, UndefinedConstant());
|
||||
|
||||
// 2.b Let promise be ! PromiseResolve( %Promise%, result).
|
||||
Node* const promise = AllocateAndInitJSPromise(context);
|
||||
InternalResolvePromise(context, promise, result);
|
||||
|
||||
// 2.c Let valueThunk be equivalent to a function that returns value.
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
Node* const value_thunk = CreateValueThunkFunction(value, native_context);
|
||||
|
||||
// 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%).
|
||||
Node* const promise_capability = AllocateAndInitJSPromise(context, promise);
|
||||
|
||||
// 2.e Return PerformPromiseThen(promise, valueThunk, undefined,
|
||||
// promiseCapability).
|
||||
InternalPerformPromiseThen(context, promise, value_thunk, UndefinedConstant(),
|
||||
promise_capability, UndefinedConstant(),
|
||||
UndefinedConstant());
|
||||
Return(promise_capability);
|
||||
}
|
||||
|
||||
TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) {
|
||||
Node* const context = Parameter(3);
|
||||
|
||||
Node* const reason = LoadContextElement(context, kOnFinallySlot);
|
||||
CallRuntime(Runtime::kThrow, context, reason);
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
|
||||
Node* PromiseBuiltinsAssembler::CreateThrowerFunctionContext(
|
||||
Node* reason, Node* native_context) {
|
||||
Node* const context =
|
||||
CreatePromiseContext(native_context, kOnFinallyContextLength);
|
||||
StoreContextElementNoWriteBarrier(context, kOnFinallySlot, reason);
|
||||
return context;
|
||||
}
|
||||
|
||||
Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason,
|
||||
Node* native_context) {
|
||||
Node* const thrower_context =
|
||||
CreateThrowerFunctionContext(reason, native_context);
|
||||
Node* const map = LoadContextElement(
|
||||
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
|
||||
Node* const thrower_info = LoadContextElement(
|
||||
native_context, Context::PROMISE_THROWER_FINALLY_SHARED_FUN);
|
||||
Node* const thrower =
|
||||
AllocateFunctionWithMapAndContext(map, thrower_info, thrower_context);
|
||||
return thrower;
|
||||
}
|
||||
|
||||
TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) {
|
||||
CSA_ASSERT_JS_ARGC_EQ(this, 1);
|
||||
|
||||
Node* const reason = Parameter(1);
|
||||
Node* const context = Parameter(4);
|
||||
|
||||
Node* const on_finally = LoadContextElement(context, kOnFinallySlot);
|
||||
|
||||
// 2.a Let result be ? Call(onFinally, undefined).
|
||||
Callable call_callable = CodeFactory::Call(isolate());
|
||||
Node* result =
|
||||
CallJS(call_callable, context, on_finally, UndefinedConstant());
|
||||
|
||||
// 2.b Let promise be ! PromiseResolve( %Promise%, result).
|
||||
Node* const promise = AllocateAndInitJSPromise(context);
|
||||
InternalResolvePromise(context, promise, result);
|
||||
|
||||
// 2.c Let thrower be equivalent to a function that throws reason.
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
Node* const thrower = CreateThrowerFunction(reason, native_context);
|
||||
|
||||
// 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%).
|
||||
Node* const promise_capability = AllocateAndInitJSPromise(context, promise);
|
||||
|
||||
// 2.e Return PerformPromiseThen(promise, thrower, undefined,
|
||||
// promiseCapability).
|
||||
InternalPerformPromiseThen(context, promise, thrower, UndefinedConstant(),
|
||||
promise_capability, UndefinedConstant(),
|
||||
UndefinedConstant());
|
||||
Return(promise_capability);
|
||||
}
|
||||
|
||||
TF_BUILTIN(PromiseFinally, PromiseBuiltinsAssembler) {
|
||||
CSA_ASSERT_JS_ARGC_EQ(this, 1);
|
||||
|
||||
// 1. Let promise be the this value.
|
||||
Node* const promise = Parameter(0);
|
||||
Node* const on_finally = Parameter(1);
|
||||
Node* const context = Parameter(4);
|
||||
|
||||
// 2. If IsPromise(promise) is false, throw a TypeError exception.
|
||||
ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE,
|
||||
"Promise.prototype.finally");
|
||||
|
||||
Variable var_then_finally(this, MachineRepresentation::kTagged),
|
||||
var_catch_finally(this, MachineRepresentation::kTagged);
|
||||
|
||||
Label if_notcallable(this, Label::kDeferred), perform_finally(this);
|
||||
|
||||
// 3. Let thenFinally be ! CreateThenFinally(onFinally).
|
||||
// 4. Let catchFinally be ! CreateCatchFinally(onFinally).
|
||||
GotoIf(TaggedIsSmi(on_finally), &if_notcallable);
|
||||
Node* const on_finally_map = LoadMap(on_finally);
|
||||
GotoIfNot(IsCallableMap(on_finally_map), &if_notcallable);
|
||||
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
Node* then_finally = nullptr;
|
||||
Node* catch_finally = nullptr;
|
||||
std::tie(then_finally, catch_finally) =
|
||||
CreatePromiseFinallyFunctions(on_finally, native_context);
|
||||
var_then_finally.Bind(then_finally);
|
||||
var_catch_finally.Bind(catch_finally);
|
||||
Goto(&perform_finally);
|
||||
|
||||
Bind(&if_notcallable);
|
||||
{
|
||||
var_then_finally.Bind(on_finally);
|
||||
var_catch_finally.Bind(on_finally);
|
||||
Goto(&perform_finally);
|
||||
}
|
||||
|
||||
// 5. Return PerformPromiseThen(promise, valueThunk, undefined,
|
||||
// promiseCapability).
|
||||
Bind(&perform_finally);
|
||||
Label if_nativepromise(this), if_custompromise(this, Label::kDeferred);
|
||||
BranchIfFastPath(context, promise, &if_nativepromise, &if_custompromise);
|
||||
|
||||
Bind(&if_nativepromise);
|
||||
{
|
||||
Node* deferred_promise = AllocateAndInitJSPromise(context, promise);
|
||||
InternalPerformPromiseThen(context, promise, var_then_finally.value(),
|
||||
var_catch_finally.value(), deferred_promise,
|
||||
UndefinedConstant(), UndefinedConstant());
|
||||
Return(deferred_promise);
|
||||
}
|
||||
|
||||
Bind(&if_custompromise);
|
||||
{
|
||||
Isolate* isolate = this->isolate();
|
||||
Node* const then_str = HeapConstant(isolate->factory()->then_string());
|
||||
Callable getproperty_callable = CodeFactory::GetProperty(isolate);
|
||||
Node* const then =
|
||||
CallStub(getproperty_callable, context, promise, then_str);
|
||||
Callable call_callable = CodeFactory::Call(isolate);
|
||||
// 5. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
|
||||
Node* const result =
|
||||
CallJS(call_callable, context, then, promise, var_then_finally.value(),
|
||||
var_catch_finally.value());
|
||||
Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -36,6 +36,18 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
kCapabilitiesContextLength,
|
||||
};
|
||||
|
||||
// This is used by the PromiseThenFinally and PromiseCatchFinally
|
||||
// builtins to store the onFinally in the onFinallySlot.
|
||||
//
|
||||
// This is also used by the PromiseValueThunkFinally to store the
|
||||
// value in the onFinallySlot and PromiseThrowerFinally to store the
|
||||
// reason in the onFinallySlot.
|
||||
enum PromiseFinallyContextSlot {
|
||||
kOnFinallySlot = Context::MIN_CONTEXT_SLOTS,
|
||||
|
||||
kOnFinallyContextLength,
|
||||
};
|
||||
|
||||
explicit PromiseBuiltinsAssembler(CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
// These allocate and initialize a promise with pending state and
|
||||
@ -115,6 +127,15 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
bool debug_event);
|
||||
void InternalPromiseReject(Node* context, Node* promise, Node* value,
|
||||
Node* debug_event);
|
||||
std::pair<Node*, Node*> CreatePromiseFinallyFunctions(Node* on_finally,
|
||||
Node* native_context);
|
||||
Node* CreatePromiseFinallyContext(Node* on_finally, Node* native_context);
|
||||
|
||||
Node* CreateValueThunkFunction(Node* value, Node* native_context);
|
||||
Node* CreateValueThunkFunctionContext(Node* value, Node* native_context);
|
||||
|
||||
Node* CreateThrowerFunctionContext(Node* reason, Node* native_context);
|
||||
Node* CreateThrowerFunction(Node* reason, Node* native_context);
|
||||
|
||||
private:
|
||||
Node* AllocateJSPromise(Node* context);
|
||||
|
@ -666,6 +666,11 @@ class Isolate;
|
||||
TFJ(PromiseResolve, 1) \
|
||||
TFJ(PromiseReject, 1) \
|
||||
TFJ(InternalPromiseReject, 3) \
|
||||
TFJ(PromiseFinally, 1) \
|
||||
TFJ(PromiseThenFinally, 1) \
|
||||
TFJ(PromiseCatchFinally, 1) \
|
||||
TFJ(PromiseValueThunkFinally, 0) \
|
||||
TFJ(PromiseThrowerFinally, 0) \
|
||||
\
|
||||
/* Proxy */ \
|
||||
CPP(ProxyConstructor) \
|
||||
|
@ -290,6 +290,14 @@ enum ContextLookupFlags {
|
||||
V(PROMISE_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
|
||||
promise_resolve_shared_fun) \
|
||||
V(PROMISE_REJECT_SHARED_FUN, SharedFunctionInfo, promise_reject_shared_fun) \
|
||||
V(PROMISE_THEN_FINALLY_SHARED_FUN, SharedFunctionInfo, \
|
||||
promise_then_finally_shared_fun) \
|
||||
V(PROMISE_CATCH_FINALLY_SHARED_FUN, SharedFunctionInfo, \
|
||||
promise_catch_finally_shared_fun) \
|
||||
V(PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN, SharedFunctionInfo, \
|
||||
promise_value_thunk_finally_shared_fun) \
|
||||
V(PROMISE_THROWER_FINALLY_SHARED_FUN, SharedFunctionInfo, \
|
||||
promise_thrower_finally_shared_fun) \
|
||||
V(PROMISE_PROTOTYPE_MAP_INDEX, Map, promise_prototype_map) \
|
||||
V(REGEXP_EXEC_FUNCTION_INDEX, JSFunction, regexp_exec_function) \
|
||||
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
|
||||
|
@ -205,7 +205,8 @@ DEFINE_IMPLICATION(es_staging, move_object_start)
|
||||
V(harmony_function_tostring, "harmony Function.prototype.toString") \
|
||||
V(harmony_class_fields, "harmony public fields in class literals") \
|
||||
V(harmony_async_iteration, "harmony async iteration") \
|
||||
V(harmony_dynamic_import, "harmony dynamic import")
|
||||
V(harmony_dynamic_import, "harmony dynamic import") \
|
||||
V(harmony_promise_finally, "harmony Promise.prototype.finally")
|
||||
|
||||
// Features that are complete (but still behind --harmony/es-staging flag).
|
||||
#define HARMONY_STAGED(V) \
|
||||
|
@ -500,6 +500,7 @@ void JSPromise::JSPromisePrint(std::ostream& os) { // NOLINT
|
||||
os << "\n - fulfill_reactions = " << Brief(fulfill_reactions());
|
||||
os << "\n - reject_reactions = " << Brief(reject_reactions());
|
||||
os << "\n - has_handler = " << has_handler();
|
||||
os << "\n ";
|
||||
}
|
||||
|
||||
void JSRegExp::JSRegExpPrint(std::ostream& os) { // NOLINT
|
||||
|
661
test/mjsunit/harmony/promise-prototype-finally.js
Normal file
661
test/mjsunit/harmony/promise-prototype-finally.js
Normal file
@ -0,0 +1,661 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-promise-finally --allow-natives-syntax
|
||||
|
||||
var asyncAssertsExpected = 0;
|
||||
|
||||
function assertUnreachable() {
|
||||
%AbortJS("Unreachable: failure");
|
||||
}
|
||||
|
||||
function assertAsyncRan() {
|
||||
++asyncAssertsExpected;
|
||||
}
|
||||
|
||||
function assertAsync(b, s) {
|
||||
if (b) {
|
||||
print(s, "succeeded");
|
||||
} else {
|
||||
%AbortJS(s + " FAILED!");
|
||||
}
|
||||
--asyncAssertsExpected;
|
||||
}
|
||||
|
||||
function assertEqualsAsync(b, s) {
|
||||
if (b === s) {
|
||||
print(b, "===", s, "succeeded");
|
||||
} else {
|
||||
%AbortJS(b + "===" + s + " FAILED!");
|
||||
}
|
||||
--asyncAssertsExpected;
|
||||
}
|
||||
|
||||
function assertAsyncDone(iteration) {
|
||||
var iteration = iteration || 0;
|
||||
%EnqueueMicrotask(function() {
|
||||
if (asyncAssertsExpected === 0)
|
||||
assertAsync(true, "all");
|
||||
else if (
|
||||
iteration > 10 // Shouldn't take more.
|
||||
)
|
||||
assertAsync(false, "all... " + asyncAssertsExpected);
|
||||
else
|
||||
assertAsyncDone(iteration + 1);
|
||||
});
|
||||
}
|
||||
|
||||
(function() {
|
||||
assertThrows(
|
||||
function() {
|
||||
Promise.prototype.finally.call(5);
|
||||
},
|
||||
TypeError
|
||||
);
|
||||
})();
|
||||
|
||||
// resolve/finally/then
|
||||
(function() {
|
||||
Promise.resolve(3).finally().then(
|
||||
x => {
|
||||
assertEqualsAsync(3, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally/then
|
||||
(function() {
|
||||
Promise.reject(3).finally().then(assertUnreachable, x => {
|
||||
assertEqualsAsync(3, x);
|
||||
});
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-return-notcallable/then
|
||||
(function() {
|
||||
Promise.resolve(3).finally(2).then(
|
||||
x => {
|
||||
assertEqualsAsync(3, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-return-notcallable/then
|
||||
(function() {
|
||||
Promise.reject(3).finally(2).then(
|
||||
assertUnreachable, e => {
|
||||
assertEqualsAsync(3, e);
|
||||
});
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally/catch
|
||||
(function() {
|
||||
Promise.reject(3).finally().catch(reason => {
|
||||
assertEqualsAsync(3, reason);
|
||||
});
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally/then/catch
|
||||
(function() {
|
||||
Promise.reject(3).finally().then(assertUnreachable).catch(reason => {
|
||||
assertEqualsAsync(3, reason);
|
||||
});
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/then/finally/then
|
||||
(function() {
|
||||
Promise.resolve(3)
|
||||
.then(x => {
|
||||
assertEqualsAsync(3, x);
|
||||
return x;
|
||||
})
|
||||
.finally()
|
||||
.then(
|
||||
x => {
|
||||
assertEqualsAsync(3, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/catch/finally/then
|
||||
(function() {
|
||||
Promise.reject(3)
|
||||
.catch(x => {
|
||||
assertEqualsAsync(3, x);
|
||||
return x;
|
||||
})
|
||||
.finally()
|
||||
.then(
|
||||
x => {
|
||||
assertEqualsAsync(3, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-throw/then
|
||||
(function() {
|
||||
Promise.resolve(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
throw 1;
|
||||
})
|
||||
.then(assertUnreachable, function onRejected(reason) {
|
||||
assertEqualsAsync(1, reason);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-throw/then
|
||||
(function() {
|
||||
Promise.reject(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
throw 1;
|
||||
})
|
||||
.then(assertUnreachable, function onRejected(reason) {
|
||||
assertEqualsAsync(1, reason);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-return/then
|
||||
(function() {
|
||||
Promise.resolve(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return 4;
|
||||
})
|
||||
.then(
|
||||
x => {
|
||||
assertEqualsAsync(x, 3);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-return/then
|
||||
(function() {
|
||||
Promise.reject(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return 4;
|
||||
})
|
||||
.then(assertUnreachable, x => {
|
||||
assertEqualsAsync(x, 3);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/catch-throw/finally-throw/then
|
||||
(function() {
|
||||
Promise.reject(3)
|
||||
.catch(e => {
|
||||
assertEqualsAsync(3, e);
|
||||
throw e;
|
||||
})
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
throw 4;
|
||||
})
|
||||
.then(assertUnreachable, function onRejected(e) {
|
||||
assertEqualsAsync(4, e);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/then-throw/finally-throw/then
|
||||
(function() {
|
||||
Promise.resolve(3)
|
||||
.then(e => {
|
||||
assertEqualsAsync(3, e);
|
||||
throw e;
|
||||
})
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
throw 4;
|
||||
})
|
||||
.then(assertUnreachable, function onRejected(e) {
|
||||
assertEqualsAsync(4, e);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-return-rejected-promise/then
|
||||
(function() {
|
||||
Promise.resolve(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return Promise.reject(4);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(4, e);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-return-rejected-promise/then
|
||||
(function() {
|
||||
Promise.reject(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return Promise.reject(4);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(4, e);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-return-resolved-promise/then
|
||||
(function() {
|
||||
Promise.resolve(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return Promise.resolve(4);
|
||||
})
|
||||
.then(
|
||||
x => {
|
||||
assertEqualsAsync(3, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-return-resolved-promise/then
|
||||
(function() {
|
||||
Promise.reject(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return Promise.resolve(4);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(3, e);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-return-resolved-promise/then
|
||||
(function() {
|
||||
Promise.reject(3)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return Promise.resolve(4);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(3, e);
|
||||
});
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-thenable-resolve/then
|
||||
(function() {
|
||||
var thenable = {
|
||||
then: function(onResolve, onReject) {
|
||||
onResolve(5);
|
||||
}
|
||||
};
|
||||
|
||||
Promise.resolve(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return thenable;
|
||||
})
|
||||
.then(
|
||||
x => {
|
||||
assertEqualsAsync(5, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-thenable-resolve/then
|
||||
(function() {
|
||||
var thenable = {
|
||||
then: function(onResolve, onReject) {
|
||||
onResolve(1);
|
||||
}
|
||||
};
|
||||
|
||||
Promise.reject(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return thenable;
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(5, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-thenable-reject/then
|
||||
(function() {
|
||||
var thenable = {
|
||||
then: function(onResolve, onReject) {
|
||||
onReject(1);
|
||||
}
|
||||
};
|
||||
|
||||
Promise.reject(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return thenable;
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(1, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-thenable-reject/then
|
||||
(function() {
|
||||
var thenable = {
|
||||
then: function(onResolve, onReject) {
|
||||
onReject(1);
|
||||
}
|
||||
};
|
||||
|
||||
Promise.resolve(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return thenable;
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(1, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally/finally/then
|
||||
(function() {
|
||||
Promise.resolve(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.then(
|
||||
x => {
|
||||
assertEqualsAsync(5, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-throw/finally/then
|
||||
(function() {
|
||||
Promise.resolve(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
throw 1;
|
||||
})
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(1, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-return-rejected-promise/finally/then
|
||||
(function() {
|
||||
Promise.resolve(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return Promise.reject(1);
|
||||
})
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(1, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally/finally/then
|
||||
(function() {
|
||||
Promise.reject(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(5, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-throw/finally/then
|
||||
(function() {
|
||||
Promise.reject(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
throw 1;
|
||||
})
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(1, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-return-rejected-promise/finally/then
|
||||
(function() {
|
||||
Promise.reject(5)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return Promise.reject(1);
|
||||
})
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(1, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// resolve/finally-deferred-resolve/then
|
||||
(function() {
|
||||
var resolve, reject;
|
||||
var deferred = new Promise((x, y) => {
|
||||
resolve = x;
|
||||
reject = y;
|
||||
});
|
||||
Promise.resolve(1)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return deferred;
|
||||
})
|
||||
.then(
|
||||
x => {
|
||||
assertEqualsAsync(1, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
|
||||
resolve(5);
|
||||
})();
|
||||
|
||||
// resolve/finally-deferred-reject/then
|
||||
(function() {
|
||||
var resolve, reject;
|
||||
var deferred = new Promise((x, y) => {
|
||||
resolve = x;
|
||||
reject = y;
|
||||
});
|
||||
Promise.resolve(1)
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
return deferred;
|
||||
})
|
||||
.then(assertUnreachable, e => {
|
||||
assertEqualsAsync(5, e);
|
||||
});
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
|
||||
reject(5);
|
||||
})();
|
||||
|
||||
// all/finally/then
|
||||
(function() {
|
||||
var resolve, reject;
|
||||
var deferred = new Promise((x, y) => {
|
||||
resolve = x;
|
||||
reject = y;
|
||||
});
|
||||
|
||||
Promise.all([deferred])
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.then(
|
||||
([x]) => {
|
||||
assertEqualsAsync(1, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
|
||||
resolve(1);
|
||||
})();
|
||||
|
||||
// race/finally/then
|
||||
(function() {
|
||||
var resolve, reject;
|
||||
var d1 = new Promise((x, y) => {
|
||||
resolve = x;
|
||||
reject = y;
|
||||
});
|
||||
var d2 = new Promise((x, y) => {
|
||||
resolve = x;
|
||||
reject = y;
|
||||
});
|
||||
|
||||
Promise.race([d1, d2])
|
||||
.finally(function onFinally() {
|
||||
assertEqualsAsync(0, arguments.length);
|
||||
})
|
||||
.then(
|
||||
x => {
|
||||
assertEqualsAsync(1, x);
|
||||
},
|
||||
assertUnreachable
|
||||
);
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
|
||||
resolve(1);
|
||||
})();
|
||||
|
||||
// resolve/finally-customthen/then
|
||||
(function() {
|
||||
class MyPromise extends Promise {
|
||||
then(onFulfilled, onRejected) {
|
||||
assertEqualsAsync(5, onFulfilled);
|
||||
assertEqualsAsync(5, onRejected);
|
||||
return super.then(onFulfilled, onRejected);
|
||||
}
|
||||
}
|
||||
|
||||
MyPromise.resolve(3).finally(5);
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
// reject/finally-customthen/then
|
||||
(function() {
|
||||
class MyPromise extends Promise {
|
||||
then(onFulfilled, onRejected) {
|
||||
assertEqualsAsync(5, onFulfilled);
|
||||
assertEqualsAsync(5, onRejected);
|
||||
return super.then(onFulfilled, onRejected);
|
||||
}
|
||||
}
|
||||
|
||||
MyPromise.reject(3).finally(5);
|
||||
|
||||
assertAsyncRan();
|
||||
assertAsyncRan();
|
||||
})();
|
||||
|
||||
var descriptor = Object.getOwnPropertyDescriptor(Promise.prototype, 'finally');
|
||||
assertTrue(descriptor.writable);
|
||||
assertTrue(descriptor.configurable);
|
||||
assertFalse(descriptor.enumerable);
|
||||
assertEquals("finally", Promise.prototype.finally.name);
|
||||
assertEquals(1, Promise.prototype.finally.length);
|
||||
|
||||
assertAsyncDone();
|
Loading…
Reference in New Issue
Block a user