access-info.cc 47.1 KB
Newer Older
1 2 3 4
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/compiler/access-info.h"
6

7 8
#include <ostream>

9
#include "src/builtins/accessors.h"
10
#include "src/compiler/compilation-dependencies.h"
11
#include "src/compiler/compilation-dependency.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 19
#include "src/objects/field-index-inl.h"
#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 26 27 28

namespace v8 {
namespace internal {
namespace compiler {

29 30
namespace {

31
bool CanInlinePropertyAccess(Handle<Map> map, AccessMode access_mode) {
32 33 34
  // We can inline property access to prototypes of all primitives, except
  // the special Oddball ones that have no wrapper counterparts (i.e. Null,
  // Undefined and TheHole).
35 36 37 38
  // We can only inline accesses to dictionary mode holders if the access is a
  // load and the holder is a prototype. The latter ensures a 1:1
  // relationship between the map and the object (and therefore the property
  // dictionary).
39
  STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_HEAP_OBJECT_TYPE);
40
  if (map->IsBooleanMap()) return true;
41
  if (map->instance_type() < LAST_PRIMITIVE_HEAP_OBJECT_TYPE) return true;
42 43 44 45 46 47 48 49 50 51
  if (map->IsJSObjectMap()) {
    if (map->is_dictionary_map()) {
      if (!V8_DICT_PROPERTY_CONST_TRACKING_BOOL) return false;
      return access_mode == AccessMode::kLoad && map->is_prototype_map();
    }
    return !map->has_named_interceptor() &&
           // TODO(verwaest): Allowlist contexts to which we have access.
           !map->is_access_check_needed();
  }
  return false;
52 53
}

54 55 56 57 58 59 60 61 62 63 64 65 66
#ifdef DEBUG
bool HasFieldRepresentationDependenciesOnMap(
    ZoneVector<CompilationDependency const*>& dependencies,
    Handle<Map> const& field_owner_map) {
  for (auto dep : dependencies) {
    if (dep->IsFieldRepresentationDependencyOnMap(field_owner_map)) {
      return true;
    }
  }
  return false;
}
#endif

67 68 69 70
}  // namespace


std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
71
  switch (access_mode) {
72
    case AccessMode::kLoad:
73
      return os << "Load";
74
    case AccessMode::kStore:
75
      return os << "Store";
76 77
    case AccessMode::kStoreInLiteral:
      return os << "StoreInLiteral";
78 79
    case AccessMode::kHas:
      return os << "Has";
80 81 82 83
  }
  UNREACHABLE();
}

84 85 86
ElementAccessInfo::ElementAccessInfo(
    ZoneVector<Handle<Map>>&& lookup_start_object_maps,
    ElementsKind elements_kind, Zone* zone)
87
    : elements_kind_(elements_kind),
88
      lookup_start_object_maps_(lookup_start_object_maps),
89
      transition_sources_(zone) {
90
  CHECK(!lookup_start_object_maps.empty());
91
}
92

93
// static
94 95 96 97 98 99 100
PropertyAccessInfo PropertyAccessInfo::Invalid(Zone* zone) {
  return PropertyAccessInfo(zone);
}

// static
PropertyAccessInfo PropertyAccessInfo::NotFound(Zone* zone,
                                                Handle<Map> receiver_map,
101
                                                MaybeHandle<JSObject> holder) {
102
  return PropertyAccessInfo(zone, kNotFound, holder, {{receiver_map}, zone});
103 104 105 106
}

// static
PropertyAccessInfo PropertyAccessInfo::DataField(
107
    Zone* zone, Handle<Map> receiver_map,
108
    ZoneVector<CompilationDependency const*>&& dependencies,
109
    FieldIndex field_index, Representation field_representation,
110 111
    Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map,
    MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) {
112 113 114
  DCHECK_IMPLIES(
      field_representation.IsDouble(),
      HasFieldRepresentationDependenciesOnMap(dependencies, field_owner_map));
115
  return PropertyAccessInfo(kDataField, holder, transition_map, field_index,
116 117 118
                            field_representation, field_type, field_owner_map,
                            field_map, {{receiver_map}, zone},
                            std::move(dependencies));
119 120
}

121
// static
122
PropertyAccessInfo PropertyAccessInfo::FastDataConstant(
123
    Zone* zone, Handle<Map> receiver_map,
124
    ZoneVector<CompilationDependency const*>&& dependencies,
125
    FieldIndex field_index, Representation field_representation,
126 127
    Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map,
    MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) {
128 129 130
  return PropertyAccessInfo(kFastDataConstant, holder, transition_map,
                            field_index, field_representation, field_type,
                            field_owner_map, field_map, {{receiver_map}, zone},
131
                            std::move(dependencies));
132 133
}

134
// static
135
PropertyAccessInfo PropertyAccessInfo::FastAccessorConstant(
136
    Zone* zone, Handle<Map> receiver_map, Handle<Object> constant,
137
    MaybeHandle<JSObject> holder) {
138
  return PropertyAccessInfo(zone, kFastAccessorConstant, holder, constant,
139
                            MaybeHandle<Name>(), {{receiver_map}, zone});
140 141
}

142
// static
143 144 145 146
PropertyAccessInfo PropertyAccessInfo::ModuleExport(Zone* zone,
                                                    Handle<Map> receiver_map,
                                                    Handle<Cell> cell) {
  return PropertyAccessInfo(zone, kModuleExport, MaybeHandle<JSObject>(), cell,
147
                            MaybeHandle<Name>{}, {{receiver_map}, zone});
148 149
}

150
// static
151 152 153 154
PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone,
                                                    Handle<Map> receiver_map) {
  return PropertyAccessInfo(zone, kStringLength, MaybeHandle<JSObject>(),
                            {{receiver_map}, zone});
155 156
}

157 158 159
// static
PropertyAccessInfo PropertyAccessInfo::DictionaryProtoDataConstant(
    Zone* zone, Handle<Map> receiver_map, Handle<JSObject> holder,
160
    InternalIndex dictionary_index, Handle<Name> name) {
161
  return PropertyAccessInfo(zone, kDictionaryProtoDataConstant, holder,
162
                            {{receiver_map}, zone}, dictionary_index, name);
163 164 165 166 167
}

