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