1 2// Copyright 2015 the V8 project authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5 6#include "src/compiler/access-info.h" 7 8#include <ostream> 9 10#include "src/builtins/accessors.h" 11#include "src/compiler/compilation-dependencies.h" 12#include "src/compiler/simplified-operator.h" 13#include "src/compiler/type-cache.h" 14#include "src/ic/call-optimization.h" 15#include "src/ic/handler-configuration.h" 16#include "src/logging/counters.h" 17#include "src/objects/cell-inl.h" 18#include "src/objects/field-index-inl.h" 19#include "src/objects/field-type.h" 20#include "src/objects/module-inl.h" 21#include "src/objects/objects-inl.h" 22#include "src/objects/struct-inl.h" 23#include "src/objects/templates.h" 24 25namespace v8 { 26namespace internal { 27namespace compiler { 28 29namespace { 30 31bool CanInlinePropertyAccess(MapRef map, AccessMode access_mode) { 32 // We can inline property access to prototypes of all primitives, except 33 // the special Oddball ones that have no wrapper counterparts (i.e. Null, 34 // Undefined and TheHole). 35 // We can only inline accesses to dictionary mode holders if the access is a 36 // load and the holder is a prototype. The latter ensures a 1:1 37 // relationship between the map and the object (and therefore the property 38 // dictionary). 39 STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_HEAP_OBJECT_TYPE); 40 if (map.object()->IsBooleanMap()) return true; 41 if (map.instance_type() < LAST_PRIMITIVE_HEAP_OBJECT_TYPE) return true; 42 if (map.object()->IsJSObjectMap()) { 43 if (map.is_dictionary_map()) { 44 if (!V8_DICT_PROPERTY_CONST_TRACKING_BOOL) return false; 45 return access_mode == AccessMode::kLoad && 46 map.object()->is_prototype_map(); 47 } 48 return !map.object()->has_named_interceptor() && 49 // TODO(verwaest): Allowlist contexts to which we have access. 50 !map.is_access_check_needed(); 51 } 52 return false; 53} 54 55#ifdef DEBUG 56bool HasFieldRepresentationDependenciesOnMap( 57 ZoneVector<CompilationDependency const*>& dependencies, 58 Handle<Map> const& field_owner_map) { 59 for (auto dep : dependencies) { 60 if (CompilationDependencies::IsFieldRepresentationDependencyOnMap( 61 dep, field_owner_map)) { 62 return true; 63 } 64 } 65 return false; 66} 67#endif 68 69} // namespace 70 71 72std::ostream& operator<<(std::ostream& os, AccessMode access_mode) { 73 switch (access_mode) { 74 case AccessMode::kLoad: 75 return os << "Load"; 76 case AccessMode::kStore: 77 return os << "Store"; 78 case AccessMode::kStoreInLiteral: 79 return os << "StoreInLiteral"; 80 case AccessMode::kHas: 81 return os << "Has"; 82 case AccessMode::kDefine: 83 return os << "Define"; 84 } 85 UNREACHABLE(); 86} 87 88ElementAccessInfo::ElementAccessInfo( 89 ZoneVector<MapRef>&& lookup_start_object_maps, ElementsKind elements_kind, 90 Zone* zone) 91 : elements_kind_(elements_kind), 92 lookup_start_object_maps_(lookup_start_object_maps), 93 transition_sources_(zone) { 94 CHECK(!lookup_start_object_maps.empty()); 95} 96 97// static 98PropertyAccessInfo PropertyAccessInfo::Invalid(Zone* zone) { 99 return PropertyAccessInfo(zone); 100} 101 102// static 103PropertyAccessInfo PropertyAccessInfo::NotFound( 104 Zone* zone, MapRef receiver_map, base::Optional<JSObjectRef> holder) { 105 return PropertyAccessInfo(zone, kNotFound, holder, {{receiver_map}, zone}); 106} 107 108// static 109PropertyAccessInfo PropertyAccessInfo::DataField( 110 Zone* zone, MapRef receiver_map, 111 ZoneVector<CompilationDependency const*>&& dependencies, 112 FieldIndex field_index, Representation field_representation, 113 Type field_type, MapRef field_owner_map, base::Optional<MapRef> field_map, 114 base::Optional<JSObjectRef> holder, base::Optional<MapRef> transition_map) { 115 DCHECK(!field_representation.IsNone()); 116 DCHECK_IMPLIES( 117 field_representation.IsDouble(), 118 HasFieldRepresentationDependenciesOnMap( 119 dependencies, transition_map.has_value() 120 ? transition_map->object() 121 : holder.has_value() ? holder->map().object() 122 : receiver_map.object())); 123 return PropertyAccessInfo(kDataField, holder, transition_map, field_index, 124 field_representation, field_type, field_owner_map, 125 field_map, {{receiver_map}, zone}, 126 std::move(dependencies)); 127} 128 129// static 130PropertyAccessInfo PropertyAccessInfo::FastDataConstant( 131 Zone* zone, MapRef receiver_map, 132 ZoneVector<CompilationDependency const*>&& dependencies, 133 FieldIndex field_index, Representation field_representation, 134 Type field_type, MapRef field_owner_map, base::Optional<MapRef> field_map, 135 base::Optional<JSObjectRef> holder, base::Optional<MapRef> transition_map) { 136 DCHECK(!field_representation.IsNone()); 137 return PropertyAccessInfo(kFastDataConstant, holder, transition_map, 138 field_index, field_representation, field_type, 139 field_owner_map, field_map, {{receiver_map}, zone}, 140 std::move(dependencies)); 141} 142 143// static 144PropertyAccessInfo PropertyAccessInfo::FastAccessorConstant( 145 Zone* zone, MapRef receiver_map, base::Optional<ObjectRef> constant, 146 base::Optional<JSObjectRef> holder) { 147 return PropertyAccessInfo(zone, kFastAccessorConstant, holder, constant, {}, 148 {{receiver_map}, zone}); 149} 150 151// static 152PropertyAccessInfo PropertyAccessInfo::ModuleExport(Zone* zone, 153 MapRef receiver_map, 154 CellRef cell) { 155 return PropertyAccessInfo(zone, kModuleExport, {}, cell, {}, 156 {{receiver_map}, zone}); 157} 158 159// static 160PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone, 161 MapRef receiver_map) { 162 return PropertyAccessInfo(zone, kStringLength, {}, {{receiver_map}, zone}); 163} 164 165// static 166PropertyAccessInfo PropertyAccessInfo::DictionaryProtoDataConstant( 167 Zone* zone, MapRef receiver_map, JSObjectRef holder, 168 InternalIndex dictionary_index, NameRef name) { 169 return PropertyAccessInfo(zone, kDictionaryProtoDataConstant, holder, 170 {{receiver_map}, zone}, dictionary_index, name); 171} 172 173// static 174PropertyAccessInfo PropertyAccessInfo::DictionaryProtoAccessorConstant( 175 Zone* zone, MapRef receiver_map, base::Optional<JSObjectRef> holder, 176 ObjectRef constant, NameRef property_name) { 177 return PropertyAccessInfo(zone, kDictionaryProtoAccessorConstant, holder, 178 constant, property_name, {{receiver_map}, zone}); 179} 180 181PropertyAccessInfo::PropertyAccessInfo(Zone* zone) 182 : kind_(kInvalid), 183 lookup_start_object_maps_(zone), 184 unrecorded_dependencies_(zone), 185 field_representation_(Representation::None()), 186 field_type_(Type::None()), 187 dictionary_index_(InternalIndex::NotFound()) {} 188 189PropertyAccessInfo::PropertyAccessInfo( 190 Zone* zone, Kind kind, base::Optional<JSObjectRef> holder, 191 ZoneVector<MapRef>&& lookup_start_object_maps) 192 : kind_(kind), 193 lookup_start_object_maps_(lookup_start_object_maps), 194 holder_(holder), 195 unrecorded_dependencies_(zone), 196 field_representation_(Representation::None()), 197 field_type_(Type::None()), 198 dictionary_index_(InternalIndex::NotFound()) {} 199 200PropertyAccessInfo::PropertyAccessInfo( 201 Zone* zone, Kind kind, base::Optional<JSObjectRef> holder, 202 base::Optional<ObjectRef> constant, base::Optional<NameRef> name, 203 ZoneVector<MapRef>&& lookup_start_object_maps) 204 : kind_(kind), 205 lookup_start_object_maps_(lookup_start_object_maps), 206 constant_(constant), 207 holder_(holder), 208 unrecorded_dependencies_(zone), 209 field_representation_(Representation::None()), 210 field_type_(Type::Any()), 211 dictionary_index_(InternalIndex::NotFound()), 212 name_(name) { 213 DCHECK_IMPLIES(kind == kDictionaryProtoAccessorConstant, name.has_value()); 214} 215 216PropertyAccessInfo::PropertyAccessInfo( 217 Kind kind, base::Optional<JSObjectRef> holder, 218 base::Optional<MapRef> transition_map, FieldIndex field_index, 219 Representation field_representation, Type field_type, 220 MapRef field_owner_map, base::Optional<MapRef> field_map, 221 ZoneVector<MapRef>&& lookup_start_object_maps, 222 ZoneVector<CompilationDependency const*>&& unrecorded_dependencies) 223 : kind_(kind), 224 lookup_start_object_maps_(lookup_start_object_maps), 225 holder_(holder), 226 unrecorded_dependencies_(std::move(unrecorded_dependencies)), 227 transition_map_(transition_map), 228 field_index_(field_index), 229 field_representation_(field_representation), 230 field_type_(field_type), 231 field_owner_map_(field_owner_map), 232 field_map_(field_map), 233 dictionary_index_(InternalIndex::NotFound()) { 234 DCHECK_IMPLIES(transition_map.has_value(), 235 field_owner_map.equals(transition_map.value())); 236} 237 238PropertyAccessInfo::PropertyAccessInfo( 239 Zone* zone, Kind kind, base::Optional<JSObjectRef> holder, 240 ZoneVector<MapRef>&& lookup_start_object_maps, 241 InternalIndex dictionary_index, NameRef name) 242 : kind_(kind), 243 lookup_start_object_maps_(lookup_start_object_maps), 244 holder_(holder), 245 unrecorded_dependencies_(zone), 246 field_representation_(Representation::None()), 247 field_type_(Type::Any()), 248 dictionary_index_(dictionary_index), 249 name_{name} {} 250 251namespace { 252 253template <class RefT> 254bool OptionalRefEquals(base::Optional<RefT> lhs, base::Optional<RefT> rhs) { 255 if (!lhs.has_value()) return !rhs.has_value(); 256 if (!rhs.has_value()) return false; 257 return lhs->equals(rhs.value()); 258} 259 260template <class T> 261void AppendVector(ZoneVector<T>* dst, const ZoneVector<T>& src) { 262 dst->insert(dst->end(), src.begin(), src.end()); 263} 264 265} // namespace 266 267bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, 268 AccessMode access_mode, Zone* zone) { 269 if (kind_ != that->kind_) return false; 270 if (!OptionalRefEquals(holder_, that->holder_)) return false; 271 272 switch (kind_) { 273 case kInvalid: 274 DCHECK_EQ(that->kind_, kInvalid); 275 return true; 276 277 case kDataField: 278 case kFastDataConstant: { 279 // Check if we actually access the same field (we use the 280 // GetFieldAccessStubKey method here just like the ICs do 281 // since that way we only compare the relevant bits of the 282 // field indices). 283 if (field_index_.GetFieldAccessStubKey() != 284 that->field_index_.GetFieldAccessStubKey()) { 285 return false; 286 } 287 288 switch (access_mode) { 289 case AccessMode::kHas: 290 case AccessMode::kLoad: { 291 if (!field_representation_.Equals(that->field_representation_)) { 292 if (field_representation_.IsDouble() || 293 that->field_representation_.IsDouble()) { 294 return false; 295 } 296 field_representation_ = Representation::Tagged(); 297 } 298 if (!OptionalRefEquals(field_map_, that->field_map_)) { 299 field_map_ = {}; 300 } 301 break; 302 } 303 case AccessMode::kStore: 304 case AccessMode::kStoreInLiteral: 305 case AccessMode::kDefine: { 306 // For stores, the field map and field representation information 307 // must match exactly, otherwise we cannot merge the stores. We 308 // also need to make sure that in case of transitioning stores, 309 // the transition targets match. 310 if (!OptionalRefEquals(field_map_, that->field_map_) || 311 !field_representation_.Equals(that->field_representation_) || 312 !OptionalRefEquals(transition_map_, that->transition_map_)) { 313 return false; 314 } 315 break; 316 } 317 } 318 319 field_type_ = Type::Union(field_type_, that->field_type_, zone); 320 AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_); 321 AppendVector(&unrecorded_dependencies_, that->unrecorded_dependencies_); 322 return true; 323 } 324 325 case kDictionaryProtoAccessorConstant: 326 case kFastAccessorConstant: { 327 // Check if we actually access the same constant. 328 if (!OptionalRefEquals(constant_, that->constant_)) return false; 329 330 DCHECK(unrecorded_dependencies_.empty()); 331 DCHECK(that->unrecorded_dependencies_.empty()); 332 AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_); 333 return true; 334 } 335 336 case kDictionaryProtoDataConstant: { 337 DCHECK_EQ(AccessMode::kLoad, access_mode); 338 if (dictionary_index_ != that->dictionary_index_) return false; 339 AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_); 340 return true; 341 } 342 343 case kNotFound: 344 case kStringLength: { 345 DCHECK(unrecorded_dependencies_.empty()); 346 DCHECK(that->unrecorded_dependencies_.empty()); 347 AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_); 348 return true; 349 } 350 case kModuleExport: 351 return false; 352 } 353} 354 355ConstFieldInfo PropertyAccessInfo::GetConstFieldInfo() const { 356 return IsFastDataConstant() ? ConstFieldInfo(field_owner_map_->object()) 357 : ConstFieldInfo::None(); 358} 359 360AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker, 361 CompilationDependencies* dependencies, 362 Zone* zone) 363 : broker_(broker), 364 dependencies_(dependencies), 365 type_cache_(TypeCache::Get()), 366 zone_(zone) {} 367 368base::Optional<ElementAccessInfo> AccessInfoFactory::ComputeElementAccessInfo( 369 MapRef map, AccessMode access_mode) const { 370 if (!map.CanInlineElementAccess()) return {}; 371 return ElementAccessInfo({{map}, zone()}, map.elements_kind(), zone()); 372} 373 374bool AccessInfoFactory::ComputeElementAccessInfos( 375 ElementAccessFeedback const& feedback, 376 ZoneVector<ElementAccessInfo>* access_infos) const { 377 AccessMode access_mode = feedback.keyed_mode().access_mode(); 378 if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) { 379 // For polymorphic loads of similar elements kinds (i.e. all tagged or all 380 // double), always use the "worst case" code without a transition. This is 381 // much faster than transitioning the elements to the worst case, trading a 382 // TransitionElementsKind for a CheckMaps, avoiding mutation of the array. 383 base::Optional<ElementAccessInfo> access_info = 384 ConsolidateElementLoad(feedback); 385 if (access_info.has_value()) { 386 access_infos->push_back(*access_info); 387 return true; 388 } 389 } 390 391 for (auto const& group : feedback.transition_groups()) { 392 DCHECK(!group.empty()); 393 base::Optional<MapRef> target = 394 MakeRefAssumeMemoryFence(broker(), group.front()); 395 base::Optional<ElementAccessInfo> access_info = 396 ComputeElementAccessInfo(target.value(), access_mode); 397 if (!access_info.has_value()) return false; 398 399 for (size_t i = 1; i < group.size(); ++i) { 400 base::Optional<MapRef> map_ref = 401 MakeRefAssumeMemoryFence(broker(), group[i]); 402 if (!map_ref.has_value()) continue; 403 access_info->AddTransitionSource(map_ref.value()); 404 } 405 access_infos->push_back(*access_info); 406 } 407 return true; 408} 409 410PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( 411 MapRef receiver_map, MapRef map, NameRef name, 412 base::Optional<JSObjectRef> holder, InternalIndex descriptor, 413 AccessMode access_mode) const { 414 DCHECK(descriptor.is_found()); 415 // TODO(jgruber,v8:7790): Use DescriptorArrayRef instead. 416 Handle<DescriptorArray> descriptors = map.instance_descriptors().object(); 417 PropertyDetails const details = descriptors->GetDetails(descriptor); 418 int index = descriptors->GetFieldIndex(descriptor); 419 Representation details_representation = details.representation(); 420 if (details_representation.IsNone()) { 421 // The ICs collect feedback in PREMONOMORPHIC state already, 422 // but at this point the {receiver_map} might still contain 423 // fields for which the representation has not yet been 424 // determined by the runtime. So we need to catch this case 425 // here and fall back to use the regular IC logic instead. 426 return Invalid(); 427 } 428 FieldIndex field_index = FieldIndex::ForPropertyIndex(*map.object(), index, 429 details_representation); 430 // Private brands are used when loading private methods, which are stored in a 431 // BlockContext, an internal object. 432 Type field_type = name.object()->IsPrivateBrand() ? Type::OtherInternal() 433 : Type::NonInternal(); 434 base::Optional<MapRef> field_map; 435 436 ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone()); 437 438 Handle<FieldType> descriptors_field_type = 439 broker()->CanonicalPersistentHandle( 440 descriptors->GetFieldType(descriptor)); 441 base::Optional<ObjectRef> descriptors_field_type_ref = 442 TryMakeRef<Object>(broker(), descriptors_field_type); 443 if (!descriptors_field_type_ref.has_value()) return Invalid(); 444 445 if (details_representation.IsSmi()) { 446 field_type = Type::SignedSmall(); 447 unrecorded_dependencies.push_back( 448 dependencies()->FieldRepresentationDependencyOffTheRecord( 449 map, descriptor, details_representation)); 450 } else if (details_representation.IsDouble()) { 451 field_type = type_cache_->kFloat64; 452 unrecorded_dependencies.push_back( 453 dependencies()->FieldRepresentationDependencyOffTheRecord( 454 map, descriptor, details_representation)); 455 } else if (details_representation.IsHeapObject()) { 456 if (descriptors_field_type->IsNone()) { 457 switch (access_mode) { 458 case AccessMode::kStore: 459 case AccessMode::kStoreInLiteral: 460 case AccessMode::kDefine: 461 // Store is not safe if the field type was cleared. 462 return Invalid(); 463 case AccessMode::kLoad: 464 case AccessMode::kHas: 465 break; 466 } 467 468 // The field type was cleared by the GC, so we don't know anything 469 // about the contents now. 470 } 471 unrecorded_dependencies.push_back( 472 dependencies()->FieldRepresentationDependencyOffTheRecord( 473 map, descriptor, details_representation)); 474 if (descriptors_field_type->IsClass()) { 475 // Remember the field map, and try to infer a useful type. 476 base::Optional<MapRef> maybe_field_map = 477 TryMakeRef(broker(), descriptors_field_type->AsClass()); 478 if (!maybe_field_map.has_value()) return Invalid(); 479 field_type = Type::For(maybe_field_map.value()); 480 field_map = maybe_field_map; 481 } 482 } else { 483 CHECK(details_representation.IsTagged()); 484 } 485 // TODO(turbofan): We may want to do this only depending on the use 486 // of the access info. 487 unrecorded_dependencies.push_back( 488 dependencies()->FieldTypeDependencyOffTheRecord( 489 map, descriptor, descriptors_field_type_ref.value())); 490 491 PropertyConstness constness; 492 if (details.IsReadOnly() && !details.IsConfigurable()) { 493 constness = PropertyConstness::kConst; 494 } else { 495 constness = dependencies()->DependOnFieldConstness(map, descriptor); 496 } 497 498 // Note: FindFieldOwner may be called multiple times throughout one 499 // compilation. This is safe since its result is fixed for a given map and 500 // descriptor. 501 MapRef field_owner_map = map.FindFieldOwner(descriptor); 502 503 switch (constness) { 504 case PropertyConstness::kMutable: 505 return PropertyAccessInfo::DataField( 506 zone(), receiver_map, std::move(unrecorded_dependencies), field_index, 507 details_representation, field_type, field_owner_map, field_map, 508 holder, {}); 509 510 case PropertyConstness::kConst: 511 return PropertyAccessInfo::FastDataConstant( 512 zone(), receiver_map, std::move(unrecorded_dependencies), field_index, 513 details_representation, field_type, field_owner_map, field_map, 514 holder, {}); 515 } 516 UNREACHABLE(); 517} 518 519namespace { 520 521using AccessorsObjectGetter = std::function<Handle<Object>()>; 522 523PropertyAccessInfo AccessorAccessInfoHelper( 524 Isolate* isolate, Zone* zone, JSHeapBroker* broker, 525 const AccessInfoFactory* ai_factory, MapRef receiver_map, NameRef name, 526 MapRef map, base::Optional<JSObjectRef> holder, AccessMode access_mode, 527 AccessorsObjectGetter get_accessors) { 528 if (map.instance_type() == JS_MODULE_NAMESPACE_TYPE) { 529 DCHECK(map.object()->is_prototype_map()); 530 Handle<PrototypeInfo> proto_info = broker->CanonicalPersistentHandle( 531 PrototypeInfo::cast(map.object()->prototype_info())); 532 Handle<JSModuleNamespace> module_namespace = 533 broker->CanonicalPersistentHandle( 534 JSModuleNamespace::cast(proto_info->module_namespace())); 535 Handle<Cell> cell = broker->CanonicalPersistentHandle( 536 Cell::cast(module_namespace->module().exports().Lookup( 537 isolate, name.object(), Smi::ToInt(name.object()->GetHash())))); 538 if (cell->value(kRelaxedLoad).IsTheHole(isolate)) { 539 // This module has not been fully initialized yet. 540 return PropertyAccessInfo::Invalid(zone); 541 } 542 base::Optional<CellRef> cell_ref = TryMakeRef(broker, cell); 543 if (!cell_ref.has_value()) { 544 return PropertyAccessInfo::Invalid(zone); 545 } 546 return PropertyAccessInfo::ModuleExport(zone, receiver_map, 547 cell_ref.value()); 548 } 549 if (access_mode == AccessMode::kHas) { 550 // kHas is not supported for dictionary mode objects. 551 DCHECK(!map.is_dictionary_map()); 552 553 // HasProperty checks don't call getter/setters, existence is sufficient. 554 return PropertyAccessInfo::FastAccessorConstant(zone, receiver_map, {}, 555 holder); 556 } 557 Handle<Object> maybe_accessors = get_accessors(); 558 if (!maybe_accessors->IsAccessorPair()) { 559 return PropertyAccessInfo::Invalid(zone); 560 } 561 Handle<AccessorPair> accessors = Handle<AccessorPair>::cast(maybe_accessors); 562 Handle<Object> accessor = broker->CanonicalPersistentHandle( 563 access_mode == AccessMode::kLoad ? accessors->getter(kAcquireLoad) 564 : accessors->setter(kAcquireLoad)); 565 566 base::Optional<ObjectRef> accessor_ref = TryMakeRef(broker, accessor); 567 if (!accessor_ref.has_value()) return PropertyAccessInfo::Invalid(zone); 568 569 if (!accessor->IsJSFunction()) { 570 CallOptimization optimization(broker->local_isolate_or_isolate(), accessor); 571 if (!optimization.is_simple_api_call() || 572 optimization.IsCrossContextLazyAccessorPair( 573 *broker->target_native_context().object(), *map.object())) { 574 return PropertyAccessInfo::Invalid(zone); 575 } 576 577 CallOptimization::HolderLookup lookup; 578 Handle<JSObject> holder_handle = broker->CanonicalPersistentHandle( 579 optimization.LookupHolderOfExpectedType( 580 broker->local_isolate_or_isolate(), receiver_map.object(), 581 &lookup)); 582 if (lookup == CallOptimization::kHolderNotFound) { 583 return PropertyAccessInfo::Invalid(zone); 584 } 585 DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver, 586 holder_handle.is_null()); 587 DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, 588 !holder_handle.is_null()); 589 590 if (holder_handle.is_null()) { 591 holder = {}; 592 } else { 593 holder = TryMakeRef(broker, holder_handle); 594 if (!holder.has_value()) return PropertyAccessInfo::Invalid(zone); 595 } 596 } 597 if (access_mode == AccessMode::kLoad) { 598 base::Optional<Name> cached_property_name = 599 FunctionTemplateInfo::TryGetCachedPropertyName(isolate, *accessor); 600 if (cached_property_name.has_value()) { 601 base::Optional<NameRef> cached_property_name_ref = 602 TryMakeRef(broker, cached_property_name.value()); 603 if (cached_property_name_ref.has_value()) { 604 PropertyAccessInfo access_info = ai_factory->ComputePropertyAccessInfo( 605 map, cached_property_name_ref.value(), access_mode); 606 if (!access_info.IsInvalid()) return access_info; 607 } 608 } 609 } 610 611 if (map.is_dictionary_map()) { 612 return PropertyAccessInfo::DictionaryProtoAccessorConstant( 613 zone, receiver_map, holder, accessor_ref.value(), name); 614 } else { 615 return PropertyAccessInfo::FastAccessorConstant( 616 zone, receiver_map, accessor_ref.value(), holder); 617 } 618} 619 620} // namespace 621 622PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo( 623 MapRef receiver_map, NameRef name, MapRef holder_map, 624 base::Optional<JSObjectRef> holder, InternalIndex descriptor, 625 AccessMode access_mode) const { 626 DCHECK(descriptor.is_found()); 627 Handle<DescriptorArray> descriptors = broker()->CanonicalPersistentHandle( 628 holder_map.object()->instance_descriptors(kRelaxedLoad)); 629 SLOW_DCHECK(descriptor == 630 descriptors->Search(*name.object(), *holder_map.object())); 631 632 auto get_accessors = [&]() { 633 return broker()->CanonicalPersistentHandle( 634 descriptors->GetStrongValue(descriptor)); 635 }; 636 return AccessorAccessInfoHelper(isolate(), zone(), broker(), this, 637 receiver_map, name, holder_map, holder, 638 access_mode, get_accessors); 639} 640 641PropertyAccessInfo AccessInfoFactory::ComputeDictionaryProtoAccessInfo( 642 MapRef receiver_map, NameRef name, JSObjectRef holder, 643 InternalIndex dictionary_index, AccessMode access_mode, 644 PropertyDetails details) const { 645 CHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); 646 DCHECK(holder.map().object()->is_prototype_map()); 647 DCHECK_EQ(access_mode, AccessMode::kLoad); 648 649 // We can only inline accesses to constant properties. 650 if (details.constness() != PropertyConstness::kConst) { 651 return Invalid(); 652 } 653 654 if (details.kind() == PropertyKind::kData) { 655 return PropertyAccessInfo::DictionaryProtoDataConstant( 656 zone(), receiver_map, holder, dictionary_index, name); 657 } 658 659 auto get_accessors = [&]() { 660 return JSObject::DictionaryPropertyAt(isolate(), holder.object(), 661 dictionary_index); 662 }; 663 return AccessorAccessInfoHelper(isolate(), zone(), broker(), this, 664 receiver_map, name, holder.map(), holder, 665 access_mode, get_accessors); 666} 667 668bool AccessInfoFactory::TryLoadPropertyDetails( 669 MapRef map, base::Optional<JSObjectRef> maybe_holder, NameRef name, 670 InternalIndex* index_out, PropertyDetails* details_out) const { 671 if (map.is_dictionary_map()) { 672 DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); 673 DCHECK(map.object()->is_prototype_map()); 674 675 DisallowGarbageCollection no_gc; 676 677 if (!maybe_holder.has_value()) { 678 // TODO(v8:11457) In this situation, we have a dictionary mode prototype 679 // as a receiver. Consider other means of obtaining the holder in this 680 // situation. 681 682 // Without the holder, we can't get the property details. 683 return false; 684 } 685 686 Handle<JSObject> holder = maybe_holder->object(); 687 if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) { 688 SwissNameDictionary dict = holder->property_dictionary_swiss(); 689 *index_out = dict.FindEntry(isolate(), name.object()); 690 if (index_out->is_found()) { 691 *details_out = dict.DetailsAt(*index_out); 692 } 693 } else { 694 NameDictionary dict = holder->property_dictionary(); 695 *index_out = dict.FindEntry(isolate(), name.object()); 696 if (index_out->is_found()) { 697 *details_out = dict.DetailsAt(*index_out); 698 } 699 } 700 } else { 701 DescriptorArray descriptors = *map.instance_descriptors().object(); 702 *index_out = descriptors.Search(*name.object(), *map.object(), true); 703 if (index_out->is_found()) { 704 *details_out = descriptors.GetDetails(*index_out); 705 } 706 } 707 708 return true; 709} 710 711PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( 712 MapRef map, NameRef name, AccessMode access_mode) const { 713 CHECK(name.IsUniqueName()); 714 715 // Dictionary property const tracking is unsupported with concurrent inlining. 716 CHECK(!V8_DICT_PROPERTY_CONST_TRACKING_BOOL); 717 718 JSHeapBroker::MapUpdaterGuardIfNeeded mumd_scope(broker()); 719 720 if (access_mode == AccessMode::kHas && !map.object()->IsJSReceiverMap()) { 721 return Invalid(); 722 } 723 724 // Check if it is safe to inline property access for the {map}. 725 if (!CanInlinePropertyAccess(map, access_mode)) { 726 return Invalid(); 727 } 728 729 // We support fast inline cases for certain JSObject getters. 730 if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) { 731 PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name); 732 if (!access_info.IsInvalid()) return access_info; 733 } 734 735 // Only relevant if V8_DICT_PROPERTY_CONST_TRACKING enabled. 736 bool dictionary_prototype_on_chain = false; 737 bool fast_mode_prototype_on_chain = false; 738 739 // Remember the receiver map. We use {map} as loop variable. 740 MapRef receiver_map = map; 741 base::Optional<JSObjectRef> holder; 742 743 // Perform the implicit ToObject for primitives here. 744 // Implemented according to ES6 section 7.3.2 GetV (V, P). 745 // Note: Keep sync'd with 746 // CompilationDependencies::DependOnStablePrototypeChains. 747 if (receiver_map.IsPrimitiveMap()) { 748 base::Optional<JSFunctionRef> constructor = 749 broker()->target_native_context().GetConstructorFunction(receiver_map); 750 if (!constructor.has_value()) return Invalid(); 751 map = constructor->initial_map(broker()->dependencies()); 752 DCHECK(!map.IsPrimitiveMap()); 753 } 754 755 while (true) { 756 PropertyDetails details = PropertyDetails::Empty(); 757 InternalIndex index = InternalIndex::NotFound(); 758 if (!TryLoadPropertyDetails(map, holder, name, &index, &details)) { 759 return Invalid(); 760 } 761 762 if (index.is_found()) { 763 if (access_mode == AccessMode::kStore || 764 access_mode == AccessMode::kStoreInLiteral) { 765 DCHECK(!map.is_dictionary_map()); 766 767 // Don't bother optimizing stores to read-only properties. 768 if (details.IsReadOnly()) return Invalid(); 769 770 if (details.kind() == PropertyKind::kData && holder.has_value()) { 771 // This is a store to a property not found on the receiver but on a 772 // prototype. According to ES6 section 9.1.9 [[Set]], we need to 773 // create a new data property on the receiver. We can still optimize 774 // if such a transition already exists. 775 return LookupTransition(receiver_map, name, holder, NONE); 776 } 777 } 778 779 if (map.is_dictionary_map()) { 780 DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); 781 782 if (fast_mode_prototype_on_chain) { 783 // TODO(v8:11248) While the work on dictionary mode prototypes is in 784 // progress, we may still see fast mode objects on the chain prior to 785 // reaching a dictionary mode prototype holding the property . Due to 786 // this only being an intermediate state, we don't stupport these kind 787 // of heterogenous prototype chains. 788 return Invalid(); 789 } 790 791 // TryLoadPropertyDetails only succeeds if we know the holder. 792 return ComputeDictionaryProtoAccessInfo( 793 receiver_map, name, holder.value(), index, access_mode, details); 794 } 795 796 if (dictionary_prototype_on_chain) { 797 // If V8_DICT_PROPERTY_CONST_TRACKING_BOOL was disabled, then a 798 // dictionary prototype would have caused a bailout earlier. 799 DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); 800 801 // TODO(v8:11248) We have a fast mode holder, but there was a dictionary 802 // mode prototype earlier on the chain. Note that seeing a fast mode 803 // prototype even though V8_DICT_PROPERTY_CONST_TRACKING is enabled 804 // should only be possible while the implementation of dictionary mode 805 // prototypes is work in progress. Eventually, enabling 806 // V8_DICT_PROPERTY_CONST_TRACKING will guarantee that all prototypes 807 // are always in dictionary mode, making this case unreachable. However, 808 // due to the complications of checking dictionary mode prototypes for 809 // modification, we don't attempt to support dictionary mode prototypes 810 // occuring before a fast mode holder on the chain. 811 return Invalid(); 812 } 813 if (details.location() == PropertyLocation::kField) { 814 if (details.kind() == PropertyKind::kData) { 815 return ComputeDataFieldAccessInfo(receiver_map, map, name, holder, 816 index, access_mode); 817 } else { 818 DCHECK_EQ(PropertyKind::kAccessor, details.kind()); 819 // TODO(turbofan): Add support for general accessors? 820 return Invalid(); 821 } 822 } else { 823 DCHECK_EQ(PropertyLocation::kDescriptor, details.location()); 824 DCHECK_EQ(PropertyKind::kAccessor, details.kind()); 825 return ComputeAccessorDescriptorAccessInfo(receiver_map, name, map, 826 holder, index, access_mode); 827 } 828 829 UNREACHABLE(); 830 } 831 832 // The property wasn't found on {map}. Look on the prototype if appropriate. 833 DCHECK(!index.is_found()); 834 835 // Don't search on the prototype chain for special indices in case of 836 // integer indexed exotic objects (see ES6 section 9.4.5). 837 if (map.object()->IsJSTypedArrayMap() && name.IsString()) { 838 if (broker()->IsMainThread()) { 839 if (IsSpecialIndex(String::cast(*name.object()))) { 840 return Invalid(); 841 } 842 } else { 843 // TODO(jgruber): We are being conservative here since we can't access 844 // string contents from background threads. Should that become possible 845 // in the future, remove this bailout. 846 return Invalid(); 847 } 848 } 849 850 // Don't search on the prototype when storing in literals, or performing a 851 // Define operation 852 if (access_mode == AccessMode::kStoreInLiteral || 853 access_mode == AccessMode::kDefine) { 854 PropertyAttributes attrs = NONE; 855 if (name.object()->IsPrivate()) { 856 // When PrivateNames are added to an object, they are by definition 857 // non-enumerable. 858 attrs = DONT_ENUM; 859 } 860 return LookupTransition(receiver_map, name, holder, attrs); 861 } 862 863 // Don't lookup private symbols on the prototype chain. 864 if (name.object()->IsPrivate()) { 865 return Invalid(); 866 } 867 868 if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && holder.has_value()) { 869 // At this point, we are past the first loop iteration. 870 DCHECK(holder->object()->map().is_prototype_map()); 871 DCHECK(!holder->map().equals(receiver_map)); 872 873 fast_mode_prototype_on_chain = 874 fast_mode_prototype_on_chain || !map.is_dictionary_map(); 875 dictionary_prototype_on_chain = 876 dictionary_prototype_on_chain || map.is_dictionary_map(); 877 } 878 879 // Walk up the prototype chain. 880 // Load the map's prototype's map to guarantee that every time we use it, 881 // we use the same Map. 882 HeapObjectRef prototype = map.prototype(); 883 884 MapRef map_prototype_map = prototype.map(); 885 if (!map_prototype_map.object()->IsJSObjectMap()) { 886 // Don't allow proxies on the prototype chain. 887 if (!prototype.IsNull()) { 888 DCHECK(prototype.object()->IsJSProxy()); 889 return Invalid(); 890 } 891 892 DCHECK(prototype.IsNull()); 893 894 if (dictionary_prototype_on_chain) { 895 // TODO(v8:11248) See earlier comment about 896 // dictionary_prototype_on_chain. We don't support absent properties 897 // with dictionary mode prototypes on the chain, either. This is again 898 // just due to how we currently deal with dependencies for dictionary 899 // properties during finalization. 900 return Invalid(); 901 } 902 903 // Store to property not found on the receiver or any prototype, we need 904 // to transition to a new data property. 905 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) 906 if (access_mode == AccessMode::kStore) { 907 return LookupTransition(receiver_map, name, holder, NONE); 908 } 909 910 // The property was not found (access returns undefined or throws 911 // depending on the language mode of the load operation. 912 // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver) 913 return PropertyAccessInfo::NotFound(zone(), receiver_map, holder); 914 } 915 916 holder = prototype.AsJSObject(); 917 map = map_prototype_map; 918 919 if (!CanInlinePropertyAccess(map, access_mode)) { 920 return Invalid(); 921 } 922 923 // Successful lookup on prototype chain needs to guarantee that all the 924 // prototypes up to the holder have stable maps, except for dictionary-mode 925 // prototypes. We currently do this by taking a 926 // DependOnStablePrototypeChains dependency in the caller. 927 // 928 // TODO(jgruber): This is brittle and easy to miss. Consider a refactor 929 // that moves the responsibility of taking the dependency into 930 // AccessInfoFactory. 931 } 932 UNREACHABLE(); 933} 934 935PropertyAccessInfo AccessInfoFactory::FinalizePropertyAccessInfosAsOne( 936 ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode) const { 937 ZoneVector<PropertyAccessInfo> merged_access_infos(zone()); 938 MergePropertyAccessInfos(access_infos, access_mode, &merged_access_infos); 939 if (merged_access_infos.size() == 1) { 940 PropertyAccessInfo& result = merged_access_infos.front(); 941 if (!result.IsInvalid()) { 942 result.RecordDependencies(dependencies()); 943 return result; 944 } 945 } 946 return Invalid(); 947} 948 949void PropertyAccessInfo::RecordDependencies( 950 CompilationDependencies* dependencies) { 951 for (CompilationDependency const* d : unrecorded_dependencies_) { 952 dependencies->RecordDependency(d); 953 } 954 unrecorded_dependencies_.clear(); 955} 956 957bool AccessInfoFactory::FinalizePropertyAccessInfos( 958 ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode, 959 ZoneVector<PropertyAccessInfo>* result) const { 960 if (access_infos.empty()) return false; 961 MergePropertyAccessInfos(access_infos, access_mode, result); 962 for (PropertyAccessInfo const& info : *result) { 963 if (info.IsInvalid()) return false; 964 } 965 for (PropertyAccessInfo& info : *result) { 966 info.RecordDependencies(dependencies()); 967 } 968 return true; 969} 970 971void AccessInfoFactory::MergePropertyAccessInfos( 972 ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode, 973 ZoneVector<PropertyAccessInfo>* result) const { 974 DCHECK(result->empty()); 975 for (auto it = infos.begin(), end = infos.end(); it != end; ++it) { 976 bool merged = false; 977 for (auto ot = it + 1; ot != end; ++ot) { 978 if (ot->Merge(&(*it), access_mode, zone())) { 979 merged = true; 980 break; 981 } 982 } 983 if (!merged) result->push_back(*it); 984 } 985 CHECK(!result->empty()); 986} 987 988Isolate* AccessInfoFactory::isolate() const { return broker()->isolate(); } 989 990namespace { 991 992Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind, 993 ElementsKind that_kind) { 994 if (IsHoleyElementsKind(this_kind)) { 995 that_kind = GetHoleyElementsKind(that_kind); 996 } else if (IsHoleyElementsKind(that_kind)) { 997 this_kind = GetHoleyElementsKind(this_kind); 998 } 999 if (this_kind == that_kind) return Just(this_kind); 1000 if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) { 1001 if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) { 1002 return Just(this_kind); 1003 } 1004 if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) { 1005 return Just(that_kind); 1006 } 1007 } 1008 return Nothing<ElementsKind>(); 1009} 1010 1011} // namespace 1012 1013base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad( 1014 ElementAccessFeedback const& feedback) const { 1015 if (feedback.transition_groups().empty()) return {}; 1016 1017 DCHECK(!feedback.transition_groups().front().empty()); 1018 Handle<Map> first_map = feedback.transition_groups().front().front(); 1019 base::Optional<MapRef> first_map_ref = TryMakeRef(broker(), first_map); 1020 if (!first_map_ref.has_value()) return {}; 1021 InstanceType instance_type = first_map_ref->instance_type(); 1022 ElementsKind elements_kind = first_map_ref->elements_kind(); 1023 1024 ZoneVector<MapRef> maps(zone()); 1025 for (auto const& group : feedback.transition_groups()) { 1026 for (Handle<Map> map_handle : group) { 1027 base::Optional<MapRef> map = TryMakeRef(broker(), map_handle); 1028 if (!map.has_value()) return {}; 1029 if (map->instance_type() != instance_type || 1030 !map->CanInlineElementAccess()) { 1031 return {}; 1032 } 1033 if (!GeneralizeElementsKind(elements_kind, map->elements_kind()) 1034 .To(&elements_kind)) { 1035 return {}; 1036 } 1037 maps.push_back(map.value()); 1038 } 1039 } 1040 1041 return ElementAccessInfo(std::move(maps), elements_kind, zone()); 1042} 1043 1044PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor( 1045 MapRef map, NameRef name) const { 1046 // Check for String::length field accessor. 1047 if (map.object()->IsStringMap()) { 1048 if (Name::Equals(isolate(), name.object(), 1049 isolate()->factory()->length_string())) { 1050 return PropertyAccessInfo::StringLength(zone(), map); 1051 } 1052 return Invalid(); 1053 } 1054 // Check for special JSObject field accessors. 1055 FieldIndex field_index; 1056 if (Accessors::IsJSObjectFieldAccessor(isolate(), map.object(), name.object(), 1057 &field_index)) { 1058 Type field_type = Type::NonInternal(); 1059 Representation field_representation = Representation::Tagged(); 1060 if (map.object()->IsJSArrayMap()) { 1061 DCHECK(Name::Equals(isolate(), isolate()->factory()->length_string(), 1062 name.object())); 1063 // The JSArray::length property is a smi in the range 1064 // [0, FixedDoubleArray::kMaxLength] in case of fast double 1065 // elements, a smi in the range [0, FixedArray::kMaxLength] 1066 // in case of other fast elements, and [0, kMaxUInt32] in 1067 // case of other arrays. 1068 if (IsDoubleElementsKind(map.elements_kind())) { 1069 field_type = type_cache_->kFixedDoubleArrayLengthType; 1070 field_representation = Representation::Smi(); 1071 } else if (IsFastElementsKind(map.elements_kind())) { 1072 field_type = type_cache_->kFixedArrayLengthType; 1073 field_representation = Representation::Smi(); 1074 } else { 1075 field_type = type_cache_->kJSArrayLengthType; 1076 } 1077 } 1078 // Special fields are always mutable. 1079 return PropertyAccessInfo::DataField(zone(), map, {{}, zone()}, field_index, 1080 field_representation, field_type, map, 1081 {}, {}, {}); 1082 } 1083 return Invalid(); 1084} 1085 1086PropertyAccessInfo AccessInfoFactory::LookupTransition( 1087 MapRef map, NameRef name, base::Optional<JSObjectRef> holder, 1088 PropertyAttributes attrs) const { 1089 // Check if the {map} has a data transition with the given {name}. 1090 Map transition = 1091 TransitionsAccessor(isolate(), *map.object(), true) 1092 .SearchTransition(*name.object(), PropertyKind::kData, attrs); 1093 if (transition.is_null()) return Invalid(); 1094 base::Optional<MapRef> maybe_transition_map = 1095 TryMakeRef(broker(), transition); 1096 if (!maybe_transition_map.has_value()) return Invalid(); 1097 MapRef transition_map = maybe_transition_map.value(); 1098 1099 InternalIndex const number = transition_map.object()->LastAdded(); 1100 Handle<DescriptorArray> descriptors = 1101 transition_map.instance_descriptors().object(); 1102 PropertyDetails const details = descriptors->GetDetails(number); 1103 1104 // Don't bother optimizing stores to read-only properties. 1105 if (details.IsReadOnly()) return Invalid(); 1106 1107 // TODO(bmeurer): Handle transition to data constant? 1108 if (details.location() != PropertyLocation::kField) return Invalid(); 1109 1110 int const index = details.field_index(); 1111 Representation details_representation = details.representation(); 1112 if (details_representation.IsNone()) return Invalid(); 1113 1114 FieldIndex field_index = FieldIndex::ForPropertyIndex( 1115 *transition_map.object(), index, details_representation); 1116 Type field_type = Type::NonInternal(); 1117 base::Optional<MapRef> field_map; 1118 1119 ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone()); 1120 if (details_representation.IsSmi()) { 1121 field_type = Type::SignedSmall(); 1122 unrecorded_dependencies.push_back( 1123 dependencies()->FieldRepresentationDependencyOffTheRecord( 1124 transition_map, number, details_representation)); 1125 } else if (details_representation.IsDouble()) { 1126 field_type = type_cache_->kFloat64; 1127 unrecorded_dependencies.push_back( 1128 dependencies()->FieldRepresentationDependencyOffTheRecord( 1129 transition_map, number, details_representation)); 1130 } else if (details_representation.IsHeapObject()) { 1131 // Extract the field type from the property details (make sure its 1132 // representation is TaggedPointer to reflect the heap object case). 1133 // TODO(jgruber,v8:7790): Use DescriptorArrayRef instead. 1134 Handle<FieldType> descriptors_field_type = 1135 broker()->CanonicalPersistentHandle(descriptors->GetFieldType(number)); 1136 base::Optional<ObjectRef> descriptors_field_type_ref = 1137 TryMakeRef<Object>(broker(), descriptors_field_type); 1138 if (!descriptors_field_type_ref.has_value()) return Invalid(); 1139 1140 if (descriptors_field_type->IsNone()) { 1141 // Store is not safe if the field type was cleared. 1142 return Invalid(); 1143 } 1144 unrecorded_dependencies.push_back( 1145 dependencies()->FieldRepresentationDependencyOffTheRecord( 1146 transition_map, number, details_representation)); 1147 if (descriptors_field_type->IsClass()) { 1148 unrecorded_dependencies.push_back( 1149 dependencies()->FieldTypeDependencyOffTheRecord( 1150 transition_map, number, *descriptors_field_type_ref)); 1151 // Remember the field map, and try to infer a useful type. 1152 base::Optional<MapRef> maybe_field_map = 1153 TryMakeRef(broker(), descriptors_field_type->AsClass()); 1154 if (!maybe_field_map.has_value()) return Invalid(); 1155 field_type = Type::For(maybe_field_map.value()); 1156 field_map = maybe_field_map; 1157 } 1158 } 1159 1160 unrecorded_dependencies.push_back( 1161 dependencies()->TransitionDependencyOffTheRecord(transition_map)); 1162 // Transitioning stores *may* store to const fields. The resulting 1163 // DataConstant access infos can be distinguished from later, i.e. redundant, 1164 // stores to the same constant field by the presence of a transition map. 1165 switch (dependencies()->DependOnFieldConstness(transition_map, number)) { 1166 case PropertyConstness::kMutable: 1167 return PropertyAccessInfo::DataField( 1168 zone(), map, std::move(unrecorded_dependencies), field_index, 1169 details_representation, field_type, transition_map, field_map, holder, 1170 transition_map); 1171 case PropertyConstness::kConst: 1172 return PropertyAccessInfo::FastDataConstant( 1173 zone(), map, std::move(unrecorded_dependencies), field_index, 1174 details_representation, field_type, transition_map, field_map, holder, 1175 transition_map); 1176 } 1177 UNREACHABLE(); 1178} 1179 1180} // namespace compiler 1181} // namespace internal 1182} // namespace v8 1183