[builtins] Also use the Promise#then protector for Promise#finally().

Add a fast-path to Promise#finally, which skips the "then" lookup of the
Promise#then lookup chain is intact, similar to what we already do for
Promise#catch.

Drive-by-fix: Also use the @@species protector to speed up the lookup
of the SpeciesConstructor in Promise#finally.

Bug: v8:7253
Change-Id: If77e779a0188904effc4528beffc8f0bdd7c2efe
Reviewed-on: https://chromium-review.googlesource.com/902283
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51116}
This commit is contained in:
Benedikt Meurer 2018-02-05 19:36:14 +01:00 committed by Commit Bot
parent 6703dacdd6
commit d4f072ced3
10 changed files with 398 additions and 101 deletions

View File

@ -612,6 +612,62 @@ void PromiseBuiltinsAssembler::PromiseFulfill(
}
}
template <typename... TArgs>
Node* PromiseBuiltinsAssembler::InvokeThen(Node* native_context, Node* receiver,
TArgs... args) {
CSA_ASSERT(this, IsNativeContext(native_context));
VARIABLE(var_result, MachineRepresentation::kTagged);
Label if_fast(this), if_slow(this, Label::kDeferred), done(this, &var_result);
GotoIf(TaggedIsSmi(receiver), &if_slow);
Node* const receiver_map = LoadMap(receiver);
// We can skip the "then" lookup on {receiver} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
BranchIfPromiseThenLookupChainIntact(native_context, receiver_map, &if_fast,
&if_slow);
BIND(&if_fast);
{
Node* const then =
LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
Node* const result =
CallJS(CodeFactory::CallFunction(
isolate(), ConvertReceiverMode::kNotNullOrUndefined),
native_context, then, receiver, args...);
var_result.Bind(result);
Goto(&done);
}
BIND(&if_slow);
{
Node* const then = GetProperty(native_context, receiver,
isolate()->factory()->then_string());
Node* const result = CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
native_context, then, receiver, args...);
var_result.Bind(result);
Goto(&done);
}
BIND(&done);
return var_result.value();
}
void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
Node* native_context, Node* promise_map, Label* if_fast, Label* if_slow) {
CSA_ASSERT(this, IsNativeContext(native_context));
CSA_ASSERT(this, IsJSPromiseMap(promise_map));
Node* const promise_prototype =
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
GotoIfForceSlowPath(if_slow);
GotoIfNot(WordEqual(LoadMapPrototype(promise_map), promise_prototype),
if_slow);
Branch(IsSpeciesProtectorCellInvalid(), if_slow, if_fast);
}
void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
Node* native_context, Node* receiver_map, Label* if_fast, Label* if_slow) {
CSA_ASSERT(this, IsMap(receiver_map));
@ -907,13 +963,9 @@ TF_BUILTIN(PromisePrototypeThen, PromiseBuiltinsAssembler) {
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const promise_prototype =
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
Node* const promise_map = LoadMap(promise);
GotoIfNot(WordEqual(LoadMapPrototype(promise_map), promise_prototype),
&slow_constructor);
Branch(IsSpeciesProtectorCellInvalid(), &slow_constructor,
&fast_promise_capability);
BranchIfPromiseSpeciesLookupChainIntact(
native_context, promise_map, &fast_promise_capability, &slow_constructor);
BIND(&slow_constructor);
Node* const constructor =
@ -1015,38 +1067,8 @@ TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {
Node* const context = Parameter(Descriptor::kContext);
// 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
VARIABLE(var_then, MachineRepresentation::kTagged);
Label if_fast(this), if_slow(this, Label::kDeferred), done(this);
GotoIf(TaggedIsSmi(receiver), &if_slow);
Node* const receiver_map = LoadMap(receiver);
// We can skip the "then" lookup on {receiver} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
Node* const native_context = LoadNativeContext(context);
BranchIfPromiseThenLookupChainIntact(native_context, receiver_map, &if_fast,
&if_slow);
BIND(&if_fast);
{
var_then.Bind(
LoadContextElement(native_context, Context::PROMISE_THEN_INDEX));
Goto(&done);
}
BIND(&if_slow);
{
var_then.Bind(
GetProperty(context, receiver, isolate()->factory()->then_string()));
Goto(&done);
}
BIND(&done);
Node* const then = var_then.value();
Node* const result = CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
context, then, receiver, on_fulfilled, on_rejected);
Return(result);
Return(InvokeThen(native_context, receiver, on_fulfilled, on_rejected));
}
// ES section #sec-promiseresolvethenablejob
@ -1461,16 +1483,11 @@ TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) {
CallBuiltin(Builtins::kPromiseResolve, context, constructor, result);
// 7. Let valueThunk be equivalent to a function that returns value.
Node* native_context = LoadNativeContext(context);
Node* const native_context = LoadNativeContext(context);
Node* const value_thunk = CreateValueThunkFunction(value, native_context);
// 8. Return ? Invoke(promise, "then", « valueThunk »).
Node* const promise_then =
GetProperty(context, promise, factory()->then_string());
Node* const result_promise = CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
context, promise_then, promise, value_thunk);
Return(result_promise);
Return(InvokeThen(native_context, promise, value_thunk));
}
TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) {
@ -1523,35 +1540,44 @@ TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) {
CallBuiltin(Builtins::kPromiseResolve, context, constructor, result);
// 7. Let thrower be equivalent to a function that throws reason.
Node* native_context = LoadNativeContext(context);
Node* const native_context = LoadNativeContext(context);
Node* const thrower = CreateThrowerFunction(reason, native_context);
// 8. Return ? Invoke(promise, "then", « thrower »).
Node* const promise_then =
GetProperty(context, promise, factory()->then_string());
Node* const result_promise = CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
context, promise_then, promise, thrower);
Return(result_promise);
Return(InvokeThen(native_context, promise, thrower));
}
TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) {
CSA_ASSERT_JS_ARGC_EQ(this, 1);
// 1. Let promise be the this value.
Node* const promise = Parameter(Descriptor::kReceiver);
Node* const receiver = Parameter(Descriptor::kReceiver);
Node* const on_finally = Parameter(Descriptor::kOnFinally);
Node* const context = Parameter(Descriptor::kContext);
// 2. If Type(promise) is not Object, throw a TypeError exception.
ThrowIfNotJSReceiver(context, promise, MessageTemplate::kCalledOnNonObject,
ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
"Promise.prototype.finally");
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const constructor = SpeciesConstructor(context, promise, promise_fun);
VARIABLE(var_constructor, MachineRepresentation::kTagged, promise_fun);
Label slow_constructor(this, Label::kDeferred), done_constructor(this);
Node* const receiver_map = LoadMap(receiver);
GotoIfNot(IsJSPromiseMap(receiver_map), &slow_constructor);
BranchIfPromiseSpeciesLookupChainIntact(native_context, receiver_map,
&done_constructor, &slow_constructor);
BIND(&slow_constructor);
{
Node* const constructor =
SpeciesConstructor(context, receiver, promise_fun);
var_constructor.Bind(constructor);
Goto(&done_constructor);
}
BIND(&done_constructor);
Node* const constructor = var_constructor.value();
// 4. Assert: IsConstructor(C) is true.
CSA_ASSERT(this, IsConstructor(constructor));
@ -1593,13 +1619,8 @@ TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) {
// 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
BIND(&perform_finally);
Node* const promise_then =
GetProperty(context, promise, factory()->then_string());
Node* const result_promise = CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
context, promise_then, promise, var_then_finally.value(),
var_catch_finally.value());
Return(result_promise);
Return(InvokeThen(native_context, receiver, var_then_finally.value(),
var_catch_finally.value()));
}
TF_BUILTIN(ResolveNativePromise, PromiseBuiltinsAssembler) {

View File

@ -131,6 +131,14 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
void PromiseFulfill(Node* context, Node* promise, Node* result,
v8::Promise::PromiseState status);
// We can shortcut the SpeciesConstructor on {promise_map} if it's
// [[Prototype]] is the (initial) Promise.prototype and the @@species
// protector is intact, as that guards the lookup path for the "constructor"
// property on JSPromise instances which have the %PromisePrototype%.
void BranchIfPromiseSpeciesLookupChainIntact(Node* native_context,
Node* promise_map,
Label* if_fast, Label* if_slow);
// We can skip the "then" lookup on {receiver_map} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then() protector
// is intact, as that guards the lookup path for the "then" property
@ -139,6 +147,9 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
Node* receiver_map, Label* if_fast,
Label* if_slow);
template <typename... TArgs>
Node* InvokeThen(Node* native_context, Node* receiver, TArgs... args);
void BranchIfAccessCheckFailed(Node* context, Node* native_context,
Node* promise_constructor, Node* executor,
Label* if_noaccess);

