[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:
gsathya 2017-02-17 14:10:28 -08:00 committed by Commit bot
parent 00a379a03e
commit 18ad0f13af
8 changed files with 976 additions and 2 deletions

View File

@ -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++) {

View File

@ -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

View File

@ -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);

View File

@ -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) \

View File

@ -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) \

View File

@ -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) \

View File

@ -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

View 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();