// static
PropertyAccessInfo PropertyAccessInfo::DictionaryProtoAccessorConstant(
    Zone* zone, Handle<Map> receiver_map, MaybeHandle<JSObject> holder,
168
    Handle<Object> constant, Handle<Name> property_name) {
169
  return PropertyAccessInfo(zone, kDictionaryProtoAccessorConstant, holder,
170
                            constant, property_name, {{receiver_map}, zone});
171 172
}

173 174 175 176 177 178 179 180 181 182 183 184 185 186
// static
MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::DataField(
    int offset, bool is_inobject, Representation field_representation,
    Type field_type) {
  return MinimorphicLoadPropertyAccessInfo(kDataField, offset, is_inobject,
                                           field_representation, field_type);
}

// static
MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::Invalid() {
  return MinimorphicLoadPropertyAccessInfo(
      kInvalid, -1, false, Representation::None(), Type::None());
}

187
PropertyAccessInfo::PropertyAccessInfo(Zone* zone)
188
    : kind_(kInvalid),
189
      lookup_start_object_maps_(zone),
190
      unrecorded_dependencies_(zone),
191
      field_representation_(Representation::None()),
192 193
      field_type_(Type::None()),
      dictionary_index_(InternalIndex::NotFound()) {}
194

195 196 197
PropertyAccessInfo::PropertyAccessInfo(
    Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
    ZoneVector<Handle<Map>>&& lookup_start_object_maps)
198
    : kind_(kind),
199
      lookup_start_object_maps_(lookup_start_object_maps),
200
      holder_(holder),
201
      unrecorded_dependencies_(zone),
202
      field_representation_(Representation::None()),
203 204
      field_type_(Type::None()),
      dictionary_index_(InternalIndex::NotFound()) {}
205

206 207
PropertyAccessInfo::PropertyAccessInfo(
    Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
208 209
    Handle<Object> constant, MaybeHandle<Name> property_name,
    ZoneVector<Handle<Map>>&& lookup_start_object_maps)
210
    : kind_(kind),
211
      lookup_start_object_maps_(lookup_start_object_maps),
212 213
      constant_(constant),
      holder_(holder),
214
      unrecorded_dependencies_(zone),
215
      field_representation_(Representation::None()),
216
      field_type_(Type::Any()),
217 218 219 220 221
      dictionary_index_(InternalIndex::NotFound()),
      name_(property_name) {
  DCHECK_IMPLIES(kind == kDictionaryProtoAccessorConstant,
                 !property_name.is_null());
}
222
PropertyAccessInfo::PropertyAccessInfo(
223
    Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
224
    FieldIndex field_index, Representation field_representation,
225
    Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map,
226
    ZoneVector<Handle<Map>>&& lookup_start_object_maps,
227
    ZoneVector<CompilationDependency const*>&& unrecorded_dependencies)
228
    : kind_(kind),
229
      lookup_start_object_maps_(lookup_start_object_maps),
230
      holder_(holder),
231
      unrecorded_dependencies_(std::move(unrecorded_dependencies)),
232 233
      transition_map_(transition_map),
      field_index_(field_index),
234
      field_representation_(field_representation),
235
      field_type_(field_type),
236
      field_owner_map_(field_owner_map),
237 238
      field_map_(field_map),
      dictionary_index_(InternalIndex::NotFound()) {
239 240 241
  DCHECK_IMPLIES(!transition_map.is_null(),
                 field_owner_map.address() == transition_map.address());
}
242

243 244 245
PropertyAccessInfo::PropertyAccessInfo(
    Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
    ZoneVector<Handle<Map>>&& lookup_start_object_maps,
246
    InternalIndex dictionary_index, Handle<Name> name)
247 248 249 250 251 252
    : kind_(kind),
      lookup_start_object_maps_(lookup_start_object_maps),
      holder_(holder),
      unrecorded_dependencies_(zone),
      field_representation_(Representation::None()),
      field_type_(Type::Any()),
253 254
      dictionary_index_(dictionary_index),
      name_{name} {}
255

256 257 258 259 260 261 262 263 264
MinimorphicLoadPropertyAccessInfo::MinimorphicLoadPropertyAccessInfo(
    Kind kind, int offset, bool is_inobject,
    Representation field_representation, Type field_type)
    : kind_(kind),
      is_inobject_(is_inobject),
      offset_(offset),
      field_representation_(field_representation),
      field_type_(field_type) {}

