// 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::LoadProxyFast(
Isolate* isolate, DirectHandle<Map> target_map,
DirectHandle<Map> handler_map, DirectHandle<Smi> get_smi_handler,
DirectHandle<Object> trap_method) {
Handle<Smi> smi_handler = LoadProxy(isolate);
Handle<LoadHandler> handler =
isolate->factory()->NewLoadHandler(LoadHandler::kProxyDataFieldCount);
handler->set_smi_handler(*smi_handler);
handler->set_validity_cell(Map::kNoValidityCellSentinel);
// data1: target map
handler->set_data1(MakeWeak(Cast<HeapObject>(*target_map)));
// LoadIC
// data2: handler map
handler->set_data2(MakeWeak(Cast<HeapObject>(*handler_map)));
// data3: smi handler
handler->set_data3(*get_smi_handler);
// CallIC
// data4: trap method
handler->set_data4(MakeWeak(Cast<HeapObject>(*trap_method)));
// data5: counter
handler->set_data5(Smi::zero());
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);
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);
// 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, ";
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: {
os << "kField, is in object = "
<< LoadHandler::IsInobjectBits::decode(raw_handler)
<< ", is double = " << LoadHandler::IsDoubleBits::decode(raw_handler)
<< ", storage offset in words = "
<< LoadHandler::StorageOffsetInWordsBits::decode(raw_handler)
<< ", descriptor index = "
<< LoadHandler::DescriptorIndexBits::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;
case LoadHandler::Kind::kGeneric:
os << "kGeneric";
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()
<< ", storage offset in words = "
<< StoreHandler::StorageOffsetInWordsBits::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 (Is<Code>(handler)) {
Tagged<Code> code = TrustedCast<Code>(handler);
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);
}
if (load_handler->data_field_count() >= 4) {
os << ", data4 = ";
ShortPrint(load_handler->data4(), os);
}
if (load_handler->data_field_count() >= 5) {
os << ", data5 = ";
ShortPrint(load_handler->data5(), 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 (Is<Code>(handler)) {
Tagged<Code> code = TrustedCast<Code>(handler);
os << "StoreHandler(builtin = ";
ShortPrint(code, os);
os << ")" << std::endl;
} else {
os << "StoreHandler(<unexpected>)(" << Brief(handler) << ")" << std::endl;
}
}
#endif // defined(OBJECT_PRINT)
} // namespace internal
} // namespace v8