V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
access-info.cc
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <ostream>
6 
7 #include "src/compiler/access-info.h"
8 
9 #include "src/accessors.h"
10 #include "src/compiler/compilation-dependencies.h"
11 #include "src/compiler/type-cache.h"
12 #include "src/field-index-inl.h"
13 #include "src/field-type.h"
14 #include "src/ic/call-optimization.h"
15 #include "src/objects-inl.h"
16 #include "src/objects/module-inl.h"
17 #include "src/objects/templates.h"
18 
19 namespace v8 {
20 namespace internal {
21 namespace compiler {
22 
23 namespace {
24 
25 bool CanInlineElementAccess(Handle<Map> map) {
26  if (!map->IsJSObjectMap()) return false;
27  if (map->is_access_check_needed()) return false;
28  if (map->has_indexed_interceptor()) return false;
29  ElementsKind const elements_kind = map->elements_kind();
30  if (IsFastElementsKind(elements_kind)) return true;
31  if (IsFixedTypedArrayElementsKind(elements_kind) &&
32  elements_kind != BIGUINT64_ELEMENTS &&
33  elements_kind != BIGINT64_ELEMENTS) {
34  return true;
35  }
36  return false;
37 }
38 
39 
40 bool CanInlinePropertyAccess(Handle<Map> map) {
41  // We can inline property access to prototypes of all primitives, except
42  // the special Oddball ones that have no wrapper counterparts (i.e. Null,
43  // Undefined and TheHole).
44  STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_TYPE);
45  if (map->IsBooleanMap()) return true;
46  if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
47  return map->IsJSObjectMap() && !map->is_dictionary_map() &&
48  !map->has_named_interceptor() &&
49  // TODO(verwaest): Whitelist contexts to which we have access.
50  !map->is_access_check_needed();
51 }
52 
53 } // namespace
54 
55 
56 std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
57  switch (access_mode) {
58  case AccessMode::kLoad:
59  return os << "Load";
60  case AccessMode::kStore:
61  return os << "Store";
62  case AccessMode::kStoreInLiteral:
63  return os << "StoreInLiteral";
64  }
65  UNREACHABLE();
66 }
67 
68 ElementAccessInfo::ElementAccessInfo() = default;
69 
70 ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
71  ElementsKind elements_kind)
72  : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {}
73 
74 // static
75 PropertyAccessInfo PropertyAccessInfo::NotFound(MapHandles const& receiver_maps,
76  MaybeHandle<JSObject> holder) {
77  return PropertyAccessInfo(kNotFound, holder, receiver_maps);
78 }
79 
80 // static
81 PropertyAccessInfo PropertyAccessInfo::DataConstant(
82  MapHandles const& receiver_maps, Handle<Object> constant,
83  MaybeHandle<JSObject> holder) {
84  return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
85 }
86 
87 // static
88 PropertyAccessInfo PropertyAccessInfo::DataField(
89  PropertyConstness constness, MapHandles const& receiver_maps,
90  FieldIndex field_index, MachineRepresentation field_representation,
91  Type field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
92  MaybeHandle<Map> transition_map) {
93  Kind kind =
94  constness == PropertyConstness::kConst ? kDataConstantField : kDataField;
95  return PropertyAccessInfo(kind, holder, transition_map, field_index,
96  field_representation, field_type, field_map,
97  receiver_maps);
98 }
99 
100 // static
101 PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
102  MapHandles const& receiver_maps, Handle<Object> constant,
103  MaybeHandle<JSObject> holder) {
104  return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
105 }
106 
107 // static
108 PropertyAccessInfo PropertyAccessInfo::ModuleExport(
109  MapHandles const& receiver_maps, Handle<Cell> cell) {
110  return PropertyAccessInfo(kModuleExport, MaybeHandle<JSObject>(), cell,
111  receiver_maps);
112 }
113 
114 // static
115 PropertyAccessInfo PropertyAccessInfo::StringLength(
116  MapHandles const& receiver_maps) {
117  return PropertyAccessInfo(kStringLength, MaybeHandle<JSObject>(),
118  receiver_maps);
119 }
120 
121 PropertyAccessInfo::PropertyAccessInfo()
122  : kind_(kInvalid),
123  field_representation_(MachineRepresentation::kNone),
124  field_type_(Type::None()) {}
125 
126 PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
127  MapHandles const& receiver_maps)
128  : kind_(kind),
129  receiver_maps_(receiver_maps),
130  holder_(holder),
131  field_representation_(MachineRepresentation::kNone),
132  field_type_(Type::None()) {}
133 
134 PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
135  Handle<Object> constant,
136  MapHandles const& receiver_maps)
137  : kind_(kind),
138  receiver_maps_(receiver_maps),
139  constant_(constant),
140  holder_(holder),
141  field_representation_(MachineRepresentation::kNone),
142  field_type_(Type::Any()) {}
143 
144 PropertyAccessInfo::PropertyAccessInfo(
145  Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
146  FieldIndex field_index, MachineRepresentation field_representation,
147  Type field_type, MaybeHandle<Map> field_map,
148  MapHandles const& receiver_maps)
149  : kind_(kind),
150  receiver_maps_(receiver_maps),
151  transition_map_(transition_map),
152  holder_(holder),
153  field_index_(field_index),
154  field_representation_(field_representation),
155  field_type_(field_type),
156  field_map_(field_map) {}
157 
158 bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
159  AccessMode access_mode, Zone* zone) {
160  if (this->kind_ != that->kind_) return false;
161  if (this->holder_.address() != that->holder_.address()) return false;
162 
163  switch (this->kind_) {
164  case kInvalid:
165  break;
166 
167  case kDataField:
168  case kDataConstantField: {
169  // Check if we actually access the same field (we use the
170  // GetFieldAccessStubKey method here just like the ICs do
171  // since that way we only compare the relevant bits of the
172  // field indices).
173  if (this->field_index_.GetFieldAccessStubKey() ==
174  that->field_index_.GetFieldAccessStubKey()) {
175  switch (access_mode) {
176  case AccessMode::kLoad: {
177  if (this->field_representation_ != that->field_representation_) {
178  if (!IsAnyTagged(this->field_representation_) ||
179  !IsAnyTagged(that->field_representation_)) {
180  return false;
181  }
182  this->field_representation_ = MachineRepresentation::kTagged;
183  }
184  if (this->field_map_.address() != that->field_map_.address()) {
185  this->field_map_ = MaybeHandle<Map>();
186  }
187  break;
188  }
189  case AccessMode::kStore:
190  case AccessMode::kStoreInLiteral: {
191  // For stores, the field map and field representation information
192  // must match exactly, otherwise we cannot merge the stores. We
193  // also need to make sure that in case of transitioning stores,
194  // the transition targets match.
195  if (this->field_map_.address() != that->field_map_.address() ||
196  this->field_representation_ != that->field_representation_ ||
197  this->transition_map_.address() !=
198  that->transition_map_.address()) {
199  return false;
200  }
201  break;
202  }
203  }
204  // Merge the field type.
205  this->field_type_ =
206  Type::Union(this->field_type_, that->field_type_, zone);
207  // Merge the receiver maps.
208  this->receiver_maps_.insert(this->receiver_maps_.end(),
209  that->receiver_maps_.begin(),
210  that->receiver_maps_.end());
211  return true;
212  }
213  return false;
214  }
215 
216  case kDataConstant:
217  case kAccessorConstant: {
218  // Check if we actually access the same constant.
219  if (this->constant_.address() == that->constant_.address()) {
220  this->receiver_maps_.insert(this->receiver_maps_.end(),
221  that->receiver_maps_.begin(),
222  that->receiver_maps_.end());
223  return true;
224  }
225  return false;
226  }
227 
228  case kNotFound:
229  case kStringLength: {
230  this->receiver_maps_.insert(this->receiver_maps_.end(),
231  that->receiver_maps_.begin(),
232  that->receiver_maps_.end());
233  return true;
234  }
235  case kModuleExport: {
236  return false;
237  }
238  }
239 
240  UNREACHABLE();
241 }
242 
243 Handle<Cell> PropertyAccessInfo::export_cell() const {
244  DCHECK_EQ(kModuleExport, kind_);
245  return Handle<Cell>::cast(constant_);
246 }
247 
248 AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
249  CompilationDependencies* dependencies,
250  Handle<Context> native_context, Zone* zone)
251  : broker_(broker),
252  dependencies_(dependencies),
253  native_context_(native_context),
254  isolate_(native_context->GetIsolate()),
255  type_cache_(TypeCache::Get()),
256  zone_(zone) {
257  DCHECK(native_context->IsNativeContext());
258 }
259 
260 
261 bool AccessInfoFactory::ComputeElementAccessInfo(
262  Handle<Map> map, AccessMode access_mode, ElementAccessInfo* access_info) {
263  // Check if it is safe to inline element access for the {map}.
264  if (!CanInlineElementAccess(map)) return false;
265  ElementsKind const elements_kind = map->elements_kind();
266  *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
267  return true;
268 }
269 
270 bool AccessInfoFactory::ComputeElementAccessInfos(
271  MapHandles const& maps, AccessMode access_mode,
272  ZoneVector<ElementAccessInfo>* access_infos) {
273  if (access_mode == AccessMode::kLoad) {
274  // For polymorphic loads of similar elements kinds (i.e. all tagged or all
275  // double), always use the "worst case" code without a transition. This is
276  // much faster than transitioning the elements to the worst case, trading a
277  // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
278  ElementAccessInfo access_info;
279  if (ConsolidateElementLoad(maps, &access_info)) {
280  access_infos->push_back(access_info);
281  return true;
282  }
283  }
284 
285  // Collect possible transition targets.
286  MapHandles possible_transition_targets;
287  possible_transition_targets.reserve(maps.size());
288  for (Handle<Map> map : maps) {
289  if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
290  if (CanInlineElementAccess(map) &&
291  IsFastElementsKind(map->elements_kind()) &&
292  GetInitialFastElementsKind() != map->elements_kind()) {
293  possible_transition_targets.push_back(map);
294  }
295  }
296  }
297 
298  // Separate the actual receiver maps and the possible transition sources.
299  MapHandles receiver_maps;
300  receiver_maps.reserve(maps.size());
301  MapTransitionList transitions(maps.size());
302  for (Handle<Map> map : maps) {
303  if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
304  // Don't generate elements kind transitions from stable maps.
305  Map transition_target = map->is_stable()
306  ? Map()
307  : map->FindElementsKindTransitionedMap(
308  isolate(), possible_transition_targets);
309  if (transition_target.is_null()) {
310  receiver_maps.push_back(map);
311  } else {
312  transitions.push_back(
313  std::make_pair(map, handle(transition_target, isolate())));
314  }
315  }
316  }
317 
318  for (Handle<Map> receiver_map : receiver_maps) {
319  // Compute the element access information.
320  ElementAccessInfo access_info;
321  if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
322  return false;
323  }
324 
325  // Collect the possible transitions for the {receiver_map}.
326  for (auto transition : transitions) {
327  if (transition.second.is_identical_to(receiver_map)) {
328  access_info.transitions().push_back(transition);
329  }
330  }
331 
332  // Schedule the access information.
333  access_infos->push_back(access_info);
334  }
335  return true;
336 }
337 
338 
339 bool AccessInfoFactory::ComputePropertyAccessInfo(
340  Handle<Map> map, Handle<Name> name, AccessMode access_mode,
341  PropertyAccessInfo* access_info) {
342  // Check if it is safe to inline property access for the {map}.
343  if (!CanInlinePropertyAccess(map)) return false;
344 
345  // Compute the receiver type.
346  Handle<Map> receiver_map = map;
347 
348  // Property lookups require the name to be internalized.
349  name = isolate()->factory()->InternalizeName(name);
350 
351  // We support fast inline cases for certain JSObject getters.
352  if (access_mode == AccessMode::kLoad &&
353  LookupSpecialFieldAccessor(map, name, access_info)) {
354  return true;
355  }
356 
357  MaybeHandle<JSObject> holder;
358  do {
359  // Lookup the named property on the {map}.
360  Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
361  int const number = descriptors->Search(*name, *map);
362  if (number != DescriptorArray::kNotFound) {
363  PropertyDetails const details = descriptors->GetDetails(number);
364  if (access_mode == AccessMode::kStore ||
365  access_mode == AccessMode::kStoreInLiteral) {
366  // Don't bother optimizing stores to read-only properties.
367  if (details.IsReadOnly()) {
368  return false;
369  }
370  // Check for store to data property on a prototype.
371  if (details.kind() == kData && !holder.is_null()) {
372  // Store to property not found on the receiver but on a prototype, we
373  // need to transition to a new data property.
374  // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
375  return LookupTransition(receiver_map, name, holder, access_info);
376  }
377  }
378  if (details.location() == kField) {
379  if (details.kind() == kData) {
380  int index = descriptors->GetFieldIndex(number);
381  Representation details_representation = details.representation();
382  FieldIndex field_index =
383  FieldIndex::ForPropertyIndex(*map, index, details_representation);
384  Type field_type = Type::NonInternal();
385  MachineRepresentation field_representation =
386  MachineRepresentation::kTagged;
387  MaybeHandle<Map> field_map;
388  if (details_representation.IsSmi()) {
389  field_type = Type::SignedSmall();
390  field_representation = MachineRepresentation::kTaggedSigned;
391  } else if (details_representation.IsDouble()) {
392  field_type = type_cache_.kFloat64;
393  field_representation = MachineRepresentation::kFloat64;
394  } else if (details_representation.IsHeapObject()) {
395  // Extract the field type from the property details (make sure its
396  // representation is TaggedPointer to reflect the heap object case).
397  field_representation = MachineRepresentation::kTaggedPointer;
398  Handle<FieldType> descriptors_field_type(
399  descriptors->GetFieldType(number), isolate());
400  if (descriptors_field_type->IsNone()) {
401  // Store is not safe if the field type was cleared.
402  if (access_mode == AccessMode::kStore) return false;
403 
404  // The field type was cleared by the GC, so we don't know anything
405  // about the contents now.
406  } else if (descriptors_field_type->IsClass()) {
407  MapRef map_ref(broker(), map);
408  map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
409  dependencies()->DependOnFieldType(map_ref, number);
410  // Remember the field map, and try to infer a useful type.
411  Handle<Map> map(descriptors_field_type->AsClass(), isolate());
412  field_type = Type::For(MapRef(broker(), map));
413  field_map = MaybeHandle<Map>(map);
414  }
415  }
416  *access_info = PropertyAccessInfo::DataField(
417  details.constness(), MapHandles{receiver_map}, field_index,
418  field_representation, field_type, field_map, holder);
419  return true;
420  } else {
421  DCHECK_EQ(kAccessor, details.kind());
422  // TODO(turbofan): Add support for general accessors?
423  return false;
424  }
425 
426  } else {
427  DCHECK_EQ(kDescriptor, details.location());
428  if (details.kind() == kData) {
429  DCHECK(!FLAG_track_constant_fields);
430  *access_info = PropertyAccessInfo::DataConstant(
431  MapHandles{receiver_map},
432  handle(descriptors->GetStrongValue(number), isolate()), holder);
433  return true;
434  } else {
435  DCHECK_EQ(kAccessor, details.kind());
436  if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
437  DCHECK(map->is_prototype_map());
438  Handle<PrototypeInfo> proto_info =
439  Map::GetOrCreatePrototypeInfo(map, isolate());
440  Handle<JSModuleNamespace> module_namespace(
441  JSModuleNamespace::cast(proto_info->module_namespace()),
442  isolate());
443  Handle<Cell> cell(
444  Cell::cast(module_namespace->module()->exports()->Lookup(
445  ReadOnlyRoots(isolate()), name,
446  Smi::ToInt(name->GetHash()))),
447  isolate());
448  if (cell->value()->IsTheHole(isolate())) {
449  // This module has not been fully initialized yet.
450  return false;
451  }
452  *access_info = PropertyAccessInfo::ModuleExport(
453  MapHandles{receiver_map}, cell);
454  return true;
455  }
456  Handle<Object> accessors(descriptors->GetStrongValue(number),
457  isolate());
458  if (!accessors->IsAccessorPair()) return false;
459  Handle<Object> accessor(
460  access_mode == AccessMode::kLoad
461  ? Handle<AccessorPair>::cast(accessors)->getter()
462  : Handle<AccessorPair>::cast(accessors)->setter(),
463  isolate());
464  if (!accessor->IsJSFunction()) {
465  CallOptimization optimization(isolate(), accessor);
466  if (!optimization.is_simple_api_call()) return false;
467  if (optimization.IsCrossContextLazyAccessorPair(*native_context_,
468  *map)) {
469  return false;
470  }
471 
472  CallOptimization::HolderLookup lookup;
473  holder =
474  optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
475  if (lookup == CallOptimization::kHolderNotFound) return false;
476  DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
477  holder.is_null());
478  DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound,
479  !holder.is_null());
480  if (V8_UNLIKELY(FLAG_runtime_stats)) return false;
481  }
482  if (access_mode == AccessMode::kLoad) {
483  Handle<Name> cached_property_name;
484  if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(),
485  accessor)
486  .ToHandle(&cached_property_name)) {
487  if (ComputePropertyAccessInfo(map, cached_property_name,
488  access_mode, access_info)) {
489  return true;
490  }
491  }
492  }
493  *access_info = PropertyAccessInfo::AccessorConstant(
494  MapHandles{receiver_map}, accessor, holder);
495  return true;
496  }
497  }
498  UNREACHABLE();
499  }
500 
501  // Don't search on the prototype chain for special indices in case of
502  // integer indexed exotic objects (see ES6 section 9.4.5).
503  if (map->IsJSTypedArrayMap() && name->IsString() &&
504  IsSpecialIndex(String::cast(*name))) {
505  return false;
506  }
507 
508  // Don't search on the prototype when storing in literals
509  if (access_mode == AccessMode::kStoreInLiteral) {
510  return LookupTransition(receiver_map, name, holder, access_info);
511  }
512 
513  // Don't lookup private symbols on the prototype chain.
514  if (name->IsPrivate()) return false;
515 
516  // Walk up the prototype chain.
517  if (!map->prototype()->IsJSObject()) {
518  // Perform the implicit ToObject for primitives here.
519  // Implemented according to ES6 section 7.3.2 GetV (V, P).
520  Handle<JSFunction> constructor;
521  if (Map::GetConstructorFunction(map, native_context())
522  .ToHandle(&constructor)) {
523  map = handle(constructor->initial_map(), isolate());
524  DCHECK(map->prototype()->IsJSObject());
525  } else if (map->prototype()->IsNull(isolate())) {
526  // Store to property not found on the receiver or any prototype, we need
527  // to transition to a new data property.
528  // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
529  if (access_mode == AccessMode::kStore) {
530  return LookupTransition(receiver_map, name, holder, access_info);
531  }
532  // The property was not found, return undefined or throw depending
533  // on the language mode of the load operation.
534  // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
535  *access_info =
536  PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
537  return true;
538  } else {
539  return false;
540  }
541  }
542  Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
543  if (map_prototype->map()->is_deprecated()) {
544  // Try to migrate the prototype object so we don't embed the deprecated
545  // map into the optimized code.
546  JSObject::TryMigrateInstance(map_prototype);
547  }
548  map = handle(map_prototype->map(), isolate());
549  holder = map_prototype;
550  } while (CanInlinePropertyAccess(map));
551  return false;
552 }
553 
554 bool AccessInfoFactory::ComputePropertyAccessInfo(
555  MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
556  PropertyAccessInfo* access_info) {
557  ZoneVector<PropertyAccessInfo> access_infos(zone());
558  if (ComputePropertyAccessInfos(maps, name, access_mode, &access_infos) &&
559  access_infos.size() == 1) {
560  *access_info = access_infos.front();
561  return true;
562  }
563  return false;
564 }
565 
566 bool AccessInfoFactory::ComputePropertyAccessInfos(
567  MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
568  ZoneVector<PropertyAccessInfo>* access_infos) {
569  for (Handle<Map> map : maps) {
570  if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
571  PropertyAccessInfo access_info;
572  if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
573  return false;
574  }
575  // Try to merge the {access_info} with an existing one.
576  bool merged = false;
577  for (PropertyAccessInfo& other_info : *access_infos) {
578  if (other_info.Merge(&access_info, access_mode, zone())) {
579  merged = true;
580  break;
581  }
582  }
583  if (!merged) access_infos->push_back(access_info);
584  }
585  }
586  return true;
587 }
588 
589 namespace {
590 
591 Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
592  ElementsKind that_kind) {
593  if (IsHoleyElementsKind(this_kind)) {
594  that_kind = GetHoleyElementsKind(that_kind);
595  } else if (IsHoleyElementsKind(that_kind)) {
596  this_kind = GetHoleyElementsKind(this_kind);
597  }
598  if (this_kind == that_kind) return Just(this_kind);
599  if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
600  if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
601  return Just(this_kind);
602  }
603  if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
604  return Just(that_kind);
605  }
606  }
607  return Nothing<ElementsKind>();
608 }
609 
610 } // namespace
611 
612 bool AccessInfoFactory::ConsolidateElementLoad(MapHandles const& maps,
613  ElementAccessInfo* access_info) {
614  if (maps.empty()) return false;
615  InstanceType instance_type = maps.front()->instance_type();
616  ElementsKind elements_kind = maps.front()->elements_kind();
617  for (Handle<Map> map : maps) {
618  if (!CanInlineElementAccess(map) || map->instance_type() != instance_type) {
619  return false;
620  }
621  if (!GeneralizeElementsKind(elements_kind, map->elements_kind())
622  .To(&elements_kind)) {
623  return false;
624  }
625  }
626  *access_info = ElementAccessInfo(maps, elements_kind);
627  return true;
628 }
629 
630 bool AccessInfoFactory::LookupSpecialFieldAccessor(
631  Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) {
632  // Check for String::length field accessor.
633  if (map->IsStringMap()) {
634  if (Name::Equals(isolate(), name, factory()->length_string())) {
635  *access_info = PropertyAccessInfo::StringLength(MapHandles{map});
636  return true;
637  }
638  return false;
639  }
640  // Check for special JSObject field accessors.
641  FieldIndex field_index;
642  if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
643  Type field_type = Type::NonInternal();
644  MachineRepresentation field_representation = MachineRepresentation::kTagged;
645  if (map->IsJSArrayMap()) {
646  DCHECK(Name::Equals(isolate(), factory()->length_string(), name));
647  // The JSArray::length property is a smi in the range
648  // [0, FixedDoubleArray::kMaxLength] in case of fast double
649  // elements, a smi in the range [0, FixedArray::kMaxLength]
650  // in case of other fast elements, and [0, kMaxUInt32] in
651  // case of other arrays.
652  if (IsDoubleElementsKind(map->elements_kind())) {
653  field_type = type_cache_.kFixedDoubleArrayLengthType;
654  field_representation = MachineRepresentation::kTaggedSigned;
655  } else if (IsFastElementsKind(map->elements_kind())) {
656  field_type = type_cache_.kFixedArrayLengthType;
657  field_representation = MachineRepresentation::kTaggedSigned;
658  } else {
659  field_type = type_cache_.kJSArrayLengthType;
660  }
661  }
662  // Special fields are always mutable.
663  *access_info = PropertyAccessInfo::DataField(
664  PropertyConstness::kMutable, MapHandles{map}, field_index,
665  field_representation, field_type);
666  return true;
667  }
668  return false;
669 }
670 
671 
672 bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
673  MaybeHandle<JSObject> holder,
674  PropertyAccessInfo* access_info) {
675  // Check if the {map} has a data transition with the given {name}.
676  Map transition =
677  TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
678  if (transition.is_null()) return false;
679 
680  Handle<Map> transition_map(transition, isolate());
681  int const number = transition_map->LastAdded();
682  PropertyDetails const details =
683  transition_map->instance_descriptors()->GetDetails(number);
684  // Don't bother optimizing stores to read-only properties.
685  if (details.IsReadOnly()) return false;
686  // TODO(bmeurer): Handle transition to data constant?
687  if (details.location() != kField) return false;
688  int const index = details.field_index();
689  Representation details_representation = details.representation();
690  FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
691  details_representation);
692  Type field_type = Type::NonInternal();
693  MaybeHandle<Map> field_map;
694  MachineRepresentation field_representation = MachineRepresentation::kTagged;
695  if (details_representation.IsSmi()) {
696  field_type = Type::SignedSmall();
697  field_representation = MachineRepresentation::kTaggedSigned;
698  } else if (details_representation.IsDouble()) {
699  field_type = type_cache_.kFloat64;
700  field_representation = MachineRepresentation::kFloat64;
701  } else if (details_representation.IsHeapObject()) {
702  // Extract the field type from the property details (make sure its
703  // representation is TaggedPointer to reflect the heap object case).
704  field_representation = MachineRepresentation::kTaggedPointer;
705  Handle<FieldType> descriptors_field_type(
706  transition_map->instance_descriptors()->GetFieldType(number),
707  isolate());
708  if (descriptors_field_type->IsNone()) {
709  // Store is not safe if the field type was cleared.
710  return false;
711  } else if (descriptors_field_type->IsClass()) {
712  MapRef transition_map_ref(broker(), transition_map);
713  transition_map_ref
714  .SerializeOwnDescriptors(); // TODO(neis): Remove later.
715  dependencies()->DependOnFieldType(transition_map_ref, number);
716  // Remember the field map, and try to infer a useful type.
717  Handle<Map> map(descriptors_field_type->AsClass(), isolate());
718  field_type = Type::For(MapRef(broker(), map));
719  field_map = MaybeHandle<Map>(map);
720  }
721  }
722  dependencies()->DependOnTransition(MapRef(broker(), transition_map));
723  // Transitioning stores are never stores to constant fields.
724  *access_info = PropertyAccessInfo::DataField(
725  PropertyConstness::kMutable, MapHandles{map}, field_index,
726  field_representation, field_type, field_map, holder, transition_map);
727  return true;
728 }
729 
730 
731 Factory* AccessInfoFactory::factory() const { return isolate()->factory(); }
732 
733 } // namespace compiler
734 } // namespace internal
735 } // namespace v8
Definition: libplatform.h:13
Definition: v8.h:3134