265 266
bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
                               AccessMode access_mode, Zone* zone) {
267 268 269 270 271
  if (this->kind_ != that->kind_) return false;
  if (this->holder_.address() != that->holder_.address()) return false;

  switch (this->kind_) {
    case kInvalid:
272
      return that->kind_ == kInvalid;
273

274
    case kDataField:
275
    case kFastDataConstant: {
276 277 278 279 280 281
      // Check if we actually access the same field (we use the
      // GetFieldAccessStubKey method here just like the ICs do
      // since that way we only compare the relevant bits of the
      // field indices).
      if (this->field_index_.GetFieldAccessStubKey() ==
          that->field_index_.GetFieldAccessStubKey()) {
282
        switch (access_mode) {
283
          case AccessMode::kHas:
284
          case AccessMode::kLoad: {
285 286 287 288
            if (!this->field_representation_.Equals(
                    that->field_representation_)) {
              if (this->field_representation_.IsDouble() ||
                  that->field_representation_.IsDouble()) {
289 290
                return false;
              }
291
              this->field_representation_ = Representation::Tagged();
292 293 294 295 296 297 298 299 300 301 302 303 304
            }
            if (this->field_map_.address() != that->field_map_.address()) {
              this->field_map_ = MaybeHandle<Map>();
            }
            break;
          }
          case AccessMode::kStore:
          case AccessMode::kStoreInLiteral: {
            // For stores, the field map and field representation information
            // must match exactly, otherwise we cannot merge the stores. We
            // also need to make sure that in case of transitioning stores,
            // the transition targets match.
            if (this->field_map_.address() != that->field_map_.address() ||
305 306
                !this->field_representation_.Equals(
                    that->field_representation_) ||
307 308 309 310 311 312 313 314 315
                this->transition_map_.address() !=
                    that->transition_map_.address()) {
              return false;
            }
            break;
          }
        }
        this->field_type_ =
            Type::Union(this->field_type_, that->field_type_, zone);
316 317 318 319
        this->lookup_start_object_maps_.insert(
            this->lookup_start_object_maps_.end(),
            that->lookup_start_object_maps_.begin(),
            that->lookup_start_object_maps_.end());
320 321 322 323
        this->unrecorded_dependencies_.insert(
            this->unrecorded_dependencies_.end(),
            that->unrecorded_dependencies_.begin(),
            that->unrecorded_dependencies_.end());
324 325 326 327 328
        return true;
      }
      return false;
    }

329
    case kDictionaryProtoAccessorConstant:
330
    case kFastAccessorConstant: {
331 332
      // Check if we actually access the same constant.
      if (this->constant_.address() == that->constant_.address()) {
333 334
        DCHECK(this->unrecorded_dependencies_.empty());
        DCHECK(that->unrecorded_dependencies_.empty());
335 336 337 338
        this->lookup_start_object_maps_.insert(
            this->lookup_start_object_maps_.end(),
            that->lookup_start_object_maps_.begin(),
            that->lookup_start_object_maps_.end());
339 340 341 342
        return true;
      }
      return false;
    }
343

344 345 346 347 348 349 350 351 352 353 354 355
    case kDictionaryProtoDataConstant: {
      DCHECK_EQ(AccessMode::kLoad, access_mode);
      if (this->dictionary_index_ == that->dictionary_index_) {
        this->lookup_start_object_maps_.insert(
            this->lookup_start_object_maps_.end(),
            that->lookup_start_object_maps_.begin(),
            that->lookup_start_object_maps_.end());
        return true;
      }
      return false;
    }

356 357
    case kNotFound:
    case kStringLength: {
358 359
      DCHECK(this->unrecorded_dependencies_.empty());
      DCHECK(that->unrecorded_dependencies_.empty());
360 361 362 363
      this->lookup_start_object_maps_.insert(
          this->lookup_start_object_maps_.end(),
          that->lookup_start_object_maps_.begin(),
          that->lookup_start_object_maps_.end());
364 365
      return true;
    }
366
    case kModuleExport:
367
      return false;
368 369 370
  }
}

371
ConstFieldInfo PropertyAccessInfo::GetConstFieldInfo() const {
372
  if (IsFastDataConstant()) {
373 374 375 376 377
    return ConstFieldInfo(field_owner_map_.ToHandleChecked());
  }
  return ConstFieldInfo::None();
}

378
AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
379
                                     CompilationDependencies* dependencies,
380
                                     Zone* zone)
381
    : broker_(broker),
382
      dependencies_(dependencies),
383
      type_cache_(TypeCache::Get()),
384
      zone_(zone) {}
385

386 387
base::Optional<ElementAccessInfo> AccessInfoFactory::ComputeElementAccessInfo(
    Handle<Map> map, AccessMode access_mode) const {
388
  // Check if it is safe to inline element access for the {map}.
389
  base::Optional<MapRef> map_ref = TryMakeRef(broker(), map);
390 391 392
  if (!map_ref.has_value()) return {};
  if (!CanInlineElementAccess(*map_ref)) return base::nullopt;
  ElementsKind const elements_kind = map_ref->elements_kind();
393
  return ElementAccessInfo({{map}, zone()}, elements_kind, zone());
394 395
}

396
bool AccessInfoFactory::ComputeElementAccessInfos(
397
    ElementAccessFeedback const& feedback,
398
    ZoneVector<ElementAccessInfo>* access_infos) const {
399
  AccessMode access_mode = feedback.keyed_mode().access_mode();
400
  if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
401 402 403 404
    // For polymorphic loads of similar elements kinds (i.e. all tagged or all
    // double), always use the "worst case" code without a transition.  This is
    // much faster than transitioning the elements to the worst case, trading a
    // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
405
    base::Optional<ElementAccessInfo> access_info =
406
        ConsolidateElementLoad(feedback);
407 408
    if (access_info.has_value()) {
      access_infos->push_back(*access_info);
409
      return true;
410 411 412
    }
  }

413 414 415
  for (auto const& group : feedback.transition_groups()) {
    DCHECK(!group.empty());
    Handle<Map> target = group.front();
416
    base::Optional<ElementAccessInfo> access_info =
417
        ComputeElementAccessInfo(target, access_mode);
418
    if (!access_info.has_value()) return false;
419

420 421
    for (size_t i = 1; i < group.size(); ++i) {
      access_info->AddTransitionSource(group[i]);
422
    }
423
    access_infos->push_back(*access_info);
424 425 426
  }
  return true;
}
427

428
PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
429
    Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
430 431
    InternalIndex descriptor, AccessMode access_mode) const {
  DCHECK(descriptor.is_found());
432 433
  Handle<DescriptorArray> descriptors = broker()->CanonicalPersistentHandle(
      map->instance_descriptors(kAcquireLoad));
434 435
  PropertyDetails const details = descriptors->GetDetails(descriptor);
  int index = descriptors->GetFieldIndex(descriptor);
436
  Representation details_representation = details.representation();
437 438 439 440 441 442
  if (details_representation.IsNone()) {
    // The ICs collect feedback in PREMONOMORPHIC state already,
    // but at this point the {receiver_map} might still contain
    // fields for which the representation has not yet been
    // determined by the runtime. So we need to catch this case
    // here and fall back to use the regular IC logic instead.
443
    return Invalid();
444
  }
445 446 447 448
  FieldIndex field_index =
      FieldIndex::ForPropertyIndex(*map, index, details_representation);
  Type field_type = Type::NonInternal();
  MaybeHandle<Map> field_map;
449

450
  base::Optional<MapRef> map_ref = TryMakeRef(broker(), map);
451
  if (!map_ref.has_value()) return Invalid();
452

453
  ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone());
454
  if (!map_ref->TrySerializeOwnDescriptor(descriptor)) {
455 456
    return Invalid();
  }
