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

[builtins] Widen the fast-path for Promise builtins.

This adds a new isolate wide Promise#then protector, which guards the
"then" lookup for all JSPromise instances whose [[Prototype]] is the
initial %PromisePrototype%. Thus arbitrary mutations to the
Promise.prototype (i.e. monkey-patching other methods or installing
new functions) no longer sent you down the slow-path. Use this protector
in Promise.prototype.catch and in Promise.resolve.

Drive-by-fix: Restructure the resolve logic a bit and avoid the
expensive and large SameValue check, which can be turned into a simple
reference equal, as the promise in there is known to be a JSPromise
anyways.

Bug: v8:7253
Change-Id: If68b12c6bc6ca9c4d10552ae84854ebc3b5774f9
Reviewed-on: https://chromium-review.googlesource.com/899302
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51085}
parent 3a0372f9
......@@ -2420,10 +2420,6 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<Map> prototype_map(prototype->map());
Map::SetShouldBeFastPrototypeMap(prototype_map, true, isolate);
// Store the initial Promise.prototype map. This is used in fast-path
// checks. Do not alter the prototype after this point.
native_context()->set_promise_prototype_map(*prototype_map);
{ // Internal: PromiseInternalConstructor
// Also exposed as extrasUtils.createPromise.
Handle<JSFunction> function =
......@@ -4445,7 +4441,6 @@ void Genesis::InitializeGlobal_harmony_promise_finally() {
// 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<SharedFunctionInfo> info = SimpleCreateSharedFunctionInfo(
......
......@@ -345,50 +345,6 @@ TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) {
Return(result_promise);
}
// Promise fast path implementations rely on unmodified JSPromise instances.
// We use a fairly coarse granularity for this and simply check whether both
// the promise itself is unmodified (i.e. its map has not changed) and its
// prototype is unmodified.
// TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp
void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise,
Label* if_isunmodified,
Label* if_ismodified) {
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
BranchIfFastPath(native_context, promise_fun, promise, if_isunmodified,
if_ismodified);
}
void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context,
Node* promise_fun,
Node* promise,
Label* if_isunmodified,
Label* if_ismodified) {
CSA_ASSERT(this, IsNativeContext(native_context));
CSA_ASSERT(this,
WordEqual(promise_fun,
LoadContextElement(native_context,
Context::PROMISE_FUNCTION_INDEX)));
GotoIfForceSlowPath(if_ismodified);
Node* const map = LoadMap(promise);
Node* const initial_map =
LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
Node* const has_initialmap = WordEqual(map, initial_map);
GotoIfNot(has_initialmap, if_ismodified);
Node* const initial_proto_initial_map =
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX);
Node* const proto_map = LoadMap(CAST(LoadMapPrototype(map)));
Node* const proto_has_initialmap =
WordEqual(proto_map, initial_proto_initial_map);
Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
}
Node* PromiseBuiltinsAssembler::AllocatePromiseReaction(
Node* next, Node* promise_or_capability, Node* fulfill_handler,
Node* reject_handler) {
......@@ -452,15 +408,14 @@ Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Node* promise,
Node* result) {
Isolate* isolate = this->isolate();
CSA_ASSERT(this, TaggedIsNotSmi(promise));
CSA_ASSERT(this, IsJSPromise(promise));
Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred),
if_rejectpromise(this, Label::kDeferred), out(this);
VARIABLE(var_reason, MachineRepresentation::kTagged);
VARIABLE(var_then, MachineRepresentation::kTagged);
Label do_enqueue(this), fulfill(this), if_nocycle(this),
if_cycle(this, Label::kDeferred),
if_rejectpromise(this, Label::kDeferred), out(this);
Label cycle_check(this);
GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &cycle_check);
CallRuntime(Runtime::kPromiseHookResolve, context, promise);
......@@ -468,39 +423,40 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
BIND(&cycle_check);
// 6. If SameValue(resolution, promise) is true, then
BranchIfSameValue(promise, result, &if_cycle, &if_nocycle);
BIND(&if_nocycle);
// We can use pointer comparison here, since the {promise} is guaranteed
// to be a JSPromise inside this function and thus is reference comparable.
GotoIf(WordEqual(promise, result), &if_cycle);
// 7. If Type(resolution) is not Object, then
GotoIf(TaggedIsSmi(result), &fulfill);
GotoIfNot(IsJSReceiver(result), &fulfill);
Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred);
Node* const result_map = LoadMap(result);
GotoIfNot(IsJSReceiverMap(result_map), &fulfill);
// We can skip the "then" lookup on {result} 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%.
Label if_fast(this), if_slow(this, Label::kDeferred);
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
BranchIfFastPath(native_context, promise_fun, result, &if_nativepromise,
&if_notnativepromise);
BranchIfPromiseThenLookupChainIntact(native_context, result_map, &if_fast,
&if_slow);
// Resolution is a native promise and if it's already resolved or
// rejected, shortcircuit the resolution procedure by directly
// reusing the value from the promise.
BIND(&if_nativepromise);
BIND(&if_fast);
{
// TODO(gsathya): Use a marker here instead of the actual then
// callback, and check for the marker in PromiseResolveThenableJob
// and perform PromiseThen.
Node* const then =
LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
var_then.Bind(then);
Goto(&do_enqueue);
}
BIND(&if_notnativepromise);
BIND(&if_slow);
{
// 8. Let then be Get(resolution, "then").
Node* const then =
GetProperty(context, result, isolate->factory()->then_string());
GetProperty(context, result, isolate()->factory()->then_string());
// 9. If then is an abrupt completion, then
GotoIfException(then, &if_rejectpromise, &var_reason);
......@@ -518,18 +474,21 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Node* const info = AllocatePromiseResolveThenableJobTask(
promise, var_then.value(), result, native_context);
Label enqueue(this);
GotoIfNot(IsDebugActive(), &enqueue);
Label enqueue(this), is_debug_active(this, Label::kDeferred);
Branch(IsDebugActive(), &is_debug_active, &enqueue);
BIND(&is_debug_active);
{
GotoIf(TaggedIsSmi(result), &enqueue);
GotoIfNot(HasInstanceType(result, JS_PROMISE_TYPE), &enqueue);
GotoIfNot(IsJSPromise(result), &enqueue);
// Mark the dependency of the new promise on the resolution
Node* const key =
HeapConstant(isolate->factory()->promise_handled_by_symbol());
HeapConstant(isolate()->factory()->promise_handled_by_symbol());
CallRuntime(Runtime::kSetProperty, context, result, key, promise,
SmiConstant(LanguageMode::kStrict));
Goto(&enqueue);
}
// 12. Perform EnqueueJob("PromiseJobs",
// PromiseResolveThenableJob, « promise, resolution, thenAction»).
......@@ -653,6 +612,20 @@ void PromiseBuiltinsAssembler::PromiseFulfill(
}
}
void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
Node* native_context, Node* receiver_map, Label* if_fast, Label* if_slow) {
CSA_ASSERT(this, IsMap(receiver_map));
CSA_ASSERT(this, IsNativeContext(native_context));
GotoIfForceSlowPath(if_slow);
GotoIfNot(IsJSPromiseMap(receiver_map), if_slow);
Node* const promise_prototype =
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
GotoIfNot(WordEqual(LoadMapPrototype(receiver_map), promise_prototype),
if_slow);
Branch(IsPromiseThenProtectorCellInvalid(), if_slow, if_fast);
}
void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
Node* context, Node* native_context, Node* promise_constructor,
Node* executor, Label* if_noaccess) {
......@@ -1036,7 +1009,7 @@ TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
// Promise.prototype.catch ( onRejected )
TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {
// 1. Let promise be the this value.
Node* const promise = Parameter(Descriptor::kReceiver);
Node* const receiver = Parameter(Descriptor::kReceiver);
Node* const on_fulfilled = UndefinedConstant();
Node* const on_rejected = Parameter(Descriptor::kOnRejected);
Node* const context = Parameter(Descriptor::kContext);
......@@ -1044,12 +1017,18 @@ TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {
// 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(promise), &if_slow);
BranchIfFastPath(context, promise, &if_fast, &if_slow);
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);
{
Node* const native_context = LoadNativeContext(context);
var_then.Bind(
LoadContextElement(native_context, Context::PROMISE_THEN_INDEX));
Goto(&done);
......@@ -1058,7 +1037,7 @@ TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {
BIND(&if_slow);
{
var_then.Bind(
GetProperty(context, promise, isolate()->factory()->then_string()));
GetProperty(context, receiver, isolate()->factory()->then_string()));
Goto(&done);
}
......@@ -1066,7 +1045,7 @@ TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {
Node* const then = var_then.value();
Node* const result = CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
context, then, promise, on_fulfilled, on_rejected);
context, then, receiver, on_fulfilled, on_rejected);
Return(result);
}
......
......@@ -127,16 +127,18 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
void InternalResolvePromise(Node* context, Node* promise, Node* result);
void BranchIfFastPath(Node* context, Node* promise, Label* if_isunmodified,
Label* if_ismodified);
void BranchIfFastPath(Node* native_context, Node* promise_fun, Node* promise,
Label* if_isunmodified, Label* if_ismodified);
Node* CreatePromiseContext(Node* native_context, int slots);
void PromiseFulfill(Node* context, Node* promise, Node* result,
v8::Promise::PromiseState status);
// 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
// on JSPromise instances which have the (initial) %PromisePrototype%.
void BranchIfPromiseThenLookupChainIntact(Node* native_context,
Node* receiver_map, Label* if_fast,
Label* if_slow);
void BranchIfAccessCheckFailed(Node* context, Node* native_context,
Node* promise_constructor, Node* executor,
Label* if_noaccess);
......
......@@ -4165,6 +4165,13 @@ Node* CodeStubAssembler::IsNoElementsProtectorCellInvalid() {
return WordEqual(cell_value, invalid);
}
Node* CodeStubAssembler::IsPromiseThenProtectorCellInvalid() {
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(Heap::kPromiseThenProtectorRootIndex);
Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
return WordEqual(cell_value, invalid);
}
Node* CodeStubAssembler::IsSpeciesProtectorCellInvalid() {
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(Heap::kSpeciesProtectorRootIndex);
......@@ -7560,6 +7567,7 @@ void CodeStubAssembler::CheckForAssociatedProtector(Node* name,
if_protector);
GotoIf(WordEqual(name, LoadRoot(Heap::kis_concat_spreadable_symbolRootIndex)),
if_protector);
GotoIf(WordEqual(name, LoadRoot(Heap::kthen_stringRootIndex)), if_protector);
// Fall through if no case matched.
}
......
......@@ -1143,7 +1143,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsShortExternalStringInstanceType(Node* instance_type);
Node* IsSpecialReceiverInstanceType(Node* instance_type);
Node* IsSpecialReceiverMap(Node* map);
Node* IsSpeciesProtectorCellInvalid();
Node* IsStringInstanceType(Node* instance_type);
Node* IsString(Node* object);
Node* IsSymbolInstanceType(Node* instance_type);
......@@ -1156,6 +1155,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
return IsSharedFunctionInfoMap(LoadMap(object));
}
Node* IsPromiseThenProtectorCellInvalid();
Node* IsSpeciesProtectorCellInvalid();
// True iff |object| is a Smi or a HeapNumber.
Node* IsNumber(Node* object);
// True iff |object| is a Smi or a HeapNumber or a BigInt.
......
......@@ -10,7 +10,6 @@
#include "src/code-stubs.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/access-info.h"
#include "src/compiler/allocation-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
......@@ -3944,27 +3943,30 @@ Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) {
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();
// 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());
if (receiver_maps.size() != 1) return NoChange();
Handle<Map> receiver_map = receiver_maps[0];
// Lookup the "then" method on the {receiver_map}.
PropertyAccessInfo access_info;
AccessInfoFactory access_info_factory(dependencies(), native_context(),
graph()->zone());
if (!access_info_factory.ComputePropertyAccessInfo(
receiver_map, factory()->then_string(), AccessMode::kLoad,
&access_info) ||
!access_info.IsDataConstant()) {
// 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();
}
dependencies()->AssumePrototypeMapsStable(receiver_map, access_info.holder());
Handle<Object> then = access_info.constant();
}
// Add a code dependency on the necessary protectors.
dependencies()->AssumePropertyCell(factory()->promise_then_protector());
// If the {receiver_maps} aren't reliable, we need to repeat the
// map check here, guarded by the CALL_IC.
......@@ -3978,7 +3980,8 @@ Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) {
// Massage the {node} to call "then" instead by first removing all inputs
// following the onRejected parameter, and then filling up the parameters
// to two inputs from the left with undefined.
NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(then), 0);
Node* target = jsgraph()->Constant(handle(native_context()->promise_then()));
NodeProperties::ReplaceValueInput(node, target, 0);
NodeProperties::ReplaceEffectInput(node, effect);
for (; arity > 1; --arity) node->RemoveInput(3);
for (; arity < 2; ++arity) {
......
......@@ -338,7 +338,6 @@ enum ContextLookupFlags {
V(PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN, SharedFunctionInfo, \
promise_all_resolve_element_shared_fun) \
V(PROMISE_PROTOTYPE_INDEX, JSObject, promise_prototype) \
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) \
V(REGEXP_LAST_MATCH_INFO_INDEX, RegExpMatchInfo, regexp_last_match_info) \
......
......@@ -215,6 +215,7 @@ using v8::MemoryPressureLevel;
V(PropertyCell, array_buffer_neutering_protector, \
ArrayBufferNeuteringProtector) \
V(PropertyCell, promise_hook_protector, PromiseHookProtector) \
V(PropertyCell, promise_then_protector, PromiseThenProtector) \
/* Special numbers */ \
V(HeapNumber, nan_value, NanValue) \
V(HeapNumber, hole_nan_value, HoleNanValue) \
......
......@@ -643,6 +643,10 @@ void Heap::CreateInitialObjects() {
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_promise_hook_protector(*cell);
cell = factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_promise_then_protector(*cell);
set_serialized_objects(empty_fixed_array());
set_serialized_global_proxy_sizes(empty_fixed_array());
......
......@@ -3482,6 +3482,13 @@ bool Isolate::IsPromiseHookProtectorIntact() {
return is_promise_hook_protector_intact;
}
bool Isolate::IsPromiseThenLookupChainIntact() {
PropertyCell* promise_then_cell = heap()->promise_then_protector();
bool is_promise_then_protector_intact =
Smi::ToInt(promise_then_cell->value()) == kProtectorValid;
return is_promise_then_protector_intact;
}
void Isolate::UpdateNoElementsProtectorOnSetElement(Handle<JSObject> object) {
DisallowHeapAllocation no_gc;
if (!object->map()->is_prototype_map()) return;
......@@ -3550,6 +3557,15 @@ void Isolate::InvalidatePromiseHookProtector() {
DCHECK(!IsPromiseHookProtectorIntact());
}
void Isolate::InvalidatePromiseThenProtector() {
DCHECK(factory()->promise_then_protector()->value()->IsSmi());
DCHECK(IsPromiseThenLookupChainIntact());
PropertyCell::SetValueWithInvalidation(
factory()->promise_then_protector(),
handle(Smi::FromInt(kProtectorInvalid), this));
DCHECK(!IsPromiseThenLookupChainIntact());
}
bool Isolate::IsAnyInitialArrayPrototype(Handle<JSArray> array) {
DisallowHeapAllocation no_gc;
return IsInAnyContext(*array, Context::INITIAL_ARRAY_PROTOTYPE_INDEX);
......
......@@ -1106,6 +1106,10 @@ class Isolate {
// active.
bool IsPromiseHookProtectorIntact();
// Make sure a lookup of "then" on any JSPromise whose [[Prototype]] is the
// initial %PromisePrototype% yields the initial method.
bool IsPromiseThenLookupChainIntact();
// On intent to set an element in object, make sure that appropriate
// notifications occur if the set is on the elements of the array or
// object prototype. Also ensure that changes to prototype chain between
......@@ -1127,6 +1131,7 @@ class Isolate {
void InvalidateArrayIteratorProtector();
void InvalidateArrayBufferNeuteringProtector();
void InvalidatePromiseHookProtector();
void InvalidatePromiseThenProtector();
// Returns true if array is the initial array prototype in any native context.
bool IsAnyInitialArrayPrototype(Handle<JSArray> array);
......
......@@ -306,6 +306,14 @@ void LookupIterator::InternalUpdateProtector() {
if (holder_->IsJSArray()) {
isolate_->InvalidateArrayIteratorProtector();
}
} else if (*name_ == heap()->then_string()) {
if (!isolate_->IsPromiseThenLookupChainIntact()) return;
// Setting the "then" property on any JSPromise instance or on the
// initial %PromisePrototype% invalidates the Promise#then protector.
if (holder_->IsJSPromise() ||
isolate_->IsInAnyContext(*holder_, Context::PROMISE_PROTOTYPE_INDEX)) {
isolate_->InvalidatePromiseThenProtector();
}
}
}
......
......@@ -275,11 +275,12 @@ class V8_EXPORT_PRIVATE LookupIterator final BASE_EMBEDDED {
inline void UpdateProtector() {
if (IsElement()) return;
// This list must be kept in sync with
// CodeStubAssembler::HasAssociatedProtector!
// CodeStubAssembler::CheckForAssociatedProtector!
if (*name_ == heap()->is_concat_spreadable_symbol() ||
*name_ == heap()->constructor_string() ||
*name_ == heap()->species_symbol() ||
*name_ == heap()->iterator_symbol()) {
*name_ == heap()->iterator_symbol() ||
*name_ == heap()->then_string()) {
InternalUpdateProtector();
}
}
......
......@@ -112,8 +112,6 @@ TEST(ContextMaps) {
Context::STRING_FUNCTION_INDEX);
VerifyStoredPrototypeMap(isolate, Context::REGEXP_PROTOTYPE_MAP_INDEX,
Context::REGEXP_FUNCTION_INDEX);
VerifyStoredPrototypeMap(isolate, Context::PROMISE_PROTOTYPE_MAP_INDEX,
Context::PROMISE_FUNCTION_INDEX);
}
TEST(InitialObjects) {
......
// 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.catch(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);
// 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.catch(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);
// 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.catch(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));
......@@ -337,9 +337,9 @@ KNOWN_OBJECTS = {
("OLD_SPACE", 0x029d9): "FastArrayIterationProtector",
("OLD_SPACE", 0x029e9): "ArrayIteratorProtector",
("OLD_SPACE", 0x02a11): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x02a61): "InfinityValue",
("OLD_SPACE", 0x02a71): "MinusZeroValue",
("OLD_SPACE", 0x02a81): "MinusInfinityValue",
("OLD_SPACE", 0x02a89): "InfinityValue",
("OLD_SPACE", 0x02a99): "MinusZeroValue",
("OLD_SPACE", 0x02aa9): "MinusInfinityValue",
}
# List of known V8 Frame Markers.
......
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