1 // Copyright 2014 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 "src/ic/handler-compiler.h" 6 7 #include "src/field-type.h" 8 #include "src/ic/call-optimization.h" 9 #include "src/ic/ic-inl.h" 10 #include "src/ic/ic.h" 11 #include "src/isolate-inl.h" 12 13 namespace v8 { 14 namespace internal { 15 16 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name, 17 Handle<Map> stub_holder, 18 Code::Kind kind, 19 CacheHolderFlag cache_holder) { 20 Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder); 21 Code* code = stub_holder->LookupInCodeCache(*name, flags); 22 if (code == nullptr) return Handle<Code>(); 23 return handle(code); 24 } 25 26 27 Handle<Code> NamedLoadHandlerCompiler::ComputeLoadNonexistent( 28 Handle<Name> name, Handle<Map> receiver_map) { 29 Isolate* isolate = name->GetIsolate(); 30 if (receiver_map->prototype()->IsNull(isolate)) { 31 // TODO(jkummerow/verwaest): If there is no prototype and the property 32 // is nonexistent, introduce a builtin to handle this (fast properties 33 // -> return undefined, dictionary properties -> do negative lookup). 34 return Handle<Code>(); 35 } 36 CacheHolderFlag flag; 37 Handle<Map> stub_holder_map = 38 IC::GetHandlerCacheHolder(receiver_map, false, isolate, &flag); 39 40 // If no dictionary mode objects are present in the prototype chain, the load 41 // nonexistent IC stub can be shared for all names for a given map and we use 42 // the empty string for the map cache in that case. If there are dictionary 43 // mode objects involved, we need to do negative lookups in the stub and 44 // therefore the stub will be specific to the name. 45 Handle<Name> cache_name = 46 receiver_map->is_dictionary_map() 47 ? name 48 : Handle<Name>::cast(isolate->factory()->nonexistent_symbol()); 49 Handle<Map> current_map = stub_holder_map; 50 Handle<JSObject> last(JSObject::cast(receiver_map->prototype())); 51 while (true) { 52 if (current_map->is_dictionary_map()) cache_name = name; 53 if (current_map->prototype()->IsNull(isolate)) break; 54 if (name->IsPrivate()) { 55 // TODO(verwaest): Use nonexistent_private_symbol. 56 cache_name = name; 57 if (!current_map->has_hidden_prototype()) break; 58 } 59 60 last = handle(JSObject::cast(current_map->prototype())); 61 current_map = handle(last->map()); 62 } 63 // Compile the stub that is either shared for all names or 64 // name specific if there are global objects involved. 65 Handle<Code> handler = PropertyHandlerCompiler::Find( 66 cache_name, stub_holder_map, Code::LOAD_IC, flag); 67 if (!handler.is_null()) return handler; 68 69 TRACE_HANDLER_STATS(isolate, LoadIC_LoadNonexistent); 70 NamedLoadHandlerCompiler compiler(isolate, receiver_map, last, flag); 71 handler = compiler.CompileLoadNonexistent(cache_name); 72 Map::UpdateCodeCache(stub_holder_map, cache_name, handler); 73 return handler; 74 } 75 76 77 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind, 78 Handle<Name> name) { 79 Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder()); 80 Handle<Code> code = GetCodeWithFlags(flags, name); 81 PROFILE(isolate(), CodeCreateEvent(CodeEventListener::HANDLER_TAG, 82 AbstractCode::cast(*code), *name)); 83 #ifdef DEBUG 84 code->VerifyEmbeddedObjects(); 85 #endif 86 return code; 87 } 88 89 90 #define __ ACCESS_MASM(masm()) 91 92 93 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg, 94 Handle<Name> name, 95 Label* miss, 96 ReturnHolder return_what) { 97 PrototypeCheckType check_type = SKIP_RECEIVER; 98 int function_index = map()->IsPrimitiveMap() 99 ? map()->GetConstructorFunctionIndex() 100 : Map::kNoConstructorFunctionIndex; 101 if (function_index != Map::kNoConstructorFunctionIndex) { 102 GenerateDirectLoadGlobalFunctionPrototype(masm(), function_index, 103 scratch1(), miss); 104 Object* function = isolate()->native_context()->get(function_index); 105 Object* prototype = JSFunction::cast(function)->instance_prototype(); 106 Handle<Map> map(JSObject::cast(prototype)->map()); 107 set_map(map); 108 object_reg = scratch1(); 109 check_type = CHECK_ALL_MAPS; 110 } 111 112 // Check that the maps starting from the prototype haven't changed. 113 return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name, 114 miss, check_type, return_what); 115 } 116 117 118 // Frontend for store uses the name register. It has to be restored before a 119 // miss. 120 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg, 121 Handle<Name> name, 122 Label* miss, 123 ReturnHolder return_what) { 124 return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name, 125 miss, SKIP_RECEIVER, return_what); 126 } 127 128 129 Register PropertyHandlerCompiler::Frontend(Handle<Name> name) { 130 Label miss; 131 if (IC::ICUseVector(kind())) { 132 PushVectorAndSlot(); 133 } 134 Register reg = FrontendHeader(receiver(), name, &miss, RETURN_HOLDER); 135 FrontendFooter(name, &miss); 136 // The footer consumes the vector and slot from the stack if miss occurs. 137 if (IC::ICUseVector(kind())) { 138 DiscardVectorAndSlot(); 139 } 140 return reg; 141 } 142 143 144 void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name, 145 Label* miss, 146 Register scratch1, 147 Register scratch2) { 148 Register holder_reg; 149 Handle<Map> last_map; 150 if (holder().is_null()) { 151 holder_reg = receiver(); 152 last_map = map(); 153 // If |type| has null as its prototype, |holder()| is 154 // Handle<JSObject>::null(). 155 DCHECK(last_map->prototype() == isolate()->heap()->null_value()); 156 } else { 157 last_map = handle(holder()->map()); 158 // This condition matches the branches below. 159 bool need_holder = 160 last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap(); 161 holder_reg = 162 FrontendHeader(receiver(), name, miss, 163 need_holder ? RETURN_HOLDER : DONT_RETURN_ANYTHING); 164 } 165 166 if (last_map->is_dictionary_map()) { 167 if (last_map->IsJSGlobalObjectMap()) { 168 Handle<JSGlobalObject> global = 169 holder().is_null() 170 ? Handle<JSGlobalObject>::cast(isolate()->global_object()) 171 : Handle<JSGlobalObject>::cast(holder()); 172 GenerateCheckPropertyCell(masm(), global, name, scratch1, miss); 173 } else { 174 if (!name->IsUniqueName()) { 175 DCHECK(name->IsString()); 176 name = factory()->InternalizeString(Handle<String>::cast(name)); 177 } 178 DCHECK(holder().is_null() || 179 holder()->property_dictionary()->FindEntry(name) == 180 NameDictionary::kNotFound); 181 GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1, 182 scratch2); 183 } 184 } 185 } 186 187 188 Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name, 189 FieldIndex field) { 190 Register reg = Frontend(name); 191 __ Move(receiver(), reg); 192 LoadFieldStub stub(isolate(), field); 193 GenerateTailCall(masm(), stub.GetCode()); 194 return GetCode(kind(), name); 195 } 196 197 198 Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name, 199 int constant_index) { 200 Register reg = Frontend(name); 201 __ Move(receiver(), reg); 202 LoadConstantStub stub(isolate(), constant_index); 203 GenerateTailCall(masm(), stub.GetCode()); 204 return GetCode(kind(), name); 205 } 206 207 208 Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent( 209 Handle<Name> name) { 210 Label miss; 211 if (IC::ICUseVector(kind())) { 212 DCHECK(kind() == Code::LOAD_IC); 213 PushVectorAndSlot(); 214 } 215 NonexistentFrontendHeader(name, &miss, scratch2(), scratch3()); 216 if (IC::ICUseVector(kind())) { 217 DiscardVectorAndSlot(); 218 } 219 GenerateLoadConstant(isolate()->factory()->undefined_value()); 220 FrontendFooter(name, &miss); 221 return GetCode(kind(), name); 222 } 223 224 225 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( 226 Handle<Name> name, Handle<AccessorInfo> callback) { 227 Register reg = Frontend(name); 228 if (FLAG_runtime_call_stats) { 229 TailCallBuiltin(masm(), Builtins::kLoadIC_Slow); 230 } else { 231 GenerateLoadCallback(reg, callback); 232 } 233 return GetCode(kind(), name); 234 } 235 236 237 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( 238 Handle<Name> name, const CallOptimization& call_optimization, 239 int accessor_index) { 240 DCHECK(call_optimization.is_simple_api_call()); 241 Register holder = Frontend(name); 242 if (FLAG_runtime_call_stats) { 243 TailCallBuiltin(masm(), Builtins::kLoadIC_Slow); 244 } else { 245 GenerateApiAccessorCall(masm(), call_optimization, map(), receiver(), 246 scratch2(), false, no_reg, holder, accessor_index); 247 } 248 return GetCode(kind(), name); 249 } 250 251 252 void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) { 253 if (IC::ICUseVector(kind())) { 254 if (holder_reg.is(receiver())) { 255 PushVectorAndSlot(); 256 } else { 257 DCHECK(holder_reg.is(scratch1())); 258 PushVectorAndSlot(scratch2(), scratch3()); 259 } 260 } 261 } 262 263 264 void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg, 265 PopMode mode) { 266 if (IC::ICUseVector(kind())) { 267 if (mode == DISCARD) { 268 DiscardVectorAndSlot(); 269 } else { 270 if (holder_reg.is(receiver())) { 271 PopVectorAndSlot(); 272 } else { 273 DCHECK(holder_reg.is(scratch1())); 274 PopVectorAndSlot(scratch2(), scratch3()); 275 } 276 } 277 } 278 } 279 280 281 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor( 282 LookupIterator* it) { 283 // So far the most popular follow ups for interceptor loads are DATA and 284 // AccessorInfo, so inline only them. Other cases may be added 285 // later. 286 bool inline_followup = false; 287 switch (it->state()) { 288 case LookupIterator::TRANSITION: 289 UNREACHABLE(); 290 case LookupIterator::ACCESS_CHECK: 291 case LookupIterator::INTERCEPTOR: 292 case LookupIterator::JSPROXY: 293 case LookupIterator::NOT_FOUND: 294 case LookupIterator::INTEGER_INDEXED_EXOTIC: 295 break; 296 case LookupIterator::DATA: 297 inline_followup = 298 it->property_details().type() == DATA && !it->is_dictionary_holder(); 299 break; 300 case LookupIterator::ACCESSOR: { 301 Handle<Object> accessors = it->GetAccessors(); 302 if (accessors->IsAccessorInfo()) { 303 Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors); 304 inline_followup = 305 info->getter() != NULL && 306 AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map()); 307 } else if (accessors->IsAccessorPair()) { 308 Handle<JSObject> property_holder(it->GetHolder<JSObject>()); 309 Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(), 310 isolate()); 311 if (!(getter->IsJSFunction() || getter->IsFunctionTemplateInfo())) { 312 break; 313 } 314 if (!property_holder->HasFastProperties()) break; 315 CallOptimization call_optimization(getter); 316 Handle<Map> receiver_map = map(); 317 inline_followup = call_optimization.is_simple_api_call() && 318 call_optimization.IsCompatibleReceiverMap( 319 receiver_map, property_holder); 320 } 321 } 322 } 323 324 Label miss; 325 InterceptorVectorSlotPush(receiver()); 326 bool lost_holder_register = false; 327 auto holder_orig = holder(); 328 // non masking interceptors must check the entire chain, so temporarily reset 329 // the holder to be that last element for the FrontendHeader call. 330 if (holder()->GetNamedInterceptor()->non_masking()) { 331 DCHECK(!inline_followup); 332 JSObject* last = *holder(); 333 PrototypeIterator iter(isolate(), last); 334 while (!iter.IsAtEnd()) { 335 lost_holder_register = true; 336 // Casting to JSObject is fine here. The LookupIterator makes sure to 337 // look behind non-masking interceptors during the original lookup, and 338 // we wouldn't try to compile a handler if there was a Proxy anywhere. 339 last = iter.GetCurrent<JSObject>(); 340 iter.Advance(); 341 } 342 auto last_handle = handle(last); 343 set_holder(last_handle); 344 } 345 Register reg = FrontendHeader(receiver(), it->name(), &miss, RETURN_HOLDER); 346 // Reset the holder so further calculations are correct. 347 set_holder(holder_orig); 348 if (lost_holder_register) { 349 if (*it->GetReceiver() == *holder()) { 350 reg = receiver(); 351 } else { 352 // Reload lost holder register. 353 auto cell = isolate()->factory()->NewWeakCell(holder()); 354 __ LoadWeakValue(reg, cell, &miss); 355 } 356 } 357 FrontendFooter(it->name(), &miss); 358 InterceptorVectorSlotPop(reg); 359 if (inline_followup) { 360 // TODO(368): Compile in the whole chain: all the interceptors in 361 // prototypes and ultimate answer. 362 GenerateLoadInterceptorWithFollowup(it, reg); 363 } else { 364 GenerateLoadInterceptor(reg); 365 } 366 return GetCode(kind(), it->name()); 367 } 368 369 void NamedLoadHandlerCompiler::GenerateLoadCallback( 370 Register reg, Handle<AccessorInfo> callback) { 371 DCHECK(receiver().is(ApiGetterDescriptor::ReceiverRegister())); 372 __ Move(ApiGetterDescriptor::HolderRegister(), reg); 373 // The callback is alive if this instruction is executed, 374 // so the weak cell is not cleared and points to data. 375 Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback); 376 __ GetWeakValue(ApiGetterDescriptor::CallbackRegister(), cell); 377 378 CallApiGetterStub stub(isolate()); 379 __ TailCallStub(&stub); 380 } 381 382 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor( 383 LookupIterator* it, Register interceptor_reg) { 384 Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>()); 385 386 Handle<Map> holder_map(holder()->map()); 387 set_map(holder_map); 388 set_holder(real_named_property_holder); 389 390 Label miss; 391 InterceptorVectorSlotPush(interceptor_reg); 392 Register reg = 393 FrontendHeader(interceptor_reg, it->name(), &miss, RETURN_HOLDER); 394 FrontendFooter(it->name(), &miss); 395 // We discard the vector and slot now because we don't miss below this point. 396 InterceptorVectorSlotPop(reg, DISCARD); 397 398 switch (it->state()) { 399 case LookupIterator::ACCESS_CHECK: 400 case LookupIterator::INTERCEPTOR: 401 case LookupIterator::JSPROXY: 402 case LookupIterator::NOT_FOUND: 403 case LookupIterator::INTEGER_INDEXED_EXOTIC: 404 case LookupIterator::TRANSITION: 405 UNREACHABLE(); 406 case LookupIterator::DATA: { 407 DCHECK_EQ(DATA, it->property_details().type()); 408 __ Move(receiver(), reg); 409 LoadFieldStub stub(isolate(), it->GetFieldIndex()); 410 GenerateTailCall(masm(), stub.GetCode()); 411 break; 412 } 413 case LookupIterator::ACCESSOR: 414 if (it->GetAccessors()->IsAccessorInfo()) { 415 Handle<AccessorInfo> info = 416 Handle<AccessorInfo>::cast(it->GetAccessors()); 417 DCHECK_NOT_NULL(info->getter()); 418 GenerateLoadCallback(reg, info); 419 } else { 420 Handle<Object> function = handle( 421 AccessorPair::cast(*it->GetAccessors())->getter(), isolate()); 422 CallOptimization call_optimization(function); 423 GenerateApiAccessorCall(masm(), call_optimization, holder_map, 424 receiver(), scratch2(), false, no_reg, reg, 425 it->GetAccessorIndex()); 426 } 427 } 428 } 429 430 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter( 431 Handle<Name> name, int accessor_index, int expected_arguments) { 432 Register holder = Frontend(name); 433 GenerateLoadViaGetter(masm(), map(), receiver(), holder, accessor_index, 434 expected_arguments, scratch2()); 435 return GetCode(kind(), name); 436 } 437 438 439 // TODO(verwaest): Cleanup. holder() is actually the receiver. 440 Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition( 441 Handle<Map> transition, Handle<Name> name) { 442 Label miss; 443 444 PushVectorAndSlot(); 445 446 // Check that we are allowed to write this. 447 bool is_nonexistent = holder()->map() == transition->GetBackPointer(); 448 if (is_nonexistent) { 449 // Find the top object. 450 Handle<JSObject> last; 451 PrototypeIterator::WhereToEnd end = 452 name->IsPrivate() ? PrototypeIterator::END_AT_NON_HIDDEN 453 : PrototypeIterator::END_AT_NULL; 454 PrototypeIterator iter(isolate(), holder(), kStartAtPrototype, end); 455 while (!iter.IsAtEnd()) { 456 last = PrototypeIterator::GetCurrent<JSObject>(iter); 457 iter.Advance(); 458 } 459 if (!last.is_null()) set_holder(last); 460 NonexistentFrontendHeader(name, &miss, scratch1(), scratch2()); 461 } else { 462 FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING); 463 DCHECK(holder()->HasFastProperties()); 464 } 465 466 int descriptor = transition->LastAdded(); 467 Handle<DescriptorArray> descriptors(transition->instance_descriptors()); 468 PropertyDetails details = descriptors->GetDetails(descriptor); 469 Representation representation = details.representation(); 470 DCHECK(!representation.IsNone()); 471 472 // Stub is never generated for objects that require access checks. 473 DCHECK(!transition->is_access_check_needed()); 474 475 // Call to respective StoreTransitionStub. 476 bool virtual_args = StoreTransitionHelper::HasVirtualSlotArg(); 477 Register map_reg = StoreTransitionHelper::MapRegister(); 478 479 if (details.type() == DATA_CONSTANT) { 480 DCHECK(descriptors->GetValue(descriptor)->IsJSFunction()); 481 Register tmp = 482 virtual_args ? VectorStoreICDescriptor::VectorRegister() : map_reg; 483 GenerateRestoreMap(transition, tmp, scratch2(), &miss); 484 GenerateConstantCheck(tmp, descriptor, value(), scratch2(), &miss); 485 if (virtual_args) { 486 // This will move the map from tmp into map_reg. 487 RearrangeVectorAndSlot(tmp, map_reg); 488 } else { 489 PopVectorAndSlot(); 490 } 491 GenerateRestoreName(name); 492 StoreTransitionStub stub(isolate()); 493 GenerateTailCall(masm(), stub.GetCode()); 494 495 } else { 496 if (representation.IsHeapObject()) { 497 GenerateFieldTypeChecks(descriptors->GetFieldType(descriptor), value(), 498 &miss); 499 } 500 StoreTransitionStub::StoreMode store_mode = 501 Map::cast(transition->GetBackPointer())->unused_property_fields() == 0 502 ? StoreTransitionStub::ExtendStorageAndStoreMapAndValue 503 : StoreTransitionStub::StoreMapAndValue; 504 505 Register tmp = 506 virtual_args ? VectorStoreICDescriptor::VectorRegister() : map_reg; 507 GenerateRestoreMap(transition, tmp, scratch2(), &miss); 508 if (virtual_args) { 509 RearrangeVectorAndSlot(tmp, map_reg); 510 } else { 511 PopVectorAndSlot(); 512 } 513 GenerateRestoreName(name); 514 StoreTransitionStub stub(isolate(), 515 FieldIndex::ForDescriptor(*transition, descriptor), 516 representation, store_mode); 517 GenerateTailCall(masm(), stub.GetCode()); 518 } 519 520 GenerateRestoreName(&miss, name); 521 PopVectorAndSlot(); 522 TailCallBuiltin(masm(), MissBuiltin(kind())); 523 524 return GetCode(kind(), name); 525 } 526 527 bool NamedStoreHandlerCompiler::RequiresFieldTypeChecks( 528 FieldType* field_type) const { 529 return field_type->IsClass(); 530 } 531 532 533 Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) { 534 Label miss; 535 DCHECK(it->representation().IsHeapObject()); 536 537 FieldType* field_type = *it->GetFieldType(); 538 bool need_save_restore = false; 539 if (RequiresFieldTypeChecks(field_type)) { 540 need_save_restore = IC::ICUseVector(kind()); 541 if (need_save_restore) PushVectorAndSlot(); 542 GenerateFieldTypeChecks(field_type, value(), &miss); 543 if (need_save_restore) PopVectorAndSlot(); 544 } 545 546 StoreFieldStub stub(isolate(), it->GetFieldIndex(), it->representation()); 547 GenerateTailCall(masm(), stub.GetCode()); 548 549 __ bind(&miss); 550 if (need_save_restore) PopVectorAndSlot(); 551 TailCallBuiltin(masm(), MissBuiltin(kind())); 552 return GetCode(kind(), it->name()); 553 } 554 555 556 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter( 557 Handle<JSObject> object, Handle<Name> name, int accessor_index, 558 int expected_arguments) { 559 Register holder = Frontend(name); 560 GenerateStoreViaSetter(masm(), map(), receiver(), holder, accessor_index, 561 expected_arguments, scratch2()); 562 563 return GetCode(kind(), name); 564 } 565 566 567 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( 568 Handle<JSObject> object, Handle<Name> name, 569 const CallOptimization& call_optimization, int accessor_index) { 570 Register holder = Frontend(name); 571 if (FLAG_runtime_call_stats) { 572 GenerateRestoreName(name); 573 TailCallBuiltin(masm(), Builtins::kStoreIC_Slow); 574 } else { 575 GenerateApiAccessorCall(masm(), call_optimization, handle(object->map()), 576 receiver(), scratch2(), true, value(), holder, 577 accessor_index); 578 } 579 return GetCode(kind(), name); 580 } 581 582 583 #undef __ 584 585 void ElementHandlerCompiler::CompileElementHandlers( 586 MapHandleList* receiver_maps, CodeHandleList* handlers) { 587 for (int i = 0; i < receiver_maps->length(); ++i) { 588 Handle<Map> receiver_map = receiver_maps->at(i); 589 Handle<Code> cached_stub; 590 591 if (receiver_map->IsStringMap()) { 592 cached_stub = LoadIndexedStringStub(isolate()).GetCode(); 593 } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) { 594 cached_stub = isolate()->builtins()->KeyedLoadIC_Slow(); 595 } else { 596 bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; 597 ElementsKind elements_kind = receiver_map->elements_kind(); 598 599 // No need to check for an elements-free prototype chain here, the 600 // generated stub code needs to check that dynamically anyway. 601 bool convert_hole_to_undefined = 602 (is_js_array && elements_kind == FAST_HOLEY_ELEMENTS && 603 *receiver_map == isolate()->get_initial_js_array_map(elements_kind)); 604 605 if (receiver_map->has_indexed_interceptor() && 606 !receiver_map->GetIndexedInterceptor()->getter()->IsUndefined( 607 isolate()) && 608 !receiver_map->GetIndexedInterceptor()->non_masking()) { 609 cached_stub = LoadIndexedInterceptorStub(isolate()).GetCode(); 610 } else if (IsSloppyArgumentsElements(elements_kind)) { 611 cached_stub = KeyedLoadSloppyArgumentsStub(isolate()).GetCode(); 612 } else if (IsFastElementsKind(elements_kind) || 613 IsFixedTypedArrayElementsKind(elements_kind)) { 614 cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind, 615 convert_hole_to_undefined).GetCode(); 616 } else { 617 DCHECK(elements_kind == DICTIONARY_ELEMENTS); 618 cached_stub = LoadDictionaryElementStub(isolate()).GetCode(); 619 } 620 } 621 622 handlers->Add(cached_stub); 623 } 624 } 625 } // namespace internal 626 } // namespace v8 627