457 458
  if (details_representation.IsSmi()) {
    field_type = Type::SignedSmall();
459
    unrecorded_dependencies.push_back(
460
        dependencies()->FieldRepresentationDependencyOffTheRecord(*map_ref,
461
                                                                  descriptor));
462 463
  } else if (details_representation.IsDouble()) {
    field_type = type_cache_->kFloat64;
464
    unrecorded_dependencies.push_back(
465
        dependencies()->FieldRepresentationDependencyOffTheRecord(*map_ref,
466
                                                                  descriptor));
467 468 469
  } else if (details_representation.IsHeapObject()) {
    // Extract the field type from the property details (make sure its
    // representation is TaggedPointer to reflect the heap object case).
470 471 472
    Handle<FieldType> descriptors_field_type =
        broker()->CanonicalPersistentHandle(
            descriptors->GetFieldType(descriptor));
473 474
    if (descriptors_field_type->IsNone()) {
      // Store is not safe if the field type was cleared.
475
      if (access_mode == AccessMode::kStore) {
476
        return Invalid();
477
      }
478 479 480

      // The field type was cleared by the GC, so we don't know anything
      // about the contents now.
481
    }
482
    unrecorded_dependencies.push_back(
483
        dependencies()->FieldRepresentationDependencyOffTheRecord(*map_ref,
484
                                                                  descriptor));
485
    if (descriptors_field_type->IsClass()) {
486
      // Remember the field map, and try to infer a useful type.
487 488
      Handle<Map> map = broker()->CanonicalPersistentHandle(
          descriptors_field_type->AsClass());
489
      base::Optional<MapRef> maybe_ref = TryMakeRef(broker(), map);
490 491
      if (!maybe_ref.has_value()) return Invalid();
      field_type = Type::For(*maybe_ref);
492 493
      field_map = MaybeHandle<Map>(map);
    }
494 495
  } else {
    CHECK(details_representation.IsTagged());
496
  }
497 498 499
  // TODO(turbofan): We may want to do this only depending on the use
  // of the access info.
  unrecorded_dependencies.push_back(
500
      dependencies()->FieldTypeDependencyOffTheRecord(*map_ref, descriptor));
501

502 503 504 505
  PropertyConstness constness;
  if (details.IsReadOnly() && !details.IsConfigurable()) {
    constness = PropertyConstness::kConst;
  } else {
506
    constness = dependencies()->DependOnFieldConstness(*map_ref, descriptor);
507
  }
508 509 510
  // TODO(v8:11670): Make FindFieldOwner and friends robust wrt concurrency.
  Handle<Map> field_owner_map = broker()->CanonicalPersistentHandle(
      map->FindFieldOwner(isolate(), descriptor));
511
  switch (constness) {
512 513
    case PropertyConstness::kMutable:
      return PropertyAccessInfo::DataField(
514
          zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
515 516
          details_representation, field_type, field_owner_map, field_map,
          holder);
517
    case PropertyConstness::kConst:
518
      return PropertyAccessInfo::FastDataConstant(
519
          zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
520 521
          details_representation, field_type, field_owner_map, field_map,
          holder);
522 523
  }
  UNREACHABLE();
524 525
}

526 527 528 529 530 531 532 533
namespace {
using AccessorsObjectGetter = std::function<Handle<Object>()>;

PropertyAccessInfo AccessorAccessInfoHelper(
    Isolate* isolate, Zone* zone, JSHeapBroker* broker,
    const AccessInfoFactory* ai_factory, Handle<Map> receiver_map,
    Handle<Name> name, Handle<Map> map, MaybeHandle<JSObject> holder,
    AccessMode access_mode, AccessorsObjectGetter get_accessors) {
534 535
  if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
    DCHECK(map->is_prototype_map());
536 537 538 539 540 541 542 543
    Handle<PrototypeInfo> proto_info = broker->CanonicalPersistentHandle(
        PrototypeInfo::cast(map->prototype_info()));
    Handle<JSModuleNamespace> module_namespace =
        broker->CanonicalPersistentHandle(
            JSModuleNamespace::cast(proto_info->module_namespace()));
    Handle<Cell> cell = broker->CanonicalPersistentHandle(
        Cell::cast(module_namespace->module().exports().Lookup(
            isolate, name, Smi::ToInt(name->GetHash()))));
544
    if (cell->value().IsTheHole(isolate)) {
545
      // This module has not been fully initialized yet.
546
      return PropertyAccessInfo::Invalid(zone);
547
    }
548
    return PropertyAccessInfo::ModuleExport(zone, receiver_map, cell);
549
  }
550
  if (access_mode == AccessMode::kHas) {
551 552 553
    // kHas is not supported for dictionary mode objects.
    DCHECK(!map->is_dictionary_map());

554
    // HasProperty checks don't call getter/setters, existence is sufficient.
555
    return PropertyAccessInfo::FastAccessorConstant(zone, receiver_map,
556
                                                    Handle<Object>(), holder);
557
  }
558 559
  Handle<Object> maybe_accessors = get_accessors();
  if (!maybe_accessors->IsAccessorPair()) {
560
    return PropertyAccessInfo::Invalid(zone);
561
  }
562 563 564 565 566 567 568 569
  Handle<AccessorPair> accessors = Handle<AccessorPair>::cast(maybe_accessors);
  Handle<Object> accessor = broker->CanonicalPersistentHandle(
      access_mode == AccessMode::kLoad ? accessors->getter()
                                       : accessors->setter());

  ObjectData* data = broker->TryGetOrCreateData(accessor);
  if (data == nullptr) return PropertyAccessInfo::Invalid(zone);

570
  if (!accessor->IsJSFunction()) {
571
    CallOptimization optimization(broker->local_isolate_or_isolate(), accessor);
572 573
    if (!optimization.is_simple_api_call() ||
        optimization.IsCrossContextLazyAccessorPair(
574 575
            *broker->target_native_context().object(), *map)) {
      return PropertyAccessInfo::Invalid(zone);
576 577 578
    }

    CallOptimization::HolderLookup lookup;
579 580 581
    holder = broker->CanonicalPersistentHandle(
        optimization.LookupHolderOfExpectedType(
            broker->local_isolate_or_isolate(), receiver_map, &lookup));
582
    if (lookup == CallOptimization::kHolderNotFound) {
583
      return PropertyAccessInfo::Invalid(zone);
584
    }
585 586 587 588 589
    DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
                   holder.is_null());
    DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
  }
  if (access_mode == AccessMode::kLoad) {
590 591 592 593 594
    base::Optional<Name> maybe_cached_property_name =
        FunctionTemplateInfo::TryGetCachedPropertyName(isolate, *accessor);
    if (maybe_cached_property_name.has_value()) {
      Handle<Name> cached_property_name =
          broker->CanonicalPersistentHandle(maybe_cached_property_name.value());
595 596
      PropertyAccessInfo access_info = ai_factory->ComputePropertyAccessInfo(
          map, cached_property_name, access_mode);
597
      if (!access_info.IsInvalid()) return access_info;
598 599
    }
  }
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
  if (map->is_dictionary_map()) {
    return PropertyAccessInfo::DictionaryProtoAccessorConstant(
        zone, receiver_map, holder, accessor, name);
  } else {
    return PropertyAccessInfo::FastAccessorConstant(zone, receiver_map,
                                                    accessor, holder);
  }
}

}  // namespace

PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
    Handle<Map> receiver_map, Handle<Name> name, Handle<Map> holder_map,
    MaybeHandle<JSObject> holder, InternalIndex descriptor,
    AccessMode access_mode) const {
  DCHECK(descriptor.is_found());
616 617
  Handle<DescriptorArray> descriptors = broker()->CanonicalPersistentHandle(
      holder_map->instance_descriptors(kRelaxedLoad));
618 619 620
  SLOW_DCHECK(descriptor == descriptors->Search(*name, *holder_map));

  auto get_accessors = [&]() {
621 622
    return broker()->CanonicalPersistentHandle(
        descriptors->GetStrongValue(descriptor));
623 624 625 626
  };
  return AccessorAccessInfoHelper(isolate(), zone(), broker(), this,
                                  receiver_map, name, holder_map, holder,
                                  access_mode, get_accessors);
627
}
628

629 630 631 632 633 634 635 636 637 638
PropertyAccessInfo AccessInfoFactory::ComputeDictionaryProtoAccessInfo(
    Handle<Map> receiver_map, Handle<Name> name, Handle<JSObject> holder,
    InternalIndex dictionary_index, AccessMode access_mode,
    PropertyDetails details) const {
  CHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
  DCHECK(holder->map().is_prototype_map());
  DCHECK_EQ(access_mode, AccessMode::kLoad);

  // We can only inline accesses to constant properties.
  if (details.constness() != PropertyConstness::kConst) {
639
    return Invalid();
640 641 642 643 644 645 646
  }

  if (details.kind() == PropertyKind::kData) {
    return PropertyAccessInfo::DictionaryProtoDataConstant(
        zone(), receiver_map, holder, dictionary_index, name);
  }

647
  auto get_accessors = [&]() {
648
    return JSObject::DictionaryPropertyAt(isolate(), holder, dictionary_index);
649
  };
650
  Handle<Map> holder_map = broker()->CanonicalPersistentHandle(holder->map());
651 652 653
  return AccessorAccessInfoHelper(isolate(), zone(), broker(), this,
                                  receiver_map, name, holder_map, holder,
                                  access_mode, get_accessors);
654 655
}

656 657 658 659 660 661 662 663 664 665 666 667 668 669
MinimorphicLoadPropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
    MinimorphicLoadPropertyAccessFeedback const& feedback) const {
  DCHECK(feedback.handler()->IsSmi());
  int handler = Smi::cast(*feedback.handler()).value();
  bool is_inobject = LoadHandler::IsInobjectBits::decode(handler);
  bool is_double = LoadHandler::IsDoubleBits::decode(handler);
  int offset = LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize;
  Representation field_rep =
      is_double ? Representation::Double() : Representation::Tagged();
  Type field_type = is_double ? Type::Number() : Type::Any();
  return MinimorphicLoadPropertyAccessInfo::DataField(offset, is_inobject,
                                                      field_rep, field_type);
}

670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
bool AccessInfoFactory::TryLoadPropertyDetails(
    Handle<Map> map, MaybeHandle<JSObject> maybe_holder, Handle<Name> name,
    InternalIndex* index_out, PropertyDetails* details_out) const {
  if (map->is_dictionary_map()) {
    DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
    DCHECK(map->is_prototype_map());

    DisallowGarbageCollection no_gc;

    if (maybe_holder.is_null()) {
      // TODO(v8:11457) In this situation, we have a dictionary mode prototype
      // as a receiver. Consider other means of obtaining the holder in this
      // situation.

      // Without the holder, we can't get the property details.
      return false;
    }

    Handle<JSObject> holder = maybe_holder.ToHandleChecked();
689
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
      SwissNameDictionary dict = holder->property_dictionary_swiss();
      *index_out = dict.FindEntry(isolate(), name);
      if (index_out->is_found()) {
        *details_out = dict.DetailsAt(*index_out);
      }
    } else {
      NameDictionary dict = holder->property_dictionary();
      *index_out = dict.FindEntry(isolate(), name);
      if (index_out->is_found()) {
        *details_out = dict.DetailsAt(*index_out);
      }
    }
  } else {
    DescriptorArray descriptors = map->instance_descriptors(kAcquireLoad);
    *index_out =
        descriptors.Search(*name, *map, broker()->is_concurrent_inlining());
    if (index_out->is_found()) {
      *details_out = descriptors.GetDetails(*index_out);
    }
  }

  return true;
}

714 715
PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
    Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
716 717
  CHECK(name->IsUniqueName());

718
  JSHeapBroker::MapUpdaterGuardIfNeeded mumd_scope(broker());
719

720
  if (access_mode == AccessMode::kHas && !map->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 731 732
  if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
    PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
    if (!access_info.IsInvalid()) return access_info;
733 734
  }

735 736 737 738
  // Only relevant if V8_DICT_PROPERTY_CONST_TRACKING enabled.
  bool dictionary_prototype_on_chain = false;
  bool fast_mode_prototype_on_chain = false;

739 740
  // Remember the receiver map. We use {map} as loop variable.
  Handle<Map> receiver_map = map;
741
  MaybeHandle<JSObject> holder;
