// 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/transitions.h" namespace v8 { namespace internal { namespace { template Handle SetBitFieldValue(Isolate* isolate, Handle 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 int InitPrototypeChecksImpl(Isolate* isolate, Handle handler, Handle* smi_handler, Handle receiver_map, Handle holder, Handle data1, MaybeHandle 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 native_context = isolate->native_context(); handler->set_data2(native_context->self_weak_cell()); } else { // Enable access checks on receiver. typedef typename ICHandler::DoAccessCheckOnReceiverBits Bit; *smi_handler = SetBitFieldValue(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(isolate, *smi_handler, true); } } if (fill_handler) { handler->set_data1(*data1); } Handle data2; if (maybe_data2.ToHandle(&data2)) { 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(*data2); } else { DCHECK_EQ(1, checks_count); handler->set_data3(*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 int GetPrototypeCheckCount( Isolate* isolate, Handle* smi_handler, Handle receiver_map, Handle holder, Handle data1, MaybeHandle maybe_data2 = MaybeHandle()) { DCHECK_NOT_NULL(smi_handler); return InitPrototypeChecksImpl(isolate, Handle(), smi_handler, receiver_map, holder, data1, maybe_data2); } template void InitPrototypeChecks( Isolate* isolate, Handle handler, Handle receiver_map, Handle holder, Handle data1, MaybeHandle maybe_data2 = MaybeHandle()) { InitPrototypeChecksImpl( isolate, handler, nullptr, receiver_map, holder, data1, maybe_data2); } } // namespace // static Handle LoadHandler::LoadFromPrototype(Isolate* isolate, Handle receiver_map, Handle holder, Handle smi_handler, MaybeHandle maybe_data1, MaybeHandle maybe_data2) { Handle data1; if (!maybe_data1.ToHandle(&data1)) { data1 = Map::GetOrCreatePrototypeWeakCell(holder, isolate); } int checks_count = GetPrototypeCheckCount( isolate, &smi_handler, receiver_map, holder, data1, maybe_data2); Handle validity_cell = Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); int data_count = 1 + checks_count; Handle 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 LoadHandler::LoadFullChain(Isolate* isolate, Handle receiver_map, Handle holder, Handle smi_handler) { Handle end; // null handle, means full prototype chain lookup. Handle data1 = holder; int checks_count = GetPrototypeCheckCount( isolate, &smi_handler, receiver_map, end, data1); Handle 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 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(Object* handler) { DisallowHeapAllocation no_gc; if (handler->IsSmi()) { int const raw_handler = Smi::cast(handler)->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 StoreHandler::StoreElementTransition( Isolate* isolate, Handle receiver_map, Handle transition, KeyedAccessStoreMode store_mode) { bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; ElementsKind elements_kind = receiver_map->elements_kind(); Handle stub = ElementsTransitionAndStoreStub( isolate, elements_kind, transition->elements_kind(), is_js_array, store_mode) .GetCode(); Handle validity_cell = Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); Handle cell = Map::WeakCellForMap(transition); Handle handler = isolate->factory()->NewStoreHandler(1); handler->set_smi_handler(*stub); handler->set_validity_cell(*validity_cell); handler->set_data1(*cell); return handler; } Handle StoreHandler::StoreTransition(Isolate* isolate, Handle transition_map) { bool is_dictionary_map = transition_map->is_dictionary_map(); #ifdef DEBUG if (!is_dictionary_map) { int descriptor = transition_map->LastAdded(); Handle descriptors(transition_map->instance_descriptors()); 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 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 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 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); } Handle cell = Map::WeakCellForMap(transition_map); return cell; } } // static Handle StoreHandler::StoreThroughPrototype( Isolate* isolate, Handle receiver_map, Handle holder, Handle smi_handler, MaybeHandle maybe_data1, MaybeHandle maybe_data2) { Handle data1; if (!maybe_data1.ToHandle(&data1)) { data1 = Map::GetOrCreatePrototypeWeakCell(holder, isolate); } int checks_count = GetPrototypeCheckCount( isolate, &smi_handler, receiver_map, holder, data1, maybe_data2); Handle validity_cell = Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); DCHECK_IMPLIES(validity_cell->IsSmi(), checks_count == 0); int data_count = 1 + checks_count; Handle 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 Handle StoreHandler::StoreGlobal(Isolate* isolate, Handle cell) { return isolate->factory()->NewWeakCell(cell); } // static Handle StoreHandler::StoreProxy(Isolate* isolate, Handle receiver_map, Handle proxy, Handle receiver) { Handle smi_handler = StoreProxy(isolate); if (receiver.is_identical_to(proxy)) return smi_handler; Handle holder_cell = isolate->factory()->NewWeakCell(proxy); return StoreThroughPrototype(isolate, receiver_map, proxy, smi_handler, holder_cell); } Object* StoreHandler::ValidHandlerOrNull(Object* raw_handler, Name* name, Handle* out_transition) { Smi* valid = Smi::FromInt(Map::kPrototypeChainValid); DCHECK(raw_handler->IsStoreHandler()); // Check validity cell. StoreHandler* handler = StoreHandler::cast(raw_handler); Object* raw_validity_cell = handler->validity_cell(); // |raw_valitity_cell| can be Smi::kZero if no validity cell is required // (which counts as valid). if (raw_validity_cell->IsCell() && Cell::cast(raw_validity_cell)->value() != valid) { return nullptr; } // We use this ValidHandlerOrNull() function only for transitioning store // handlers which are not applicable to receivers that require access checks. DCHECK(handler->smi_handler()->IsSmi()); DCHECK( !DoAccessCheckOnReceiverBits::decode(Smi::ToInt(handler->smi_handler()))); // Check if the transition target is deprecated. WeakCell* target_cell = GetTransitionCell(raw_handler); Map* transition = Map::cast(target_cell->value()); if (transition->is_deprecated()) return nullptr; *out_transition = handle(transition); return raw_handler; } } // namespace internal } // namespace v8