View File

@ -5,6 +5,7 @@
#include "src/compiler/js-call-reducer.h"
#include "src/api.h"
#include "src/builtins/builtins-promise-gen.h"
#include "src/builtins/builtins-utils.h"
#include "src/code-factory.h"
#include "src/code-stubs.h"
@ -2968,6 +2969,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceAsyncFunctionPromiseRelease(node);
case Builtins::kPromisePrototypeCatch:
return ReducePromisePrototypeCatch(node);
case Builtins::kPromisePrototypeFinally:
return ReducePromisePrototypeFinally(node);
case Builtins::kPromisePrototypeThen:
return ReducePromisePrototypeThen(node);
default:
@ -3932,6 +3935,7 @@ Reduction JSCallReducer::ReduceAsyncFunctionPromiseRelease(Node* node) {
return Replace(value);
}
// ES section #sec-promise.prototype.catch
Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
@ -3991,7 +3995,150 @@ Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) {
node, javascript()->Call(2 + arity, p.frequency(), p.feedback(),
ConvertReceiverMode::kNotNullOrUndefined,
p.speculation_mode()));
return Changed(node);
Reduction const reduction = ReducePromisePrototypeThen(node);
return reduction.Changed() ? reduction : Changed(node);
}
// ES section #sec-promise.prototype.finally
Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
int arity = static_cast<int>(p.arity() - 2);
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* on_finally = arity >= 1 ? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
// Check that promises aren't being observed through (debug) hooks.
if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange();
// Check that the Promise#then protector is intact. This protector guards
// that all JSPromise instances whose [[Prototype]] is the initial
// %PromisePrototype% yield the initial %PromisePrototype%.then method
// when looking up "then".
if (!isolate()->IsPromiseThenLookupChainIntact()) return NoChange();
// Also check that the @@species protector is intact, which guards the
// lookup of "constructor" on JSPromise instances, whoch [[Prototype]] is
// the initial %PromisePrototype%, and the Symbol.species lookup on the
// %PromisePrototype%.
if (!isolate()->IsSpeciesLookupChainIntact()) return NoChange();
// Check if we know something about {receiver} already.
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
DCHECK_NE(0, receiver_maps.size());
// Check whether all {receiver_maps} are JSPromise maps and
// have the initial Promise.prototype as their [[Prototype]].
for (Handle<Map> receiver_map : receiver_maps) {
if (!receiver_map->IsJSPromiseMap()) return NoChange();
if (receiver_map->prototype() != native_context()->promise_prototype()) {
return NoChange();
}
}
// Add a code dependency on the necessary protectors.
dependencies()->AssumePropertyCell(factory()->promise_hook_protector());
dependencies()->AssumePropertyCell(factory()->promise_then_protector());
dependencies()->AssumePropertyCell(factory()->species_protector());
// If the {receiver_maps} aren't reliable, we need to repeat the
// map check here, guarded by the CALL_IC.
if (result == NodeProperties::kUnreliableReceiverMaps) {
effect =
graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
receiver_maps, p.feedback()),
receiver, effect, control);
}
// Check if {on_finally} is callable, and if so wrap it into appropriate
// closures that perform the finalization.
Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), on_finally);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;
Node* catch_true;
Node* then_true;
{
Node* context = jsgraph()->HeapConstant(native_context());
Node* constructor = jsgraph()->HeapConstant(
handle(native_context()->promise_function(), isolate()));
// Allocate shared context for the closures below.
context = etrue = graph()->NewNode(
javascript()->CreateFunctionContext(
PromiseBuiltinsAssembler::kPromiseFinallyContextLength,
FUNCTION_SCOPE),
context, context, etrue, if_true);
etrue =
graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kOnFinallySlot)),
context, on_finally, etrue, if_true);
etrue =
graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kConstructorSlot)),
context, constructor, etrue, if_true);
// Allocate the closure for the reject case.
Handle<SharedFunctionInfo> catch_finally(
native_context()->promise_catch_finally_shared_fun(), isolate());
catch_true = etrue = graph()->NewNode(
javascript()->CreateClosure(catch_finally), context, etrue, if_true);
// Allocate the closure for the fulfill case.
Handle<SharedFunctionInfo> then_finally(
native_context()->promise_then_finally_shared_fun(), isolate());
then_true = etrue = graph()->NewNode(
javascript()->CreateClosure(then_finally), context, etrue, if_true);
}
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = effect;
Node* catch_false = on_finally;
Node* then_false = on_finally;
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
Node* catch_finally =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
catch_true, catch_false, control);
Node* then_finally =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
then_true, then_false, control);
// At this point we definitely know that {receiver} has one of the
// {receiver_maps}, so insert a MapGuard as a hint for the lowering
// of the call to "then" below.
effect = graph()->NewNode(simplified()->MapGuard(receiver_maps), receiver,
effect, control);
// Massage the {node} to call "then" instead by first removing all inputs
// following the onFinally parameter, and then replacing the only parameter
// input with the {on_finally} value.
Node* target = jsgraph()->Constant(handle(native_context()->promise_then()));
NodeProperties::ReplaceValueInput(node, target, 0);
NodeProperties::ReplaceEffectInput(node, effect);
NodeProperties::ReplaceControlInput(node, control);
for (; arity > 2; --arity) node->RemoveInput(2);
for (; arity < 2; ++arity)
node->InsertInput(graph()->zone(), 2, then_finally);
node->ReplaceInput(2, then_finally);
node->ReplaceInput(3, catch_finally);
NodeProperties::ChangeOp(
node, javascript()->Call(2 + arity, p.frequency(), p.feedback(),
ConvertReceiverMode::kNotNullOrUndefined,
p.speculation_mode()));
Reduction const reduction = ReducePromisePrototypeThen(node);
return reduction.Changed() ? reduction : Changed(node);
}
Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {

View File

@ -102,6 +102,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceAsyncFunctionPromiseCreate(Node* node);
Reduction ReduceAsyncFunctionPromiseRelease(Node* node);
Reduction ReducePromisePrototypeCatch(Node* node);
Reduction ReducePromisePrototypeFinally(Node* node);
Reduction ReducePromisePrototypeThen(Node* node);
Reduction ReduceSoftDeoptimize(Node* node, DeoptimizeReason reason);

View File

@ -901,49 +901,59 @@ Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
Node* feedback_vector = jsgraph()->UndefinedConstant();
// Use inline allocation of closures only for instantiation sites that have
// seen more than one instantiation, this simplifies the generated code and
// also serves as a heuristic of which allocation sites benefit from it.
FeedbackSlot slot(FeedbackVector::ToSlot(p.feedback().index()));
Handle<Cell> vector_cell(Cell::cast(p.feedback().vector()->Get(slot)));
if (vector_cell->map() == isolate()->heap()->many_closures_cell_map()) {
Handle<Map> function_map(
Map::cast(native_context()->get(shared->function_map_index())));
Node* lazy_compile_builtin = jsgraph()->HeapConstant(
handle(isolate()->builtins()->builtin(Builtins::kCompileLazy)));
DCHECK(!function_map->IsInobjectSlackTrackingInProgress());
DCHECK(!function_map->is_dictionary_map());
// Emit code to allocate the JSFunction instance.
STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(function_map->instance_size());
a.Store(AccessBuilder::ForMap(), function_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSFunctionSharedFunctionInfo(), shared);
a.Store(AccessBuilder::ForJSFunctionContext(), context);
a.Store(AccessBuilder::ForJSFunctionFeedbackVector(), vector_cell);
a.Store(AccessBuilder::ForJSFunctionCode(), lazy_compile_builtin);
STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
if (function_map->has_prototype_slot()) {
a.Store(AccessBuilder::ForJSFunctionPrototypeOrInitialMap(),
jsgraph()->TheHoleConstant());
STATIC_ASSERT(JSFunction::kSizeWithPrototype == 8 * kPointerSize);
if (p.feedback().IsValid()) {
FeedbackSlot slot(FeedbackVector::ToSlot(p.feedback().index()));
Handle<Cell> vector_cell(Cell::cast(p.feedback().vector()->Get(slot)));
if (vector_cell->map() != isolate()->heap()->many_closures_cell_map()) {
return NoChange();
}
for (int i = 0; i < function_map->GetInObjectProperties(); i++) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(function_map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
feedback_vector = jsgraph()->HeapConstant(vector_cell);
} else {
// CreateClosure without a feedback vector is only allowed for
// native (builtin) functions.
DCHECK(shared->native());
}
return NoChange();
Handle<Map> function_map(
Map::cast(native_context()->get(shared->function_map_index())));
Node* lazy_compile_builtin = jsgraph()->HeapConstant(handle(
shared->native() ? shared->code()
: isolate()->builtins()->builtin(Builtins::kCompileLazy),
isolate()));
DCHECK(!function_map->IsInobjectSlackTrackingInProgress());
DCHECK(!function_map->is_dictionary_map());
// Emit code to allocate the JSFunction instance.
STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(function_map->instance_size(), p.pretenure(), Type::Function());
a.Store(AccessBuilder::ForMap(), function_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSFunctionSharedFunctionInfo(), shared);
a.Store(AccessBuilder::ForJSFunctionContext(), context);
a.Store(AccessBuilder::ForJSFunctionFeedbackVector(), feedback_vector);
a.Store(AccessBuilder::ForJSFunctionCode(), lazy_compile_builtin);
STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
if (function_map->has_prototype_slot()) {
a.Store(AccessBuilder::ForJSFunctionPrototypeOrInitialMap(),
jsgraph()->TheHoleConstant());
STATIC_ASSERT(JSFunction::kSizeWithPrototype == 8 * kPointerSize);
}
for (int i = 0; i < function_map->GetInObjectProperties(); i++) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(function_map, i),
jsgraph()->UndefinedConstant());
}
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateIterResultObject(Node* node) {

View File

@ -651,9 +651,10 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* CreateArguments(CreateArgumentsType type);
const Operator* CreateArray(size_t arity, Handle<AllocationSite> site);
const Operator* CreateBoundFunction(size_t arity, Handle<Map> map);
const Operator* CreateClosure(Handle<SharedFunctionInfo> shared_info,
VectorSlotPair const& feedback,
PretenureFlag pretenure);
const Operator* CreateClosure(
Handle<SharedFunctionInfo> shared_info,
VectorSlotPair const& feedback = VectorSlotPair(),
PretenureFlag pretenure = NOT_TENURED);
const Operator* CreateIterResultObject();
const Operator* CreateKeyValueArray();
const Operator* CreatePromise();

View File

@ -0,0 +1,19 @@
// 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 foo(p) { return p.finally(x => x); }
const a = Promise.resolve(1);
foo(a);
foo(a);
%OptimizeFunctionOnNextCall(foo);
foo(a);
let custom_then_called = false;
a.__proto__.then = function() { custom_then_called = true; }
foo(a);
assertTrue(custom_then_called);

View File

@ -0,0 +1,19 @@
// 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 foo(p) { return p.finally(x => x); }
const a = Promise.resolve(1);
foo(a);
foo(a);
%OptimizeFunctionOnNextCall(foo);
foo(a);
let custom_then_called = false;
a.then = function() { custom_then_called = true; }
foo(a);
assertTrue(custom_then_called);

View File

@ -0,0 +1,27 @@
// 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
let custom_then_called = false;
function foo(p) {
custom_then_called = false;
p.finally(x => x);
return custom_then_called;
}
class MyPromise extends Promise {
then(onFulfilled, onRejected) {
custom_then_called = true;
return super.then(onFulfilled, onRejected);
}
}
const a = MyPromise.resolve(1);
assertTrue(foo(a));
assertTrue(foo(a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(a));

View File

@ -0,0 +1,41 @@
// 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() {
const p = Promise.resolve(1);
function foo(p) { return p.finally(); }
foo(p);
foo(p);
%OptimizeFunctionOnNextCall(foo);
foo(p);
})();
(function() {
const p = Promise.resolve(1);
function foo(p) { return p.finally(x => x); }
foo(p);
foo(p);
%OptimizeFunctionOnNextCall(foo);
foo(p);
})();
(function() {
const p = Promise.resolve(1);
function foo(p, f) { return p.finally(f); }
foo(p, x => x);
foo(p, x => x);
%OptimizeFunctionOnNextCall(foo);
foo(p, x => x);
})();
(function() {
const p = Promise.resolve(1);
function foo(p, f) { return p.finally(f).finally(f); }
foo(p, x => x);
foo(p, x => x);
%OptimizeFunctionOnNextCall(foo);
foo(p, x => x);
})();