742
  while (true) {
743 744 745
    PropertyDetails details = PropertyDetails::Empty();
    InternalIndex index = InternalIndex::NotFound();
    if (!TryLoadPropertyDetails(map, holder, name, &index, &details)) {
746
      return Invalid();
747 748 749
    }

    if (index.is_found()) {
750 751
      if (access_mode == AccessMode::kStore ||
          access_mode == AccessMode::kStoreInLiteral) {
752 753
        DCHECK(!map->is_dictionary_map());

754
        // Don't bother optimizing stores to read-only properties.
755
        if (details.IsReadOnly()) {
756
          return Invalid();
757
        }
758
        if (details.kind() == kData && !holder.is_null()) {
759 760 761 762
          // This is a store to a property not found on the receiver but on a
          // prototype. According to ES6 section 9.1.9 [[Set]], we need to
          // create a new data property on the receiver. We can still optimize
          // if such a transition already exists.
763
          return LookupTransition(receiver_map, name, holder);
764 765
        }
      }
766 767 768 769 770 771 772 773 774
      if (map->is_dictionary_map()) {
        DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);

        if (fast_mode_prototype_on_chain) {
          // TODO(v8:11248) While the work on dictionary mode prototypes is in
          // progress, we may still see fast mode objects on the chain prior to
          // reaching a dictionary mode prototype holding the property . Due to
          // this only being an intermediate state, we don't stupport these kind
          // of heterogenous prototype chains.
775
          return Invalid();
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
        }

        // TryLoadPropertyDetails only succeeds if we know the holder.
        return ComputeDictionaryProtoAccessInfo(receiver_map, name,
                                                holder.ToHandleChecked(), index,
                                                access_mode, details);
      }
      if (dictionary_prototype_on_chain) {
        // If V8_DICT_PROPERTY_CONST_TRACKING_BOOL was disabled, then a
        // dictionary prototype would have caused a bailout earlier.
        DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);

        // TODO(v8:11248) We have a fast mode holder, but there was a dictionary
        // mode prototype earlier on the chain. Note that seeing a fast mode
        // prototype even though V8_DICT_PROPERTY_CONST_TRACKING is enabled
        // should only be possible while the implementation of dictionary mode
        // prototypes is work in progress. Eventually, enabling
        // V8_DICT_PROPERTY_CONST_TRACKING will guarantee that all prototypes
        // are always in dictionary mode, making this case unreachable. However,
        // due to the complications of checking dictionary mode prototypes for
        // modification, we don't attempt to support dictionary mode prototypes
        // occuring before a fast mode holder on the chain.
798
        return Invalid();
799
      }
800 801
      if (details.location() == kField) {
        if (details.kind() == kData) {
802
          return ComputeDataFieldAccessInfo(receiver_map, map, holder, index,
803
                                            access_mode);
804 805 806
        } else {
          DCHECK_EQ(kAccessor, details.kind());
          // TODO(turbofan): Add support for general accessors?
807
          return Invalid();
808
        }
809 810
      } else {
        DCHECK_EQ(kDescriptor, details.location());
811 812
        DCHECK_EQ(kAccessor, details.kind());
        return ComputeAccessorDescriptorAccessInfo(receiver_map, name, map,
813
                                                   holder, index, access_mode);
814
      }
815

816
      UNREACHABLE();
817 818
    }

819 820
    // The property wasn't found on {map}. Look on the prototype if appropriate.

821 822
    // Don't search on the prototype chain for special indices in case of
    // integer indexed exotic objects (see ES6 section 9.4.5).
823 824 825 826 827 828 829 830 831 832 833
    if (map->IsJSTypedArrayMap() && name->IsString()) {
      if (broker()->IsMainThread()) {
        if (IsSpecialIndex(String::cast(*name))) {
          return Invalid();
        }
      } else {
        // TODO(jgruber): We are being conservative here since we can't access
        // string contents from background threads. Should that become possible
        // in the future, remove this bailout.
        return Invalid();
      }
834 835
    }

836
    // Don't search on the prototype when storing in literals.
837
    if (access_mode == AccessMode::kStoreInLiteral) {
838
      return LookupTransition(receiver_map, name, holder);
839 840
    }

841
    // Don't lookup private symbols on the prototype chain.
842
    if (name->IsPrivate()) {
843
      return Invalid();
844
    }
845

846 847 848 849 850 851 852 853 854 855 856
    if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && !holder.is_null()) {
      // At this point, we are past the first loop iteration.
      DCHECK(holder.ToHandleChecked()->map().is_prototype_map());
      DCHECK_NE(holder.ToHandleChecked()->map(), *receiver_map);

      fast_mode_prototype_on_chain =
          fast_mode_prototype_on_chain || !map->is_dictionary_map();
      dictionary_prototype_on_chain =
          dictionary_prototype_on_chain || map->is_dictionary_map();
    }

857
    // Walk up the prototype chain.
858
    base::Optional<MapRef> map_ref = TryMakeRef(broker(), map);
859 860
    if (!map_ref.has_value()) return Invalid();
    if (!map_ref->TrySerializePrototype()) return Invalid();
861

862 863
    // Acquire synchronously the map's prototype's map to guarantee that every
    // time we use it, we use the same Map.
864 865
    Handle<Map> map_prototype_map =
        broker()->CanonicalPersistentHandle(map->prototype().map(kAcquireLoad));
866
    if (!map_prototype_map->IsJSObjectMap()) {
867 868 869
      // Perform the implicit ToObject for primitives here.
      // Implemented according to ES6 section 7.3.2 GetV (V, P).
      Handle<JSFunction> constructor;
870 871 872 873 874 875 876
      base::Optional<JSFunction> maybe_constructor =
          Map::GetConstructorFunction(
              *map, *broker()->target_native_context().object());
      if (maybe_constructor.has_value()) {
        map = broker()->CanonicalPersistentHandle(
            maybe_constructor->initial_map());
        map_prototype_map = broker()->CanonicalPersistentHandle(
877
            map->prototype().map(kAcquireLoad));
878 879
        DCHECK(map_prototype_map->IsJSObjectMap());
      } else if (map->prototype().IsNull()) {
880 881 882 883 884 885
        if (dictionary_prototype_on_chain) {
          // TODO(v8:11248) See earlier comment about
          // dictionary_prototype_on_chain. We don't support absent properties
          // with dictionary mode prototypes on the chain, either. This is again
          // just due to how we currently deal with dependencies for dictionary
          // properties during finalization.
886
          return Invalid();
887 888
        }

889 890 891
        // Store to property not found on the receiver or any prototype, we need
        // to transition to a new data property.
        // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
892
        if (access_mode == AccessMode::kStore) {
893
          return LookupTransition(receiver_map, name, holder);
894
        }
895 896
        // The property was not found (access returns undefined or throws
        // depending on the language mode of the load operation.
897
        // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
898
        return PropertyAccessInfo::NotFound(zone(), receiver_map, holder);
899
      } else {
900
        return Invalid();
901 902
      }
    }
