| // Copyright 2017 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. | |
| #include "src/ic/handler-configuration.h" | |
| #include "src/code-stubs.h" | |
| #include "src/ic/handler-configuration-inl.h" | |
| #include "src/objects/data-handler-inl.h" | |
| #include "src/objects/maybe-object.h" | |
| #include "src/transitions.h" | |
| namespace v8 { | |
| namespace internal { | |
| namespace { | |
| template <typename BitField> | |
| Handle<Smi> SetBitFieldValue(Isolate* isolate, Handle<Smi> smi_handler, | |
| typename BitField::FieldType value) { | |
| int config = smi_handler->value(); | |
| config = BitField::update(config, true); | |
| return handle(Smi::FromInt(config), isolate); | |
| } | |
| // TODO(ishell): Remove templatezation once we move common bits from | |
| // Load/StoreHandler to the base class. | |
| template <typename ICHandler, bool fill_handler = true> | |
| int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler, | |
| Handle<Smi>* smi_handler, Handle<Map> receiver_map, | |
| Handle<JSReceiver> holder, MaybeObjectHandle data1, | |
| MaybeObjectHandle maybe_data2) { | |
| int checks_count = 0; | |
| // Holder-is-receiver case itself does not add entries unless there is an | |
| // optional data2 value provided. | |
| if (receiver_map->IsPrimitiveMap() || | |
| receiver_map->is_access_check_needed()) { | |
| DCHECK(!receiver_map->IsJSGlobalObjectMap()); | |
| // 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_handler) { | |
| Handle<Context> native_context = isolate->native_context(); | |
| handler->set_data2(HeapObjectReference::Weak(*native_context)); | |
| } else { | |
| // Enable access checks on receiver. | |
| typedef typename ICHandler::DoAccessCheckOnReceiverBits Bit; | |
| *smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true); | |
| } | |
| checks_count++; | |
| } else if (receiver_map->is_dictionary_map() && | |
| !receiver_map->IsJSGlobalObjectMap()) { | |
| if (!fill_handler) { | |
| // Enable lookup on receiver. | |
| typedef typename ICHandler::LookupOnReceiverBits Bit; | |
| *smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true); | |
| } | |
| } | |
| if (fill_handler) { | |
| handler->set_data1(*data1); | |
| } | |
| if (!maybe_data2.is_null()) { | |
| if (fill_handler) { | |
| // This value will go either to data2 or data3 slot depending on whether | |
| // data2 slot is already occupied by native context. | |
| if (checks_count == 0) { | |
| handler->set_data2(*maybe_data2); | |
| } else { | |
| DCHECK_EQ(1, checks_count); | |
| handler->set_data3(*maybe_data2); | |
| } | |
| } | |
| 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. | |
| template <typename ICHandler> | |
| int GetPrototypeCheckCount( | |
| Isolate* isolate, Handle<Smi>* smi_handler, Handle<Map> receiver_map, | |
| Handle<JSReceiver> holder, MaybeObjectHandle data1, | |
| MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) { | |
| DCHECK_NOT_NULL(smi_handler); | |
| return InitPrototypeChecksImpl<ICHandler, false>(isolate, Handle<ICHandler>(), | |
| smi_handler, receiver_map, | |
| holder, data1, maybe_data2); | |
| } | |
| template <typename ICHandler> | |
| void InitPrototypeChecks(Isolate* isolate, Handle<ICHandler> handler, | |
| Handle<Map> receiver_map, Handle<JSReceiver> holder, | |
| MaybeObjectHandle data1, | |
| MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) { | |
| InitPrototypeChecksImpl<ICHandler, true>( | |
| isolate, handler, nullptr, receiver_map, holder, data1, maybe_data2); | |
| } | |
| } // namespace | |
| // static | |
| Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate, | |
| Handle<Map> receiver_map, | |
| Handle<JSReceiver> holder, | |
| Handle<Smi> smi_handler, | |
| MaybeObjectHandle maybe_data1, | |
| MaybeObjectHandle maybe_data2) { | |
| MaybeObjectHandle data1; | |
| if (maybe_data1.is_null()) { | |
| data1 = MaybeObjectHandle::Weak(holder); | |
| } else { | |
| data1 = maybe_data1; | |
| } | |
| int checks_count = GetPrototypeCheckCount<LoadHandler>( | |
| isolate, &smi_handler, receiver_map, holder, data1, maybe_data2); | |
| Handle<Object> validity_cell = | |
| Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); | |
| int data_count = 1 + checks_count; | |
| Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_count); | |
| handler->set_smi_handler(*smi_handler); | |
| handler->set_validity_cell(*validity_cell); | |
| InitPrototypeChecks(isolate, handler, receiver_map, holder, data1, | |
| maybe_data2); | |
| return handler; | |
| } | |
| // static | |
| Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate, | |
| Handle<Map> receiver_map, | |
| MaybeObjectHandle holder, | |
| Handle<Smi> smi_handler) { | |
| Handle<JSReceiver> end; // null handle, means full prototype chain lookup. | |
| MaybeObjectHandle data1 = holder; | |
| int checks_count = GetPrototypeCheckCount<LoadHandler>( | |
| isolate, &smi_handler, receiver_map, end, data1); | |
| Handle<Object> validity_cell = | |
| Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); | |
| if (validity_cell->IsSmi()) { | |
| 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; | |
| } | |
| int data_count = 1 + checks_count; | |
| Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_count); | |
| handler->set_smi_handler(*smi_handler); | |
| handler->set_validity_cell(*validity_cell); | |
| InitPrototypeChecks(isolate, handler, receiver_map, end, data1); | |
| return handler; | |
| } | |
| // static | |
| KeyedAccessLoadMode LoadHandler::GetKeyedAccessLoadMode(MaybeObject* handler) { | |
| DisallowHeapAllocation no_gc; | |
| if (handler->IsSmi()) { | |
| int const raw_handler = Smi::cast(handler->ToSmi())->value(); | |
| Kind const kind = KindBits::decode(raw_handler); | |
| if ((kind == kElement || kind == kIndexedString) && | |
| AllowOutOfBoundsBits::decode(raw_handler)) { | |
| return LOAD_IGNORE_OUT_OF_BOUNDS; | |
| } | |
| } | |
| return STANDARD_LOAD; | |
| } | |
| // static | |
| Handle<Object> StoreHandler::StoreElementTransition( | |
| Isolate* isolate, Handle<Map> receiver_map, Handle<Map> transition, | |
| KeyedAccessStoreMode store_mode) { | |
| bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; | |
| ElementsKind elements_kind = receiver_map->elements_kind(); | |
| Handle<Code> stub = ElementsTransitionAndStoreStub( | |
| isolate, elements_kind, transition->elements_kind(), | |
| is_js_array, store_mode) | |
| .GetCode(); | |
| Handle<Object> validity_cell = | |
| Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); | |
| Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(1); | |
| handler->set_smi_handler(*stub); | |
| handler->set_validity_cell(*validity_cell); | |
| handler->set_data1(HeapObjectReference::Weak(*transition)); | |
| return handler; | |
| } | |
| MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate, | |
| Handle<Map> transition_map) { | |
| bool is_dictionary_map = transition_map->is_dictionary_map(); | |
| #ifdef DEBUG | |
| if (!is_dictionary_map) { | |
| int descriptor = transition_map->LastAdded(); | |
| Handle<DescriptorArray> descriptors(transition_map->instance_descriptors(), | |
| isolate); | |
| PropertyDetails details = descriptors->GetDetails(descriptor); | |
| if (descriptors->GetKey(descriptor)->IsPrivate()) { | |
| DCHECK_EQ(DONT_ENUM, details.attributes()); | |
| } else { | |
| DCHECK_EQ(NONE, details.attributes()); | |
| } | |
| Representation representation = details.representation(); | |
| DCHECK(!representation.IsNone()); | |
| } | |
| #endif | |
| // Declarative handlers don't support access checks. | |
| DCHECK(!transition_map->is_access_check_needed()); | |
| // Get validity cell value if it is necessary for the handler. | |
| Handle<Object> validity_cell; | |
| if (is_dictionary_map || !transition_map->IsPrototypeValidityCellValid()) { | |
| validity_cell = | |
| Map::GetOrCreatePrototypeChainValidityCell(transition_map, isolate); | |
| } | |
| if (is_dictionary_map) { | |
| DCHECK(!transition_map->IsJSGlobalObjectMap()); | |
| Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(0); | |
| // Store normal with enabled lookup on receiver. | |
| int config = KindBits::encode(kNormal) | LookupOnReceiverBits::encode(true); | |
| handler->set_smi_handler(Smi::FromInt(config)); | |
| handler->set_validity_cell(*validity_cell); | |
| return MaybeObjectHandle(handler); | |
| } else { | |
| // Ensure the transition map contains a valid prototype validity cell. | |
| if (!validity_cell.is_null()) { | |
| transition_map->set_prototype_validity_cell(*validity_cell); | |
| } | |
| return MaybeObjectHandle::Weak(transition_map); | |
| } | |
| } | |
| // static | |
| Handle<Object> StoreHandler::StoreThroughPrototype( | |
| Isolate* isolate, Handle<Map> receiver_map, Handle<JSReceiver> holder, | |
| Handle<Smi> smi_handler, MaybeObjectHandle maybe_data1, | |
| MaybeObjectHandle maybe_data2) { | |
| MaybeObjectHandle data1; | |
| if (maybe_data1.is_null()) { | |
| data1 = MaybeObjectHandle::Weak(holder); | |
| } else { | |
| data1 = maybe_data1; | |
| } | |
| int checks_count = GetPrototypeCheckCount<StoreHandler>( | |
| isolate, &smi_handler, receiver_map, holder, data1, maybe_data2); | |
| Handle<Object> validity_cell = | |
| Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); | |
| DCHECK_IMPLIES(validity_cell->IsSmi(), checks_count == 0); | |
| int data_count = 1 + checks_count; | |
| Handle<StoreHandler> handler = | |
| isolate->factory()->NewStoreHandler(data_count); | |
| handler->set_smi_handler(*smi_handler); | |
| handler->set_validity_cell(*validity_cell); | |
| InitPrototypeChecks(isolate, handler, receiver_map, holder, data1, | |
| maybe_data2); | |
| return handler; | |
| } | |
| // static | |
| MaybeObjectHandle StoreHandler::StoreGlobal(Handle<PropertyCell> cell) { | |
| return MaybeObjectHandle::Weak(cell); | |
| } | |
| // static | |
| Handle<Object> StoreHandler::StoreProxy(Isolate* isolate, | |
| Handle<Map> receiver_map, | |
| Handle<JSProxy> proxy, | |
| Handle<JSReceiver> receiver) { | |
| Handle<Smi> smi_handler = StoreProxy(isolate); | |
| if (receiver.is_identical_to(proxy)) return smi_handler; | |
| return StoreThroughPrototype(isolate, receiver_map, proxy, smi_handler, | |
| MaybeObjectHandle::Weak(proxy)); | |
| } | |
| } // namespace internal | |
| } // namespace v8 |