Commit be6d1292 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Optimize promise resolution.

This CL introduces new operators JSFulfillPromise and JSPromiseResolve,
corresponding to the specification operations with the same name, and
uses that to lower calls to Promise.resolve() builtin to JSPromiseResolve.

We also optimize JSPromiseResolve and JSResolvePromise further based on
information found about the value/resolution in the graph. This applies
to both Promise.resolve() builtin calls and implicit resolve operations
in async functions and async generators.

On a very simple microbenchmark like

  console.time('resolve');
  for (let i = 0; i < 1e8; ++i) Promise.resolve({i});
  console.timeEnd('resolve');

this CL reduces the execution time from around 3049ms to around 947ms,
which is a pretty significant 3x improvement. On the wikipedia benchmark
we observe an improvement around 2% with this CL.

Bug: v8:7253
Change-Id: Ic69086cdc1b724f35dbe83305795539c562ab817
Reviewed-on: https://chromium-review.googlesource.com/913488Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51387}
parent 1c71991a
...@@ -2336,7 +2336,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -2336,7 +2336,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
SimpleInstallFunction(promise_fun, "race", Builtins::kPromiseRace, 1, true); SimpleInstallFunction(promise_fun, "race", Builtins::kPromiseRace, 1, true);
SimpleInstallFunction(promise_fun, "resolve", SimpleInstallFunction(promise_fun, "resolve",
Builtins::kPromiseResolveWrapper, 1, true); Builtins::kPromiseResolveTrampoline, 1, true);
SimpleInstallFunction(promise_fun, "reject", Builtins::kPromiseReject, 1, SimpleInstallFunction(promise_fun, "reject", Builtins::kPromiseReject, 1,
true); true);
......
...@@ -825,7 +825,8 @@ namespace internal { ...@@ -825,7 +825,8 @@ namespace internal {
/* ES #sec-promiseresolvethenablejob */ \ /* ES #sec-promiseresolvethenablejob */ \
TFS(PromiseResolveThenableJob, kPromiseToResolve, kThenable, kThen) \ TFS(PromiseResolveThenableJob, kPromiseToResolve, kThenable, kThen) \
/* ES #sec-promise.resolve */ \ /* ES #sec-promise.resolve */ \
TFJ(PromiseResolveWrapper, 1, kValue) \ TFJ(PromiseResolveTrampoline, 1, kValue) \
/* ES #sec-promise-resolve */ \
TFS(PromiseResolve, kConstructor, kValue) \ TFS(PromiseResolve, kConstructor, kValue) \
/* ES #sec-promise.reject */ \ /* ES #sec-promise.reject */ \
TFJ(PromiseReject, 1, kReason) \ TFJ(PromiseReject, 1, kReason) \
......
...@@ -1129,7 +1129,7 @@ TF_BUILTIN(PromiseRejectReactionJob, PromiseBuiltinsAssembler) { ...@@ -1129,7 +1129,7 @@ TF_BUILTIN(PromiseRejectReactionJob, PromiseBuiltinsAssembler) {
PromiseReaction::kReject); PromiseReaction::kReject);
} }
TF_BUILTIN(PromiseResolveWrapper, PromiseBuiltinsAssembler) { TF_BUILTIN(PromiseResolveTrampoline, PromiseBuiltinsAssembler) {
// 1. Let C be the this value. // 1. Let C be the this value.
Node* receiver = Parameter(Descriptor::kReceiver); Node* receiver = Parameter(Descriptor::kReceiver);
Node* value = Parameter(Descriptor::kValue); Node* value = Parameter(Descriptor::kValue);
......
...@@ -537,6 +537,18 @@ bool AccessInfoFactory::ComputePropertyAccessInfo( ...@@ -537,6 +537,18 @@ bool AccessInfoFactory::ComputePropertyAccessInfo(
return false; return false;
} }
bool AccessInfoFactory::ComputePropertyAccessInfo(
MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
PropertyAccessInfo* access_info) {
ZoneVector<PropertyAccessInfo> access_infos(zone());
if (ComputePropertyAccessInfos(maps, name, access_mode, &access_infos) &&
access_infos.size() == 1) {
*access_info = access_infos.front();
return true;
}
return false;
}
bool AccessInfoFactory::ComputePropertyAccessInfos( bool AccessInfoFactory::ComputePropertyAccessInfos(
MapHandles const& maps, Handle<Name> name, AccessMode access_mode, MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
ZoneVector<PropertyAccessInfo>* access_infos) { ZoneVector<PropertyAccessInfo>* access_infos) {
......
...@@ -149,6 +149,9 @@ class AccessInfoFactory final { ...@@ -149,6 +149,9 @@ class AccessInfoFactory final {
bool ComputePropertyAccessInfo(Handle<Map> map, Handle<Name> name, bool ComputePropertyAccessInfo(Handle<Map> map, Handle<Name> name,
AccessMode access_mode, AccessMode access_mode,
PropertyAccessInfo* access_info); PropertyAccessInfo* access_info);
bool ComputePropertyAccessInfo(MapHandles const& maps, Handle<Name> name,
AccessMode access_mode,
PropertyAccessInfo* access_info);
bool ComputePropertyAccessInfos(MapHandles const& maps, Handle<Name> name, bool ComputePropertyAccessInfos(MapHandles const& maps, Handle<Name> name,
AccessMode access_mode, AccessMode access_mode,
ZoneVector<PropertyAccessInfo>* access_infos); ZoneVector<PropertyAccessInfo>* access_infos);
......
...@@ -2997,6 +2997,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -2997,6 +2997,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReducePromisePrototypeFinally(node); return ReducePromisePrototypeFinally(node);
case Builtins::kPromisePrototypeThen: case Builtins::kPromisePrototypeThen:
return ReducePromisePrototypeThen(node); return ReducePromisePrototypeThen(node);
case Builtins::kPromiseResolveTrampoline:
return ReducePromiseResolveTrampoline(node);
default: default:
break; break;
} }
...@@ -4427,6 +4429,44 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { ...@@ -4427,6 +4429,44 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
return Replace(result); return Replace(result);
} }
// ES section #sec-promise.resolve
Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* value = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Check if we know something about {receiver} already.
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult infer_receiver_maps_result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
if (infer_receiver_maps_result == NodeProperties::kNoReceiverMaps) {
return NoChange();
}
DCHECK_NE(0, receiver_maps.size());
// Only reduce when all {receiver_maps} are JSReceiver maps.
for (Handle<Map> receiver_map : receiver_maps) {
if (!receiver_map->IsJSReceiverMap()) return NoChange();
}
// Morph the {node} into a JSPromiseResolve operation.
node->ReplaceInput(0, receiver);
node->ReplaceInput(1, value);
node->ReplaceInput(2, context);
node->ReplaceInput(3, frame_state);
node->ReplaceInput(4, effect);
node->ReplaceInput(5, control);
node->TrimInputCount(6);
NodeProperties::ChangeOp(node, javascript()->PromiseResolve());
return Changed(node);
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
......
...@@ -109,6 +109,7 @@ class JSCallReducer final : public AdvancedReducer { ...@@ -109,6 +109,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReducePromisePrototypeCatch(Node* node); Reduction ReducePromisePrototypeCatch(Node* node);
Reduction ReducePromisePrototypeFinally(Node* node); Reduction ReducePromisePrototypeFinally(Node* node);
Reduction ReducePromisePrototypeThen(Node* node); Reduction ReducePromisePrototypeThen(Node* node);
Reduction ReducePromiseResolveTrampoline(Node* node);
Reduction ReduceSoftDeoptimize(Node* node, DeoptimizeReason reason); Reduction ReduceSoftDeoptimize(Node* node, DeoptimizeReason reason);
......
...@@ -85,7 +85,9 @@ REPLACE_STUB_CALL(ToName) ...@@ -85,7 +85,9 @@ REPLACE_STUB_CALL(ToName)
REPLACE_STUB_CALL(ToObject) REPLACE_STUB_CALL(ToObject)
REPLACE_STUB_CALL(ToString) REPLACE_STUB_CALL(ToString)
REPLACE_STUB_CALL(ForInEnumerate) REPLACE_STUB_CALL(ForInEnumerate)
REPLACE_STUB_CALL(FulfillPromise)
REPLACE_STUB_CALL(PerformPromiseThen) REPLACE_STUB_CALL(PerformPromiseThen)
REPLACE_STUB_CALL(PromiseResolve)
REPLACE_STUB_CALL(RejectPromise) REPLACE_STUB_CALL(RejectPromise)
REPLACE_STUB_CALL(ResolvePromise) REPLACE_STUB_CALL(ResolvePromise)
#undef REPLACE_STUB_CALL #undef REPLACE_STUB_CALL
......
...@@ -76,6 +76,10 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) { ...@@ -76,6 +76,10 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
return ReduceJSHasInPrototypeChain(node); return ReduceJSHasInPrototypeChain(node);
case IrOpcode::kJSOrdinaryHasInstance: case IrOpcode::kJSOrdinaryHasInstance:
return ReduceJSOrdinaryHasInstance(node); return ReduceJSOrdinaryHasInstance(node);
case IrOpcode::kJSPromiseResolve:
return ReduceJSPromiseResolve(node);
case IrOpcode::kJSResolvePromise:
return ReduceJSResolvePromise(node);
case IrOpcode::kJSLoadContext: case IrOpcode::kJSLoadContext:
return ReduceJSLoadContext(node); return ReduceJSLoadContext(node);
case IrOpcode::kJSLoadGlobal: case IrOpcode::kJSLoadGlobal:
...@@ -411,6 +415,87 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( ...@@ -411,6 +415,87 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
return NoChange(); return NoChange();
} }
// ES section #sec-promise-resolve
Reduction JSNativeContextSpecialization::ReduceJSPromiseResolve(Node* node) {
DCHECK_EQ(IrOpcode::kJSPromiseResolve, node->opcode());
Node* constructor = NodeProperties::GetValueInput(node, 0);
Node* value = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Check if the {constructor} is the %Promise% function.
HeapObjectMatcher m(constructor);
if (!m.Is(handle(native_context()->promise_function()))) return NoChange();
// Check if we know something about the {value}.
ZoneHandleSet<Map> value_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(value, effect, &value_maps);
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
DCHECK_NE(0, value_maps.size());
// Check that the {value} cannot be a JSPromise.
for (Handle<Map> const value_map : value_maps) {
if (value_map->IsJSPromiseMap()) return NoChange();
}
// Create a %Promise% instance and resolve it with {value}.
Node* promise = effect =
graph()->NewNode(javascript()->CreatePromise(), context, effect);
effect = graph()->NewNode(javascript()->ResolvePromise(), promise, value,
context, frame_state, effect, control);
ReplaceWithValue(node, promise, effect, control);
return Replace(promise);
}
// ES section #sec-promise-resolve-functions
Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) {
DCHECK_EQ(IrOpcode::kJSResolvePromise, node->opcode());
Node* promise = NodeProperties::GetValueInput(node, 0);
Node* resolution = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Check if we know something about the {resolution}.
ZoneHandleSet<Map> resolution_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(resolution, effect, &resolution_maps);
if (result != NodeProperties::kReliableReceiverMaps) return NoChange();
DCHECK_NE(0, resolution_maps.size());
// Compute property access info for "then" on {resolution}.
PropertyAccessInfo access_info;
AccessInfoFactory access_info_factory(dependencies(), native_context(),
graph()->zone());
if (!access_info_factory.ComputePropertyAccessInfo(
MapHandles(resolution_maps.begin(), resolution_maps.end()),
factory()->then_string(), AccessMode::kLoad, &access_info)) {
return NoChange();
}
// We can further optimize the case where {resolution}
// definitely doesn't have a "then" property.
if (!access_info.IsNotFound()) return NoChange();
PropertyAccessBuilder access_builder(jsgraph(), dependencies());
// Add proper dependencies on the {resolution}s [[Prototype]]s.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
access_builder.AssumePrototypesStable(native_context(),
access_info.receiver_maps(), holder);
}
// Simply fulfill the {promise} with the {resolution}.
Node* value = effect =
graph()->NewNode(javascript()->FulfillPromise(), promise, resolution,
context, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
ContextAccess const& access = ContextAccessOf(node->op()); ContextAccess const& access = ContextAccessOf(node->op());
......
...@@ -62,6 +62,8 @@ class JSNativeContextSpecialization final : public AdvancedReducer { ...@@ -62,6 +62,8 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
Reduction ReduceJSInstanceOf(Node* node); Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSHasInPrototypeChain(Node* node); Reduction ReduceJSHasInPrototypeChain(Node* node);
Reduction ReduceJSOrdinaryHasInstance(Node* node); Reduction ReduceJSOrdinaryHasInstance(Node* node);
Reduction ReduceJSPromiseResolve(Node* node);
Reduction ReduceJSResolvePromise(Node* node);
Reduction ReduceJSLoadContext(Node* node); Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSLoadGlobal(Node* node); Reduction ReduceJSLoadGlobal(Node* node);
Reduction ReduceJSStoreGlobal(Node* node); Reduction ReduceJSStoreGlobal(Node* node);
......
...@@ -581,7 +581,9 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) { ...@@ -581,7 +581,9 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(GeneratorRestoreInputOrDebugPos, Operator::kNoThrow, 1, 1) \ V(GeneratorRestoreInputOrDebugPos, Operator::kNoThrow, 1, 1) \
V(StackCheck, Operator::kNoWrite, 0, 0) \ V(StackCheck, Operator::kNoWrite, 0, 0) \
V(Debugger, Operator::kNoProperties, 0, 0) \ V(Debugger, Operator::kNoProperties, 0, 0) \
V(FulfillPromise, Operator::kNoDeopt | Operator::kNoThrow, 2, 1) \
V(PerformPromiseThen, Operator::kNoDeopt | Operator::kNoThrow, 4, 1) \ V(PerformPromiseThen, Operator::kNoDeopt | Operator::kNoThrow, 4, 1) \
V(PromiseResolve, Operator::kNoProperties, 2, 1) \
V(RejectPromise, Operator::kNoDeopt | Operator::kNoThrow, 3, 1) \ V(RejectPromise, Operator::kNoDeopt | Operator::kNoThrow, 3, 1) \
V(ResolvePromise, Operator::kNoDeopt | Operator::kNoThrow, 2, 1) \ V(ResolvePromise, Operator::kNoDeopt | Operator::kNoThrow, 2, 1) \
V(GetSuperConstructor, Operator::kNoWrite, 1, 1) V(GetSuperConstructor, Operator::kNoWrite, 1, 1)
......
...@@ -754,7 +754,9 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -754,7 +754,9 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* StackCheck(); const Operator* StackCheck();
const Operator* Debugger(); const Operator* Debugger();
const Operator* FulfillPromise();
const Operator* PerformPromiseThen(); const Operator* PerformPromiseThen();
const Operator* PromiseResolve();
const Operator* RejectPromise(); const Operator* RejectPromise();
const Operator* ResolvePromise(); const Operator* ResolvePromise();
......
...@@ -195,7 +195,9 @@ ...@@ -195,7 +195,9 @@
V(JSGeneratorRestoreContext) \ V(JSGeneratorRestoreContext) \
V(JSGeneratorRestoreRegister) \ V(JSGeneratorRestoreRegister) \
V(JSGeneratorRestoreInputOrDebugPos) \ V(JSGeneratorRestoreInputOrDebugPos) \
V(JSFulfillPromise) \
V(JSPerformPromiseThen) \ V(JSPerformPromiseThen) \
V(JSPromiseResolve) \
V(JSRejectPromise) \ V(JSRejectPromise) \
V(JSResolvePromise) \ V(JSResolvePromise) \
V(JSStackCheck) \ V(JSStackCheck) \
......
...@@ -115,6 +115,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) { ...@@ -115,6 +115,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSDecrement: case IrOpcode::kJSDecrement:
case IrOpcode::kJSIncrement: case IrOpcode::kJSIncrement:
case IrOpcode::kJSNegate: case IrOpcode::kJSNegate:
case IrOpcode::kJSPromiseResolve:
case IrOpcode::kJSRejectPromise: case IrOpcode::kJSRejectPromise:
case IrOpcode::kJSResolvePromise: case IrOpcode::kJSResolvePromise:
return true; return true;
......
...@@ -1836,9 +1836,16 @@ Type* Typer::Visitor::TypeJSStackCheck(Node* node) { return Type::Any(); } ...@@ -1836,9 +1836,16 @@ Type* Typer::Visitor::TypeJSStackCheck(Node* node) { return Type::Any(); }
Type* Typer::Visitor::TypeJSDebugger(Node* node) { return Type::Any(); } Type* Typer::Visitor::TypeJSDebugger(Node* node) { return Type::Any(); }
Type* Typer::Visitor::TypeJSFulfillPromise(Node* node) {
return Type::Undefined();
}
Type* Typer::Visitor::TypeJSPerformPromiseThen(Node* node) { Type* Typer::Visitor::TypeJSPerformPromiseThen(Node* node) {
// TODO(turbofan): Introduce a Type::Promise here. return Type::Receiver();
return Type::OtherObject(); }
Type* Typer::Visitor::TypeJSPromiseResolve(Node* node) {
return Type::Receiver();
} }
Type* Typer::Visitor::TypeJSRejectPromise(Node* node) { Type* Typer::Visitor::TypeJSRejectPromise(Node* node) {
......
...@@ -849,12 +849,22 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -849,12 +849,22 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckNotTyped(node); CheckNotTyped(node);
break; break;
case IrOpcode::kJSFulfillPromise:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Any());
CheckTypeIs(node, Type::Undefined());
break;
case IrOpcode::kJSPerformPromiseThen: case IrOpcode::kJSPerformPromiseThen:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Any()); CheckValueInputIs(node, 1, Type::Any());
CheckValueInputIs(node, 2, Type::Any()); CheckValueInputIs(node, 2, Type::Any());
CheckValueInputIs(node, 3, Type::Any()); CheckValueInputIs(node, 3, Type::Any());
CheckTypeIs(node, Type::OtherObject()); CheckTypeIs(node, Type::Receiver());
break;
case IrOpcode::kJSPromiseResolve:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Any());
CheckTypeIs(node, Type::Receiver());
break; break;
case IrOpcode::kJSRejectPromise: case IrOpcode::kJSRejectPromise:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
......
...@@ -195,11 +195,13 @@ class ZoneHandleSet<T>::const_iterator { ...@@ -195,11 +195,13 @@ class ZoneHandleSet<T>::const_iterator {
typedef std::forward_iterator_tag iterator_category; typedef std::forward_iterator_tag iterator_category;
typedef std::ptrdiff_t difference_type; typedef std::ptrdiff_t difference_type;
typedef Handle<T> value_type; typedef Handle<T> value_type;
typedef value_type reference;
typedef value_type* pointer;
const_iterator(const const_iterator& other) const_iterator(const const_iterator& other)
: set_(other.set_), current_(other.current_) {} : set_(other.set_), current_(other.current_) {}
Handle<T> operator*() const { return (*set_)[current_]; } reference operator*() const { return (*set_)[current_]; }
bool operator==(const const_iterator& other) const { bool operator==(const const_iterator& other) const {
return set_ == other.set_ && current_ == other.current_; return set_ == other.set_ && current_ == other.current_;
} }
......
// Copyright 2018 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: --allow-natives-syntax
function assertFulfilledWith(expected, thenable) {
assertPromiseResult(thenable, v => assertEquals(expected, v));
}
(function() {
function foo() { return Promise.resolve(); }
assertFulfilledWith(undefined, foo());
assertFulfilledWith(undefined, foo());
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(undefined, foo());
})();
(function() {
function foo(x) { return Promise.resolve(x); }
assertFulfilledWith(3, foo(3));
assertFulfilledWith(3, foo(3));
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(3, foo(3));
})();
(function() {
function foo(x, y) { return Promise.resolve(x, y); }
assertFulfilledWith(1, foo(1, 0));
assertFulfilledWith(2, foo(2, 1));
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(3, foo(3, 2));
})();
(function() {
function foo(x) { return Promise.resolve({x}); }
assertFulfilledWith({x:1}, foo(1));
assertFulfilledWith({x:2}, foo(2));
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith({x:3}, foo(3));
})();
(function() {
function foo(x) { return Promise.resolve(Promise.resolve(x)); }
assertFulfilledWith(null, foo(null));
assertFulfilledWith('a', foo('a'));
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(42, foo(42));
})();
(function() {
const thenable = new class Thenable {
then(fulfill, reject) {
fulfill(1);
}
};
function foo() { return Promise.resolve(thenable); }
assertFulfilledWith(1, foo());
assertFulfilledWith(1, foo());
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(1, foo());
})();
(function() {
const MyPromise = class MyPromise extends Promise {};
(function() {
function foo() { return MyPromise.resolve(); }
assertFulfilledWith(undefined, foo());
assertFulfilledWith(undefined, foo());
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(undefined, foo());
})();
(function() {
function foo(x) { return MyPromise.resolve(x); }
assertFulfilledWith(3, foo(3));
assertFulfilledWith(3, foo(3));
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(3, foo(3));
})();
(function() {
function foo(x, y) { return MyPromise.resolve(x, y); }
assertFulfilledWith(1, foo(1, 0));
assertFulfilledWith(2, foo(2, 1));
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(3, foo(3, 2));
})();
(function() {
function foo(x) { return MyPromise.resolve({x}); }
assertFulfilledWith({x:1}, foo(1));
assertFulfilledWith({x:2}, foo(2));
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith({x:3}, foo(3));
})();
(function() {
function foo(x) { return MyPromise.resolve(Promise.resolve(x)); }
assertFulfilledWith(null, foo(null));
assertFulfilledWith('a', foo('a'));
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(42, foo(42));
})();
(function() {
const thenable = new class Thenable {
then(fulfill, reject) {
fulfill(1);
}
};
function foo() { return MyPromise.resolve(thenable); }
assertFulfilledWith(1, foo());
assertFulfilledWith(1, foo());
%OptimizeFunctionOnNextCall(foo);
assertFulfilledWith(1, foo());
})();
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment