[ic] Move handler construction code from ic to handler-configuration
Bug: Change-Id: I91b08ad6d95e60f84cb083b444bf0eb2fff10e27 Reviewed-on: https://chromium-review.googlesource.com/663864 Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#47998}
This commit is contained in:
parent
9c3dc33efe
commit
9a0d5d9700
@ -10,6 +10,204 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
template <bool fill_array = true>
|
||||
int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
|
||||
Handle<JSReceiver> holder, Handle<Name> name,
|
||||
Handle<FixedArray> array, int first_index) {
|
||||
if (!holder.is_null() && holder->map() == *receiver_map) return 0;
|
||||
|
||||
HandleScope scope(isolate);
|
||||
int checks_count = 0;
|
||||
|
||||
if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) {
|
||||
// The validity cell check for primitive and global proxy receivers does
|
||||
// not guarantee that certain native context ever had access to other
|
||||
// native context. However, a handler created for one native context could
|
||||
// be used in other native context through the megamorphic stub cache.
|
||||
// So we record the original native context to which this handler
|
||||
// corresponds.
|
||||
if (fill_array) {
|
||||
Handle<Context> native_context = isolate->native_context();
|
||||
array->set(first_index + checks_count, native_context->self_weak_cell());
|
||||
}
|
||||
checks_count++;
|
||||
|
||||
} else if (receiver_map->IsJSGlobalObjectMap()) {
|
||||
// If we are creating a handler for [Load/Store]GlobalIC then we need to
|
||||
// check that the property did not appear in the global object.
|
||||
if (fill_array) {
|
||||
Handle<JSGlobalObject> global = isolate->global_object();
|
||||
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
|
||||
global, name, PropertyCellType::kInvalidated);
|
||||
DCHECK(cell->value()->IsTheHole(isolate));
|
||||
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
|
||||
array->set(first_index + checks_count, *weak_cell);
|
||||
}
|
||||
checks_count++;
|
||||
}
|
||||
|
||||
// Create/count entries for each global or dictionary prototype appeared in
|
||||
// the prototype chain contains from receiver till holder.
|
||||
PrototypeIterator::WhereToEnd end = name->IsPrivate()
|
||||
? PrototypeIterator::END_AT_NON_HIDDEN
|
||||
: PrototypeIterator::END_AT_NULL;
|
||||
for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd();
|
||||
iter.Advance()) {
|
||||
Handle<JSReceiver> current =
|
||||
PrototypeIterator::GetCurrent<JSReceiver>(iter);
|
||||
if (holder.is_identical_to(current)) break;
|
||||
Handle<Map> current_map(current->map(), isolate);
|
||||
|
||||
if (current_map->IsJSGlobalObjectMap()) {
|
||||
if (fill_array) {
|
||||
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(current);
|
||||
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
|
||||
global, name, PropertyCellType::kInvalidated);
|
||||
DCHECK(cell->value()->IsTheHole(isolate));
|
||||
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
|
||||
array->set(first_index + checks_count, *weak_cell);
|
||||
}
|
||||
checks_count++;
|
||||
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (fill_array) {
|
||||
DCHECK_EQ(NameDictionary::kNotFound,
|
||||
current->property_dictionary()->FindEntry(name));
|
||||
Handle<WeakCell> weak_cell =
|
||||
Map::GetOrCreatePrototypeWeakCell(current, isolate);
|
||||
array->set(first_index + checks_count, *weak_cell);
|
||||
}
|
||||
checks_count++;
|
||||
}
|
||||
}
|
||||
return checks_count;
|
||||
}
|
||||
|
||||
// Returns 0 if the validity cell check is enough to ensure that the
|
||||
// prototype chain from |receiver_map| till |holder| did not change.
|
||||
// If the |holder| is an empty handle then the full prototype chain is
|
||||
// checked.
|
||||
// Returns -1 if the handler has to be compiled or the number of prototype
|
||||
// checks otherwise.
|
||||
int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map,
|
||||
Handle<JSReceiver> holder, Handle<Name> name) {
|
||||
return InitPrototypeChecks<false>(isolate, receiver_map, holder, name,
|
||||
Handle<FixedArray>(), 0);
|
||||
}
|
||||
|
||||
enum class HolderCellRequest {
|
||||
kGlobalPropertyCell,
|
||||
kHolder,
|
||||
};
|
||||
|
||||
Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSReceiver> holder,
|
||||
Handle<Name> name, HolderCellRequest request) {
|
||||
if (request == HolderCellRequest::kGlobalPropertyCell) {
|
||||
DCHECK(holder->IsJSGlobalObject());
|
||||
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
|
||||
GlobalDictionary* dict = global->global_dictionary();
|
||||
int number = dict->FindEntry(name);
|
||||
DCHECK_NE(NameDictionary::kNotFound, number);
|
||||
Handle<PropertyCell> cell(dict->CellAt(number), isolate);
|
||||
return isolate->factory()->NewWeakCell(cell);
|
||||
}
|
||||
return Map::GetOrCreatePrototypeWeakCell(holder, isolate);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate,
|
||||
Handle<Map> receiver_map,
|
||||
Handle<JSReceiver> holder,
|
||||
Handle<Name> name,
|
||||
Handle<Smi> smi_handler) {
|
||||
int checks_count =
|
||||
GetPrototypeCheckCount(isolate, receiver_map, holder, name);
|
||||
DCHECK_LE(0, checks_count);
|
||||
|
||||
if (receiver_map->IsPrimitiveMap() ||
|
||||
receiver_map->is_access_check_needed()) {
|
||||
DCHECK(!receiver_map->is_dictionary_map());
|
||||
DCHECK_LE(1, checks_count); // For native context.
|
||||
smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
|
||||
} else if (receiver_map->is_dictionary_map() &&
|
||||
!receiver_map->IsJSGlobalObjectMap()) {
|
||||
smi_handler = EnableLookupOnReceiver(isolate, smi_handler);
|
||||
}
|
||||
|
||||
Handle<Cell> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
|
||||
DCHECK(!validity_cell.is_null());
|
||||
|
||||
// LoadIC dispatcher expects PropertyCell as a "holder" in case of kGlobal
|
||||
// handler kind.
|
||||
HolderCellRequest request = GetHandlerKind(*smi_handler) == kGlobal
|
||||
? HolderCellRequest::kGlobalPropertyCell
|
||||
: HolderCellRequest::kHolder;
|
||||
|
||||
Handle<WeakCell> holder_cell = HolderCell(isolate, holder, name, request);
|
||||
|
||||
if (checks_count == 0) {
|
||||
return isolate->factory()->NewTuple3(holder_cell, smi_handler,
|
||||
validity_cell, TENURED);
|
||||
}
|
||||
Handle<FixedArray> handler_array(isolate->factory()->NewFixedArray(
|
||||
kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(kHolderCellIndex, *holder_cell);
|
||||
InitPrototypeChecks(isolate, receiver_map, holder, name, handler_array,
|
||||
kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate,
|
||||
Handle<Map> receiver_map,
|
||||
Handle<Object> holder,
|
||||
Handle<Name> name,
|
||||
Handle<Smi> smi_handler) {
|
||||
Handle<JSReceiver> end; // null handle
|
||||
int checks_count = GetPrototypeCheckCount(isolate, receiver_map, end, name);
|
||||
DCHECK_LE(0, checks_count);
|
||||
|
||||
if (receiver_map->IsPrimitiveMap() ||
|
||||
receiver_map->is_access_check_needed()) {
|
||||
DCHECK(!receiver_map->is_dictionary_map());
|
||||
DCHECK_LE(1, checks_count); // For native context.
|
||||
smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
|
||||
} else if (receiver_map->is_dictionary_map() &&
|
||||
!receiver_map->IsJSGlobalObjectMap()) {
|
||||
smi_handler = EnableLookupOnReceiver(isolate, smi_handler);
|
||||
}
|
||||
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
|
||||
if (validity_cell.is_null()) {
|
||||
DCHECK_EQ(0, checks_count);
|
||||
// Lookup on receiver isn't supported in case of a simple smi handler.
|
||||
if (!LookupOnReceiverBits::decode(smi_handler->value())) return smi_handler;
|
||||
validity_cell = handle(Smi::kZero, isolate);
|
||||
}
|
||||
|
||||
Factory* factory = isolate->factory();
|
||||
if (checks_count == 0) {
|
||||
return factory->NewTuple3(holder, smi_handler, validity_cell, TENURED);
|
||||
}
|
||||
Handle<FixedArray> handler_array(factory->NewFixedArray(
|
||||
LoadHandler::kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(kHolderCellIndex, *holder);
|
||||
InitPrototypeChecks(isolate, receiver_map, end, name, handler_array,
|
||||
kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
// |name| can be nullptr if no name/details check needs to be performed.
|
||||
Object* StoreHandler::ValidTuple3HandlerOrNull(Object* handler, Name* name,
|
||||
Handle<Map>* out_transition) {
|
||||
@ -25,7 +223,7 @@ Object* StoreHandler::ValidTuple3HandlerOrNull(Object* handler, Name* name,
|
||||
return nullptr;
|
||||
}
|
||||
// Step 2 (optional): Check transition key.
|
||||
WeakCell* target_cell = StoreHandler::GetTuple3TransitionCell(handler);
|
||||
WeakCell* target_cell = GetTuple3TransitionCell(handler);
|
||||
if (name != nullptr) {
|
||||
if (!TransitionsAccessor::IsMatchingMap(target_cell, name, kData, NONE)) {
|
||||
return nullptr;
|
||||
@ -38,6 +236,113 @@ Object* StoreHandler::ValidTuple3HandlerOrNull(Object* handler, Name* name,
|
||||
return handler;
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> StoreHandler::StoreTransition(Isolate* isolate,
|
||||
Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<Map> transition,
|
||||
Handle<Name> name) {
|
||||
Handle<Object> smi_handler;
|
||||
if (transition->is_dictionary_map()) {
|
||||
smi_handler = StoreNormal(isolate);
|
||||
} else {
|
||||
int descriptor = transition->LastAdded();
|
||||
Handle<DescriptorArray> descriptors(transition->instance_descriptors());
|
||||
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||
Representation representation = details.representation();
|
||||
DCHECK(!representation.IsNone());
|
||||
|
||||
// Declarative handlers don't support access checks.
|
||||
DCHECK(!transition->is_access_check_needed());
|
||||
|
||||
DCHECK_EQ(kData, details.kind());
|
||||
if (details.location() == kDescriptor) {
|
||||
smi_handler = TransitionToConstant(isolate, descriptor);
|
||||
|
||||
} else {
|
||||
DCHECK_EQ(kField, details.location());
|
||||
bool extend_storage =
|
||||
Map::cast(transition->GetBackPointer())->unused_property_fields() ==
|
||||
0;
|
||||
|
||||
FieldIndex index = FieldIndex::ForDescriptor(*transition, descriptor);
|
||||
smi_handler = TransitionToField(isolate, descriptor, index,
|
||||
representation, extend_storage);
|
||||
}
|
||||
}
|
||||
// |holder| is either a receiver if the property is non-existent or
|
||||
// one of the prototypes.
|
||||
DCHECK(!holder.is_null());
|
||||
bool is_nonexistent = holder->map() == transition->GetBackPointer();
|
||||
if (is_nonexistent) holder = Handle<JSObject>::null();
|
||||
|
||||
int checks_count =
|
||||
GetPrototypeCheckCount(isolate, receiver_map, holder, name);
|
||||
|
||||
DCHECK_LE(0, checks_count);
|
||||
DCHECK(!receiver_map->IsJSGlobalObjectMap());
|
||||
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
|
||||
if (validity_cell.is_null()) {
|
||||
DCHECK_EQ(0, checks_count);
|
||||
validity_cell = handle(Smi::kZero, isolate);
|
||||
}
|
||||
|
||||
Handle<WeakCell> transition_cell = Map::WeakCellForMap(transition);
|
||||
|
||||
Factory* factory = isolate->factory();
|
||||
if (checks_count == 0) {
|
||||
return factory->NewTuple3(transition_cell, smi_handler, validity_cell,
|
||||
TENURED);
|
||||
}
|
||||
Handle<FixedArray> handler_array(
|
||||
factory->NewFixedArray(kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(kTransitionCellIndex, *transition_cell);
|
||||
InitPrototypeChecks(isolate, receiver_map, holder, name, handler_array,
|
||||
kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> StoreHandler::StoreProxy(Isolate* isolate,
|
||||
Handle<Map> receiver_map,
|
||||
Handle<JSProxy> proxy,
|
||||
Handle<JSReceiver> receiver,
|
||||
Handle<Name> name) {
|
||||
Handle<Object> smi_handler = StoreProxy(isolate);
|
||||
|
||||
if (receiver.is_identical_to(proxy)) return smi_handler;
|
||||
|
||||
int checks_count = GetPrototypeCheckCount(isolate, receiver_map, proxy, name);
|
||||
|
||||
DCHECK_LE(0, checks_count);
|
||||
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
|
||||
if (validity_cell.is_null()) {
|
||||
DCHECK_EQ(0, checks_count);
|
||||
validity_cell = handle(Smi::kZero, isolate);
|
||||
}
|
||||
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<WeakCell> holder_cell = factory->NewWeakCell(proxy);
|
||||
|
||||
if (checks_count == 0) {
|
||||
return factory->NewTuple3(holder_cell, smi_handler, validity_cell, TENURED);
|
||||
}
|
||||
Handle<FixedArray> handler_array(
|
||||
factory->NewFixedArray(kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(kTransitionCellIndex, *holder_cell);
|
||||
InitPrototypeChecks(isolate, receiver_map, proxy, name, handler_array,
|
||||
kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
Object* StoreHandler::ValidFixedArrayHandlerOrNull(
|
||||
Object* raw_handler, Name* name, Handle<Map>* out_transition) {
|
||||
DCHECK(raw_handler->IsFixedArray());
|
||||
@ -46,7 +351,7 @@ Object* StoreHandler::ValidFixedArrayHandlerOrNull(
|
||||
Object* value = Cell::cast(handler->get(kValidityCellIndex))->value();
|
||||
if (value != Smi::FromInt(Map::kPrototypeChainValid)) return nullptr;
|
||||
// Step 2: Check transition key.
|
||||
WeakCell* target_cell = StoreHandler::GetArrayTransitionCell(handler);
|
||||
WeakCell* target_cell = GetArrayTransitionCell(handler);
|
||||
if (!TransitionsAccessor::IsMatchingMap(target_cell, name, kData, NONE)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -132,15 +132,22 @@ class LoadHandler {
|
||||
// dictionary.
|
||||
static inline Handle<Smi> LoadModuleExport(Isolate* isolate, int index);
|
||||
|
||||
// Sets DoAccessCheckOnReceiverBits in given Smi-handler. The receiver
|
||||
// check is a part of a prototype chain check.
|
||||
static inline Handle<Smi> EnableAccessCheckOnReceiver(
|
||||
Isolate* isolate, Handle<Smi> smi_handler);
|
||||
// Creates a data handler that represents a load of a non-existent property.
|
||||
// {holder} is the object from which the property is loaded. If no holder is
|
||||
// needed (e.g., for "nonexistent"), null_value() may be passed in.
|
||||
static Handle<Object> LoadFullChain(Isolate* isolate,
|
||||
Handle<Map> receiver_map,
|
||||
Handle<Object> holder, Handle<Name> name,
|
||||
Handle<Smi> smi_handler);
|
||||
|
||||
// Sets LookupOnReceiverBits in given Smi-handler. The receiver
|
||||
// check is a part of a prototype chain check.
|
||||
static inline Handle<Smi> EnableLookupOnReceiver(Isolate* isolate,
|
||||
Handle<Smi> smi_handler);
|
||||
// Creates a data handler that represents a prototype chain check followed
|
||||
// by given Smi-handler that encoded a load from the holder.
|
||||
// Can be used only if GetPrototypeCheckCount() returns non negative value.
|
||||
static Handle<Object> LoadFromPrototype(Isolate* isolate,
|
||||
Handle<Map> receiver_map,
|
||||
Handle<JSReceiver> holder,
|
||||
Handle<Name> name,
|
||||
Handle<Smi> smi_handler);
|
||||
|
||||
// Creates a Smi-handler for loading a non-existent property. Works only as
|
||||
// a part of prototype chain check.
|
||||
@ -151,6 +158,17 @@ class LoadHandler {
|
||||
ElementsKind elements_kind,
|
||||
bool convert_hole_to_undefined,
|
||||
bool is_js_array);
|
||||
|
||||
private:
|
||||
// Sets DoAccessCheckOnReceiverBits in given Smi-handler. The receiver
|
||||
// check is a part of a prototype chain check.
|
||||
static inline Handle<Smi> EnableAccessCheckOnReceiver(
|
||||
Isolate* isolate, Handle<Smi> smi_handler);
|
||||
|
||||
// Sets LookupOnReceiverBits in given Smi-handler. The receiver
|
||||
// check is a part of a prototype chain check.
|
||||
static inline Handle<Smi> EnableLookupOnReceiver(Isolate* isolate,
|
||||
Handle<Smi> smi_handler);
|
||||
};
|
||||
|
||||
// A set of bit fields representing Smi handlers for stores.
|
||||
@ -226,12 +244,29 @@ class StoreHandler {
|
||||
PropertyConstness constness,
|
||||
Representation representation);
|
||||
|
||||
static Handle<Object> StoreTransition(Isolate* isolate,
|
||||
Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<Map> transition,
|
||||
Handle<Name> name);
|
||||
|
||||
static Handle<Object> StoreProxy(Isolate* isolate, Handle<Map> receiver_map,
|
||||
Handle<JSProxy> proxy,
|
||||
Handle<JSReceiver> receiver,
|
||||
Handle<Name> name);
|
||||
|
||||
// Creates a Smi-handler for storing a property to a slow object.
|
||||
static inline Handle<Smi> StoreNormal(Isolate* isolate);
|
||||
|
||||
// Creates a Smi-handler for storing a property on a proxy.
|
||||
static inline Handle<Smi> StoreProxy(Isolate* isolate);
|
||||
|
||||
private:
|
||||
static inline Handle<Smi> StoreField(Isolate* isolate, Kind kind,
|
||||
int descriptor, FieldIndex field_index,
|
||||
Representation representation,
|
||||
bool extend_storage);
|
||||
|
||||
// Creates a Smi-handler for transitioning store to a field.
|
||||
static inline Handle<Smi> TransitionToField(Isolate* isolate, int descriptor,
|
||||
FieldIndex field_index,
|
||||
@ -242,12 +277,6 @@ class StoreHandler {
|
||||
// case the only thing that needs to be done is an update of a map).
|
||||
static inline Handle<Smi> TransitionToConstant(Isolate* isolate,
|
||||
int descriptor);
|
||||
|
||||
private:
|
||||
static inline Handle<Smi> StoreField(Isolate* isolate, Kind kind,
|
||||
int descriptor, FieldIndex field_index,
|
||||
Representation representation,
|
||||
bool extend_storage);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
338
src/ic/ic.cc
338
src/ic/ic.cc
@ -647,204 +647,6 @@ Handle<Smi> LoadIC::SimpleFieldLoad(Isolate* isolate, FieldIndex index) {
|
||||
return LoadHandler::LoadField(isolate, index);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <bool fill_array = true>
|
||||
int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
|
||||
Handle<JSReceiver> holder, Handle<Name> name,
|
||||
Handle<FixedArray> array, int first_index) {
|
||||
if (!holder.is_null() && holder->map() == *receiver_map) return 0;
|
||||
|
||||
HandleScope scope(isolate);
|
||||
int checks_count = 0;
|
||||
|
||||
if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) {
|
||||
// The validity cell check for primitive and global proxy receivers does
|
||||
// not guarantee that certain native context ever had access to other
|
||||
// native context. However, a handler created for one native context could
|
||||
// be used in other native context through the megamorphic stub cache.
|
||||
// So we record the original native context to which this handler
|
||||
// corresponds.
|
||||
if (fill_array) {
|
||||
Handle<Context> native_context = isolate->native_context();
|
||||
array->set(first_index + checks_count, native_context->self_weak_cell());
|
||||
}
|
||||
checks_count++;
|
||||
|
||||
} else if (receiver_map->IsJSGlobalObjectMap()) {
|
||||
// If we are creating a handler for [Load/Store]GlobalIC then we need to
|
||||
// check that the property did not appear in the global object.
|
||||
if (fill_array) {
|
||||
Handle<JSGlobalObject> global = isolate->global_object();
|
||||
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
|
||||
global, name, PropertyCellType::kInvalidated);
|
||||
DCHECK(cell->value()->IsTheHole(isolate));
|
||||
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
|
||||
array->set(first_index + checks_count, *weak_cell);
|
||||
}
|
||||
checks_count++;
|
||||
}
|
||||
|
||||
// Create/count entries for each global or dictionary prototype appeared in
|
||||
// the prototype chain contains from receiver till holder.
|
||||
PrototypeIterator::WhereToEnd end = name->IsPrivate()
|
||||
? PrototypeIterator::END_AT_NON_HIDDEN
|
||||
: PrototypeIterator::END_AT_NULL;
|
||||
for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd();
|
||||
iter.Advance()) {
|
||||
Handle<JSReceiver> current =
|
||||
PrototypeIterator::GetCurrent<JSReceiver>(iter);
|
||||
if (holder.is_identical_to(current)) break;
|
||||
Handle<Map> current_map(current->map(), isolate);
|
||||
|
||||
if (current_map->IsJSGlobalObjectMap()) {
|
||||
if (fill_array) {
|
||||
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(current);
|
||||
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
|
||||
global, name, PropertyCellType::kInvalidated);
|
||||
DCHECK(cell->value()->IsTheHole(isolate));
|
||||
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
|
||||
array->set(first_index + checks_count, *weak_cell);
|
||||
}
|
||||
checks_count++;
|
||||
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (fill_array) {
|
||||
DCHECK_EQ(NameDictionary::kNotFound,
|
||||
current->property_dictionary()->FindEntry(name));
|
||||
Handle<WeakCell> weak_cell =
|
||||
Map::GetOrCreatePrototypeWeakCell(current, isolate);
|
||||
array->set(first_index + checks_count, *weak_cell);
|
||||
}
|
||||
checks_count++;
|
||||
}
|
||||
}
|
||||
return checks_count;
|
||||
}
|
||||
|
||||
// Returns 0 if the validity cell check is enough to ensure that the
|
||||
// prototype chain from |receiver_map| till |holder| did not change.
|
||||
// If the |holder| is an empty handle then the full prototype chain is
|
||||
// checked.
|
||||
// Returns -1 if the handler has to be compiled or the number of prototype
|
||||
// checks otherwise.
|
||||
int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map,
|
||||
Handle<JSReceiver> holder, Handle<Name> name) {
|
||||
return InitPrototypeChecks<false>(isolate, receiver_map, holder, name,
|
||||
Handle<FixedArray>(), 0);
|
||||
}
|
||||
|
||||
enum class HolderCellRequest {
|
||||
kGlobalPropertyCell,
|
||||
kHolder,
|
||||
};
|
||||
|
||||
Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSReceiver> holder,
|
||||
Handle<Name> name, HolderCellRequest request) {
|
||||
if (request == HolderCellRequest::kGlobalPropertyCell) {
|
||||
DCHECK(holder->IsJSGlobalObject());
|
||||
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
|
||||
GlobalDictionary* dict = global->global_dictionary();
|
||||
int number = dict->FindEntry(name);
|
||||
DCHECK_NE(NameDictionary::kNotFound, number);
|
||||
Handle<PropertyCell> cell(dict->CellAt(number), isolate);
|
||||
return isolate->factory()->NewWeakCell(cell);
|
||||
}
|
||||
return Map::GetOrCreatePrototypeWeakCell(holder, isolate);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map,
|
||||
Handle<JSReceiver> holder,
|
||||
Handle<Name> name,
|
||||
Handle<Smi> smi_handler) {
|
||||
int checks_count =
|
||||
GetPrototypeCheckCount(isolate(), receiver_map, holder, name);
|
||||
DCHECK_LE(0, checks_count);
|
||||
|
||||
if (receiver_map->IsPrimitiveMap() ||
|
||||
receiver_map->is_access_check_needed()) {
|
||||
DCHECK(!receiver_map->is_dictionary_map());
|
||||
DCHECK_LE(1, checks_count); // For native context.
|
||||
smi_handler =
|
||||
LoadHandler::EnableAccessCheckOnReceiver(isolate(), smi_handler);
|
||||
} else if (receiver_map->is_dictionary_map() &&
|
||||
!receiver_map->IsJSGlobalObjectMap()) {
|
||||
smi_handler = LoadHandler::EnableLookupOnReceiver(isolate(), smi_handler);
|
||||
}
|
||||
|
||||
Handle<Cell> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||
DCHECK(!validity_cell.is_null());
|
||||
|
||||
// LoadIC dispatcher expects PropertyCell as a "holder" in case of kGlobal
|
||||
// handler kind.
|
||||
HolderCellRequest request =
|
||||
LoadHandler::GetHandlerKind(*smi_handler) == LoadHandler::kGlobal
|
||||
? HolderCellRequest::kGlobalPropertyCell
|
||||
: HolderCellRequest::kHolder;
|
||||
|
||||
Handle<WeakCell> holder_cell = HolderCell(isolate(), holder, name, request);
|
||||
|
||||
if (checks_count == 0) {
|
||||
return isolate()->factory()->NewTuple3(holder_cell, smi_handler,
|
||||
validity_cell, TENURED);
|
||||
}
|
||||
Handle<FixedArray> handler_array(isolate()->factory()->NewFixedArray(
|
||||
LoadHandler::kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(LoadHandler::kHolderCellIndex, *holder_cell);
|
||||
InitPrototypeChecks(isolate(), receiver_map, holder, name, handler_array,
|
||||
LoadHandler::kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
Handle<Object> LoadIC::LoadFullChain(Handle<Map> receiver_map,
|
||||
Handle<Object> holder, Handle<Name> name,
|
||||
Handle<Smi> smi_handler) {
|
||||
Handle<JSReceiver> end; // null handle
|
||||
int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, end, name);
|
||||
DCHECK_LE(0, checks_count);
|
||||
|
||||
if (receiver_map->IsPrimitiveMap() ||
|
||||
receiver_map->is_access_check_needed()) {
|
||||
DCHECK(!receiver_map->is_dictionary_map());
|
||||
DCHECK_LE(1, checks_count); // For native context.
|
||||
smi_handler =
|
||||
LoadHandler::EnableAccessCheckOnReceiver(isolate(), smi_handler);
|
||||
} else if (receiver_map->is_dictionary_map() &&
|
||||
!receiver_map->IsJSGlobalObjectMap()) {
|
||||
smi_handler = LoadHandler::EnableLookupOnReceiver(isolate(), smi_handler);
|
||||
}
|
||||
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||
if (validity_cell.is_null()) {
|
||||
DCHECK_EQ(0, checks_count);
|
||||
// Lookup on receiver isn't supported in case of a simple smi handler.
|
||||
if (!LoadHandler::LookupOnReceiverBits::decode(smi_handler->value())) {
|
||||
return smi_handler;
|
||||
}
|
||||
validity_cell = handle(Smi::kZero, isolate());
|
||||
}
|
||||
|
||||
Factory* factory = isolate()->factory();
|
||||
if (checks_count == 0) {
|
||||
return factory->NewTuple3(holder, smi_handler, validity_cell, TENURED);
|
||||
}
|
||||
Handle<FixedArray> handler_array(factory->NewFixedArray(
|
||||
LoadHandler::kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(LoadHandler::kHolderCellIndex, *holder);
|
||||
InitPrototypeChecks(isolate(), receiver_map, end, name, handler_array,
|
||||
LoadHandler::kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
void LoadIC::UpdateCaches(LookupIterator* lookup) {
|
||||
if (state() == UNINITIALIZED && !IsLoadGlobalIC()) {
|
||||
// This is the first time we execute this inline cache. Set the target to
|
||||
@ -861,8 +663,9 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) {
|
||||
} else if (!lookup->IsFound()) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH);
|
||||
Handle<Smi> smi_handler = LoadHandler::LoadNonExistent(isolate());
|
||||
code = LoadFullChain(receiver_map(), isolate()->factory()->null_value(),
|
||||
lookup->name(), smi_handler);
|
||||
code = LoadHandler::LoadFullChain(isolate(), receiver_map(),
|
||||
isolate()->factory()->null_value(),
|
||||
lookup->name(), smi_handler);
|
||||
} else {
|
||||
if (IsLoadGlobalIC()) {
|
||||
if (lookup->TryLookupCachedProperty()) {
|
||||
@ -997,7 +800,8 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
holder_ref = Map::GetOrCreatePrototypeWeakCell(holder, isolate());
|
||||
}
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonMaskingInterceptorDH);
|
||||
return LoadFullChain(map, holder_ref, lookup->name(), smi_handler);
|
||||
return LoadHandler::LoadFullChain(isolate(), map, holder_ref,
|
||||
lookup->name(), smi_handler);
|
||||
}
|
||||
|
||||
if (receiver_is_holder) {
|
||||
@ -1007,7 +811,8 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
}
|
||||
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptorFromPrototypeDH);
|
||||
return LoadFromPrototype(map, holder, lookup->name(), smi_handler);
|
||||
return LoadHandler::LoadFromPrototype(isolate(), map, holder,
|
||||
lookup->name(), smi_handler);
|
||||
}
|
||||
|
||||
case LookupIterator::ACCESSOR: {
|
||||
@ -1094,7 +899,8 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalFromPrototypeDH);
|
||||
}
|
||||
|
||||
return LoadFromPrototype(map, holder, lookup->name(), smi_handler);
|
||||
return LoadHandler::LoadFromPrototype(isolate(), map, holder,
|
||||
lookup->name(), smi_handler);
|
||||
}
|
||||
|
||||
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
|
||||
@ -1112,7 +918,8 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadApiGetterDH);
|
||||
if (receiver_is_holder) return smi_handler;
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadApiGetterFromPrototypeDH);
|
||||
return LoadFromPrototype(map, holder, lookup->name(), smi_handler);
|
||||
return LoadHandler::LoadFromPrototype(isolate(), map, holder,
|
||||
lookup->name(), smi_handler);
|
||||
}
|
||||
|
||||
case LookupIterator::DATA: {
|
||||
@ -1126,7 +933,8 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
// global object.
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadGlobalDH);
|
||||
smi_handler = LoadHandler::LoadGlobal(isolate());
|
||||
return LoadFromPrototype(map, holder, lookup->name(), smi_handler);
|
||||
return LoadHandler::LoadFromPrototype(isolate(), map, holder,
|
||||
lookup->name(), smi_handler);
|
||||
}
|
||||
DCHECK(!holder->IsJSGlobalObject());
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalDH);
|
||||
@ -1153,7 +961,8 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
if (receiver_is_holder) return smi_handler;
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantFromPrototypeDH);
|
||||
}
|
||||
return LoadFromPrototype(map, holder, lookup->name(), smi_handler);
|
||||
return LoadHandler::LoadFromPrototype(isolate(), map, holder,
|
||||
lookup->name(), smi_handler);
|
||||
}
|
||||
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadIntegerIndexedExoticDH);
|
||||
@ -1165,7 +974,8 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
if (receiver_is_holder_proxy) {
|
||||
return smi_handler;
|
||||
}
|
||||
return LoadFromPrototype(map, holder_proxy, lookup->name(), smi_handler);
|
||||
return LoadHandler::LoadFromPrototype(isolate(), map, holder_proxy,
|
||||
lookup->name(), smi_handler);
|
||||
}
|
||||
case LookupIterator::ACCESS_CHECK:
|
||||
case LookupIterator::NOT_FOUND:
|
||||
@ -1596,112 +1406,6 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value,
|
||||
TRACE_IC("StoreIC", lookup->name());
|
||||
}
|
||||
|
||||
Handle<Object> StoreIC::StoreTransition(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<Map> transition,
|
||||
Handle<Name> name) {
|
||||
Handle<Object> smi_handler;
|
||||
if (transition->is_dictionary_map()) {
|
||||
smi_handler = StoreHandler::StoreNormal(isolate());
|
||||
} else {
|
||||
int descriptor = transition->LastAdded();
|
||||
Handle<DescriptorArray> descriptors(transition->instance_descriptors());
|
||||
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||
Representation representation = details.representation();
|
||||
DCHECK(!representation.IsNone());
|
||||
|
||||
// Declarative handlers don't support access checks.
|
||||
DCHECK(!transition->is_access_check_needed());
|
||||
|
||||
DCHECK_EQ(kData, details.kind());
|
||||
if (details.location() == kDescriptor) {
|
||||
smi_handler = StoreHandler::TransitionToConstant(isolate(), descriptor);
|
||||
|
||||
} else {
|
||||
DCHECK_EQ(kField, details.location());
|
||||
bool extend_storage =
|
||||
Map::cast(transition->GetBackPointer())->unused_property_fields() ==
|
||||
0;
|
||||
|
||||
FieldIndex index = FieldIndex::ForDescriptor(*transition, descriptor);
|
||||
smi_handler = StoreHandler::TransitionToField(
|
||||
isolate(), descriptor, index, representation, extend_storage);
|
||||
}
|
||||
}
|
||||
// |holder| is either a receiver if the property is non-existent or
|
||||
// one of the prototypes.
|
||||
DCHECK(!holder.is_null());
|
||||
bool is_nonexistent = holder->map() == transition->GetBackPointer();
|
||||
if (is_nonexistent) holder = Handle<JSObject>::null();
|
||||
|
||||
int checks_count =
|
||||
GetPrototypeCheckCount(isolate(), receiver_map, holder, name);
|
||||
|
||||
DCHECK_LE(0, checks_count);
|
||||
DCHECK(!receiver_map->IsJSGlobalObjectMap());
|
||||
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||
if (validity_cell.is_null()) {
|
||||
DCHECK_EQ(0, checks_count);
|
||||
validity_cell = handle(Smi::kZero, isolate());
|
||||
}
|
||||
|
||||
Handle<WeakCell> transition_cell = Map::WeakCellForMap(transition);
|
||||
|
||||
Factory* factory = isolate()->factory();
|
||||
if (checks_count == 0) {
|
||||
return factory->NewTuple3(transition_cell, smi_handler, validity_cell,
|
||||
TENURED);
|
||||
}
|
||||
Handle<FixedArray> handler_array(factory->NewFixedArray(
|
||||
StoreHandler::kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(StoreHandler::kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(StoreHandler::kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(StoreHandler::kTransitionCellIndex, *transition_cell);
|
||||
InitPrototypeChecks(isolate(), receiver_map, holder, name, handler_array,
|
||||
StoreHandler::kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
Handle<Object> StoreIC::StoreProxy(Handle<Map> receiver_map,
|
||||
Handle<JSProxy> proxy,
|
||||
Handle<JSReceiver> receiver,
|
||||
Handle<Name> name) {
|
||||
Handle<Object> smi_handler = StoreHandler::StoreProxy(isolate());
|
||||
|
||||
if (receiver.is_identical_to(proxy)) {
|
||||
return smi_handler;
|
||||
}
|
||||
|
||||
int checks_count =
|
||||
GetPrototypeCheckCount(isolate(), receiver_map, proxy, name);
|
||||
|
||||
DCHECK_LE(0, checks_count);
|
||||
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||
if (validity_cell.is_null()) {
|
||||
DCHECK_EQ(0, checks_count);
|
||||
validity_cell = handle(Smi::kZero, isolate());
|
||||
}
|
||||
|
||||
Factory* factory = isolate()->factory();
|
||||
Handle<WeakCell> holder_cell = factory->NewWeakCell(proxy);
|
||||
|
||||
if (checks_count == 0) {
|
||||
return factory->NewTuple3(holder_cell, smi_handler, validity_cell, TENURED);
|
||||
}
|
||||
Handle<FixedArray> handler_array(factory->NewFixedArray(
|
||||
StoreHandler::kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(StoreHandler::kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(StoreHandler::kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(StoreHandler::kTransitionCellIndex, *holder_cell);
|
||||
InitPrototypeChecks(isolate(), receiver_map, proxy, name, handler_array,
|
||||
StoreHandler::kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Handle<Object> StoreGlobal(Isolate* isolate, Handle<PropertyCell> cell) {
|
||||
@ -1730,8 +1434,8 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
DCHECK(lookup->IsCacheableTransition());
|
||||
Handle<Map> transition = lookup->transition_map();
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreTransitionDH);
|
||||
Handle<Object> handler =
|
||||
StoreTransition(receiver_map(), holder, transition, lookup->name());
|
||||
Handle<Object> handler = StoreHandler::StoreTransition(
|
||||
isolate(), receiver_map(), holder, transition, lookup->name());
|
||||
TransitionsAccessor(receiver_map())
|
||||
.UpdateHandler(*lookup->name(), *handler);
|
||||
return handler;
|
||||
@ -1845,8 +1549,8 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
Handle<JSReceiver> receiver =
|
||||
Handle<JSReceiver>::cast(lookup->GetReceiver());
|
||||
Handle<JSProxy> holder = lookup->GetHolder<JSProxy>();
|
||||
return StoreIC::StoreProxy(receiver_map(), holder, receiver,
|
||||
lookup->name());
|
||||
return StoreHandler::StoreProxy(isolate(), receiver_map(), holder,
|
||||
receiver, lookup->name());
|
||||
}
|
||||
|
||||
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
||||
|
20
src/ic/ic.h
20
src/ic/ic.h
@ -271,19 +271,6 @@ class LoadIC : public IC {
|
||||
// Creates a data handler that represents a load of a field by given index.
|
||||
static Handle<Smi> SimpleFieldLoad(Isolate* isolate, FieldIndex index);
|
||||
|
||||
// Creates a data handler that represents a prototype chain check followed
|
||||
// by given Smi-handler that encoded a load from the holder.
|
||||
// Can be used only if GetPrototypeCheckCount() returns non negative value.
|
||||
Handle<Object> LoadFromPrototype(Handle<Map> receiver_map,
|
||||
Handle<JSReceiver> holder, Handle<Name> name,
|
||||
Handle<Smi> smi_handler);
|
||||
|
||||
// Creates a data handler that represents a load of a non-existent property.
|
||||
// {holder} is the object from which the property is loaded. If no holder is
|
||||
// needed (e.g., for "nonexistent"), null_value() may be passed in.
|
||||
Handle<Object> LoadFullChain(Handle<Map> receiver_map, Handle<Object> holder,
|
||||
Handle<Name> name, Handle<Smi> smi_handler);
|
||||
|
||||
friend class IC;
|
||||
friend class NamedLoadHandlerCompiler;
|
||||
};
|
||||
@ -360,13 +347,6 @@ class StoreIC : public IC {
|
||||
Handle<Code> CompileHandler(LookupIterator* lookup) override;
|
||||
|
||||
private:
|
||||
Handle<Object> StoreTransition(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<Map> transition, Handle<Name> name);
|
||||
|
||||
Handle<Object> StoreProxy(Handle<Map> receiver_map, Handle<JSProxy> proxy,
|
||||
Handle<JSReceiver> receiver, Handle<Name> name);
|
||||
|
||||
friend class IC;
|
||||
|
||||
bool created_new_transition_ = false;
|
||||
|
Loading…
Reference in New Issue
Block a user