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