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