/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/ic/handler-configuration.cc
760 строк
29 KB
Michaël Zasso
deps: update V8 to 14.6.202.33
24 апр 2026, 19:01
Не верифицирован
24 апр 2026, 19:01
f1e0b83
Код
Авторство
О чём код?
// 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/codegen/code-factory.h" #include "src/ic/handler-configuration-inl.h" #include "src/objects/data-handler-inl.h" #include "src/objects/maybe-object.h" #include "src/objects/transitions.h" namespace v8 { namespace internal { namespace { template <typename BitField> inline Tagged<Smi> SetBitFieldValue(Tagged<Smi> smi_handler, typename BitField::FieldType value) { int config = smi_handler.value(); config = BitField::update(config, value); return Smi::FromInt(config); } // 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, DirectHandle<ICHandler> handler, Tagged<Smi>* smi_handler, DirectHandle<Map> lookup_start_object_map, MaybeObjectDirectHandle data1, MaybeObjectDirectHandle maybe_data2) { int data_size = 1; // Holder-is-lookup-start-object case itself does not add entries unless // there is an optional data2 value provided. DCHECK_IMPLIES(IsJSGlobalObjectMap(*lookup_start_object_map), lookup_start_object_map->is_prototype_map()); if (IsPrimitiveMap(*lookup_start_object_map) || lookup_start_object_map->is_access_check_needed()) { DCHECK(!IsJSGlobalObjectMap(*lookup_start_object_map)); // 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 another native context through the megamorphic stub cache. // So we record the original native context to which this handler // corresponds in the validity cell. if (fill_handler) { if (DEBUG_BOOL) { // Make sure validity cell contains native context. Tagged<Cell> validity_cell = Cast<Cell>(handler->validity_cell()); Tagged<HeapObject> heap_object; CHECK(validity_cell->maybe_value().GetHeapObject(&heap_object)); CHECK(IsNativeContext(heap_object)); } } else { // Enable access checks on the lookup start object. *smi_handler = SetBitFieldValue< typename ICHandler::DoAccessCheckOnLookupStartObjectBits>( *smi_handler, true); } } else if (lookup_start_object_map->is_dictionary_map() && !IsJSGlobalObjectMap(*lookup_start_object_map)) { if (!fill_handler) { // Enable lookup on lookup start object. *smi_handler = SetBitFieldValue<typename ICHandler::LookupOnLookupStartObjectBits>( *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 (data_size == 1) { handler->set_data2(*maybe_data2); } else { DCHECK_EQ(2, data_size); handler->set_data3(*maybe_data2); } } data_size++; } return data_size; } // Returns 0 if the validity cell check is enough to ensure that the // prototype chain from |lookup_start_object_map| till |holder| did not change. // If the |holder| is an empty handle then the full prototype chain is // checked. template <typename ICHandler> int GetHandlerDataSize( Isolate* isolate, Tagged<Smi>* smi_handler, DirectHandle<Map> lookup_start_object_map, MaybeObjectDirectHandle data1, MaybeObjectDirectHandle maybe_data2 = MaybeObjectDirectHandle()) { DCHECK_NOT_NULL(smi_handler); return InitPrototypeChecksImpl<ICHandler, false>( isolate, DirectHandle<ICHandler>(), smi_handler, lookup_start_object_map, data1, maybe_data2); } template <typename ICHandler> void InitPrototypeChecks( Isolate* isolate, DirectHandle<ICHandler> handler, DirectHandle<Map> lookup_start_object_map, MaybeObjectDirectHandle data1, MaybeObjectDirectHandle maybe_data2 = MaybeObjectDirectHandle()) { InitPrototypeChecksImpl<ICHandler, true>( isolate, handler, nullptr, lookup_start_object_map, data1, maybe_data2); } } // namespace // static Handle<LoadHandler> LoadHandler::LoadFromPrototype( Isolate* isolate, DirectHandle<Map> lookup_start_object_map, DirectHandle<JSReceiver> holder, Tagged<Smi> smi_handler, MaybeObjectDirectHandle maybe_data1, MaybeObjectDirectHandle maybe_data2) { MaybeObjectDirectHandle data1; if (maybe_data1.is_null()) { data1 = MaybeObjectDirectHandle::Weak(holder); } else { data1 = maybe_data1; } int data_size = GetHandlerDataSize<LoadHandler>( isolate, &smi_handler, lookup_start_object_map, data1, maybe_data2); DirectHandle<UnionOf<Smi, Cell>> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(lookup_start_object_map, isolate); // There must be a validity cell in case access check is needed. // ICs support access checks only for JSGlobalProxy objects and they are // guaranteed to have validity cell. DCHECK_IMPLIES( *validity_cell == Map::kNoValidityCellSentinel, !DoAccessCheckOnLookupStartObjectBits::decode(smi_handler.value())); Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size); handler->set_smi_handler(smi_handler); handler->set_validity_cell(*validity_cell); InitPrototypeChecks(isolate, direct_handle(handler), lookup_start_object_map, data1, maybe_data2); return handler; } Handle<Object> LoadHandler::LoadNonExistent( Isolate* isolate, DirectHandle<Map> lookup_start_object_map) { Tagged<Smi> smi_handler = Smi::FromInt(KindBits::encode(Kind::kNonExistent)); MaybeObjectDirectHandle data1 = MaybeObjectDirectHandle(isolate->factory()->null_value()); int data_size = GetHandlerDataSize<LoadHandler>( isolate, &smi_handler, lookup_start_object_map, data1); DirectHandle<PrototypeInfo> prototype_info; DirectHandle<UnionOf<Smi, Cell>> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(lookup_start_object_map, isolate, &prototype_info); // There must be a validity cell in case access check is needed. // ICs support access checks only for JSGlobalProxy objects and they are // guaranteed to have validity cell. DCHECK_IMPLIES( *validity_cell == Map::kNoValidityCellSentinel, !DoAccessCheckOnLookupStartObjectBits::decode(smi_handler.value())); // Try to fetch cached handler if it was already created. int handler_index; if (*validity_cell == Map::kNoValidityCellSentinel) { DCHECK_EQ(1, data_size); // Lookup on lookup start object isn't supported in case of a simple smi // handler. if (!LookupOnLookupStartObjectBits::decode(smi_handler.value())) { return handle(smi_handler, isolate); } // Handlers with Smi validity cells are not cacheable. handler_index = -1; } else { // |smi_handler| already has do-access-check-on-lookup or lookup-on-receiver // bits set if necessary, so just use them in order to figure out the index // of the cached non-existent handler. int config = smi_handler.value(); const uint32_t non_default_mask = DoAccessCheckOnLookupStartObjectBits::kMask | LookupOnLookupStartObjectBits::kMask; if (config & non_default_mask) { if (config & LookupOnLookupStartObjectBits::kMask) { handler_index = PrototypeInfo::kLoadNonExistentHandlerWithLookupOnReceiver; } else { DCHECK(config & DoAccessCheckOnLookupStartObjectBits::kMask); // This is a less often used version, we don't cache it. handler_index = -1; } } else { handler_index = PrototypeInfo::kLoadNonExistentHandlerDefault; } if (handler_index >= 0) { auto maybe_handler = prototype_info->cached_handler(handler_index); if (!IsSmi(maybe_handler)) { Tagged<LoadHandler> handler = Cast<LoadHandler>(maybe_handler); // Handler's validity cell must be the up-to-date one. DCHECK_EQ(handler->validity_cell(), *validity_cell); return handle(handler, isolate); } // Proceed with allocating a handler and caching it in prototype info. } } Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size); handler->set_smi_handler(smi_handler); handler->set_validity_cell(*validity_cell); InitPrototypeChecks(isolate, direct_handle(handler), lookup_start_object_map, data1); if (handler_index >= 0) { prototype_info->set_cached_handler(handler_index, *handler); } return handler; } // static Handle<LoadHandler> LoadHandler::LoadInterceptorHolderIsLookupStartupObject( Isolate* isolate, DirectHandle<Map> lookup_start_object_map, DirectHandle<InterceptorInfo> interceptor_info) { Tagged<Smi> smi_handler = LoadInterceptor(interceptor_info->non_masking()); bool access_check_needed = lookup_start_object_map->is_access_check_needed(); // Enable access checks on the lookup start object if needed. smi_handler = SetBitFieldValue<DoAccessCheckOnLookupStartObjectBits>( smi_handler, access_check_needed); // This handler doesn't set LookupOnLookupStartObjectBits because // interceptor callbacks should be called before the own property lookup, // while if this bit was set the IC dispatcher would perform the own lookup // even before interpreting the IC kind value. DirectHandle<UnionOf<Smi, Cell>> validity_cell; if (access_check_needed || interceptor_info->non_masking()) { // Validity cell in required for access check (it caches the respective // NativeContext) and it's required for the non-masking case to guard // against modifications of the whole prototype chain. validity_cell = Map::GetOrCreatePrototypeChainValidityCell( lookup_start_object_map, isolate); } int data_size = 2; // null (holder) + interceptor_info. Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size); handler->set_smi_handler(smi_handler); handler->set_validity_cell( validity_cell.is_null() ? Map::kNoValidityCellSentinel : *validity_cell); handler->set_data1(MakeWeak(ReadOnlyRoots(isolate).null_value())); handler->set_data2(*interceptor_info); return handler; } // static KeyedAccessLoadMode LoadHandler::GetKeyedAccessLoadMode( Tagged<MaybeObject> handler) { DisallowGarbageCollection no_gc; if (IsSmi(handler)) { int const raw_handler = handler.ToSmi().value(); Kind const kind = KindBits::decode(raw_handler); if (kind == Kind::kElement || kind == Kind::kIndexedString) { bool handle_oob = AllowOutOfBoundsBits::decode(raw_handler); bool handle_holes = AllowHandlingHole::decode(raw_handler); return CreateKeyedAccessLoadMode(handle_oob, handle_holes); } } return KeyedAccessLoadMode::kInBounds; } // static KeyedAccessStoreMode StoreHandler::GetKeyedAccessStoreMode( Tagged<MaybeObject> handler) { DisallowGarbageCollection no_gc; if (IsSmi(handler)) { int const raw_handler = handler.ToSmi().value(); Kind const kind = KindBits::decode(raw_handler); // All the handlers except the Slow Handler that use tshe // KeyedAccessStoreMode, compute it using KeyedAccessStoreModeForBuiltin // method. Hence if any other Handler get to this path, just return // KeyedAccessStoreMode::kInBounds. if (kind != Kind::kSlow) { return KeyedAccessStoreMode::kInBounds; } KeyedAccessStoreMode store_mode = KeyedAccessStoreModeBits::decode(raw_handler); return store_mode; } return KeyedAccessStoreMode::kInBounds; } // static Handle<Object> StoreHandler::StoreElementTransition( Isolate* isolate, DirectHandle<Map> receiver_map, DirectHandle<Map> transition, KeyedAccessStoreMode store_mode, MaybeDirectHandle<UnionOf<Smi, Cell>> prev_validity_cell) { DirectHandle<Code> code = ElementsTransitionAndStoreBuiltin(isolate, store_mode); DirectHandle<UnionOf<Smi, Cell>> validity_cell; if (!prev_validity_cell.ToHandle(&validity_cell)) { validity_cell = Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); } Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(1); handler->set_smi_handler(*code); handler->set_validity_cell(*validity_cell); handler->set_data1(MakeWeak(*transition)); return handler; } // static MaybeObjectHandle StoreHandler::StoreOwnTransition(Isolate* isolate, Handle<Map> transition_map) { bool is_dictionary_map = transition_map->is_dictionary_map(); #ifdef DEBUG if (!is_dictionary_map) { InternalIndex descriptor = transition_map->LastAdded(); DirectHandle<DescriptorArray> descriptors( transition_map->instance_descriptors(isolate), isolate); PropertyDetails details = descriptors->GetDetails(descriptor); if (descriptors->GetKey(descriptor)->IsAnyPrivate()) { 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()); // StoreOwnTransition does not involve any prototype checks. if (is_dictionary_map) { DCHECK(!IsJSGlobalObjectMap(*transition_map)); int config = KindBits::encode(Kind::kNormal); return MaybeObjectHandle(Tagged<Object>(Smi::FromInt(config)), isolate); } else { return MaybeObjectHandle::Weak(transition_map); } } // static MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate, Handle<Map> transition_map) { bool is_dictionary_map = transition_map->is_dictionary_map(); // We should not create transition handlers for fast prototype maps // because they are not reusable anyway. DCHECK_IMPLIES(transition_map->is_prototype_map(), is_dictionary_map); #ifdef DEBUG if (!is_dictionary_map) { InternalIndex descriptor = transition_map->LastAdded(); DirectHandle<DescriptorArray> descriptors( transition_map->instance_descriptors(isolate), isolate); // Private fields must be added via StoreOwnTransition handler. DCHECK(!descriptors->GetKey(descriptor)->IsAnyPrivateName()); PropertyDetails details = descriptors->GetDetails(descriptor); if (descriptors->GetKey(descriptor)->IsAnyPrivate()) { 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. DirectHandle<UnionOf<Smi, Cell>> validity_cell; if (is_dictionary_map || !transition_map->IsPrototypeValidityCellValid()) { validity_cell = Map::GetOrCreatePrototypeChainValidityCell(transition_map, isolate); } if (is_dictionary_map) { DCHECK(!IsJSGlobalObjectMap(*transition_map)); Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(0); // Store normal with enabled lookup on receiver. int config = KindBits::encode(Kind::kNormal) | LookupOnLookupStartObjectBits::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, kRelaxedStore); } return MaybeObjectHandle::Weak(transition_map); } } // static Handle<Object> StoreHandler::StoreThroughPrototype( Isolate* isolate, DirectHandle<Map> receiver_map, DirectHandle<JSReceiver> holder, Tagged<Smi> smi_handler, MaybeObjectDirectHandle maybe_data1, MaybeObjectDirectHandle maybe_data2) { MaybeObjectDirectHandle data1; if (maybe_data1.is_null()) { data1 = MaybeObjectDirectHandle::Weak(holder); } else { data1 = maybe_data1; } int data_size = GetHandlerDataSize<StoreHandler>( isolate, &smi_handler, receiver_map, data1, maybe_data2); DirectHandle<UnionOf<Smi, Cell>> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); // There must be a validity cell in case access check is needed. // ICs support access checks only for JSGlobalProxy objects and they are // guaranteed to have validity cell. DCHECK_IMPLIES( *validity_cell == Map::kNoValidityCellSentinel, !DoAccessCheckOnLookupStartObjectBits::decode(smi_handler.value())); Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(data_size); handler->set_smi_handler(smi_handler); handler->set_validity_cell(*validity_cell); InitPrototypeChecks(isolate, direct_handle(handler), receiver_map, data1, maybe_data2); return handler; } // static Handle<StoreHandler> StoreHandler::StoreInterceptorHolderIsReceiver( Isolate* isolate, DirectHandle<Map> lookup_start_object_map, DirectHandle<InterceptorInfo> interceptor_info) { Tagged<Smi> smi_handler = StoreInterceptor(interceptor_info->non_masking()); bool access_check_needed = lookup_start_object_map->is_access_check_needed(); // Enable access checks on the lookup start object if needed. smi_handler = SetBitFieldValue<DoAccessCheckOnLookupStartObjectBits>( smi_handler, access_check_needed); // This handler doesn't set LookupOnLookupStartObjectBits because // interceptor callbacks should be called before the own property lookup, // while if this bit was set the IC dispatcher would perform the own lookup // even before interpreting the IC kind value. DirectHandle<UnionOf<Smi, Cell>> validity_cell; if (access_check_needed || interceptor_info->non_masking()) { // Validity cell in required for access check (it caches the respective // NativeContext) and it's required for the non-masking case to guard // against modifications of the whole prototype chain. validity_cell = Map::GetOrCreatePrototypeChainValidityCell( lookup_start_object_map, isolate); } int data_size = 2; // null (holder) + interceptor_info. Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(data_size); handler->set_smi_handler(smi_handler); handler->set_validity_cell( validity_cell.is_null() ? Map::kNoValidityCellSentinel : *validity_cell); handler->set_data1(MakeWeak(ReadOnlyRoots(isolate).null_value())); handler->set_data2(*interceptor_info); return handler; } // static MaybeObjectHandle StoreHandler::StoreGlobal(Handle<PropertyCell> cell) { return MaybeObjectHandle::Weak(cell); } // static Handle<Object> StoreHandler::StoreProxy(Isolate* isolate, DirectHandle<Map> receiver_map, Handle<JSProxy> proxy, DirectHandle<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, MaybeObjectDirectHandle::Weak(proxy)); } bool LoadHandler::CanHandleHolderNotLookupStart(Tagged<Object> handler) { if (IsSmi(handler)) { auto kind = LoadHandler::KindBits::decode(handler.ToSmi().value()); return kind == LoadHandler::Kind::kSlow || kind == LoadHandler::Kind::kNonExistent; } return IsLoadHandler(handler); } #if defined(OBJECT_PRINT) namespace { void PrintSmiLoadHandler(int raw_handler, std::ostream& os) { LoadHandler::Kind kind = LoadHandler::KindBits::decode(raw_handler); os << "kind = "; switch (kind) { case LoadHandler::Kind::kElement: os << "kElement, "; if (LoadHandler::IsWasmArrayBits::decode(raw_handler)) { os << "WasmArray, " << LoadHandler::WasmArrayTypeBits::decode(raw_handler); } else { os << "allow out of bounds = " << LoadHandler::AllowOutOfBoundsBits::decode(raw_handler) << ", is JSArray = " << LoadHandler::IsJsArrayBits::decode(raw_handler) << ", allow reading holes = " << LoadHandler::AllowHandlingHole::decode(raw_handler) << ", elements kind = " << ElementsKindToString( LoadHandler::ElementsKindBits::decode(raw_handler)); } break; case LoadHandler::Kind::kElementWithTransition: os << "kElementWithTransition, "; os << "allow out of bounds = " << LoadHandler::AllowOutOfBoundsBits::decode(raw_handler) << ", is JSArray = " << LoadHandler::IsJsArrayBits::decode(raw_handler) << ", allow reading holes = " << LoadHandler::AllowHandlingHole::decode(raw_handler) << ", elements kind = " << ElementsKindToString( LoadHandler::ElementsKindBits::decode(raw_handler)); break; case LoadHandler::Kind::kIndexedString: os << "kIndexedString, allow out of bounds = " << LoadHandler::AllowOutOfBoundsBits::decode(raw_handler); break; case LoadHandler::Kind::kNormal: os << "kNormal"; break; case LoadHandler::Kind::kGlobal: os << "kGlobal"; break; case LoadHandler::Kind::kField: { if (LoadHandler::IsWasmStructBits::decode(raw_handler)) { os << "kField, WasmStruct, type = " << LoadHandler::WasmFieldTypeBits::decode(raw_handler) << ", field offset = " << LoadHandler::WasmFieldOffsetBits::decode(raw_handler); } else { os << "kField, is in object = " << LoadHandler::IsInobjectBits::decode(raw_handler) << ", is double = " << LoadHandler::IsDoubleBits::decode(raw_handler) << ", field index = " << LoadHandler::FieldIndexBits::decode(raw_handler); } break; } case LoadHandler::Kind::kConstantFromPrototype: os << "kConstantFromPrototype"; break; case LoadHandler::Kind::kAccessorFromPrototype: os << "kAccessorFromPrototype"; break; case LoadHandler::Kind::kNativeDataProperty: os << "kNativeDataProperty, descriptor = " << LoadHandler::DescriptorBits::decode(raw_handler); break; case LoadHandler::Kind::kApiGetter: os << "kApiGetter"; break; case LoadHandler::Kind::kInterceptor: os << "kInterceptor"; break; case LoadHandler::Kind::kSlow: os << "kSlow"; break; case LoadHandler::Kind::kProxy: os << "kProxy"; break; case LoadHandler::Kind::kNonExistent: os << "kNonExistent"; break; case LoadHandler::Kind::kModuleExport: os << "kModuleExport, exports index = " << LoadHandler::ExportsIndexBits::decode(raw_handler); break; default: os << "<invalid value " << static_cast<int>(kind) << ">"; break; } } void PrintSmiStoreHandler(int raw_handler, std::ostream& os) { StoreHandler::Kind kind = StoreHandler::KindBits::decode(raw_handler); os << "kind = "; switch (kind) { case StoreHandler::Kind::kField: case StoreHandler::Kind::kConstField: { os << "k"; if (kind == StoreHandler::Kind::kConstField) { os << "Const"; } Representation representation = Representation::FromKind( StoreHandler::RepresentationBits::decode(raw_handler)); os << "Field, descriptor = " << StoreHandler::DescriptorBits::decode(raw_handler) << ", is in object = " << StoreHandler::IsInobjectBits::decode(raw_handler) << ", representation = " << representation.Mnemonic() << ", field index = " << StoreHandler::FieldIndexBits::decode(raw_handler); break; } case StoreHandler::Kind::kAccessorFromPrototype: os << "kAccessorFromPrototype"; break; case StoreHandler::Kind::kNativeDataProperty: os << "kNativeDataProperty, descriptor = " << StoreHandler::DescriptorBits::decode(raw_handler); break; case StoreHandler::Kind::kApiSetter: os << "kApiSetter"; break; case StoreHandler::Kind::kGlobalProxy: os << "kGlobalProxy"; break; case StoreHandler::Kind::kNormal: os << "kNormal"; break; case StoreHandler::Kind::kInterceptor: os << "kInterceptor"; break; case StoreHandler::Kind::kSlow: { KeyedAccessStoreMode keyed_access_store_mode = StoreHandler::KeyedAccessStoreModeBits::decode(raw_handler); os << "kSlow, keyed access store mode = " << keyed_access_store_mode; break; } case StoreHandler::Kind::kGeneric: os << "kGeneric"; break; case StoreHandler::Kind::kProxy: os << "kProxy"; break; case StoreHandler::Kind::kSharedStructField: os << "kSharedStructField"; break; case StoreHandler::Kind::kKindsNumber: UNREACHABLE(); } } } // namespace // static void LoadHandler::PrintHandler(Tagged<Object> handler, std::ostream& os) { DisallowGarbageCollection no_gc; if (IsSmi(handler)) { int raw_handler = handler.ToSmi().value(); os << "LoadHandler(Smi)("; PrintSmiLoadHandler(raw_handler, os); os << ")"; } else if (Tagged<Code> code; TryCast(handler, &code)) { os << "LoadHandler(Code)(" << Builtins::name(code->builtin_id()) << ")"; } else if (IsSymbol(handler)) { os << "LoadHandler(Symbol)(" << Brief(Cast<Symbol>(handler)) << ")"; } else if (IsLoadHandler(handler)) { Tagged<LoadHandler> load_handler = Cast<LoadHandler>(handler); int raw_handler = Cast<Smi>(load_handler->smi_handler()).value(); os << "LoadHandler(do access check on lookup start object = " << DoAccessCheckOnLookupStartObjectBits::decode(raw_handler) << ", lookup on lookup start object = " << LookupOnLookupStartObjectBits::decode(raw_handler) << ", "; PrintSmiLoadHandler(raw_handler, os); if (load_handler->data_field_count() >= 1) { os << ", data1 = "; ShortPrint(load_handler->data1(), os); } if (load_handler->data_field_count() >= 2) { os << ", data2 = "; ShortPrint(load_handler->data2(), os); } if (load_handler->data_field_count() >= 3) { os << ", data3 = "; ShortPrint(load_handler->data3(), os); } os << ", validity cell = "; ShortPrint(load_handler->validity_cell(), os); os << ")"; } else { os << "LoadHandler(<unexpected>)(" << Brief(handler) << ")"; } } void StoreHandler::PrintHandler(Tagged<Object> handler, std::ostream& os) { DisallowGarbageCollection no_gc; if (IsSmi(handler)) { int raw_handler = handler.ToSmi().value(); os << "StoreHandler(Smi)("; PrintSmiStoreHandler(raw_handler, os); os << ")" << std::endl; } else if (IsStoreHandler(handler)) { os << "StoreHandler("; Tagged<StoreHandler> store_handler = Cast<StoreHandler>(handler); if (Tagged<Code> code; TryCast(store_handler->smi_handler(), &code)) { os << "builtin = "; ShortPrint(code, os); } else { int raw_handler = Cast<Smi>(store_handler->smi_handler()).value(); os << "do access check on lookup start object = " << DoAccessCheckOnLookupStartObjectBits::decode(raw_handler) << ", lookup on lookup start object = " << LookupOnLookupStartObjectBits::decode(raw_handler) << ", "; PrintSmiStoreHandler(raw_handler, os); } if (store_handler->data_field_count() >= 1) { os << ", data1 = "; ShortPrint(store_handler->data1(), os); } if (store_handler->data_field_count() >= 2) { os << ", data2 = "; ShortPrint(store_handler->data2(), os); } if (store_handler->data_field_count() >= 3) { os << ", data3 = "; ShortPrint(store_handler->data3(), os); } os << ", validity cell = "; ShortPrint(store_handler->validity_cell(), os); os << ")" << std::endl; } else if (IsMap(handler)) { os << "StoreHandler(field transition to " << Brief(handler) << ")" << std::endl; } else if (Tagged<Code> code; TryCast(handler, &code)) { os << "StoreHandler(builtin = "; ShortPrint(code, os); os << ")" << std::endl; } else { os << "StoreHandler(<unexpected>)(" << Brief(handler) << ")" << std::endl; } } std::ostream& operator<<(std::ostream& os, WasmValueType type) { return os << WasmValueType2String(type); } #endif // defined(OBJECT_PRINT) } // namespace internal } // namespace v8