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