903

904 905
    holder =
        broker()->CanonicalPersistentHandle(JSObject::cast(map->prototype()));
906
    map = map_prototype_map;
907
    CHECK(!map->is_deprecated());
908

909
    if (!CanInlinePropertyAccess(map, access_mode)) {
910
      return Invalid();
911
    }
912

913 914 915 916
    // Successful lookup on prototype chain needs to guarantee that all the
    // prototypes up to the holder have stable maps, except for dictionary-mode
    // prototypes.
    CHECK_IMPLIES(!map->is_dictionary_map(), map->is_stable());
917
  }
918
  UNREACHABLE();
919
}
920

921 922 923 924 925 926 927 928 929 930
PropertyAccessInfo AccessInfoFactory::FinalizePropertyAccessInfosAsOne(
    ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode) const {
  ZoneVector<PropertyAccessInfo> merged_access_infos(zone());
  MergePropertyAccessInfos(access_infos, access_mode, &merged_access_infos);
  if (merged_access_infos.size() == 1) {
    PropertyAccessInfo& result = merged_access_infos.front();
    if (!result.IsInvalid()) {
      result.RecordDependencies(dependencies());
      return result;
    }
931
  }
932
  return Invalid();
933 934
}

935
void AccessInfoFactory::ComputePropertyAccessInfos(
936
    MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
937
    ZoneVector<PropertyAccessInfo>* access_infos) const {
938
  DCHECK(access_infos->empty());
939
  for (Handle<Map> map : maps) {
940
    access_infos->push_back(ComputePropertyAccessInfo(map, name, access_mode));
941
  }
942
}
943

944 945
void PropertyAccessInfo::RecordDependencies(
    CompilationDependencies* dependencies) {
946
  for (CompilationDependency const* d : unrecorded_dependencies_) {
947 948 949 950 951
    dependencies->RecordDependency(d);
  }
  unrecorded_dependencies_.clear();
}

952
bool AccessInfoFactory::FinalizePropertyAccessInfos(
953 954
    ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode,
    ZoneVector<PropertyAccessInfo>* result) const {
955
  if (access_infos.empty()) return false;
956 957 958 959 960 961 962 963 964 965 966
  MergePropertyAccessInfos(access_infos, access_mode, result);
  for (PropertyAccessInfo const& info : *result) {
    if (info.IsInvalid()) return false;
  }
  for (PropertyAccessInfo& info : *result) {
    info.RecordDependencies(dependencies());
  }
  return true;
}

void AccessInfoFactory::MergePropertyAccessInfos(
967 968 969
    ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
    ZoneVector<PropertyAccessInfo>* result) const {
  DCHECK(result->empty());
970 971 972 973 974 975
  for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
    bool merged = false;
    for (auto ot = it + 1; ot != end; ++ot) {
      if (ot->Merge(&(*it), access_mode, zone())) {
        merged = true;
        break;
976
      }
977
    }
978
    if (!merged) result->push_back(*it);
979
  }
980
  CHECK(!result->empty());
981 982
}

983 984
Isolate* AccessInfoFactory::isolate() const { return broker()->isolate(); }

985 986 987 988
namespace {

Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
                                           ElementsKind that_kind) {
989
  if (IsHoleyElementsKind(this_kind)) {
990
    that_kind = GetHoleyElementsKind(that_kind);
991
  } else if (IsHoleyElementsKind(that_kind)) {
992 993 994
    this_kind = GetHoleyElementsKind(this_kind);
  }
  if (this_kind == that_kind) return Just(this_kind);
995
  if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
    if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
      return Just(this_kind);
    }
    if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
      return Just(that_kind);
    }
  }
  return Nothing<ElementsKind>();
}

}  // namespace

1008
base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad(
1009 1010 1011 1012
    ElementAccessFeedback const& feedback) const {
  if (feedback.transition_groups().empty()) return base::nullopt;

  DCHECK(!feedback.transition_groups().front().empty());
1013
  Handle<Map> first_map = feedback.transition_groups().front().front();
1014
  base::Optional<MapRef> first_map_ref = TryMakeRef(broker(), first_map);
1015 1016 1017
  if (!first_map_ref.has_value()) return {};
  InstanceType instance_type = first_map_ref->instance_type();
  ElementsKind elements_kind = first_map_ref->elements_kind();
1018

1019
  ZoneVector<Handle<Map>> maps(zone());
1020 1021
  for (auto const& group : feedback.transition_groups()) {
    for (Handle<Map> map_handle : group) {
1022
      base::Optional<MapRef> map = TryMakeRef(broker(), map_handle);
1023 1024 1025
      if (!map.has_value()) return {};
      if (map->instance_type() != instance_type ||
          !CanInlineElementAccess(*map)) {
1026 1027
        return base::nullopt;
      }
1028
      if (!GeneralizeElementsKind(elements_kind, map->elements_kind())
1029 1030 1031
               .To(&elements_kind)) {
        return base::nullopt;
      }
1032
      maps.push_back(map->object());
1033
    }
1034 1035
  }

1036
  return ElementAccessInfo(std::move(maps), elements_kind, zone());
1037
}
1038

1039 1040
PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
    Handle<Map> map, Handle<Name> name) const {
1041 1042
  // Check for String::length field accessor.
  if (map->IsStringMap()) {
1043
    if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
1044
      return PropertyAccessInfo::StringLength(zone(), map);
1045
    }
1046
    return Invalid();
1047
  }
1048
  // Check for special JSObject field accessors.
1049
  FieldIndex field_index;
1050
  if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
1051
    Type field_type = Type::NonInternal();
1052
    Representation field_representation = Representation::Tagged();
