• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 namespace v8 {
26 namespace internal {
27 namespace compiler {
28 
29 namespace {
30 
CanInlinePropertyAccess(MapRef map,AccessMode access_mode)31 bool 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
HasFieldRepresentationDependenciesOnMap(ZoneVector<CompilationDependency const * > & dependencies,Handle<Map> const & field_owner_map)56 bool 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 
operator <<(std::ostream & os,AccessMode access_mode)72 std::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 
ElementAccessInfo(ZoneVector<MapRef> && lookup_start_object_maps,ElementsKind elements_kind,Zone * zone)88 ElementAccessInfo::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
Invalid(Zone * zone)98 PropertyAccessInfo PropertyAccessInfo::Invalid(Zone* zone) {
99   return PropertyAccessInfo(zone);
100 }
101 
102 // static
NotFound(Zone * zone,MapRef receiver_map,base::Optional<JSObjectRef> holder)103 PropertyAccessInfo 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
DataField(Zone * zone,MapRef receiver_map,ZoneVector<CompilationDependency const * > && dependencies,FieldIndex field_index,Representation field_representation,Type field_type,MapRef field_owner_map,base::Optional<MapRef> field_map,base::Optional<JSObjectRef> holder,base::Optional<MapRef> transition_map)109 PropertyAccessInfo 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
FastDataConstant(Zone * zone,MapRef receiver_map,ZoneVector<CompilationDependency const * > && dependencies,FieldIndex field_index,Representation field_representation,Type field_type,MapRef field_owner_map,base::Optional<MapRef> field_map,base::Optional<JSObjectRef> holder,base::Optional<MapRef> transition_map)130 PropertyAccessInfo 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
FastAccessorConstant(Zone * zone,MapRef receiver_map,base::Optional<ObjectRef> constant,base::Optional<JSObjectRef> holder)144 PropertyAccessInfo 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
ModuleExport(Zone * zone,MapRef receiver_map,CellRef cell)152 PropertyAccessInfo 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
StringLength(Zone * zone,MapRef receiver_map)160 PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone,
161                                                     MapRef receiver_map) {
162   return PropertyAccessInfo(zone, kStringLength, {}, {{receiver_map}, zone});
163 }
164 
165 // static
DictionaryProtoDataConstant(Zone * zone,MapRef receiver_map,JSObjectRef holder,InternalIndex dictionary_index,NameRef name)166 PropertyAccessInfo 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
DictionaryProtoAccessorConstant(Zone * zone,MapRef receiver_map,base::Optional<JSObjectRef> holder,ObjectRef constant,NameRef property_name)174 PropertyAccessInfo 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 
PropertyAccessInfo(Zone * zone)181 PropertyAccessInfo::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 
PropertyAccessInfo(Zone * zone,Kind kind,base::Optional<JSObjectRef> holder,ZoneVector<MapRef> && lookup_start_object_maps)189 PropertyAccessInfo::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 
PropertyAccessInfo(Zone * zone,Kind kind,base::Optional<JSObjectRef> holder,base::Optional<ObjectRef> constant,base::Optional<NameRef> name,ZoneVector<MapRef> && lookup_start_object_maps)200 PropertyAccessInfo::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 
PropertyAccessInfo(Kind kind,base::Optional<JSObjectRef> holder,base::Optional<MapRef> transition_map,FieldIndex field_index,Representation field_representation,Type field_type,MapRef field_owner_map,base::Optional<MapRef> field_map,ZoneVector<MapRef> && lookup_start_object_maps,ZoneVector<CompilationDependency const * > && unrecorded_dependencies)216 PropertyAccessInfo::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 
PropertyAccessInfo(Zone * zone,Kind kind,base::Optional<JSObjectRef> holder,ZoneVector<MapRef> && lookup_start_object_maps,InternalIndex dictionary_index,NameRef name)238 PropertyAccessInfo::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 
251 namespace {
252 
253 template <class RefT>
OptionalRefEquals(base::Optional<RefT> lhs,base::Optional<RefT> rhs)254 bool 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 
260 template <class T>
AppendVector(ZoneVector<T> * dst,const ZoneVector<T> & src)261 void AppendVector(ZoneVector<T>* dst, const ZoneVector<T>& src) {
262   dst->insert(dst->end(), src.begin(), src.end());
263 }
264 
265 }  // namespace
266 
Merge(PropertyAccessInfo const * that,AccessMode access_mode,Zone * zone)267 bool 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 
GetConstFieldInfo() const355 ConstFieldInfo PropertyAccessInfo::GetConstFieldInfo() const {
356   return IsFastDataConstant() ? ConstFieldInfo(field_owner_map_->object())
357                               : ConstFieldInfo::None();
358 }
359 
AccessInfoFactory(JSHeapBroker * broker,CompilationDependencies * dependencies,Zone * zone)360 AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
361                                      CompilationDependencies* dependencies,
362                                      Zone* zone)
363     : broker_(broker),
364       dependencies_(dependencies),
365       type_cache_(TypeCache::Get()),
366       zone_(zone) {}
367 
ComputeElementAccessInfo(MapRef map,AccessMode access_mode) const368 base::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 
ComputeElementAccessInfos(ElementAccessFeedback const & feedback,ZoneVector<ElementAccessInfo> * access_infos) const374 bool 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 
ComputeDataFieldAccessInfo(MapRef receiver_map,MapRef map,NameRef name,base::Optional<JSObjectRef> holder,InternalIndex descriptor,AccessMode access_mode) const410 PropertyAccessInfo 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 
519 namespace {
520 
521 using AccessorsObjectGetter = std::function<Handle<Object>()>;
522 
AccessorAccessInfoHelper(Isolate * isolate,Zone * zone,JSHeapBroker * broker,const AccessInfoFactory * ai_factory,MapRef receiver_map,NameRef name,MapRef map,base::Optional<JSObjectRef> holder,AccessMode access_mode,AccessorsObjectGetter get_accessors)523 PropertyAccessInfo 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 
ComputeAccessorDescriptorAccessInfo(MapRef receiver_map,NameRef name,MapRef holder_map,base::Optional<JSObjectRef> holder,InternalIndex descriptor,AccessMode access_mode) const622 PropertyAccessInfo 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 
ComputeDictionaryProtoAccessInfo(MapRef receiver_map,NameRef name,JSObjectRef holder,InternalIndex dictionary_index,AccessMode access_mode,PropertyDetails details) const641 PropertyAccessInfo 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 
TryLoadPropertyDetails(MapRef map,base::Optional<JSObjectRef> maybe_holder,NameRef name,InternalIndex * index_out,PropertyDetails * details_out) const668 bool 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 
ComputePropertyAccessInfo(MapRef map,NameRef name,AccessMode access_mode) const711 PropertyAccessInfo 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 
FinalizePropertyAccessInfosAsOne(ZoneVector<PropertyAccessInfo> access_infos,AccessMode access_mode) const935 PropertyAccessInfo 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 
RecordDependencies(CompilationDependencies * dependencies)949 void PropertyAccessInfo::RecordDependencies(
950     CompilationDependencies* dependencies) {
951   for (CompilationDependency const* d : unrecorded_dependencies_) {
952     dependencies->RecordDependency(d);
953   }
954   unrecorded_dependencies_.clear();
955 }
956 
FinalizePropertyAccessInfos(ZoneVector<PropertyAccessInfo> access_infos,AccessMode access_mode,ZoneVector<PropertyAccessInfo> * result) const957 bool 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 
MergePropertyAccessInfos(ZoneVector<PropertyAccessInfo> infos,AccessMode access_mode,ZoneVector<PropertyAccessInfo> * result) const971 void 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 
isolate() const988 Isolate* AccessInfoFactory::isolate() const { return broker()->isolate(); }
989 
990 namespace {
991 
GeneralizeElementsKind(ElementsKind this_kind,ElementsKind that_kind)992 Maybe<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 
ConsolidateElementLoad(ElementAccessFeedback const & feedback) const1013 base::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 
LookupSpecialFieldAccessor(MapRef map,NameRef name) const1044 PropertyAccessInfo 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 
LookupTransition(MapRef map,NameRef name,base::Optional<JSObjectRef> holder,PropertyAttributes attrs) const1086 PropertyAccessInfo 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