1053
    if (map->IsJSArrayMap()) {
1054 1055
      DCHECK(
          Name::Equals(isolate(), isolate()->factory()->length_string(), name));
1056 1057 1058 1059 1060
      // The JSArray::length property is a smi in the range
      // [0, FixedDoubleArray::kMaxLength] in case of fast double
      // elements, a smi in the range [0, FixedArray::kMaxLength]
      // in case of other fast elements, and [0, kMaxUInt32] in
      // case of other arrays.
1061
      if (IsDoubleElementsKind(map->elements_kind())) {
1062
        field_type = type_cache_->kFixedDoubleArrayLengthType;
1063
        field_representation = Representation::Smi();
1064
      } else if (IsFastElementsKind(map->elements_kind())) {
1065
        field_type = type_cache_->kFixedArrayLengthType;
1066
        field_representation = Representation::Smi();
1067
      } else {
1068
        field_type = type_cache_->kJSArrayLengthType;
1069 1070
      }
    }
1071
    // Special fields are always mutable.
1072
    return PropertyAccessInfo::DataField(zone(), map, {{}, zone()}, field_index,
1073
                                         field_representation, field_type, map);
1074
  }
1075
  return Invalid();
1076 1077
}

1078 1079
PropertyAccessInfo AccessInfoFactory::LookupTransition(
    Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const {
1080
  // Check if the {map} has a data transition with the given {name}.
1081
  Map transition =
1082 1083
      TransitionsAccessor(isolate(), map, broker()->is_concurrent_inlining())
          .SearchTransition(*name, kData, NONE);
1084
  if (transition.is_null()) {
1085
    return Invalid();
1086
  }
1087

1088
  Handle<Map> transition_map = broker()->CanonicalPersistentHandle(transition);
1089
  InternalIndex const number = transition_map->LastAdded();
1090 1091
  Handle<DescriptorArray> descriptors = broker()->CanonicalPersistentHandle(
      transition_map->instance_descriptors(kAcquireLoad));
1092
  PropertyDetails const details = descriptors->GetDetails(number);
1093
  // Don't bother optimizing stores to read-only properties.
1094
  if (details.IsReadOnly()) {
1095
    return Invalid();
1096
  }
1097
  // TODO(bmeurer): Handle transition to data constant?
1098
  if (details.location() != kField) {
1099
    return Invalid();
1100
  }
1101 1102
  int const index = details.field_index();
  Representation details_representation = details.representation();
1103 1104
  FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
                                                        details_representation);
1105
  Type field_type = Type::NonInternal();
1106
  MaybeHandle<Map> field_map;
1107

1108
  base::Optional<MapRef> transition_map_ref =
1109
      TryMakeRef(broker(), transition_map);
1110
  if (!transition_map_ref.has_value()) return Invalid();
1111

1112
  ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone());
1113 1114
  if (details_representation.IsSmi()) {
    field_type = Type::SignedSmall();
1115
    if (!transition_map_ref->TrySerializeOwnDescriptor(number)) {
1116 1117
      return Invalid();
    }
1118 1119
    unrecorded_dependencies.push_back(
        dependencies()->FieldRepresentationDependencyOffTheRecord(
1120
            *transition_map_ref, number));
1121
  } else if (details_representation.IsDouble()) {
1122
    field_type = type_cache_->kFloat64;
1123
    if (!transition_map_ref->TrySerializeOwnDescriptor(number)) {
1124 1125
      return Invalid();
    }
1126 1127
    unrecorded_dependencies.push_back(
        dependencies()->FieldRepresentationDependencyOffTheRecord(
1128
            *transition_map_ref, number));
1129 1130 1131
  } else if (details_representation.IsHeapObject()) {
    // Extract the field type from the property details (make sure its
    // representation is TaggedPointer to reflect the heap object case).
1132 1133
    Handle<FieldType> descriptors_field_type =
        broker()->CanonicalPersistentHandle(descriptors->GetFieldType(number));
1134 1135
    if (descriptors_field_type->IsNone()) {
      // Store is not safe if the field type was cleared.
1136 1137
      return Invalid();
    }
1138
    if (!transition_map_ref->TrySerializeOwnDescriptor(number)) {
1139
      return Invalid();
1140
    }
1141 1142
    unrecorded_dependencies.push_back(
        dependencies()->FieldRepresentationDependencyOffTheRecord(
1143
            *transition_map_ref, number));
1144
    if (descriptors_field_type->IsClass()) {
1145
      unrecorded_dependencies.push_back(
1146
          dependencies()->FieldTypeDependencyOffTheRecord(*transition_map_ref,
1147
                                                          number));
1148
      // Remember the field map, and try to infer a useful type.
1149 1150
      Handle<Map> map = broker()->CanonicalPersistentHandle(
          descriptors_field_type->AsClass());
1151
      base::Optional<MapRef> map_ref = TryMakeRef(broker(), map);
1152 1153 1154
      if (!map_ref.has_value()) return Invalid();
      field_type = Type::For(*map_ref);
      field_map = map;
1155 1156
    }
  }
1157 1158 1159
  unrecorded_dependencies.push_back(
      dependencies()->TransitionDependencyOffTheRecord(*transition_map_ref));
  transition_map_ref->SerializeBackPointer();  // For BuildPropertyStore.
1160 1161 1162
  // Transitioning stores *may* store to const fields. The resulting
  // DataConstant access infos can be distinguished from later, i.e. redundant,
  // stores to the same constant field by the presence of a transition map.
1163
  switch (dependencies()->DependOnFieldConstness(*transition_map_ref, number)) {
1164 1165 1166
    case PropertyConstness::kMutable:
      return PropertyAccessInfo::DataField(
          zone(), map, std::move(unrecorded_dependencies), field_index,
1167
          details_representation, field_type, transition_map, field_map, holder,
1168 1169
          transition_map);
    case PropertyConstness::kConst:
1170
      return PropertyAccessInfo::FastDataConstant(
1171
          zone(), map, std::move(unrecorded_dependencies), field_index,
1172
          details_representation, field_type, transition_map, field_map, holder,
1173 1174 1175
          transition_map);
  }
  UNREACHABLE();
1176 1177 1178 1179 1180
}

}  // namespace compiler
}  // namespace internal
}  // namespace v8